playurl.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package bvc
  2. import (
  3. "context"
  4. "fmt"
  5. "go-common/library/ecode"
  6. "go-common/library/log"
  7. bm "go-common/library/net/http/blademaster"
  8. "go-common/library/net/metadata"
  9. "go-common/library/sync/errgroup"
  10. "go-common/library/xstr"
  11. "math"
  12. "net/url"
  13. "strconv"
  14. )
  15. const livePlayURLHost = "live-playurl.bilibili.co"
  16. const livePlayURLAddr = "http://live-playurl.bilibili.co/room/v1/Room/playUrl"
  17. const livePlayURLMultiAddr = "http://%s/stream/v1/multiplayurl"
  18. type PlayUrlItem struct {
  19. RoomId int64 `json:"room_id"`
  20. CurrentQuality int64 `json:"current_quality"`
  21. AcceptQuality []int64 `json:"accept_quality"`
  22. Url map[string]string `json:"url"`
  23. }
  24. type respPlayUrl struct {
  25. Code int `json:"code"`
  26. Msg string `json:"msg"`
  27. Data []*PlayUrlItem `json:"data"`
  28. }
  29. // 历史原因,单个的接口和视频云@yuanfeng确认数据结构不统一,后面统一,暂时先不用
  30. // @see http://live-hub.bilibili.co/doc/playurl/
  31. func (c *Client) GetPlayUrl(ctx *bm.Context, cid, quality, ptype, needHttps, unicomFree int64, platform string) (resp *respPlayUrl) {
  32. cli := bm.NewClient(c.getConf())
  33. param := url.Values{}
  34. param.Set("cid", strconv.FormatInt(cid, 10))
  35. param.Set("quality", strconv.FormatInt(quality, 10))
  36. param.Set("platform", platform)
  37. param.Set("ptype", strconv.FormatInt(ptype, 10))
  38. param.Set("https_url_req", strconv.FormatInt(needHttps, 10))
  39. param.Set("unicom_free", strconv.FormatInt(unicomFree, 10))
  40. err := cli.Get(ctx, livePlayURLAddr, "", param, &resp)
  41. if err != nil {
  42. log.Error("call %s error(%v)", livePlayURLAddr, err)
  43. return
  44. }
  45. if resp.Code != ecode.OK.Code() {
  46. log.Error("call %s response error, code(%d) message(%s)", livePlayURLAddr, resp.Code, resp.Msg)
  47. return
  48. }
  49. return
  50. }
  51. // 批量接口,一次最好20个
  52. func (c *Client) GetPlayUrlMulti(ctx context.Context, roomIds []int64,
  53. needHttps, quality, build int64, platform string) (result map[int64]*PlayUrlItem) {
  54. cli := bm.NewClient(c.getConf())
  55. result = make(map[int64]*PlayUrlItem)
  56. userIp := metadata.String(ctx, metadata.RemoteIP)
  57. if userIp == "127.0.0.1" || c.mock == "1" {
  58. userIp = "222.73.196.18"
  59. }
  60. bvcHost := c.getBvcHost(livePlayURLHost)
  61. if quality == 0 {
  62. quality = 4
  63. }
  64. chunkSize := 20
  65. lens := len(roomIds)
  66. if lens <= 0 {
  67. return
  68. }
  69. // 批次
  70. chunkNum := int(math.Ceil(float64(lens) / float64(chunkSize)))
  71. wg, _ := errgroup.WithContext(ctx)
  72. // 空间换时间
  73. chunkResult := make([][]*PlayUrlItem, chunkNum)
  74. for i := 1; i <= chunkNum; i++ {
  75. x := i
  76. wg.Go(func() error {
  77. param := url.Values{}
  78. chunkRoomIds := make([]int64, 20)
  79. if x == chunkNum {
  80. chunkRoomIds = roomIds[(x-1)*chunkSize:]
  81. } else {
  82. chunkRoomIds = roomIds[(x-1)*chunkSize : x*chunkSize]
  83. }
  84. param.Set("room_ids", xstr.JoinInts(chunkRoomIds))
  85. param.Set("platform", platform)
  86. param.Set("https_url_req", strconv.FormatInt(needHttps, 10))
  87. param.Set("quality", strconv.FormatInt(quality, 10))
  88. param.Set("build", strconv.FormatInt(build, 10))
  89. param.Set("req_biz", "live-app-interface")
  90. var resp respPlayUrl
  91. bvcUrl := fmt.Sprintf(livePlayURLMultiAddr, bvcHost)
  92. req, err := cli.NewRequest("GET", bvcUrl, userIp, param)
  93. if err != nil {
  94. log.Error("GetPlayUrlMulti client.NewRequest: get error(%v)", err)
  95. return nil
  96. }
  97. //req.Header.Add("x-backend-bili-real-ip", userIp)
  98. if err := cli.Do(ctx, req, &resp); err != nil {
  99. log.Error("GetPlayUrlMulti client.Do: get error(%v)", err)
  100. return nil
  101. }
  102. if resp.Code != 0 {
  103. log.Error("GetPlayUrlMulti call %s response error, code(%d) message(%s) param(%+v) room_ids(%+v)", bvcUrl, resp.Code, resp.Msg, param, chunkRoomIds)
  104. return nil
  105. }
  106. if resp.Data == nil || len(resp.Data) <= 0 {
  107. log.Warn("GetPlayUrlMulti call %s response empty, code(%d) message(%s) param(%+v) room_ids(%+v)", bvcUrl, resp.Code, resp.Msg, param, chunkRoomIds)
  108. return nil
  109. }
  110. //chunkResult = append(chunkResult, resp.Data)
  111. chunkResult[x-1] = resp.Data
  112. return nil
  113. })
  114. }
  115. if err := wg.Wait(); err != nil {
  116. log.Error("GetPlayUrlMulti get playurl wait error(%+v)", err)
  117. return nil
  118. }
  119. // 整理数据
  120. for _, chunkItemList := range chunkResult {
  121. for _, item := range chunkItemList {
  122. if item != nil {
  123. result[item.RoomId] = item
  124. }
  125. }
  126. }
  127. return
  128. }