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

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
}
}
}