video.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. package service
  2. import (
  3. "context"
  4. "html/template"
  5. "sync"
  6. "go-common/app/interface/main/playlist/conf"
  7. "go-common/app/interface/main/playlist/dao"
  8. "go-common/app/interface/main/playlist/model"
  9. arcmdl "go-common/app/service/main/archive/api"
  10. "go-common/app/service/main/archive/model/archive"
  11. favmdl "go-common/app/service/main/favorite/model"
  12. "go-common/library/ecode"
  13. "go-common/library/log"
  14. "go-common/library/net/metadata"
  15. "go-common/library/sync/errgroup"
  16. )
  17. const (
  18. _vUpload = "vupload"
  19. _aidBulkSize = 50
  20. )
  21. var _empAids = make([]int64, 0)
  22. // Videos get playlist video list by pid.
  23. func (s *Service) Videos(c context.Context, pid int64, pn, ps int) (res *model.ArcList, err error) {
  24. var (
  25. aids []int64
  26. arcSorts []*model.ArcSort
  27. arcs map[int64]*arcmdl.ViewReply
  28. stat *model.PlStat
  29. ip = metadata.String(c, metadata.RemoteIP)
  30. )
  31. res = &model.ArcList{List: make([]*model.PlView, 0)}
  32. if stat, err = s.plByPid(c, pid); err != nil || stat == nil {
  33. return
  34. }
  35. if _, err = s.fav.Folder(c, &favmdl.ArgFolder{Type: favmdl.TypePlayVideo, Mid: stat.Mid, Fid: stat.Fid, RealIP: ip}); err != nil {
  36. dao.PromError("Folder接口错误", "s.fav.Folder(%d,%d) error(%v)", stat.Mid, stat.Fid, err)
  37. return
  38. }
  39. start := (pn - 1) * ps
  40. end := start + ps - 1
  41. if arcSorts, err = s.videos(c, pid, start, end); err != nil {
  42. return
  43. }
  44. //TODO check aids from fav
  45. for _, v := range arcSorts {
  46. aids = append(aids, v.Aid)
  47. }
  48. if arcs, err = s.views(c, aids, ip); err != nil {
  49. log.Error("s.arc.Views3(%v) error(%v)", aids, err)
  50. return
  51. }
  52. for _, v := range arcSorts {
  53. if arc, ok := arcs[v.Aid]; ok {
  54. view := &model.PlView{View: &model.View{Arc: arc.Arc, Pages: arc.Pages}, PlayDesc: template.HTMLEscapeString(v.Desc)}
  55. res.List = append(res.List, view)
  56. }
  57. }
  58. return
  59. }
  60. // ToView get playlist view page data.
  61. func (s *Service) ToView(c context.Context, mid, pid int64) (res *model.ToView, err error) {
  62. var (
  63. aids []int64
  64. arcSorts []*model.ArcSort
  65. views map[int64]*arcmdl.ViewReply
  66. info *model.Playlist
  67. ip = metadata.String(c, metadata.RemoteIP)
  68. )
  69. if info, err = s.Info(c, mid, pid); err != nil {
  70. return
  71. }
  72. res = &model.ToView{Playlist: info}
  73. if arcSorts, err = s.videos(c, pid, 0, info.Count-1); err != nil {
  74. return
  75. }
  76. //TODO check aids from fav
  77. for _, v := range arcSorts {
  78. aids = append(aids, v.Aid)
  79. }
  80. if views, err = s.views(c, aids, ip); err != nil {
  81. log.Error("s.views(%v) error(%v)", aids, err)
  82. return
  83. }
  84. res.List = make([]*model.View, 0)
  85. for _, v := range arcSorts {
  86. if arc, ok := views[v.Aid]; ok {
  87. view := &model.View{Arc: arc.Arc, Pages: arc.Pages}
  88. res.List = append(res.List, view)
  89. }
  90. }
  91. return
  92. }
  93. // CheckVideo add video to playlist.
  94. func (s *Service) CheckVideo(c context.Context, mid, pid int64, aids []int64) (videos model.Videos, err error) {
  95. var (
  96. stat *model.PlStat
  97. fav *favmdl.Folder
  98. ip = metadata.String(c, metadata.RemoteIP)
  99. )
  100. if stat, err = s.plByPid(c, pid); err != nil {
  101. return
  102. }
  103. if stat == nil {
  104. err = ecode.PlNotExist
  105. dao.PromError("CheckVideo:播单不存在", "s.fav.Folder(%d) error(%v)", pid, err)
  106. return
  107. }
  108. argFolder := &favmdl.ArgFolder{Type: favmdl.TypePlayVideo, Fid: stat.Fid, Mid: stat.Mid, Vmid: 0, RealIP: ip}
  109. if fav, err = s.fav.Folder(c, argFolder); err != nil || fav == nil {
  110. dao.PromError("CheckVideo收藏Forder:rpc错误", "s.fav.Folder(%+v) error(%v)", argFolder, err)
  111. return
  112. }
  113. if videos, _, _, err = s.filterArc(c, mid, pid, aids, ip); err != nil {
  114. log.Error("s.filterArc(%v) error(%v)", aids, err)
  115. }
  116. return
  117. }
  118. // AddVideo add video to playlist.
  119. func (s *Service) AddVideo(c context.Context, mid, pid int64, aids []int64) (videos model.Videos, err error) {
  120. var (
  121. lastID, sort, fid int64
  122. arcSorts []*model.ArcSort
  123. ip = metadata.String(c, metadata.RemoteIP)
  124. )
  125. if videos, sort, fid, err = s.filterArc(c, mid, pid, aids, ip); err != nil {
  126. log.Error("s.filterArc(%v) error(%v)", aids, err)
  127. return
  128. }
  129. if len(videos.RightAids) == 0 {
  130. return
  131. }
  132. for _, aid := range videos.RightAids {
  133. sort += conf.Conf.Rule.SortStep
  134. arcSorts = append(arcSorts, &model.ArcSort{Aid: aid, Sort: sort, Desc: ""})
  135. }
  136. arg := &favmdl.ArgMultiAdd{Type: favmdl.TypePlayVideo, Mid: mid, Oids: videos.RightAids, Fid: fid, RealIP: ip}
  137. if err = s.fav.MultiAdd(c, arg); err != nil {
  138. dao.PromError("添加播单视频rpc错误", "s.fav.MultiAdd(%+v) error(%v)", arg, err)
  139. return
  140. }
  141. if lastID, err = s.dao.BatchAddArc(c, pid, arcSorts); err != nil || lastID == 0 {
  142. log.Error("s.dao.BatchAddArc(%d,%+v) error(%v)", pid, arcSorts, err)
  143. return
  144. }
  145. if lastID > 0 {
  146. s.cache.Save(func() {
  147. s.dao.SetArcsCache(context.Background(), pid, arcSorts)
  148. })
  149. }
  150. s.updatePlTime(c, mid, pid)
  151. return
  152. }
  153. // DelVideo del video from playlist.
  154. func (s *Service) DelVideo(c context.Context, mid, pid int64, aids []int64) (err error) {
  155. var (
  156. affected int64
  157. stat *model.PlStat
  158. ip = metadata.String(c, metadata.RemoteIP)
  159. )
  160. if stat, err = s.plByPid(c, pid); err != nil {
  161. return
  162. }
  163. if stat == nil {
  164. err = ecode.PlNotExist
  165. dao.PromError("DelVideo:播单不存在", "s.fav.Folder(%d) error(%v)", pid, err)
  166. return
  167. }
  168. arg := &favmdl.ArgMultiDel{Type: favmdl.TypePlayVideo, Mid: mid, Oids: aids, Fid: stat.Fid, RealIP: ip}
  169. if err = s.fav.MultiDel(c, arg); err != nil {
  170. dao.PromError("删除播单视频rpc错误", "s.fav.MultiDel(%+v) error(%v)", arg, err)
  171. return
  172. }
  173. if affected, err = s.dao.BatchDelArc(c, pid, aids); err != nil || affected == 0 {
  174. log.Error("s.dao.BatchDelArc(%d,%v) error(%v)", mid, aids, err)
  175. return
  176. }
  177. if affected > 0 {
  178. s.cache.Save(func() {
  179. s.dao.DelArcsCache(context.Background(), pid, aids)
  180. })
  181. }
  182. s.updatePlTime(c, mid, pid)
  183. return
  184. }
  185. // SortVideo sort playlist video.
  186. func (s *Service) SortVideo(c context.Context, mid, pid, aid, sort int64) (err error) {
  187. var (
  188. info *favmdl.Favorites
  189. aidSort, preSort, afSort, orderNum, affected int64
  190. desc string
  191. arcs []*model.ArcSort
  192. plStat *model.PlStat
  193. top, bottom, isPlaylist, reset bool
  194. ip = metadata.String(c, metadata.RemoteIP)
  195. )
  196. if plStat, err = s.plByPid(c, pid); err != nil {
  197. return
  198. }
  199. if plStat == nil {
  200. err = ecode.PlNotExist
  201. dao.PromError("SortVideo:播单不存在", "s.fav.Folder(%d) error(%v)", pid, err)
  202. return
  203. }
  204. if plStat.ID == 0 {
  205. err = ecode.PlNotExist
  206. return
  207. } else if mid != plStat.Mid {
  208. err = ecode.PlNotUser
  209. return
  210. }
  211. if info, err = s.fav.Favorites(c, &favmdl.ArgFavs{Type: favmdl.TypePlayVideo, Mid: mid, Fid: plStat.Fid, Pn: 1, Ps: 1, RealIP: ip}); err != nil {
  212. dao.PromError("获取播单信息rpc错误", "s.fav.Favorites(%d,%d) error(%v)", mid, plStat.Fid, err)
  213. return
  214. } else if sort > int64(info.Page.Count) {
  215. err = ecode.PlSortOverflow
  216. return
  217. }
  218. if isPlaylist, err = s.fav.IsFavedByFid(c, &favmdl.ArgIsFavedByFid{Type: favmdl.TypePlayVideo, Mid: mid, Fid: plStat.Fid, Oid: aid, RealIP: ip}); err != nil {
  219. dao.PromError("播单下视频rpc错误", "s.fav.IsFavedByFid(%d,%d,%d) error(%v)", mid, plStat.Fid, aid, err)
  220. return
  221. } else if !isPlaylist {
  222. err = ecode.PlVideoAlreadyDel
  223. return
  224. }
  225. if arcs, err = s.videos(c, pid, 0, info.Page.Count-1); err != nil {
  226. return
  227. }
  228. if sort == _first {
  229. top = true
  230. } else if sort == int64(info.Page.Count) {
  231. bottom = true
  232. }
  233. for k, v := range arcs {
  234. if k == 0 && top {
  235. afSort = v.Sort
  236. }
  237. if k == len(arcs)-1 && bottom {
  238. preSort = v.Sort
  239. }
  240. if aid == v.Aid {
  241. if sort == int64(k+1) {
  242. return
  243. }
  244. aidSort = v.Sort
  245. desc = v.Desc
  246. }
  247. if sort == int64(k+1) {
  248. if !top && !bottom {
  249. if aidSort > sort {
  250. preSort = arcs[k].Sort
  251. afSort = arcs[k+1].Sort
  252. } else {
  253. preSort = arcs[k-1].Sort
  254. afSort = arcs[k].Sort
  255. }
  256. }
  257. }
  258. }
  259. if top {
  260. orderNum = afSort / 2
  261. } else if bottom {
  262. orderNum = preSort + conf.Conf.Rule.SortStep
  263. } else {
  264. orderNum = preSort + (afSort-preSort)/2
  265. }
  266. if orderNum == preSort || orderNum == afSort || orderNum <= conf.Conf.Rule.MinSort || orderNum > s.maxSort {
  267. reset = true
  268. if affected, err = s.resetArcSort(c, pid); err != nil {
  269. dao.PromError("重置视频排序错误", "s.dao.UpdateArcSort(%d,%d) error(%v)", pid, aid, err)
  270. return
  271. }
  272. } else {
  273. if affected, err = s.dao.UpdateArcSort(c, pid, aid, orderNum); err != nil {
  274. dao.PromError("更新视频排序错误", "s.dao.UpdateArcSort(%d,%d) error(%v)", pid, aid, err)
  275. return
  276. }
  277. }
  278. if affected > 0 {
  279. s.cache.Save(func() {
  280. if reset {
  281. if err = s.dao.DelCache(context.Background(), pid); err != nil {
  282. log.Error("s.dao.DelCache() pid(%d), error(%v)", pid, err)
  283. return
  284. }
  285. s.videos(context.Background(), pid, 0, info.Page.Count-1)
  286. } else {
  287. s.dao.AddArcCache(context.Background(), pid, &model.ArcSort{Aid: aid, Sort: orderNum, Desc: desc})
  288. }
  289. })
  290. }
  291. return
  292. }
  293. // EditVideoDesc edit playlist video desc.
  294. func (s *Service) EditVideoDesc(c context.Context, mid, pid, aid int64, desc string) (err error) {
  295. var (
  296. affected int64
  297. plStat *model.PlStat
  298. isPlaylist bool
  299. ip = metadata.String(c, metadata.RemoteIP)
  300. )
  301. if plStat, err = s.plByPid(c, pid); err != nil {
  302. return
  303. }
  304. if plStat == nil {
  305. err = ecode.PlNotExist
  306. dao.PromError("AddVideo:播单不存在", "s.fav.Folder(%d) error(%v)", pid, err)
  307. return
  308. }
  309. if plStat.ID == 0 {
  310. err = ecode.PlNotExist
  311. return
  312. } else if mid != plStat.Mid {
  313. err = ecode.PlNotUser
  314. return
  315. }
  316. if isPlaylist, err = s.fav.IsFavedByFid(c, &favmdl.ArgIsFavedByFid{Type: favmdl.TypePlayVideo, Mid: mid, Fid: plStat.Fid, Oid: aid, RealIP: ip}); err != nil {
  317. dao.PromError("播单下视频rpc错误", "s.fav.IsFavedByFid(%d,%d,%d) error(%v)", mid, plStat.Fid, aid, err)
  318. return
  319. } else if !isPlaylist {
  320. err = ecode.PlVideoAlreadyDel
  321. return
  322. }
  323. if affected, err = s.dao.UpdateArcDesc(c, pid, aid, desc); err != nil {
  324. log.Error("s.dao.UpdateArcDesc(%d,%d,%s) error(%v)", pid, aid, desc, err)
  325. return
  326. }
  327. if affected > 0 {
  328. s.cache.Save(func() {
  329. s.dao.SetArcDescCache(context.Background(), pid, aid, desc)
  330. })
  331. }
  332. s.updatePlTime(c, mid, pid)
  333. return
  334. }
  335. // SearchVideos search add videos.
  336. func (s *Service) SearchVideos(c context.Context, pn, ps int, query string) (res []*model.SearchArc, count int, err error) {
  337. if res, count, err = s.dao.SearchVideo(c, pn, ps, query); err != nil {
  338. log.Error("s.dao.SearchVideo(%s) error(%v)", query, err)
  339. }
  340. if len(res) == 0 {
  341. res = make([]*model.SearchArc, 0)
  342. }
  343. return
  344. }
  345. func (s *Service) videos(c context.Context, pid int64, start, end int) (res []*model.ArcSort, err error) {
  346. var (
  347. arcs []*model.ArcSort
  348. )
  349. if arcs, err = s.dao.ArcsCache(c, pid, start, end); err != nil || len(arcs) == 0 {
  350. err = nil
  351. if arcs, err = s.dao.Videos(c, pid); err != nil {
  352. log.Error("s.dao.Videos(%d) error(%v)", pid, err)
  353. return
  354. }
  355. length := len(arcs)
  356. if length > 0 {
  357. s.cache.Save(func() {
  358. s.dao.SetArcsCache(context.Background(), pid, arcs)
  359. })
  360. }
  361. if length < start {
  362. res = []*model.ArcSort{}
  363. return
  364. }
  365. if length > end+1 {
  366. res = arcs[start : end+1]
  367. } else {
  368. res = arcs[start:]
  369. }
  370. return
  371. }
  372. res = arcs
  373. return
  374. }
  375. func (s *Service) resetArcSort(c context.Context, pid int64) (affected int64, err error) {
  376. var (
  377. arcs, afArcs []*model.ArcSort
  378. )
  379. if arcs, err = s.dao.Videos(c, pid); err != nil {
  380. log.Error("s.dao.Videos(%d) error(%v)", pid, err)
  381. return
  382. }
  383. sort := conf.Conf.Rule.BeginSort
  384. for _, v := range arcs {
  385. sort += s.c.Rule.SortStep
  386. afArcs = append(afArcs, &model.ArcSort{Aid: v.Aid, Desc: v.Desc, Sort: sort})
  387. }
  388. affected, err = s.dao.BatchUpdateArcSort(c, pid, afArcs)
  389. return
  390. }
  391. func (s *Service) filterArc(c context.Context, mid, pid int64, aids []int64, ip string) (res model.Videos, sort, fid int64, err error) {
  392. var (
  393. mutex = sync.Mutex{}
  394. aidsLen = len(aids)
  395. rightAids, rsRight, wrongAids []int64
  396. group, errCtx = errgroup.WithContext(c)
  397. tmpArc []*model.ArcSort
  398. exists map[int64]bool
  399. stat *model.Playlist
  400. )
  401. sort = conf.Conf.Rule.BeginSort
  402. if stat, err = s.Info(c, 0, pid); err != nil {
  403. return
  404. }
  405. if mid != stat.Mid {
  406. err = ecode.PlNotUser
  407. return
  408. }
  409. fid = stat.ID
  410. exists = make(map[int64]bool, stat.Count)
  411. if stat.Count > 0 {
  412. if stat.Count > conf.Conf.Rule.MaxVideoCnt {
  413. err = ecode.PlVideoOverflow
  414. return
  415. }
  416. if tmpArc, err = s.videos(c, pid, 0, stat.Count-1); err != nil {
  417. return
  418. }
  419. for _, v := range tmpArc {
  420. exists[v.Aid] = true
  421. }
  422. if tmpLen := len(tmpArc); tmpLen < stat.Count {
  423. sort = tmpArc[tmpLen-1].Sort
  424. } else {
  425. sort = tmpArc[stat.Count-1].Sort
  426. }
  427. }
  428. tmpRight := make(map[int64]struct{})
  429. for i := 0; i < aidsLen; i += _aidBulkSize {
  430. var partAids []int64
  431. if i+_aidBulkSize > aidsLen {
  432. partAids = aids[i:]
  433. } else {
  434. partAids = aids[i : i+_aidBulkSize]
  435. }
  436. group.Go(func() (err error) {
  437. var arcs *arcmdl.ViewsReply
  438. arg := &arcmdl.ViewsRequest{Aids: partAids}
  439. if arcs, err = s.arcClient.Views(errCtx, arg); err != nil {
  440. log.Error("s.arcClient.Views(%v) error(%v)", partAids, err)
  441. return
  442. }
  443. mutex.Lock()
  444. for _, aid := range partAids {
  445. if arcReply, ok := arcs.Views[aid]; !ok || arcs.Views[aid] == nil {
  446. wrongAids = append(wrongAids, aid)
  447. } else if !arcReply.Arc.IsNormal() ||
  448. exists[aid] ||
  449. arcReply.Arc.Rights.UGCPay == 1 ||
  450. arcReply.Arc.AttrVal(archive.AttrBitIsBangumi) == archive.AttrYes ||
  451. arcReply.Arc.AttrVal(archive.AttrBitIsMovie) == archive.AttrYes ||
  452. (len(arcReply.Pages) > 0 && arcReply.Pages[0].From != _vUpload) {
  453. wrongAids = append(wrongAids, aid)
  454. } else {
  455. rightAids = append(rightAids, aid)
  456. tmpRight[aid] = struct{}{}
  457. }
  458. }
  459. mutex.Unlock()
  460. return
  461. })
  462. }
  463. err = group.Wait()
  464. if rightAids == nil {
  465. rightAids = _empAids
  466. rsRight = _empAids
  467. } else if wrongAids == nil {
  468. wrongAids = _empAids
  469. }
  470. if stat.Count+len(rightAids) > conf.Conf.Rule.MaxVideoCnt {
  471. err = ecode.PlVideoOverflow
  472. return
  473. }
  474. for _, aid := range aids {
  475. if _, ok := tmpRight[aid]; ok {
  476. rsRight = append(rsRight, aid)
  477. }
  478. }
  479. res = model.Videos{RightAids: rsRight, WrongAids: wrongAids}
  480. return
  481. }
  482. func (s *Service) views(c context.Context, aids []int64, ip string) (views map[int64]*arcmdl.ViewReply, err error) {
  483. var (
  484. mutex = sync.Mutex{}
  485. aidsLen = len(aids)
  486. group, errCtx = errgroup.WithContext(c)
  487. )
  488. views = make(map[int64]*arcmdl.ViewReply, aidsLen)
  489. for i := 0; i < aidsLen; i += _aidBulkSize {
  490. var partAids []int64
  491. if i+_aidBulkSize > aidsLen {
  492. partAids = aids[i:]
  493. } else {
  494. partAids = aids[i : i+_aidBulkSize]
  495. }
  496. group.Go(func() (err error) {
  497. var arcs *arcmdl.ViewsReply
  498. arg := &arcmdl.ViewsRequest{Aids: partAids}
  499. if arcs, err = s.arcClient.Views(errCtx, arg); err != nil {
  500. log.Error("s.arcClient.Views(%v) error(%v)", partAids, err)
  501. return
  502. }
  503. mutex.Lock()
  504. for _, v := range arcs.Views {
  505. views[v.Arc.Aid] = v
  506. }
  507. mutex.Unlock()
  508. return
  509. })
  510. }
  511. err = group.Wait()
  512. return
  513. }