Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

11
app/interface/main/player/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
.idea/*
.project
.buildpath
.externalToolBuilders
.settings
.DS_Store
.a
*.iml
.vscode/*
player
cmd/cmd

View File

@@ -0,0 +1,21 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/main/player/cmd:all-srcs",
"//app/interface/main/player/conf:all-srcs",
"//app/interface/main/player/dao:all-srcs",
"//app/interface/main/player/http:all-srcs",
"//app/interface/main/player/model:all-srcs",
"//app/interface/main/player/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,444 @@
### 播放器聚合接口
#### Version 2.31.1
> 2.player tag icon.
#### Version 2.31.0
> 2.player add view points.
#### Version 2.30.1
> 2.playurl auto qn
#### Version 2.30.0
> 2.player special for bnj aids
#### Version 2.29.1
> 2.ugc pay no default video.
#### Version 2.29.0
> 1.no video shot for ugc pay archive
> 2.ugc playurl v3
#### Version 2.28.1
##### Features
> 1.fix icon empty data.
#### Version 2.28.0
##### Features
> 1.ip 查询改为调用location rpc 方法
#### Version 2.27.0
##### Features
> 1.新版弹幕蒙板
#### Version 2.26.0
##### Features
> 1.arc acc 切 grpc
#### Version 2.25.3
##### Features
> 1.player icon tick change
#### Version 2.25.2
##### Features
> 1.playurl 接口dash字段
#### Version 2.25.1
##### Features
> 1.player.so 接口新增icon
#### Version 2.25.0
##### Features
> 1.player.so 接口新增subtitle
#### Version 2.24.1
##### Features
> 1.支持h5高清晰度
#### Version 2.24.0
##### Features
> 1.playurl player=1 特殊签名
#### Version 2.23.1
##### Features
> 1.log 优化
#### Version 2.23.0
##### Features
> 1.playurl 支持html5访问
> 2.playurl 新增session参数支持
#### Version 2.22.2
##### Features
> 1.playurl qn 登陆限制
#### Version 2.22.1
##### Features
> 1.改用metadata.remoteIP
#### Version 2.22.0
##### Features
> 1.playurl 新增internal接口
#### Version 2.21.1
##### Features
> 1.playurl 接口新增参数
#### Version 2.21.0
##### Features
> 1.identify 切换到 auth
#### Version 2.20.1
##### Bug Fixes
> 1.playurl mid 判断
#### Version 2.20.0
##### Features
> 1.新增接口playurl.
#### Version 2.19.0
##### Features
> 1.change conf.
#### Version 2.18.1
##### Features
> 1.use bm
#### Version 2.18.0
##### Features
> 1.update infoc sdk
#### Version 2.17.0
##### Features
> 1.player接口新增弹幕蒙板数据
#### Version 2.16.2
##### Features
> 1.carousel新增id返回
#### Version 2.16.1
##### Features
> 1.移除player db 依赖
#### Version 2.16.0
##### Features
> 1.使用discovery接入archive
> 2.使用discovery接入account
> 3.使用discovery接入resource
#### Version 2.15.1
##### Features
> 1.official兼容
#### Version 2.15.0
##### Features
> 1.player 迁移到main目录
#### Version 2.14.3
##### Features
> 1.使用account-service v7
#### Version 2.14.2
##### Features
> 1.history服务迁移目录修改该服务的包引用路径
#### Version 2.14.1
##### Features
> 1.player is_admin字段去除rank判断.
#### Version 2.14.0
##### Features
> 1.player 依赖 Profile 接口换成 UserInfo
> 2.请求协管加白名单
#### Version 2.13.1
##### Bug Fixes
> 1.playurl/token 接口新增base64 token 字段
#### Version 2.13.0
##### Bug Fixes
> 1.新增 playurl/token 接口
#### Version 2.12.1
##### Bug Fixes
> 1.修改player.so router
#### Version 2.12.0
##### Features
> 1.接入bm
#### Version 2.11.0
##### Features
> 1.carousel 对接 resource-service
#### Version 2.10.1
##### Features
> 1.修改视频在线人数接口返回结构
#### Version 2.10.0
##### Features
> 1.接口添加视频在线人数
> 2.改errgroup调用
#### Version 2.9.0
##### Features
> 1.添加infoc上报
#### Version 2.8.0
##### Features
> 1.all rpc archive2 to archive3
#### Version 2.7.2
##### Features
> 1.所有 依赖RPC view2 换成 view3
> 2.配合基础库修改http client 方法
#### Version 2.7.1
##### Features
> 1.player 依赖RPC 接口换成 view3
#### Version 2.7.0
##### Features
> 1.添加videoShot接口
#### Version 2.6.2
##### Features
> 1.player添加block_time字段
#### Version 2.6.1
##### Features
> 1.player去除register
#### Version 2.6.0
##### Features
> 1.合并大仓库
#### Version 2.5.1
##### Features
> 1.player接口返回是否有下一p标志
#### Version 2.5.0
##### Features
> 1.添加多P视频自动播放pagelist接口
##### Bug Fixes
> 2.协管角色字段fix
#### Version 2.4.0
##### Features
> 1.player接口添加协管角色字段
#### Version 2.3.2
##### Bug Fixes
> 1.player 添加播放器参数fix
#### Version 2.3.1
##### Features
> 1.prom 修改
#### Version 2.3.0
##### Features
> 1.接入prom
> 2.去除冗余代码
#### Version 2.2.0
##### Features
> 1.player接口修改依赖rpc方法
#### Version 2.1.2
##### Features
> 1.player 返回播放器配置
#### Version 2.1.1
##### Features
> 1.player 添加播放器配置
> 1.player 只下放当前分p进度
#### Version 2.1.0
##### Features
> 1.player 还原版本
#### Version 2.0.4
##### Features
> 1.player cid没找到返回兼容数据
#### Version 2.0.3
##### Features
> 1.player 番剧特殊处理
#### Version 2.0.2
##### Features
> 1.player 添加refer log
#### Version 2.0.1
##### Features
> 1.player 接口cid添加判断
#### Version 2.0.0
##### Features
> 1.player 接口修改依赖rpc方法
#### Version 1.9.0
##### Features
> 1.player 接口log添加refer
#### Version 1.4.5
##### Features
> 1.修改chan长度
#### Version 1.4.4
##### Features
> 1.player接口下放播放进度
#### Version 1.4.3
##### Features
> 1.player接口优先去除添加播放历史
#### Version 1.4.2
##### Features
> 1.player接口获取视频时长增加判断
#### Version 1.4.1
##### Features
> 1.player接口返回播放进度
> 2.去除player接口添加播放历史
#### Version 1.4.0
##### Features
> 1.升级vendor
> 2.升级新的配置中心
#### Version 1.3.0
##### Features
> 1.go-business 升级
> 2.添加monitorPing方法
#### Version 1.2.6
##### Features
> 1.carousel 跨域修复
#### Version 1.2.5
##### Features
> 1.添加增加crc加密的user_hash字段
#### Version 1.2.4
##### Features
> 1.添加历史refer判断增加白名单User-Agent
#### Version 1.2.3
##### Bug Fixes
> 1.修复carousel content格式
#### Version 1.2.2
##### Features
> 1.升级go business
#### Version 1.2.1
##### Bug Fixes
> 1.修复播放器轮播图排序问题
#### Version 1.2.0
##### Features
> 1.更新vendor
#### Version 1.1.11
##### Features
> 1.添加播放历史refer校验添加static域名
#### Version 1.1.10
##### Features
> 1.添加播放历史新增refer校验
> 2.player返回结果dm_hostface http://改成 // 自适应
#### Version 1.1.9
##### Bug Fixes
> 1.修复pastView,matView初始化为nil
#### Version 1.1.8
##### Features
> 1.缓存拜年祭视频
> 2.优化接入配置中心
#### Version 1.1.7
##### Features
> 1.添加wss节点
#### Version 1.1.6
##### Bug Fixes
> 1.修复dm_index slice越界 panic错误
#### Version 1.1.5
##### Features
> 1.增加对deault_dm dm_host灰度量控制
#### Version 1.1.4
##### Features
> 1.去除多余节点
#### Version 1.1.3
##### Features
> 1.修改template dm_host
#### Version 1.1.2
##### Features
> 1.增加拜年祭接口,包括viewPage,隐藏分p,隐藏分p开关
> 1.调整参数错误返回
> 2.更新vendor
> 3.去除permission9999禁言
> 4.历史记录url改成v2
#### Version 1.1.1
##### Bug Fixes
> 1.修复policy map排序bug
#### Version 1.1.0
##### Features
> 1.升级vendor
#### Version 1.0.9
##### Features
> 1.增加播放器配置策略接口
> 2.新增广播节点
#### Version 1.0.8
##### Features
> 1.参数验证错误或者出现err直接到not found页面
> 2.修改coins解析,更新go-business
> 3.click改成站内站外数量合
#### Version 1.0.7
##### Features
> 1.cache__dispatch不做逻辑处理,只返回空
> 2.block_time直接干掉
> 3.返回头添加X-Remote-IP
> 4,增加节点zoneip
> 5.升级基础库
#### Version 1.0.6
##### Features
> 1.修复money科学计数法显示
> 2.影视区电视剧分区三级分区纳入bottom=1
> 3.lastplaytime暂时回传0
#### Version 1.0.5
> 1.修复permission
##### Version 1.0.4
> 1.修复解析名字err错误判断
#### Version 1.0.3
##### Features
> 1.增加错误日志,解析错误等
#### Version 1.0.2
##### Features
> 1.player接口增加节点,修改allow_bp为bool
#### Version 1.0.1
##### Features
> 1.player接口增加广播节点
#### Version 1.0.0
##### Features
> 1.初始化项目

View File

@@ -0,0 +1,11 @@
# Owner
liweijia
zhapuyu
# Author
wuhao02
# Reviewer
zhapuyu
guanyanliang
liweijia

View File

@@ -0,0 +1,17 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- liweijia
- wuhao02
- zhapuyu
labels:
- interface
- interface/main/player
- main
options:
no_parent_owners: true
reviewers:
- guanyanliang
- liweijia
- wuhao02
- zhapuyu

View File

@@ -0,0 +1,18 @@
#### player-interface
##### 项目简介
> 1.给播放器提供接口
##### 编译环境
> 请使用golang v1.7.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common
##### 编译执行
> 在主目录执行go build。
> 编译后可执行 ./player -conf player-test.toml 使用项目本地配置文件启动服务。
> 也可执行 ./player -conf_appid=player-interface -conf_version=v2.1.0 -conf_host=172.16.33.134:9011 -conf_path=/data/conf/player-interface -conf_env=10 -conf_token=SEHXM8x1vYhIUaZvQUmyWnMYJrF9jHJY 使用配置中心测试环境配置启动服务如无法启动可检查token是否正确。
##### 特别说明
> http接口文档可参考 http://info.bilibili.co/pages/viewpage.action?pageId=3679441

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = [
"player-example.toml",
"player-test.toml",
],
importpath = "go-common/app/interface/main/player/cmd",
tags = ["automanaged"],
deps = [
"//app/interface/main/player/conf:go_default_library",
"//app/interface/main/player/http:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,48 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/interface/main/player/conf"
"go-common/app/interface/main/player/http"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
log.Init(conf.Conf.XLog)
defer log.Close()
log.Info("play-interface start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
// ecode
ecode.Init(conf.Conf.Ecode)
// service init
http.Init(conf.Conf)
// monitor
// signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("play-interface get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("play-interface exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,172 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
pid = "/tmp/player.pid"
dir = "./"
perf = "127.0.0.1:6260"
checkFile = "/data/www/player-interface.html"
family = "player-interface"
address = "172.16.33.56"
env = "test"
tick = "5m"
[Broadcast]
tcpAddr = "172.16.0.238:4080"
wsAddr = "172.16.0.238:4090"
wssAddr = "chat2.bilibili.com:4095"
begin = "2016-10-31 00:00:00"
end = "2017-01-20 23:59:59"
[policy]
id=1
des="D区广告方案新版灰度"
type="用户随机-尾号"
start="2016-12-06 00:00:00"
end="2017-12-01 00:00:00"
mtime="2016-12-07 15:09:00"
[[pitem]]
Id=1
Data="new"
Comment="新样式"
ExtData="00-04"
[[pitem]]
Id=2
Data="old"
Comment="旧样式"
ExtData="05-99"
[[pitem]]
Id=3
Data="old"
Comment="默认/未登录"
ExtData="default"
[matsuri]
pastID = 5461206
matID = 5461533
matTime = "2016-12-15T16:57:03+08:00"
tick = "10s"
[identify]
whiteAccessKey = ""
whiteMid = 0
[identify.app]
key = "6a29f8ed87407c11"
secret = "d3c5a85f5b895a03735b5d20a273bc57"
[identify.memcache]
name = "go-business/identify"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.com"
secret = "http://open.bilibili.com"
[identify.authHTTPClient]
dial = "1s"
timeout = "1s"
keepAlive = "60s"
timer = 1000
[identify.secretHTTPClient]
dial = "1s"
timeout = "1s"
keepAlive = "60s"
timer = 1000
[player]
IPFile = "/data/conf/iprepo/iprepo.txt"
[host]
apiCo = "http://api.bilibili.co"
[xlog]
dir = "/data/log/player-interface/"
[ecode]
service = "player-interface"
[ecode.mysql]
name = "[player-interface]tcp@172.16.33.54:3306"
dsn = "test:test@tcp(172.16.33.54:3306)/bilibili_apm?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 5
[tracer]
proto = "tcp"
addr = "172.16.33.46:5140"
tag = "platform/player-inferface"
[app]
key = "3c4e41f926e51656"
secret = "26a2095b60c24154521d24ae62b885bb"
[httpClient]
dial = "500ms"
timeout = "500ms"
keepAlive = "60s"
timer = 1000
[multiHttp]
[multiHttp.outer]
addrs = ["0.0.0.0:6261"]
maxListen = 1000
[multiHttp.inner]
addrs = ["0.0.0.0:6262"]
maxListen = 1000
[multiHttp.local]
addrs = ["0.0.0.0:6263"]
maxListen = 1000
[mysql]
[mysql.player]
addr = "172.16.33.54"
dsn = "test:test@tcp(172.16.33.54:3306)/bilibili_player?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
[mysql.dede]
addr = "172.16.0.5"
dsn = "bilibili:5Fq2M4FbPZK4fhtE@tcp(172.16.0.5:3306)/bilibili?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
[accountRPC]
pullInterval = "10s"
[accountRPC.client]
proto = "tcp"
addr = "172.16.33.56:6079"
timeout = "1s"
timer = 1000
[[accountRPC.backup]]
proto = "tcp"
addr = "172.16.33.56:6079"
timeout = "1s"
timer = 1000
[accountRPC.zookeeper]
root = "/microservice/account-service/"
addrs = ["172.16.33.54:2181"]
timeout = "30s"
[archiveRPC]
pullInterval = "10s"
[archiveRPC.client]
proto = "tcp"
addr = "172.16.33.56:6089"
timeout = "1s"
timer = 1000
[[archiveRPC.backup]]
proto = "tcp"
addr = "172.16.33.56:6089"
timeout = "1s"
timer = 1000
[archiveRPC.zookeeper]
root = "/microservice/archive-service/"
addrs = ["172.16.33.54:2181"]
timeout = "30s"

View File

@@ -0,0 +1,131 @@
[tick]
carouselTick = "5m"
paramTick = "5m"
iconTick = "1m"
[rule]
vsTimeout = "200ms"
noAssistMid = 0
tokenQn = [116,112,74]
vipQn = [116,112,74]
loginQn = 32
maxFreeQn = 80
playurlGray = 10
[ugcPay]
aid = 10110688
cid = 10134389
[Broadcast]
tcpAddr = "172.16.0.238:4080"
wsAddr = "172.16.0.238:4090"
wssAddr = "chat2.bilibili.com:4095"
begin = "2016-10-31 00:00:00"
end = "2017-01-20 23:59:59"
[policy]
id=1
des="D区广告方案新版灰度"
type="用户随机-尾号"
start="2016-12-06 00:00:00"
end="2017-12-01 00:00:00"
mtime="2016-12-07 15:09:00"
[[pitem]]
Id=1
Data="new"
Comment="新样式"
ExtData="00-04"
[[pitem]]
Id=2
Data="old"
Comment="旧样式"
ExtData="05-99"
[[pitem]]
Id=3
Data="old"
Comment="默认/未登录"
ExtData="default"
[matsuri]
pastID = 5461206
matID = 5461533
matTime = "2016-12-15T16:57:03+08:00"
tick = "10s"
[player]
IPFile = "/data/conf/iprepo/iprepo.txt"
[host]
apiCo = "http://api.bilibili.co"
accCo = "http://account.bilibili.co"
playurlCo = "http://videodispatch-ugc.bilibili.co"
[xlog]
dir = "/data/log/player-interface/"
[httpClient]
key = "3c4e41f926e51656"
secret = "26a2095b60c24154521d24ae62b885bb"
dial = "500ms"
timeout = "500ms"
keepAlive = "60s"
timer = 1000
[httpClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[bm]
[bm.outer]
addr = "0.0.0.0:6261"
timeout = "1s"
[bm.local]
addr = "0.0.0.0:6263"
timeout = "1s"
[mysql]
[mysql.player]
addr = "172.16.33.54"
dsn = "test:test@tcp(172.16.33.54:3306)/bilibili_player?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
idleTimeout ="4h"
queryTimeout = "100ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[mysql.player.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[mysql.show]
addr = "172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_show?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
idleTimeout ="4h"
queryTimeout = "100ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[mysql.show.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[infoc2]
taskID = "000078"
proto = "tcp"
addr = "172.19.100.20:5401"
chanSize = 1024
[playURLToken]
secret = "iSqObdc9EoGijd2i"
playerToken = "150362b0b3bb145b9377fb49e1063241"

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/interface/main/player/conf",
tags = ["automanaged"],
deps = [
"//app/interface/main/player/model:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/rpc/warden:go_default_library",
"//library/net/trace:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,212 @@
package conf
import (
"errors"
"flag"
xtime "time"
"go-common/app/interface/main/player/model"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/log/infoc"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc"
"go-common/library/net/rpc/warden"
"go-common/library/net/trace"
"go-common/library/time"
"github.com/BurntSushi/toml"
)
// global var
var (
confPath string
client *conf.Client
Conf = &Config{}
)
// Config is service conf.
type Config struct {
// 广播
Broadcast Broadcast
// policy
Policy *model.Policy
// policy items
Pitem []*model.Pitem
// 拜年祭
Matsuri Matsuri
// XLog
XLog *log.Config
// ecode
Ecode *ecode.Config
// host
Host *Host
// tracer
Tracer *trace.Config
// auth
Auth *auth.Config
// verify
Verify *verify.Config
// bm
BM *HTTPServers
// mysql
MySQL *MySQL
// rpc
ArchiveRPC *rpc.ClientConfig
AccountRPC *rpc.ClientConfig
HistoryRPC *rpc.ClientConfig
AssistRPC *rpc.ClientConfig
ResourceRPC *rpc.ClientConfig
Dm2RPC *rpc.ClientConfig
LocRPC *rpc.ClientConfig
TagRPC *rpc.ClientConfig
// HTTPClient
HTTPClient *bm.ClientConfig
// Rule
Rule *Rule
Tick *Tick
// Infoc2
Infoc2 *infoc.Config
// PlayURLToken
PlayURLToken *PlayURLToken
// grpc client
AccClient *warden.ClientConfig
ArcClient *warden.ClientConfig
UGCPayClient *warden.ClientConfig
// icon
Icon *icon
// bnj2019
Bnj2019 *bnj2019
}
// Tick tick time.
type Tick struct {
// tick time
CarouselTick time.Duration
ParamTick time.Duration
IconTick time.Duration
}
// Rule rules
type Rule struct {
// timeout
VsTimeout time.Duration
NoAssistMid int64
VipQn []int
LoginQn int
MaxFreeQn int
AutoQn int
PlayurlGray int64
}
// Host is host info
type Host struct {
APICo string
AccCo string
PlayurlCo string
H5Playurl string
HighPlayurl string
}
// MySQL mysql.
type MySQL struct {
Show *sql.Config
}
// Broadcast breadcast.
type Broadcast struct {
TCPAddr string
WsAddr string
WssAddr string
Begin string
End string
}
// Matsuri matsuri.
type Matsuri struct {
PastID int64
MatID int64
MatTime string
Tick time.Duration
}
// PlayURLToken playurl auth token.
type PlayURLToken struct {
Secret string
PlayerToken string
}
// HTTPServers bm servers config.
type HTTPServers struct {
Outer *bm.ServerConfig
}
type bnj2019 struct {
BnjMainAid int64
BnjListAids []int64
BnjTick time.Duration
}
type icon struct {
Start xtime.Time
End xtime.Time
URL1 string
Hash1 string
URL2 string
Hash2 string
}
func init() {
flag.StringVar(&confPath, "conf", "", "config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,69 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"account_test.go",
"archive_test.go",
"creative_test.go",
"dao_test.go",
"mysql_test.go",
"online_test.go",
"playurl_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/player/conf:go_default_library",
"//app/interface/main/player/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"account.go",
"archive.go",
"creative.go",
"dao.go",
"mysql.go",
"online.go",
"playurl.go",
],
importpath = "go-common/app/interface/main/player/dao",
tags = ["automanaged"],
deps = [
"//app/interface/main/player/conf:go_default_library",
"//app/interface/main/player/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/metadata:go_default_library",
"//library/stat/prom:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,38 @@
package dao
import (
"context"
"net/url"
"strconv"
"go-common/app/interface/main/player/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
const _blockTimeURI = "/api/member/getBlockAndMoralStatus"
// BlockTime get user block time from account by mid
func (d *Dao) BlockTime(c context.Context, mid int64) (res *model.BlockTime, err error) {
var (
params = url.Values{}
ip = metadata.String(c, metadata.RemoteIP)
)
params.Set("mid", strconv.FormatInt(mid, 10))
var rs struct {
Code int `json:"code"`
Data *model.BlockTime `json:"data"`
}
if err = d.client.Get(c, d.blockTimeURL, ip, params, &rs); err != nil {
log.Error("d.client.Get(%s,%d) error(%v)", d.blockTimeURL, mid, err)
return
}
if rs.Code != ecode.OK.Code() {
log.Error("d.client.Get(%s,%d) error code(%d)", d.blockTimeURL, mid, rs.Code)
err = ecode.Int(rs.Code)
return
}
res = rs.Data
return
}

View File

@@ -0,0 +1,18 @@
package dao
import (
"context"
"testing"
"go-common/app/interface/main/player/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDao_BlockTime(t *testing.T) {
convey.Convey("error should be nill", t, func(ctx convey.C) {
res, err := d.BlockTime(context.Background(), 88889069)
ctx.So(err, convey.ShouldBeNil)
ctx.So(res, convey.ShouldHaveSameTypeAs, &model.BlockTime{})
})
}

View File

@@ -0,0 +1,38 @@
package dao
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/pkg/errors"
)
// PvData get binary data from pvdata url
func (d *Dao) PvData(c context.Context, pvURL string) (res []byte, err error) {
var (
req *http.Request
resp *http.Response
cancel func()
)
if req, err = http.NewRequest("GET", pvURL, nil); err != nil {
err = errors.Wrapf(err, "PvData http.NewRequest(%s)", pvURL)
return
}
c, cancel = context.WithTimeout(c, time.Duration(d.c.Rule.VsTimeout))
defer cancel()
req = req.WithContext(c)
if resp, err = d.vsClient.Do(req); err != nil {
err = errors.Wrapf(err, "httpClient.Do(%s)", pvURL)
return
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
err = fmt.Errorf("PvData url(%s) resp.StatusCode(%v)", pvURL, resp.StatusCode)
return
}
res, err = ioutil.ReadAll(resp.Body)
return
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"bytes"
"context"
"encoding/binary"
"io"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDao_PvData(t *testing.T) {
convey.Convey("test pvdata", t, func(ctx convey.C) {
url := "http://i3.hdslb.com/bfs/videoshot/10135146.bin?vsign=5d5c80b9c583b1ce49f0fb8ab8dd178568efa66c&ver=108653418"
res, err := d.PvData(context.Background(), url)
ctx.So(err, convey.ShouldBeNil)
var (
v uint16
pvs []uint16
buf = bytes.NewReader(res)
)
for {
if err = binary.Read(buf, binary.BigEndian, &v); err != nil {
if err != io.EOF {
ctx.Printf("binary.Read err(%v)", err)
}
err = nil
break
}
pvs = append(pvs, v)
}
ctx.So(err, convey.ShouldBeNil)
ctx.Printf("%+v", pvs)
})
}

View File

@@ -0,0 +1,38 @@
package dao
import (
"context"
"net/url"
"strconv"
"go-common/app/interface/main/player/model"
"go-common/library/ecode"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
const _viewPointsURI = "/x/internal/creative/video/viewpoints"
// ViewPoints get view points data from creative.
func (d *Dao) ViewPoints(c context.Context, aid, cid int64) (points []*model.Points, err error) {
params := url.Values{}
params.Set("aid", strconv.FormatInt(aid, 10))
params.Set("cid", strconv.FormatInt(cid, 10))
var res struct {
Code int `json:"code"`
Data struct {
Points []*model.Points `json:"points"`
} `json:"data"`
}
ip := metadata.String(c, metadata.RemoteIP)
if err = d.client.Get(c, d.viewPointsURL, ip, params, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), d.viewPointsURL+"?"+params.Encode())
return
}
points = res.Data.Points
return
}

View File

@@ -0,0 +1,26 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoViewPoints(t *testing.T) {
convey.Convey("ViewPoints", t, func(ctx convey.C) {
var (
c = context.Background()
aid = int64(10110670)
cid = int64(10134319)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
points, err := d.ViewPoints(c, aid, cid)
ctx.Convey("Then err should be nil.points should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(points, convey.ShouldNotBeNil)
ctx.Printf("%+v", points)
})
})
})
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"context"
"net/http"
"go-common/app/interface/main/player/conf"
"go-common/library/database/sql"
"go-common/library/log"
xhttp "go-common/library/net/http/blademaster"
"go-common/library/stat/prom"
)
// Dao dao.
type Dao struct {
// config
c *conf.Config
// mysql
showDB *sql.DB
// stmt
paramStmt *sql.Stmt
// client
client *xhttp.Client
vsClient *http.Client
// API URL
blockTimeURL string
onlineCountURL string
viewPointsURL string
}
// New return new dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
showDB: sql.NewMySQL(c.MySQL.Show),
client: xhttp.NewClient(c.HTTPClient),
vsClient: http.DefaultClient,
}
d.paramStmt = d.showDB.Prepared(_param)
d.blockTimeURL = c.Host.AccCo + _blockTimeURI
d.onlineCountURL = c.Host.APICo + _onlineCountURI
d.viewPointsURL = c.Host.APICo + _viewPointsURI
return
}
// Ping check service health
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.showDB.Ping(c); err != nil {
log.Error("d.show.Ping() err(%v)", err)
}
return
}
// PromError stat and log.
func PromError(name string, format string, args ...interface{}) {
prom.BusinessErrCount.Incr(name)
log.Error(format, args...)
}

View File

@@ -0,0 +1,45 @@
package dao
import (
"flag"
"os"
"strings"
"testing"
"gopkg.in/h2non/gock.v1"
"go-common/app/interface/main/player/conf"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "local" {
flag.Set("app_id", "main.web-svr.player-interface")
flag.Set("conf_token", "419a7d1d51888c17a9dbdc0abb61cee8")
flag.Set("tree_id", "2285")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/player-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
d.client.SetTransport(gock.DefaultTransport)
os.Exit(m.Run())
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"context"
"go-common/app/interface/main/player/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_param = "SELECT `name`,`value` FROM `param` WHERE `plat` = 9 AND `state` = 0"
)
// Param return player setting params.
func (d *Dao) Param(c context.Context) (param []*model.Param, err error) {
var (
rows *sql.Rows
pa *model.Param
)
if rows, err = d.paramStmt.Query(c); err != nil {
log.Error("d.paramStmt.Query() error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
pa = &model.Param{}
if err = rows.Scan(&pa.Name, &pa.Value); err != nil {
log.Error("rows.Scan() error(%v)", err)
return
}
param = append(param, pa)
}
return
}

View File

@@ -0,0 +1,25 @@
package dao
import (
"context"
"testing"
"time"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoParam(t *testing.T) {
convey.Convey("Param", t, func(ctx convey.C) {
var (
c = context.Background()
)
time.Sleep(time.Second)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
param, err := d.Param(c)
ctx.Convey("Then err should be nil.param should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(param, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,42 @@
package dao
import (
"context"
"net/url"
"strconv"
"go-common/library/ecode"
"go-common/library/net/metadata"
)
const _onlineCountURI = "/x/internal/chat/num/ol"
// OnlineCount get online count by aid and cid
func (d *Dao) OnlineCount(c context.Context, aid, cid int64) (count int64, err error) {
var (
params = url.Values{}
ip = metadata.String(c, metadata.RemoteIP)
)
params.Set("aid", strconv.FormatInt(aid, 10))
params.Set("cids", strconv.FormatInt(cid, 10))
var res struct {
Code int `json:"code"`
Data []struct {
Cid int64 `json:"cid"`
Count int64 `json:"count"`
} `json:"data"`
}
if err = d.client.Get(c, d.onlineCountURL, ip, params, &res); err != nil {
PromError("OnlineNum接口错误", "d.client.Get(%s) error(%v)", d.onlineCountURL, err)
return
}
if res.Code != ecode.OK.Code() {
PromError("OnlineNum接口Code错误", "d.client.Get(%s) code(%d) error", d.onlineCountURL, res.Code)
return
}
if len(res.Data) == 0 || res.Data[0].Cid != cid {
PromError("OnlineNum接口数据错误", "d.client.Get(%s) data(%v) error", d.onlineCountURL, res.Data)
}
count = res.Data[0].Count
return
}

View File

@@ -0,0 +1,18 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDao_OnlineCount(t *testing.T) {
convey.Convey("test online num", t, func(ctx convey.C) {
aid := int64(17592153)
cid := int64(28727160)
data, err := d.OnlineCount(context.Background(), aid, cid)
ctx.So(err, convey.ShouldBeNil)
ctx.Println(data)
})
}

View File

@@ -0,0 +1,80 @@
package dao
import (
"context"
"net/http"
"net/url"
"strconv"
"go-common/app/interface/main/player/model"
"go-common/library/ecode"
"go-common/library/net/metadata"
"github.com/pkg/errors"
)
// Playurl get playurl data.
func (d *Dao) Playurl(c context.Context, mid int64, arg *model.PlayurlArg, playurl, token string) (data *model.PlayurlRes, err error) {
params := url.Values{}
params.Set("cid", strconv.FormatInt(arg.Cid, 10))
params.Set("avid", strconv.FormatInt(arg.Aid, 10))
params.Set("qn", strconv.Itoa(arg.Qn))
if arg.Type != "" {
params.Set("type", arg.Type)
}
if arg.MaxBackup > 0 {
params.Set("max_backup", strconv.Itoa(arg.MaxBackup))
}
if arg.Npcybs > 0 {
params.Set("npcybs", strconv.Itoa(arg.Npcybs))
}
if mid > 0 {
params.Set("mid", strconv.FormatInt(mid, 10))
}
if arg.Platform != "" {
params.Set("platform", arg.Platform)
}
if arg.Buvid != "" {
params.Set("buvid", arg.Buvid)
}
if arg.Resolution != "" {
params.Set("resolution", arg.Resolution)
}
if arg.Model != "" {
params.Set("model", arg.Model)
}
if arg.Build > 0 {
params.Set("build", strconv.Itoa(arg.Build))
}
params.Set("fnver", strconv.Itoa(arg.Fnver))
if arg.Fnval > 0 {
params.Set("fnval", strconv.Itoa(arg.Fnval))
}
if arg.Session != "" {
params.Set("session", arg.Session)
}
if arg.HTML5 > 0 && arg.H5GoodQuality > 0 {
params.Set("h5_good_quality", strconv.Itoa(arg.H5GoodQuality))
}
params.Set("otype", "json")
var req *http.Request
if req, err = d.client.NewRequest(http.MethodGet, playurl, metadata.String(c, metadata.RemoteIP), params); err != nil {
return
}
if token != "" {
req.Header.Set("X-BVC-FINGERPRINT", token)
}
var res struct {
Code int `json:"code"`
*model.PlayurlRes
}
if err = d.client.Do(c, req, &res); err != nil {
err = errors.Wrapf(err, "d.client.Do(%s) (%+v)", playurl+"?"+params.Encode(), arg)
return
}
if res.Code != ecode.OK.Code() {
err = errors.Wrap(ecode.Int(res.Code), playurl+"?"+params.Encode())
}
data = res.PlayurlRes
return
}

View File

@@ -0,0 +1,38 @@
package dao
import (
"context"
"encoding/json"
"testing"
"gopkg.in/h2non/gock.v1"
"go-common/app/interface/main/player/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDao_Playurl(t *testing.T) {
convey.Convey("test playurl", t, func(ctx convey.C) {
// aid 10108138
mid := int64(908085)
//cid=47083420&qn=80&type=&otype=json&fnver=0&fnval=8
arg := &model.PlayurlArg{
Aid: 10111420,
Cid: 10151472,
Qn: 0,
OType: "xml",
//Player: 1,
Fnver: 0,
Fnval: 8,
}
token := ""
playurl := "http://uat-videodispatch-ugc.bilibili.co/v3/playurl"
defer gock.OffAll()
httpMock("GET", playurl).Reply(200).JSON(`{"code":0}`)
data, err := d.Playurl(context.Background(), mid, arg, playurl, token)
convey.So(err, convey.ShouldBeNil)
str, _ := json.Marshal(data)
convey.Println(string(str))
})
}

View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"archive.go",
"http.go",
"player.go",
"playurl.go",
],
importpath = "go-common/app/interface/main/player/http",
tags = ["automanaged"],
deps = [
"//app/interface/main/player/conf:go_default_library",
"//app/interface/main/player/model:go_default_library",
"//app/interface/main/player/service:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/log/infoc:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/metadata:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,60 @@
package http
import (
"strconv"
"go-common/app/service/main/archive/api"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
func pageList(c *bm.Context) {
var (
aid int64
err error
pages []*api.Page
)
aidStr := c.Request.Form.Get("aid")
if aid, err = strconv.ParseInt(aidStr, 10, 64); err != nil || aid <= 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if pages, err = playSvr.PageList(c, aid); err != nil {
c.JSON(nil, err)
return
}
if len(pages) == 0 {
c.JSON(nil, ecode.NothingFound)
return
}
c.JSON(pages, nil)
}
func videoShot(c *bm.Context) {
v := new(struct {
Aid int64 `form:"aid" validate:"min=1"`
Cid int64 `form:"cid"`
Index bool `form:"index"`
})
if err := c.Bind(v); err != nil {
return
}
c.JSON(playSvr.VideoShot(c, v.Aid, v.Cid, v.Index))
}
func playURLToken(c *bm.Context) {
var (
aid, cid, mid int64
err error
)
params := c.Request.Form
aidStr := params.Get("aid")
if aid, err = strconv.ParseInt(aidStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
cid, _ = strconv.ParseInt(params.Get("cid"), 10, 64)
midStr, _ := c.Get("mid")
mid = midStr.(int64)
c.JSON(playSvr.PlayURLToken(c, mid, aid, cid))
}

View File

@@ -0,0 +1,69 @@
package http
import (
"net/http"
"go-common/app/interface/main/player/conf"
"go-common/app/interface/main/player/service"
"go-common/library/log"
"go-common/library/log/infoc"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
authSvr *auth.Auth
vfySvr *verify.Verify
playSvr *service.Service
playInfoc *infoc.Infoc
)
// Init init http.
func Init(c *conf.Config) error {
authSvr = auth.New(c.Auth)
vfySvr = verify.New(c.Verify)
playSvr = service.New(c)
engine := bm.DefaultServer(c.BM.Outer)
outerRouter(engine)
internalRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
return err
}
// init infoc
if c.Infoc2 != nil {
playInfoc = infoc.New(c.Infoc2)
}
return nil
}
func outerRouter(e *bm.Engine) {
e.GET("/monitor/ping", ping)
e.GET("/x/player.so", bm.CORS(), authSvr.Guest, player)
group := e.Group("/x/player", bm.CORS())
{
group.GET("/policy", authSvr.Guest, policy)
group.GET("/carousel.so", carousel)
group.GET("/view", view)
group.GET("/matsuri", matPage)
group.GET("/pagelist", pageList)
group.GET("/videoshot", videoShot)
group.GET("/playurl/token", authSvr.User, playURLToken)
group.GET("/playurl", authSvr.Guest, playurl)
}
}
func internalRouter(e *bm.Engine) {
group := e.Group("/x/internal/player")
{
group.GET("/playurl", vfySvr.Verify, authSvr.Guest, playurl)
}
}
func ping(c *bm.Context) {
if err := playSvr.Ping(c); err != nil {
log.Error("player service ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}

View File

@@ -0,0 +1,192 @@
package http
import (
"encoding/xml"
"net/http"
"strconv"
"strings"
"time"
"go-common/app/interface/main/player/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
)
type playerInfoc struct {
ip string
ctime string
api string
buvid string
mid string
client string
itemID string
displayID string
errCode string
from string
build string
trackID string
}
const (
_api = "player"
_client = "web"
)
var (
emptyByte = []byte{}
_headerBuvid = "Buvid"
_buvid = "buvid3"
)
func player(c *bm.Context) {
var (
aid, cid, mid int64
sid model.Sid
sidCookie *http.Cookie
err error
ip = metadata.String(c, metadata.RemoteIP)
)
request := c.Request
params := request.Form
idStr := params.Get("id")
aidStr := params.Get("aid")
cidStr := params.Get("cid")
refer := request.Referer()
// response header
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("X-Remote-IP", ip)
// get mid
if midInter, ok := c.Get("mid"); ok {
mid = midInter.(int64)
}
if strings.Index(idStr, "cid:") == 0 {
cidStr = idStr[4:]
}
if cid, err = strconv.ParseInt(cidStr, 10, 64); err != nil || cid <= 0 {
c.AbortWithStatus(http.StatusBadRequest)
return
}
// get aid
if aid, err = strconv.ParseInt(aidStr, 10, 64); err != nil || aid <= 0 {
c.AbortWithStatus(http.StatusBadRequest)
return
}
sidCookie, err = request.Cookie("sid")
if err != nil || sidCookie.Value == "" {
http.SetCookie(c.Writer, &http.Cookie{Name: "sid", Value: string(sid.Create()), Path: "/", Domain: ".bilibili.com"})
}
if sidCookie != nil {
if sidCookie.Value != "" {
sidStr := sidCookie.Value
if !model.Sid(sidStr).Valid() {
http.SetCookie(c.Writer, &http.Cookie{Name: "sid", Value: string(sid.Create()), Path: "/", Domain: ".bilibili.com"})
}
}
}
var (
ips = strings.Split(c.Request.Header.Get("X-Forwarded-For"), ",")
cdnIP string
playByte []byte
)
if len(ips) >= 2 {
cdnIP = ips[1]
}
buvid := request.Header.Get(_headerBuvid)
if buvid == "" {
cookie, _ := request.Cookie(_buvid)
if cookie != nil {
buvid = cookie.Value
}
}
now := time.Now()
if playInfoc != nil {
showInfoc(ip, now, buvid, aid, mid)
}
if playByte, err = playSvr.Player(c, mid, aid, cid, cdnIP, refer, now); err != nil {
c.AbortWithStatus(http.StatusNotFound)
return
}
c.Bytes(http.StatusOK, "text/plain; charset=utf-8", playByte)
}
func carousel(c *bm.Context) {
// response header
c.Writer.Header().Set("Cache-Control", "no-cache")
c.Writer.Header().Set("Connection", "keep-alive")
type msg struct {
Items []*model.Item `xml:"item"`
}
var (
items []*model.Item
err error
)
if items, err = playSvr.Carousel(c); err != nil {
c.Bytes(http.StatusOK, "text/xml; charset=utf-8", emptyByte)
return
}
result := msg{Items: items}
output, err := xml.MarshalIndent(result, " ", " ")
if err != nil {
output = emptyByte
}
c.Bytes(http.StatusOK, "text/xml; charset=utf-8", output)
}
func policy(c *bm.Context) {
var (
id int64
mid int64
err error
)
if midInter, ok := c.Get("mid"); ok {
mid = midInter.(int64)
}
params := c.Request.Form
idStr := params.Get("id")
if id, err = strconv.ParseInt(idStr, 10, 64); err != nil || id < 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(playSvr.Policy(c, id, mid))
}
func view(c *bm.Context) {
var (
aid int64
err error
)
aidStr := c.Request.Form.Get("aid")
if aid, err = strconv.ParseInt(aidStr, 10, 64); err != nil || aid < 0 {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(playSvr.View(c, aid))
}
func matPage(c *bm.Context) {
c.JSON(playSvr.Matsuri(c, time.Now()), nil)
}
func showInfoc(ip string, now time.Time, buvid string, aid, mid int64) {
if playInfoc == nil {
return
}
info := &playerInfoc{
ip: ip,
ctime: strconv.FormatInt(now.Unix(), 10),
api: _api,
buvid: buvid,
mid: strconv.FormatInt(mid, 10),
client: _client,
itemID: strconv.FormatInt(aid, 10),
displayID: "",
errCode: "",
from: "",
build: "",
trackID: "",
}
err := playInfoc.Info(info.ip, info.ctime, info.api, info.buvid, info.mid, info.client, info.itemID, info.displayID, info.errCode, info.from, info.build, info.trackID)
log.Info("playInfoc.Info(%s,%s,%s,%s,%s) error(%v)", info.ip, info.ctime, info.buvid, info.mid, info.itemID, err)
}

View File

@@ -0,0 +1,43 @@
package http
import (
"go-common/app/interface/main/player/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/http/blademaster"
)
func playurl(c *blademaster.Context) {
var (
mid int64
err error
)
arg := new(model.PlayurlArg)
if err = c.Bind(arg); err != nil {
return
}
if midInter, ok := c.Get("mid"); ok {
mid = midInter.(int64)
}
if arg.OType == "" {
arg.OType = model.OtypeXML
}
if arg.OType != model.OtypeXML && arg.OType != model.OtypeJSON {
c.JSON(nil, ecode.RequestErr)
return
}
if arg.Player != 0 && arg.Player != 1 {
c.JSON(nil, ecode.RequestErr)
return
}
if arg.Player == 1 && arg.OType == model.OtypeXML {
log.Warn("playurl warn arg(%+v)", arg)
c.JSON(nil, ecode.RequestErr)
return
}
if arg.OType == model.OtypeJSON {
c.JSON(playSvr.Playurl(c, mid, arg))
} else {
c.XML(playSvr.Playurl(c, mid, arg))
}
}

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"player.go",
"playurl.go",
],
importpath = "go-common/app/interface/main/player/model",
tags = ["automanaged"],
deps = [
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,397 @@
package model
import (
"html/template"
"strconv"
"time"
arcmdl "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
)
var encodeTbl = []int64{
0xf3a97cb, 0x8aed379, 0xedf369a, 0x5c82647, 0xcaf6987, 0xad28536, 0xe5f2a7b, 0x72e85df,
0xac3d972, 0xca97fe5, 0xbcf5473, 0x85ad732, 0xcd6b324, 0xd549a72, 0xe72ab89, 0xfa6dc53,
0xa8e752b, 0xa73d25f, 0xcad8296, 0xb35f689, 0x7ce594b, 0x59ca743, 0xc4ab2d7, 0x9c8adf6,
0x93746c2, 0x6cea579, 0xcd36b75, 0x64a973e, 0xfa49c56, 0xb45f2d9, 0x72a56f8, 0x43d6fa9,
0x354cf2a, 0x26bf5d8, 0x39f64ad, 0xa4fd326, 0x39247d6, 0xec67f95, 0xed8c9b4, 0x29637db,
0xefabc54, 0xa9ed87c, 0xc2864ea, 0xf32d475, 0x53b6897, 0xe7f94b8, 0x7a4cfd2, 0x9a82e65,
0x369b7a4, 0x2cae6d4, 0xc7fba36, 0xd3e7846, 0xd324ba5, 0x7c56f24, 0x598c3af, 0x39fd4ae,
0x3b6472c, 0x2f9a8be, 0x9fcab42, 0x8f34aeb, 0x9e8b372, 0x8c42b9e, 0xf9b574c, 0x7c693fa,
0x245fc67, 0x823f4ce, 0x957f84d, 0xe529a87, 0xb625ead, 0xbd4f6a9, 0x863ca52, 0xd762cef,
0x8d6c479, 0xbc4f579, 0xa486fdc, 0xcd6f289, 0xda3b629, 0x4fce523, 0x2e8db97, 0xc3bf769,
0x9c64d7f, 0x52db6f7, 0x95cdf8e, 0xc872fe9, 0x964de53, 0x2bef897, 0xb7a962c, 0x38d72be,
0x26fa89c, 0x58b742e, 0xa3bd967, 0x3cae942, 0x4d3fb9c, 0xaf59ed3, 0x6f8379d, 0x2bf46d7,
0xcdbe243, 0x3754bf9, 0x82f9dc5, 0x8a46ef5, 0x5d48ac9, 0x9e6ca3d, 0xfec5a3b, 0x57dafe3,
0x82ed7a9, 0xbc3d687, 0x89ecbf7, 0x738549a, 0x928746c, 0x9cb7e83, 0xc85f9a7, 0x2947c8e,
0xba689fd, 0xebc4893, 0xa62cf7e, 0xa8e3cb5, 0xe47589d, 0x792edaf, 0x4635c2d, 0xa2c6bfe,
0xc456daf, 0x2d65f47, 0xf9ce625, 0x74a8b62, 0x9d728f5, 0x3e4a29d, 0x62a589c, 0x83cb629,
0xce5b6d3, 0x2fda9ce, 0x87af3bc, 0x837a695, 0xf935da4, 0x48b6ea2, 0x52dc4e9, 0x82a537b,
0xe23456f, 0x6cbdafe, 0x97bf34d, 0x4c72ad8, 0xa5c4982, 0x8afb76e, 0x895fca6, 0x85abd24,
0xae2475d, 0xf3c5eb8, 0xb4d2ef5, 0xbda463e, 0xf392a5e, 0x7a9fd58, 0xead48f6, 0x8a62537,
0x6c35ba2, 0x7589e24, 0xd24ec93, 0x6bc42a5, 0x34d9f87, 0xed3578f, 0x87452fa, 0x5439fca,
0x29b37c8, 0x8fe4c3b, 0x4c5368d, 0x58acf9b, 0x69c3ad2, 0xaf3827b, 0x328e46b, 0xbef7ca9,
0xda592c4, 0x45f7db2, 0xcb65a3d, 0x4578ec3, 0xc6deab9, 0xb689edc, 0x4aed59f, 0x25b9af7,
0x9b6d48f, 0x6de79bf, 0x249fa5e, 0x269a7ef, 0xd9e62a7, 0xb9a86d2, 0x539b72c, 0x8fa9ebc,
0xec397f5, 0xdbac4e2, 0x938e6fd, 0xe8a734f, 0xe4b8d7f, 0x84cd9b3, 0x75c6ef4, 0x956378c,
0x43f2d78, 0x74e9253, 0x25dbef4, 0xb7e26f9, 0x93b2c6d, 0x2faeb76, 0x3b278de, 0x6b5948f,
0x4967358, 0x49f3a7e, 0x7596ec4, 0x98cabf5, 0x95c638e, 0x6d258b7, 0x97e8b3f, 0x5ab7823,
0x53b6a89, 0xa3bc579, 0xac45d36, 0xcea9b28, 0x98f2356, 0xd694a2e, 0xf732e8a, 0xe7463d5,
0xf5ec9a8, 0x6dba984, 0xc798e5a, 0x6e9382b, 0xeac3249, 0x5238b9a, 0xd632eaf, 0xa92b685,
0xbcae435, 0x9726fd8, 0x3fcbea4, 0x5e9da23, 0xb93a4f7, 0x327d84c, 0x5db932f, 0x86274de,
0xa54bd72, 0x63f2ed5, 0x6d37285, 0xb4fe7c9, 0x549a6b3, 0x3b592ec, 0x73d456b, 0x49253b7,
0x2da9b8c, 0xb85642d, 0x37489ca, 0x726fe3b, 0x4ce6ad2, 0x376becd, 0x6f43bec, 0xf96dba4,
0xebc8d72, 0xf59b4ca, 0x263547f, 0xabcd87e, 0x3fd25ae, 0xc6f4b38, 0x36cd978, 0x6e94a37,
}
// Catalog catalog map
var Catalog = map[int8]string{
1: "system",
2: "bangumi",
3: "news",
}
// Sid sid string.
type Sid string
// Valid valid sid.
func (sid Sid) Valid() (b bool) {
var (
interval int64
msec int64
_xorKey int64
hKey int64
_dec int64
_ts int64
offset int
ms int64
err error
)
if len(sid) != 8 {
return false
}
if interval, err = strconv.ParseInt(string(sid)[0:6], 36, 64); err != nil {
return false
}
if msec, err = strconv.ParseInt(string(sid)[6:8], 36, 64); err != nil {
return false
}
_xorKey = encodeTbl[msec%256]
hKey = 1 << uint(28+msec%3)
_dec = (interval ^ _xorKey)
if (_dec & 0x70000000) != hKey {
return false
}
_ts = (_dec^hKey)*1000 + msec
_, offset = time.Now().Zone()
ms = (time.Now().UnixNano() / int64(time.Millisecond)) + (int64(offset)/60)*60000 // GMT Timestamp
return _ts <= (ms - 1388534400000 + 300000)
}
// Create create sid.
func (sid Sid) Create() (re Sid) {
var (
offset int
ms int64
_interval int64
msec int64
msesStr string
_xorKey int64
hKey int64
tsEncode string
)
_, offset = time.Now().Zone()
ms = (time.Now().UnixNano() / int64(time.Millisecond)) - (int64(offset)/60)*60000 // GMT Timestamp
_interval = ms/1000 - 1388534400
msec = ms % 1000
_xorKey = encodeTbl[msec%256]
hKey = 1 << uint(28+msec%3)
tsEncode = strconv.FormatInt(((hKey | _interval) ^ _xorKey), 36)
for len(tsEncode) < 6 {
tsEncode = "0" + tsEncode
}
msesStr = strconv.FormatInt(msec, 36)
if len(msesStr) < 2 {
msesStr = "0" + msesStr
}
sid = Sid(tsEncode + msesStr)
return sid
}
// Item item struct.
type Item struct {
Content string `xml:",cdata"`
Tooltip string `xml:"tooltip,attr"`
Bgcolor string `xml:"bgcolor,attr"`
Catalog string `xml:"catalog,attr"`
ResourceID string `xml:"resourceid,attr"`
SrcID string `xml:"srcid,attr"`
ID string `xml:"id,attr"`
}
// Carousel carousel struct.
type Carousel struct {
Backcolor string
Fontcolor string
Hint string
Link string
Content string
Category string
}
// Player player struct.
type Player struct {
IP string
Zoneid int64
Country string
Isp string // 运营商暂时不需要
Login bool
Time int64
ZoneIP string
// user info
Name string
User int64
UserHash string
Money string
Face string
IsAdmin bool
Upermission string
Level string
LevelInfo template.HTML
Vip template.HTML
OfficialVerify template.HTML
BlockTime int64
// archive info
Aid int64
Typeid int32
Maxlimit int
Click int
FwClick int32
Duration string
Arctype string
APermission bool
SuggestComment bool
Chatid int64
Vtype string
Oriurl string
Pid int64
AllowBp bool
Bottom int8
Acceptguest bool
Acceptaccel bool
Cache bool
CacheDispatch bool
BrTCP string
BrWs string
BrWss string
DefaultDm int8
//progress
LastPlayTime int64
LastCid int64
Role string
// has next page
HasNext int8
OnlineCount int64
// dm mask
MaskNew template.HTML
// subtitle
Subtitle template.HTML
// player icon
PlayerIcon template.HTML
// view points
ViewPoints template.HTML
}
// Progress progress struct.
type Progress struct {
Cid int64 `json:"cid"`
Pro int64 `json:"pro"`
}
// Policy policy struct.
type Policy struct {
ID int64 `json:"id"`
Des string `json:"description"`
Type string `json:"type"`
Start string `json:"start"`
End string `json:"end"`
Mtime string `json:"mtime"`
StartTime time.Time
EndTime time.Time
MtimeTime time.Time
Items []*Pitem `json:"items"`
}
// Pitem pitem struct
type Pitem struct {
ID int64 `json:"item_id"`
Data string `json:"data"`
Comment string `json:"comment"`
ExtData string `json:"ext_data"`
Ver int64 `json:"ver"`
}
// Param param struct.
type Param struct {
Name string
Value string
}
// BlockTime block time struct
type BlockTime struct {
BlockStatus int `json:"block_status"`
BlockedForever bool `json:"blocked_forever"`
BlockedEnd int64 `json:"blocked_end"`
}
// Videoshot player video shot struct
type Videoshot struct {
*archive.Videoshot
Index []uint16 `json:"index,omitempty"`
}
// PlayURLToken playurl token.
type PlayURLToken struct {
From string `json:"from"`
Ts int64 `json:"ts"`
Aid int64 `json:"aid"`
Cid int64 `json:"cid"`
Mid int64 `json:"mid"`
VIP int `json:"vip"`
SVIP int `json:"svip"`
Owner int `json:"owner"`
Fcs string `json:"fcs"`
Token string `json:"token"`
}
// VIPInfo vip info.
type VIPInfo struct {
Type int32 `json:"vipType"`
DueDate int64 `json:"vipDueDate"`
DueRemark string `json:"dueRemark"`
AccessStatus int32 `json:"accessStatus"`
VipStatus int32 `json:"vipStatus"`
VipStatusWarn string `json:"vipStatusWarn"`
}
// Official official.
type Official struct {
Type int8 `json:"type"`
Desc string `json:"desc"`
}
// Points is
type Points struct {
Type int `json:"type"`
From int64 `json:"from"`
To int64 `json:"to"`
Content string `json:"content"`
}
const (
// TpWithUinfo tpl with user info.
TpWithUinfo = `<ip>{{.IP}}</ip>
<zoneid>{{.Zoneid}}</zoneid>
<zoneip>{{.ZoneIP}}</zoneip>
<country>{{.Country}}</country>
<login>{{.Login}}</login>
<time>{{.Time}}</time>
<name>{{.Name}}</name>
<user>{{.User}}</user>
<user_hash>{{.UserHash}}</user_hash>
<money>{{.Money}}</money>
<face>{{.Face}}</face>
<isadmin>{{.IsAdmin}}</isadmin>
<permission>{{.Upermission}}</permission>
<level>{{.Level}}</level>
<level_info>{{.LevelInfo}}</level_info>
<vip>{{.Vip}}</vip>
<official_verify>{{.OfficialVerify}}</official_verify>
<block_time>{{.BlockTime}}</block_time>
<lastplaytime>{{.LastPlayTime}}</lastplaytime>
<lastcid>{{.LastCid}}</lastcid>
<aid>{{.Aid}}</aid>
<typeid>{{.Typeid}}</typeid>
<vtype>{{.Vtype}}</vtype>
<oriurl>{{.Oriurl}}</oriurl>
<suggest_comment>{{.SuggestComment}}</suggest_comment>
<server>chat.bilibili.com</server>
<maxlimit>{{.Maxlimit}}</maxlimit>
<click>{{.Click}}</click>
<fw_click>{{.FwClick}}</fw_click>
<chatid>{{.Chatid}}</chatid>
<pid>{{.Pid}}</pid>
<duration>{{.Duration}}</duration>
<arctype>{{.Arctype}}</arctype>
<allow_bp>{{.AllowBp}}</allow_bp>
<bottom>{{.Bottom}}</bottom>
<shot>false</shot>
<sinapi>1</sinapi>
<acceptguest>{{.Acceptguest}}</acceptguest>
<acceptaccel>{{.Acceptaccel}}</acceptaccel>
<cache>{{.Cache}}</cache>
<broadcast_tcp>{{.BrTCP}}</broadcast_tcp>
<broadcast_ws>{{.BrWs}}</broadcast_ws>
<broadcast_wss>{{.BrWss}}</broadcast_wss>
<default_dm>{{.DefaultDm}}</default_dm>
<dm_host>0://comment.bilibili.com,1://comment.bilibili.com/rc</dm_host>
<role>{{.Role}}</role>
<has_next>{{.HasNext}}</has_next>
<online_count>{{.OnlineCount}}</online_count>
<dm_mask></dm_mask>
<mask_new>{{.MaskNew}}</mask_new>
<subtitle>{{.Subtitle}}</subtitle>
<player_icon>{{.PlayerIcon}}</player_icon>
<view_points>{{.ViewPoints}}</view_points>
`
// TpWithNoUinfo tpl without user info.
TpWithNoUinfo = `<ip>{{.IP}}</ip>
<zoneip>{{.ZoneIP}}</zoneip>
<zoneid>{{.Zoneid}}</zoneid>
<country>{{.Country}}</country>
<login>{{.Login}}</login>
<time>{{.Time}}</time>
<lastplaytime>0</lastplaytime>
<lastcid>0</lastcid>
<aid>{{.Aid}}</aid>
<typeid>{{.Typeid}}</typeid>
<vtype>{{.Vtype}}</vtype>
<oriurl>{{.Oriurl}}</oriurl>
<suggest_comment>{{.SuggestComment}}</suggest_comment>
<server>chat.bilibili.com</server>
<maxlimit>{{.Maxlimit}}</maxlimit>
<click>{{.Click}}</click>
<fw_click>{{.FwClick}}</fw_click>
<chatid>{{.Chatid}}</chatid>
<pid>{{.Pid}}</pid>
<duration>{{.Duration}}</duration>
<arctype>{{.Arctype}}</arctype>
<allow_bp>{{.AllowBp}}</allow_bp>
<bottom>{{.Bottom}}</bottom>
<shot>false</shot>
<sinapi>1</sinapi>
<acceptguest>{{.Acceptguest}}</acceptguest>
<acceptaccel>{{.Acceptaccel}}</acceptaccel>
<cache>{{.Cache}}</cache>
<broadcast_tcp>{{.BrTCP}}</broadcast_tcp>
<broadcast_ws>{{.BrWs}}</broadcast_ws>
<broadcast_wss>{{.BrWss}}</broadcast_wss>
<default_dm>{{.DefaultDm}}</default_dm>
<dm_host>0://comment.bilibili.com,1://comment.bilibili.com/rc</dm_host>
<role>0</role>
<has_next>{{.HasNext}}</has_next>
<online_count>{{.OnlineCount}}</online_count>
<dm_mask></dm_mask>
<mask_new>{{.MaskNew}}</mask_new>
<subtitle>{{.Subtitle}}</subtitle>
<player_icon>{{.PlayerIcon}}</player_icon>
<view_points>{{.ViewPoints}}</view_points>
`
)
// View .
type View struct {
*arcmdl.Arc
Pages []*arcmdl.Page `json:"pages"`
}

View File

@@ -0,0 +1,82 @@
package model
// Otype playurl data type.
const (
OtypeJSON = "json"
OtypeXML = "xml"
)
// PlayurlArg playurl arg.
type PlayurlArg struct {
Cid int64 `form:"cid" validate:"min=1"`
Aid int64 `form:"avid" validate:"min=1"`
Qn int `form:"qn"`
Type string `form:"type"`
MaxBackup int `form:"max_backup"`
Npcybs int `form:"npcybs"`
Platform string `form:"platform"`
Player int `form:"player"`
Buvid string `form:"buvid"`
Resolution string `form:"resolution"`
Model string `form:"model"`
Build int `form:"build"`
OType string `form:"otype"`
Fnver int `form:"fnver"`
Fnval int `form:"fnval"`
Session string `form:"session"`
HTML5 int `form:"html5"`
H5GoodQuality int `form:"h5_good_quality"`
HighQuality int `form:"high_quality"`
}
// PlayurlRes playurl res.
type PlayurlRes struct {
From string `json:"from" xml:"from"`
Result string `json:"result" xml:"result"`
Message string `json:"message" xml:"message"`
Quality int `json:"quality" xml:"quality"`
Format string `json:"format" xml:"format"`
Timelength int64 `json:"timelength" xml:"timelength"`
AcceptFormat string `json:"accept_format" xml:"accept_format"`
AcceptDescription []string `json:"accept_description" xml:"accept_description"`
AcceptQuality []int `json:"accept_quality" xml:"accept_quality"`
VideoCodeCid int64 `json:"video_codecid" xml:"video_codecid"`
SeekParam string `json:"seek_param" xml:"seek_param"`
SeekType string `json:"seek_type" xml:"seek_type"`
Abtid int64 `json:"abtid,omitempty" xml:"abtid,omitempty"`
Durl []*struct {
Order int `json:"order" xml:"order"`
Length int64 `json:"length" xml:"length"`
Size int64 `json:"size" xml:"size"`
Ahead string `json:"ahead" xml:"ahead"`
Vhead string `json:"vhead" xml:"vhead"`
URL string `json:"url" xml:"url"`
BackupURL []string `json:"backup_url" xml:"backup_url"`
} `json:"durl,omitempty" xml:"durl,omitempty"`
Dash *struct {
Duration int64 `json:"duration"`
MinBufferTime float64 `json:"minBufferTime"`
Video []*DashItem `json:"video"`
Audio []*DashItem `json:"audio"`
} `json:"dash,omitempty" xml:"dash,omitempty"`
}
// DashItem .
type DashItem struct {
ID int64 `json:"id" xml:"id"`
BaseURL string `json:"baseUrl" xml:"baseUrl"`
BackupURL []string `json:"backupUrl" xml:"backupUrl"`
Bandwidth int64 `json:"bandwidth" xml:"bandwidth"`
MimeType string `json:"mimeType" xml:"mimeType"`
Codecs string `json:"codecs" xml:"codecs"`
Width int64 `json:"width" xml:"width"`
Height int64 `json:"height" xml:"height"`
FrameRate string `json:"frameRate" xml:"frameRate"`
Sar string `json:"sar" xml:"sar"`
StartWithSAP int64 `json:"startWithSap" xml:"startWithSap"`
SegmentBase *struct {
Initialization string `json:"Initialization" xml:"Initialization"`
IndexRange string `json:"indexRange" xml:"indexRange"`
} `json:"SegmentBase" xml:"SegmentBase"`
Codecid int64 `json:"codecid" xml:"codecid"`
}

View File

@@ -0,0 +1,76 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"archive_test.go",
"player_test.go",
"policy_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/interface/main/player/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"archive.go",
"player.go",
"playurl.go",
"policy.go",
"service.go",
],
importpath = "go-common/app/interface/main/player/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/dm2/model:go_default_library",
"//app/interface/main/dm2/rpc/client:go_default_library",
"//app/interface/main/history/model:go_default_library",
"//app/interface/main/history/rpc/client:go_default_library",
"//app/interface/main/player/conf:go_default_library",
"//app/interface/main/player/dao:go_default_library",
"//app/interface/main/player/model:go_default_library",
"//app/interface/main/tag/model:go_default_library",
"//app/interface/main/tag/rpc/client:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/archive/api/gorpc:go_default_library",
"//app/service/main/archive/model/archive:go_default_library",
"//app/service/main/assist/model/assist:go_default_library",
"//app/service/main/assist/rpc/client:go_default_library",
"//app/service/main/location/model:go_default_library",
"//app/service/main/location/rpc/client:go_default_library",
"//app/service/main/resource/model:go_default_library",
"//app/service/main/resource/rpc/client:go_default_library",
"//app/service/main/ugcpay/api/grpc/v1:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/sync/errgroup:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,184 @@
package service
import (
"bytes"
"context"
"crypto/md5"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"io"
"net/url"
"strconv"
"strings"
"time"
"go-common/app/interface/main/player/dao"
"go-common/app/interface/main/player/model"
accmdl "go-common/app/service/main/account/api"
arcmdl "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
const (
_maxLevel = 6
_hasUGCPay = 1
)
// View get view info
func (s *Service) View(c context.Context, aid int64) (view *model.View, err error) {
var viewReply *arcmdl.ViewReply
if viewReply, err = s.arcClient.View(c, &arcmdl.ViewRequest{Aid: aid}); err != nil {
dao.PromError("View接口错误", "s.arcClientView3(%d) error(%v)", aid, err)
return
}
view = &model.View{Arc: viewReply.Arc, Pages: viewReply.Pages}
return
}
// Matsuri get matsuri info
func (s *Service) Matsuri(c context.Context, now time.Time) (view *model.View) {
if now.Unix() < s.matTime.Unix() {
return s.pastView
}
if s.matOn || len(s.matView.Pages) < 1 {
return s.matView
}
view = new(model.View)
*view = *s.matView
view.Pages = view.Pages[0 : len(view.Pages)-1]
return
}
// PageList many p video pages
func (s *Service) PageList(c context.Context, aid int64) (rs []*arcmdl.Page, err error) {
ip := metadata.String(c, metadata.RemoteIP)
if rs, err = s.arc.Page3(c, &archive.ArgAid2{Aid: aid, RealIP: ip}); err != nil {
dao.PromError("Page3 接口错误", "s.arc.Page3(%d) error(%v)", aid, err)
}
return
}
// VideoShot get archive video shot data
func (s *Service) VideoShot(c context.Context, aid, cid int64, index bool) (res *model.Videoshot, err error) {
var (
viewReply *arcmdl.ViewReply
ip = metadata.String(c, metadata.RemoteIP)
)
if viewReply, err = s.arcClient.View(c, &arcmdl.ViewRequest{Aid: aid}); err != nil {
log.Error("VideoShot s.arcClient.View(%d) error(%v)", aid, err)
return
}
if !viewReply.Arc.IsNormal() || viewReply.Arc.Rights.UGCPay == _hasUGCPay {
log.Warn("VideoShot warn arc(%d) state(%d) or ugcpay(%d)", aid, viewReply.Arc.State, viewReply.Arc.Rights.UGCPay)
err = ecode.NothingFound
return
}
if cid == 0 {
if len(viewReply.Pages) == 0 {
err = ecode.NothingFound
return
}
cid = viewReply.Pages[0].Cid
}
res = &model.Videoshot{}
if res.Videoshot, err = s.arc.Videoshot2(c, &archive.ArgCid2{Aid: aid, Cid: cid, RealIP: ip}); err != nil {
log.Error("s.arc.Videoshot2(%d,%d) err(%v)", aid, cid, err)
return
}
if index && res.PvData != "" {
if pv, e := s.dao.PvData(c, res.PvData); e != nil {
log.Error("s.dao.PvData(aid:%d,cid:%d) err(%+v)", aid, cid, e)
} else if len(pv) > 0 {
var (
v uint16
pvs []uint16
buf = bytes.NewReader(pv)
)
for {
if e := binary.Read(buf, binary.BigEndian, &v); e != nil {
if e != io.EOF {
log.Warn("binary.Read pvdata(%s) err(%v)", res.PvData, e)
}
break
}
pvs = append(pvs, v)
}
res.Index = pvs
}
}
fmtVideshot(res)
return
}
func fmtVideshot(res *model.Videoshot) {
if res.PvData != "" {
res.PvData = strings.Replace(res.PvData, "http://", "//", 1)
}
for i, v := range res.Image {
res.Image[i] = strings.Replace(v, "http://", "//", 1)
}
}
// PlayURLToken get playurl token
func (s *Service) PlayURLToken(c context.Context, mid, aid, cid int64) (res *model.PlayURLToken, err error) {
var (
arcReply *arcmdl.ArcReply
ui *accmdl.CardReply
owner, svip int
vip int32
)
if arcReply, err = s.arcClient.Arc(c, &arcmdl.ArcRequest{Aid: aid}); err != nil {
dao.PromError("Arc接口错误", "s.arcClient.Arc(%d) error(%v)", aid, err)
err = ecode.NothingFound
return
}
if !arcReply.Arc.IsNormal() {
err = ecode.NothingFound
return
}
if mid == arcReply.Arc.Author.Mid {
owner = 1
}
if ui, err = s.accClient.Card3(c, &accmdl.MidReq{Mid: mid}); err != nil {
dao.PromError("Card3接口错误", "s.accClient.Card3(%d) error(%v)", mid, err)
err = ecode.AccessDenied
return
}
if vip = ui.Card.Level; vip > _maxLevel {
vip = _maxLevel
}
if ui.Card.Vip.Type != 0 && ui.Card.Vip.Status == 1 {
svip = 1
}
res = &model.PlayURLToken{
From: "pc",
Ts: time.Now().Unix(),
Aid: aid,
Cid: cid,
Mid: mid,
Owner: owner,
VIP: int(vip),
SVIP: svip,
}
params := url.Values{}
params.Set("from", res.From)
params.Set("ts", strconv.FormatInt(res.Ts, 10))
params.Set("aid", strconv.FormatInt(res.Aid, 10))
params.Set("cid", strconv.FormatInt(res.Cid, 10))
params.Set("mid", strconv.FormatInt(res.Mid, 10))
params.Set("vip", strconv.Itoa(res.VIP))
params.Set("svip", strconv.Itoa(res.SVIP))
params.Set("owner", strconv.Itoa(res.Owner))
tmp := params.Encode()
if strings.IndexByte(tmp, '+') > -1 {
tmp = strings.Replace(tmp, "+", "%20", -1)
}
mh := md5.Sum([]byte(strings.ToLower(tmp) + s.c.PlayURLToken.Secret))
res.Fcs = hex.EncodeToString(mh[:])
res.Token = base64.StdEncoding.EncodeToString([]byte(tmp + "&fcs=" + res.Fcs))
return
}

View File

@@ -0,0 +1,56 @@
package service
import (
"context"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestService_Matsuri(t *testing.T) {
Convey("matsuri", t, WithService(func(s *Service) {
data := s.Matsuri(context.Background(), time.Now())
So(data, ShouldNotBeNil)
}))
}
func TestService_View(t *testing.T) {
Convey("view", t, WithService(func(s *Service) {
aid := int64(10097666)
data, err := s.View(context.Background(), aid)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
}))
}
func TestService_PageList(t *testing.T) {
Convey("pagelist", t, WithService(func(s *Service) {
aid := int64(10097666)
data, err := s.PageList(context.Background(), aid)
So(err, ShouldBeNil)
So(len(data), ShouldBeGreaterThan, 0)
}))
}
func TestService_VideoShot(t *testing.T) {
Convey("video shot", t, WithService(func(s *Service) {
aid := int64(10097666)
cid := int64(10108404)
index := true
data, err := s.VideoShot(context.Background(), aid, cid, index)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
}))
}
func TestService_PlayURLToken(t *testing.T) {
Convey("playurl token", t, WithService(func(s *Service) {
mid := int64(88895029)
aid := int64(10097666)
cid := int64(10108404)
data, err := s.PlayURLToken(context.Background(), mid, aid, cid)
So(err, ShouldBeNil)
So(data, ShouldNotBeNil)
}))
}

View File

@@ -0,0 +1,517 @@
package service
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"fmt"
"hash/crc32"
"html/template"
"strconv"
"strings"
"time"
dm2 "go-common/app/interface/main/dm2/model"
history "go-common/app/interface/main/history/model"
"go-common/app/interface/main/player/dao"
"go-common/app/interface/main/player/model"
tagmdl "go-common/app/interface/main/tag/model"
accmdl "go-common/app/service/main/account/api"
arcmdl "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
"go-common/app/service/main/assist/model/assist"
locmdl "go-common/app/service/main/location/model"
resmdl "go-common/app/service/main/resource/model"
"go-common/library/log"
"go-common/library/net/metadata"
"go-common/library/sync/errgroup"
)
const (
_content = `<a href="%s" target="_blank"><font color="#FFFFFF">%s</font></a>`
_china = "中国"
_local = "局域网"
_accBanNor = 0 // no block
_accBanSta = 1 // block MSpacesta
_accBlockSta = 1
_dmMaskPlatWeb = 0
_mockBlockTime = 100
)
var (
_copyRightMap = map[int32]string{
0: "Nnknown",
1: "Original",
2: "Copy",
}
// if typeid in this xml add bottom = 1
_bottomMap = map[int32]struct{}{
// 番剧
33: {},
32: {},
153: {},
// 电影
82: {},
85: {},
145: {},
146: {},
147: {},
83: {},
// note 电视剧存在三级分区
15: {},
34: {},
86: {},
128: {},
// 三级分区
110: {},
111: {},
112: {},
113: {},
87: {},
88: {},
89: {},
90: {},
91: {},
92: {},
73: {},
}
iconTagIDs = map[int64]struct{}{
516: {},
374306: {},
16054: {},
18612: {},
2611047: {},
1008087: {},
50: {},
2513658: {},
56: {},
2512304: {},
6977: {},
8035683: {},
1060128: {},
}
)
// Carousel return carousel items.
func (s *Service) Carousel(c context.Context) (items []*model.Item, err error) {
items = s.caItems
return
}
// Player return player info.
func (s *Service) Player(c context.Context, mid, aid, cid int64, cdnIP, refer string, now time.Time) (res []byte, err error) {
var (
ip = metadata.String(c, metadata.RemoteIP)
vi *arcmdl.ViewReply
cuPage *arcmdl.Page
pi = &model.Player{
IP: ip,
Login: mid > 0,
Time: now.Unix(),
ZoneIP: cdnIP,
Upermission: "1000,1001",
}
withU bool
)
if vi, err = s.view(c, aid); err != nil {
dao.PromError("View接口错误", "s.arcClientView3(%d) error(%v)", aid, err)
return
} else if vi == nil || vi.Arc == nil {
log.Error("vi(%v) is nill || vi.Archive is nil", vi)
return
} else if len(vi.Pages) == 0 {
log.Error("len(vi.Pages) == 0 aid(%d)", aid)
return
}
for _, page := range vi.Pages {
if cid == page.Cid {
cuPage = page
break
}
}
if cuPage == nil {
log.Warn("cuPage is nil aid(%d) cid(%d) refer(%s)", aid, cid, refer)
}
s.fillArc(c, cid, pi, vi, cuPage, ip, now)
withU = s.fillAcc(c, pi, vi, mid, cid, ip, now)
// template
var doc = bytes.NewBuffer(nil)
if withU {
s.tWithU.Execute(doc, pi)
} else {
s.tNoU.Execute(doc, pi)
}
if s.params != "" {
doc.WriteString(s.params)
}
res = doc.Bytes()
return
}
func (s *Service) fillAcc(c context.Context, pi *model.Player, vi *arcmdl.ViewReply, mid, cid int64, ip string, now time.Time) (withU bool) {
if mid == 0 {
return
}
var (
proReply *accmdl.ProfileStatReply
pro map[int64]*history.History
err error
)
if proReply, err = s.accClient.ProfileWithStat3(c, &accmdl.MidReq{Mid: mid}); err != nil {
dao.PromError("UserInfo接口错误", "s.acc.UserInfo(%v) error(%v)", mid, err)
return
}
if proReply != nil {
withU = true
var nameBu = bytes.NewBuffer(nil)
if err = xml.EscapeText(nameBu, []byte(proReply.Profile.Name)); err != nil {
log.Error("xml.EscapeText(%s) error(%v)", proReply.Profile.Name, err)
} else {
pi.Name = nameBu.String()
}
pi.User = proReply.Profile.Mid
pi.UserHash = midCrc(proReply.Profile.Mid)
pi.Money = fmt.Sprintf("%.2f", proReply.Coins)
pi.Face = strings.Replace(proReply.Profile.Face, "http://", "//", 1)
var bs []byte
if bs, err = json.Marshal(proReply.LevelInfo); err != nil {
log.Error("json.Marshal(%v) error(%v)", proReply.LevelInfo, err)
} else {
pi.LevelInfo = template.HTML(bs)
}
vip := model.VIPInfo{Type: proReply.Profile.Vip.Type, DueDate: proReply.Profile.Vip.DueDate, VipStatus: proReply.Profile.Vip.Status}
if bs, err = json.Marshal(vip); err != nil {
log.Error("json.Marshal(%v) error(%v)", vip, err)
} else {
pi.Vip = template.HTML(bs)
}
off := &model.Official{Type: -1}
if proReply.Profile.Official.Role != 0 {
if proReply.Profile.Official.Role <= 2 {
off.Type = 0
} else {
off.Type = 1
}
off.Desc = proReply.Profile.Official.Title
}
if bs, err = json.Marshal(off); err != nil {
log.Error("json.Marshal(%v) error(%v)", off, err)
} else {
pi.OfficialVerify = template.HTML(bs)
}
group, errCtx := errgroup.WithContext(c)
if vi.Arc != nil {
pi.Upermission = userPermission(vi.Arc, proReply)
// NOTE: if vInfo==nil, no admin
if mid == vi.Arc.Author.Mid {
pi.IsAdmin = true
}
group.Go(func() error {
arg := &history.ArgPro{Mid: mid, RealIP: ip, Aids: []int64{vi.Arc.Aid}}
if pro, err = s.his.Progress(errCtx, arg); err != nil {
dao.PromError("Progress接口错误", "s.his.Progress(%d,%d) error(%v)", mid, vi.Arc.Aid, err)
} else if progress, ok := pro[vi.Arc.Aid]; ok && progress != nil && progress.Cid > 0 && progress.Cid == cid {
if progress.Pro >= 0 {
pi.LastPlayTime = 1000 * progress.Pro
pi.LastCid = progress.Cid
} else if len(vi.Pages) != 0 {
for _, page := range vi.Pages {
if page.Cid == progress.Cid {
pi.LastPlayTime = 1000 * page.Duration
pi.LastCid = progress.Cid
break
}
}
}
}
return nil
})
}
if s.c.Rule.NoAssistMid != vi.Arc.Author.Mid {
group.Go(func() error {
if assist, err := s.ass.Assist(errCtx, &assist.ArgAssist{Mid: vi.Arc.Author.Mid, AssistMid: proReply.Profile.Mid, Type: assist.TypeDm, RealIP: ip}); err != nil {
dao.PromError("Assist接口错误", "s.ass.Assist(%d,%d) error(%v)", vi.Arc.Author.Mid, proReply.Profile.Mid, err)
} else {
pi.Role = strconv.FormatInt(assist.Assist, 10)
}
return nil
})
}
if proReply.Profile.Silence == _accBanSta {
group.Go(func() error {
if blockTime, err := s.dao.BlockTime(errCtx, mid); err != nil {
dao.PromError("BlockTime接口错误", "s.dao.BlockTime(%d) error(%v)", mid, err)
} else if blockTime != nil {
if blockTime.BlockStatus == _accBlockSta {
pi.BlockTime = blockTime.BlockedEnd - now.Unix()
if blockTime.BlockedForever || blockTime.BlockedEnd == 0 {
pi.BlockTime = _mockBlockTime
}
}
}
return nil
})
}
group.Wait()
}
return
}
func (s *Service) fillArc(c context.Context, cid int64, pi *model.Player, vi *arcmdl.ViewReply, page *arcmdl.Page, ip string, now time.Time) {
// 稿件和其弹幕信息
pi.Aid = vi.Arc.Aid
pi.Typeid = vi.Arc.TypeID
if page != nil {
if page.From != "sina" {
pi.Vtype = page.From
} else {
pi.Vtype = ""
}
pi.Maxlimit = dmLimit(page.Duration)
pi.Chatid = page.Cid
pi.Oriurl = oriURL(page.From, page.Vid)
pi.Pid = int64(page.Page)
} else {
pi.Chatid = cid
pi.Maxlimit = 1500
pi.Pid = 1
}
pi.Arctype = _copyRightMap[vi.Arc.Copyright]
pi.SuggestComment = false
pi.Click = int(vi.Arc.Stat.View)
group, errCtx := errgroup.WithContext(c)
group.Go(func() error {
if click, err := s.arc.Click3(errCtx, &archive.ArgAid2{Aid: vi.Arc.Aid}); err != nil {
dao.PromError("Click接口错误", "s.arc.Click2(%d) error(%v)", vi.Arc.Aid, err)
} else if click != nil {
pi.FwClick = click.H5 + click.Outter
}
return nil
})
pi.OnlineCount = 1
group.Go(func() error {
if onlineCount, err := s.dao.OnlineCount(errCtx, pi.Aid, cid); err == nil && onlineCount > 1 {
pi.OnlineCount = onlineCount
}
return nil
})
group.Go(func() error {
pi.MaskNew = s.dmMask(errCtx, cid)
return nil
})
group.Go(func() error {
pi.Subtitle = s.dmSubtitle(errCtx, pi.Aid, cid)
return nil
})
group.Go(func() error {
if ipInfo, e := s.loc.Info(errCtx, &locmdl.ArgIP{IP: ip}); e != nil {
log.Error("fillArc s.loc.Info(%s) error(%v)", ip, e)
} else if ipInfo != nil {
pi.Zoneid = ipInfo.ZoneID
pi.Country = ipInfo.Country
pi.Acceptaccel = ipInfo.Country != _china && ipInfo.Country != _local
pi.Cache = ipInfo.Country != _china && ipInfo.Country != _local
}
return nil
})
if vi.Arc.AttrVal(archive.AttrBitHasViewpoint) == archive.AttrYes {
group.Go(func() error {
pi.ViewPoints = s.viewPoints(errCtx, pi.Aid, cid)
return nil
})
}
group.Go(func() error {
pi.PlayerIcon = s.tagPlayerIcon(errCtx, pi.Aid, ip)
return nil
})
group.Wait()
pi.Duration = formatDuration(vi.Arc.Duration)
pi.AllowBp = vi.Arc.AttrVal(archive.AttrBitAllowBp) == 1
if _, ok := _bottomMap[vi.Arc.TypeID]; ok {
pi.Bottom = 1
}
pi.Acceptguest = false
if s.BrBegin.Unix() <= now.Unix() && now.Unix() <= s.BrEnd.Unix() {
pi.BrTCP = s.c.Broadcast.TCPAddr
pi.BrWs = s.c.Broadcast.WsAddr
pi.BrWss = s.c.Broadcast.WssAddr
}
for index, pa := range vi.Pages {
if pa != nil && cid == pa.Cid && index+1 < len(vi.Pages) {
pi.HasNext = 1
}
}
}
func isAdmin(uRank int32) (b bool) {
// 32000 -> admin
// 31300 -> 评论管理员
if uRank == 31300 || uRank == 32000 {
b = true
return
}
return
}
func userPermission(a *arcmdl.Arc, u *accmdl.ProfileStatReply) (permission string) {
if u.Profile.Silence == _accBanNor || isAdmin(u.Profile.Rank) {
permission = strings.Join(append([]string{strconv.FormatInt(int64(u.Profile.Rank), 10), "1001"}), ",")
} else {
permission = "0"
}
// if a.AttrVal(archive.AttrBitNoMission) == 0 && a.Author.Mid == u.Mid {
// permission = strings.Join([]string{permission, "20000"}, ",")
// }
return
}
func oriURL(dmType, dmIndex string) (url string) {
switch dmType {
case "sina":
url = "http://p.you.video.sina.com.cn/swf/bokePlayer20131203_V4_1_42_33.swf?vid=" + dmIndex
case "youku":
url = "http://v.youku.com/v_show/id_" + dmIndex + ".html"
case "qq":
if len(dmIndex) >= 3 {
url = "http://v.qq.com/page/" + dmIndex[0:1] + "/" + dmIndex[1:2] + "/" + dmIndex[2:3] + "/" + dmIndex + ".html"
}
default:
url = ""
}
return
}
func formatDuration(duration int64) (du string) {
if duration == 0 {
du = "00:00"
} else {
var duFen, duMiao string
duFen = strconv.Itoa(int(duration / 60))
if int(duration%60) < 10 {
duMiao = "0" + strconv.Itoa(int(duration%60))
} else {
duMiao = strconv.Itoa(int(duration % 60))
}
du = duFen + ":" + duMiao
}
return
}
func midCrc(mid int64) string {
midStr := strconv.FormatInt(mid, 10)
return fmt.Sprintf("%08x", crc32.ChecksumIEEE([]byte(midStr)))
}
func dmLimit(duration int64) (limit int) {
switch {
case duration > 3600:
limit = 8000
case duration > 2400:
limit = 6000
case duration > 900:
limit = 3000
case duration > 600:
limit = 1500
case duration > 150:
limit = 1000
case duration > 60:
limit = 500
case duration > 30:
limit = 300
case duration <= 30:
limit = 100
default:
limit = 1500
}
return
}
func (s *Service) dmMask(c context.Context, cid int64) (mask template.HTML) {
if dmMask, err := s.dm2.Mask(c, &dm2.ArgMask{Cid: cid, Plat: _dmMaskPlatWeb}); err != nil {
dao.PromError("MaskList 错误", "s.dm2.MaskList cid(%d) error(%v)", cid, err)
} else if dmMask != nil && dmMask.MaskURL != "" {
dmMask.MaskURL = strings.Replace(dmMask.MaskURL, "http://", "//", 1)
if bs, err := json.Marshal(dmMask); err != nil {
log.Error("dmMask json.Marshal(%+v) error(%v)", dmMask, err)
} else {
mask = template.HTML(bs)
}
}
return
}
func (s *Service) dmSubtitle(c context.Context, aid, cid int64) (subtitle template.HTML) {
if dmSub, err := s.dm2.SubtitleGet(c, &dm2.ArgSubtitleGet{Aid: aid, Oid: cid, Type: dm2.SubTypeVideo}); err != nil {
log.Error("s.dm2.SubtitleGet aid(%d) cid(%d) error(%v)", aid, cid, err)
} else {
if dmSub != nil {
if len(dmSub.Subtitles) == 0 {
dmSub.Subtitles = make([]*dm2.VideoSubtitle, 0)
}
for _, v := range dmSub.Subtitles {
v.SubtitleURL = strings.Replace(v.SubtitleURL, "http://", "//", 1)
}
if bs, err := json.Marshal(dmSub); err != nil {
log.Error("dmSubject json.Marshal(%v) error(%v)", dmSub, err)
} else {
subtitle = template.HTML(bs)
}
}
}
return
}
func (s *Service) tagPlayerIcon(c context.Context, aid int64, ip string) (icon template.HTML) {
icon = s.icon
now := time.Now()
tags, err := s.tag.ArcTags(c, &tagmdl.ArgAid{Aid: aid, RealIP: ip})
if err != nil {
log.Error("tagPlayerIcon s.tag.ArcTags aid(%d) error(%v)", aid, err)
return
}
// TODO delete tmp logic
if now.Unix() >= s.c.Icon.Start.Unix() && now.Unix() <= s.c.Icon.End.Unix() {
for _, vt := range tags {
if _, ok := iconTagIDs[vt.ID]; ok {
playerIcon := &resmdl.PlayerIcon{
URL1: s.c.Icon.URL1,
Hash1: s.c.Icon.Hash1,
URL2: s.c.Icon.URL2,
Hash2: s.c.Icon.Hash2,
}
bs, err := json.Marshal(playerIcon)
if err != nil {
log.Error("tagPlayerIcon json.Marshal(%v) error(%v)", playerIcon, err)
continue
}
icon = template.HTML(bs)
break
}
}
}
return
}
func (s *Service) viewPoints(c context.Context, aid, cid int64) (points template.HTML) {
if data, err := s.dao.ViewPoints(c, aid, cid); err != nil {
log.Error("s.dao.ViewPoints aid(%d) cid(%d) error(%v)", aid, cid, err)
} else if len(data) > 0 {
if bs, err := json.Marshal(data); err != nil {
log.Error("viewPoints json.Marshal(%v) error(%v)", data, err)
} else {
points = template.HTML(bs)
}
}
return
}
func (s *Service) view(c context.Context, aid int64) (data *arcmdl.ViewReply, err error) {
if view, ok := s.bnj2019ViewMap[aid]; ok && view != nil {
data = view
return
}
return s.arcClient.View(c, &arcmdl.ViewRequest{Aid: aid})
}

View File

@@ -0,0 +1,61 @@
package service
import (
"context"
"flag"
"path/filepath"
"testing"
"time"
"go-common/app/interface/main/player/conf"
. "github.com/smartystreets/goconvey/convey"
)
var svf *Service
func WithService(f func(s *Service)) func() {
return func() {
dir, _ := filepath.Abs("../cmd/player-test.toml")
flag.Set("conf", dir)
conf.Init()
if svf == nil {
svf = New(conf.Conf)
}
time.Sleep(2 * time.Second)
f(svf)
}
}
func TestMidCrc(t *testing.T) {
expects := map[int64]string{
8167601: "2425c296",
123456: "0972d361",
}
for mid, crc := range expects {
if midCrc(mid) != crc {
t.Errorf("crc %v expect %s got %s", mid, crc, midCrc(mid))
}
}
}
func TestService_Carousel(t *testing.T) {
Convey("carousel len should > 0", t, WithService(func(svf *Service) {
carousel, err := svf.Carousel(context.Background())
So(err, ShouldBeNil)
So(len(carousel), ShouldBeGreaterThan, 0)
}))
}
func TestService_Player(t *testing.T) {
Convey("player should return without err", t, WithService(func(svf *Service) {
player, err := svf.Player(context.Background(), 0, 10097666, 10108404, "127.0.0.1", "", time.Now())
So(err, ShouldBeNil)
So(len(player), ShouldBeGreaterThan, 0)
}))
Convey("player should return without err", t, WithService(func(svf *Service) {
player, err := svf.Player(context.Background(), 0, 10010666, 10108404, "127.0.0.1", "", time.Now())
So(err, ShouldBeNil)
So(len(player), ShouldBeGreaterThan, 0)
}))
}

View File

@@ -0,0 +1,136 @@
package service
import (
"context"
"go-common/app/interface/main/player/model"
accmdl "go-common/app/service/main/account/api"
arcmdl "go-common/app/service/main/archive/api"
"go-common/app/service/main/archive/model/archive"
ugcmdl "go-common/app/service/main/ugcpay/api/grpc/v1"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_playurlURI = "/v2/playurl"
_playurlURIV3 = "/v3/playurl"
_h5PlayURI = "/playurl"
_highQaURI = "/v2/playurlproj"
_ugcPayOtypeArc = "archive"
_relationPaid = "paid"
)
// Playurl get playurl data.
func (s *Service) Playurl(c context.Context, mid int64, arg *model.PlayurlArg) (data *model.PlayurlRes, err error) {
var (
token, playurl string
isUGCPayArc bool
viewReply *arcmdl.ViewReply
)
if arg.HTML5 > 0 {
if arg.HighQuality > 0 {
playurl = s.highQaURL
} else {
playurl = s.h5PlayURL
}
} else {
if viewReply, err = s.view(c, arg.Aid); err != nil {
log.Error("Playurl s.arcClient.Arc aid(%d) error(%v)", arg.Aid, err)
return
}
arc := viewReply.Arc
if !arc.IsNormal() || !hasCid(viewReply.Pages, arg.Cid) {
err = ecode.NothingFound
log.Warn("Playurl verifyArchive aid(%d) can not play or no cid(%d)", arg.Aid, arg.Cid)
return
}
if arc.AttrVal(archive.AttrBitIsPGC) == archive.AttrYes || arc.AttrVal(archive.AttrBitBadgepay) == archive.AttrYes {
err = ecode.NothingFound
log.Warn("Playurl verifyArchive aid(%d) cid(%d) is pgc", arg.Aid, arg.Cid)
return
}
if arc.AttrVal(archive.AttrBitUGCPay) == archive.AttrYes {
if mid <= 0 {
err = ecode.PlayURLNotLogin
return
} else if arc.Author.Mid != mid {
var relation *ugcmdl.AssetRelationResp
if relation, err = s.ugcPayClient.AssetRelation(c, &ugcmdl.AssetRelationReq{Mid: mid, Oid: arg.Aid, Otype: _ugcPayOtypeArc}); err != nil {
log.Error("Playurl AssetRelation mid:%d aid:%d error(%+v)", mid, arg.Aid, err)
err = ecode.PlayURLNotPay
return
} else if relation.State != _relationPaid {
log.Warn("Playurl not pay aid(%d) mid(%d) state(%s)", arg.Aid, mid, relation.State)
err = ecode.PlayURLNotPay
return
}
}
isUGCPayArc = true
}
if isUGCPayArc || arg.Aid%10 < s.c.Rule.PlayurlGray {
playurl = s.playURLV3
if mid > 0 {
if arg.Qn == 0 {
arg.Qn = s.c.Rule.AutoQn
}
if _, isVipQn := s.vipQn[arg.Qn]; isVipQn {
if arc.Author.Mid != mid {
var card *accmdl.CardReply
if card, err = s.accClient.Card3(c, &accmdl.MidReq{Mid: mid}); err != nil {
log.Error("Playurl s.accClient.Card3(%d) error(%+v)", mid, err)
err = nil
arg.Qn = s.c.Rule.MaxFreeQn
} else if card.Card.Vip.Status != 1 || card.Card.Vip.Type <= 0 {
arg.Qn = s.c.Rule.MaxFreeQn
}
}
}
} else {
if arg.Qn > s.c.Rule.LoginQn {
arg.Qn = s.c.Rule.LoginQn
}
}
} else {
playurl = s.playURL
if mid > 0 {
if arg.Qn == 0 {
arg.Qn = s.c.Rule.AutoQn
}
if _, isVipQn := s.vipQn[arg.Qn]; isVipQn {
if playurlToken, e := s.PlayURLToken(c, mid, arg.Aid, arg.Cid); e != nil {
log.Warn("Playurl token arg(%+v) error(%v)", arg, e)
} else if playurlToken != nil {
token = playurlToken.Token
}
}
} else {
if arg.Qn > s.c.Rule.LoginQn {
arg.Qn = s.c.Rule.LoginQn
}
}
}
}
if data, err = s.dao.Playurl(c, mid, arg, playurl, token); err != nil {
log.Error("s.dao.Playurl mid(%d) arg(%+v) token(%s) error(%+v)", mid, arg, token, err)
// h5 high quality backup
if arg.HTML5 > 0 && arg.HighQuality > 0 {
err = nil
playurl = s.h5PlayURL
arg.HighQuality = 0
if data, err = s.dao.Playurl(c, mid, arg, playurl, token); err != nil {
log.Error("s.dao.Playurl h5 backup mid(%d) arg(%+v) token(%s) error(%+v)", mid, arg, token, err)
}
}
}
return
}
func hasCid(pages []*arcmdl.Page, cid int64) bool {
for _, v := range pages {
if cid == v.Cid {
return true
}
}
return false
}

View File

@@ -0,0 +1,92 @@
package service
import (
"context"
"strconv"
"strings"
"time"
"go-common/app/interface/main/player/model"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_userRandomType = "用户随机-尾号"
)
// Policy return policy info.
func (s *Service) Policy(c context.Context, id, mid int64) (item *model.Pitem, err error) {
var policy *model.Policy
if policy, err = s.checkPolicy(id); err != nil {
log.Error("s.getPolicy(%d) err(%v)", id, err)
return
}
switch policy.Type {
case _userRandomType:
if item, err = s.userPolicy(mid, policy); err != nil {
log.Error("s.userPolicy(%d) err(%v)", mid, err)
return
}
}
return
}
func (s *Service) checkPolicy(id int64) (policy *model.Policy, err error) {
if id != 1 {
err = ecode.PLayerPolicyNotExist
return
}
policy = s.c.Policy
if time.Now().Unix() < policy.StartTime.Unix() {
err = ecode.PLayerPolicyNotStart
return
}
if time.Now().Unix() > policy.EndTime.Unix() {
err = ecode.PLayerPolicyEnded
return
}
return
}
// 用户随机-尾号 策略方法
func (s *Service) userPolicy(mid int64, policy *model.Policy) (res *model.Pitem, err error) {
var itemMap = make(map[string]*model.Pitem, len(s.c.Pitem))
for _, item := range s.c.Pitem {
item.Ver = policy.MtimeTime.Unix()
itemMap[item.ExtData] = item
}
if mid > 0 {
utail := int(mid % 100)
for _, item := range itemMap {
var (
begin int
end int
beginAndEnd []string
)
if item.ExtData == "default" {
continue
}
beginAndEnd = strings.Split(item.ExtData, "-")
if len(beginAndEnd) != 2 {
log.Error("item.ExtData error")
return
}
if begin, err = strconv.Atoi(beginAndEnd[0]); err != nil {
log.Error("item.ExtData error")
return
}
if end, err = strconv.Atoi(beginAndEnd[1]); err != nil {
log.Error("item.ExtData error")
return
}
if utail >= begin && utail < end {
res = item
return
}
}
} else {
res = itemMap["default"]
}
return
}

View File

@@ -0,0 +1,23 @@
package service
import (
"context"
"fmt"
"testing"
"go-common/app/interface/main/player/conf"
)
// test func Player
func BenchmarkPolicy(b *testing.B) {
if err := conf.Init(); err != nil {
fmt.Println(err)
}
ser := New(conf.Conf)
c := context.Background()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
ser.Policy(c, 1, 6698028)
}
})
}

View File

@@ -0,0 +1,301 @@
package service
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"fmt"
htemplate "html/template"
"strconv"
"strings"
"text/template"
"time"
dmrpc "go-common/app/interface/main/dm2/rpc/client"
hisrpc "go-common/app/interface/main/history/rpc/client"
"go-common/app/interface/main/player/conf"
"go-common/app/interface/main/player/dao"
"go-common/app/interface/main/player/model"
tagrpc "go-common/app/interface/main/tag/rpc/client"
accclient "go-common/app/service/main/account/api"
arcclient "go-common/app/service/main/archive/api"
arcrpc "go-common/app/service/main/archive/api/gorpc"
assrpc "go-common/app/service/main/assist/rpc/client"
locrpc "go-common/app/service/main/location/rpc/client"
resmdl "go-common/app/service/main/resource/model"
resrpc "go-common/app/service/main/resource/rpc/client"
ugcclient "go-common/app/service/main/ugcpay/api/grpc/v1"
"go-common/library/ecode"
"go-common/library/log"
)
const (
_resourceID = 2319
_bgColor = "#000000"
)
// Service is a service.
type Service struct {
// config
c *conf.Config
// dao
dao *dao.Dao
// rpc
arc *arcrpc.Service2
his *hisrpc.Service
ass *assrpc.Service
res *resrpc.Service
dm2 *dmrpc.Service
loc *locrpc.Service
tag *tagrpc.Service
// memory cache
caItems []*model.Item
params string
icon htemplate.HTML
// template
tWithU *template.Template
tNoU *template.Template
// broadcast
BrBegin time.Time
BrEnd time.Time
// 拜年祭相关
matOn bool
matTime time.Time
pastView *model.View
matView *model.View
// vipQn
vipQn map[int]int
// grpc client
accClient accclient.AccountClient
arcClient arcclient.ArchiveClient
ugcPayClient ugcclient.UGCPayClient
// playurl paths
playURL string
playURLV3 string
h5PlayURL string
highQaURL string
// bnj2019 view map
bnj2019ViewMap map[int64]*arcclient.ViewReply
}
// New new and return service.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
arc: arcrpc.New2(c.ArchiveRPC),
his: hisrpc.New(c.HistoryRPC),
ass: assrpc.New(c.AssistRPC),
res: resrpc.New(c.ResourceRPC),
dm2: dmrpc.New(c.Dm2RPC),
loc: locrpc.New(c.LocRPC),
tag: tagrpc.New2(c.TagRPC),
}
s.playURL = c.Host.PlayurlCo + _playurlURI
s.playURLV3 = c.Host.PlayurlCo + _playurlURIV3
s.h5PlayURL = c.Host.H5Playurl + _h5PlayURI
s.highQaURL = c.Host.HighPlayurl + _highQaURI
var err error
if s.accClient, err = accclient.NewClient(c.AccClient); err != nil {
panic(err)
}
if s.arcClient, err = arcclient.NewClient(c.ArcClient); err != nil {
panic(err)
}
if s.ugcPayClient, err = ugcclient.NewClient(c.UGCPayClient); err != nil {
panic(err)
}
s.initVipQn()
s.caItems = []*model.Item{}
// 模板
s.tWithU, _ = template.New("with_user").Parse(model.TpWithUinfo)
s.tNoU, _ = template.New("no_user").Parse(model.TpWithNoUinfo)
// broadcast
s.BrBegin, _ = time.Parse("2006-01-02 15:04:05", c.Broadcast.Begin)
s.BrEnd, _ = time.Parse("2006-01-02 15:04:05", c.Broadcast.End)
// 初始化播放器灰度配置项目
go s.policyproc()
go s.resourceproc()
go s.paramproc()
go s.iconproc()
// 拜年祭
s.matTime, _ = time.Parse(time.RFC3339, c.Matsuri.MatTime)
s.matOn = true
go s.matProc()
go s.bnj2019Viewproc()
return
}
// Ping check service health
func (s *Service) Ping(c context.Context) (err error) {
if err = s.dao.Ping(c); err != nil {
log.Error("s.dao.Ping() error(%v)", err)
}
return
}
func (s *Service) matProc() {
var (
ctx = context.Background()
err error
)
for {
var tmp *arcclient.ViewReply
if tmp, err = s.arcClient.View(ctx, &arcclient.ViewRequest{Aid: s.c.Matsuri.PastID}); err != nil || tmp == nil {
dao.PromError("View接口错误", "s.arcClientView3(%v) tmp(%v) error(%v) ", s.c.Matsuri.PastID, tmp, err)
time.Sleep(time.Second)
continue
}
s.pastView = &model.View{Arc: tmp.Arc, Pages: tmp.Pages}
if tmp, err = s.arcClient.View(ctx, &arcclient.ViewRequest{Aid: s.c.Matsuri.MatID}); err != nil || tmp == nil {
dao.PromError("View接口错误", "s.arcClientView3(%v) tmp(%v) error(%v)", s.c.Matsuri.MatID, tmp, err)
time.Sleep(time.Second)
continue
}
s.matView = &model.View{Arc: tmp.Arc, Pages: tmp.Pages}
time.Sleep(time.Duration(s.c.Matsuri.Tick))
}
}
func (s *Service) resourceproc() {
for {
var (
items []*model.Item
err error
res *resmdl.Resource
)
if res, err = s.res.Resource(context.Background(), &resmdl.ArgRes{ResID: _resourceID}); err != nil {
dao.PromError("Resource接口错误", "s.res.Resource(%d) error(%v)", _resourceID, err)
time.Sleep(time.Second)
continue
}
if res == nil {
dao.PromError("Resource接口数据为空", "s.res.Resource(%d) is nil", _resourceID)
time.Sleep(time.Second)
continue
}
if len(res.Assignments) == 0 {
dao.PromError("Resource接口Assignments数据为空", "s.res.Resource(%d) assignments is nil", _resourceID)
time.Sleep(time.Second)
continue
}
for _, v := range res.Assignments {
item := &model.Item{
Bgcolor: _bgColor,
ResourceID: strconv.Itoa(_resourceID),
SrcID: strconv.Itoa(v.ResID),
ID: strconv.Itoa(v.ID),
}
if catalog, ok := model.Catalog[v.PlayerCategory]; ok {
item.Catalog = catalog
}
item.Content = string(rune(10)) + string(rune(13)) + fmt.Sprintf(_content, v.URL, v.Name) + string(rune(10)) + string(rune(13))
items = append(items, item)
}
s.caItems = items
time.Sleep(time.Duration(s.c.Tick.CarouselTick))
}
}
func (s *Service) paramproc() {
for {
var (
params []*model.Param
items []string
err error
)
c := context.Background()
if params, err = s.dao.Param(c); err != nil {
log.Error("s.dao.Param() error(%v)", err)
time.Sleep(time.Second)
continue
}
if len(params) == 0 {
time.Sleep(time.Duration(s.c.Tick.ParamTick))
continue
}
for _, pa := range params {
nameBy := bytes.NewBuffer(nil)
valueBy := bytes.NewBuffer(nil)
if err = xml.EscapeText(nameBy, []byte(pa.Name)); err != nil {
log.Error("xml.EscapeText(%s) error(%v)", pa.Name, err)
continue
} else {
pa.Name = nameBy.String()
}
if err = xml.EscapeText(valueBy, []byte(pa.Value)); err != nil {
log.Error("xml.EscapeText(%s) error(%v)", pa.Value, err)
continue
} else {
pa.Value = valueBy.String()
}
item := "<" + pa.Name + ">" + pa.Value + "</" + pa.Name + ">"
items = append(items, item)
}
if len(items) > 0 {
s.params = strings.Join(items, "\n")
}
time.Sleep(time.Duration(s.c.Tick.ParamTick))
}
}
func (s *Service) policyproc() {
s.c.Policy.StartTime, _ = time.Parse("2006-01-02 15:04:05", s.c.Policy.Start)
s.c.Policy.EndTime, _ = time.Parse("2006-01-02 15:04:05", s.c.Policy.End)
s.c.Policy.MtimeTime, _ = time.Parse("2006-01-02 15:04:05", s.c.Policy.Mtime)
}
func (s *Service) iconproc() {
for {
icon, err := s.res.PlayerIcon(context.Background())
if err != nil || icon == nil {
log.Error("iconproc s.res.PlayerIcon error(%v) icon(%v)", err, icon)
if ecode.Cause(err) == ecode.NothingFound {
s.icon = ""
}
time.Sleep(time.Duration(s.c.Tick.IconTick))
continue
}
icon.URL1 = strings.Replace(icon.URL1, "http://", "//", 1)
icon.URL2 = strings.Replace(icon.URL2, "http://", "//", 1)
bs, err := json.Marshal(icon)
if err != nil {
log.Error("iconproc json.Marshal(%v) error(%v)", icon, err)
time.Sleep(time.Second)
continue
}
s.icon = htemplate.HTML(bs)
time.Sleep(time.Duration(s.c.Tick.IconTick))
}
}
func (s *Service) initVipQn() {
tmp := make(map[int]int, len(s.c.Rule.VipQn))
for _, qn := range s.c.Rule.VipQn {
tmp[qn] = qn
}
s.vipQn = tmp
}
func (s *Service) bnj2019Viewproc() {
for {
time.Sleep(time.Duration(s.c.Bnj2019.BnjTick))
aids := append(s.c.Bnj2019.BnjListAids, s.c.Bnj2019.BnjMainAid)
if len(aids) == 0 {
continue
}
if views, err := s.arcClient.Views(context.Background(), &arcclient.ViewsRequest{Aids: aids}); err != nil || views == nil {
log.Error("bnj2019Viewproc s.arcClient.Views(%v) error(%v)", aids, err)
continue
} else {
tmp := make(map[int64]*arcclient.ViewReply, len(aids))
for _, aid := range aids {
if view, ok := views.Views[aid]; ok && view.Arc.IsNormal() {
tmp[aid] = view
}
}
s.bnj2019ViewMap = tmp
}
}
}