videos-list-common-page.component.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import { Component, OnDestroy, OnInit } from '@angular/core'
  2. import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router'
  3. import { ComponentPaginationLight, DisableForReuseHook, MetaService, RedirectService, ServerService } from '@app/core'
  4. import { HooksService } from '@app/core/plugins/hooks.service'
  5. import { VideoService } from '@app/shared/shared-main'
  6. import { VideoFilters, VideoFilterScope } from '@app/shared/shared-video-miniature/video-filters.model'
  7. import { ClientFilterHookName, VideoSortField } from '@shared/models'
  8. import { Subscription } from 'rxjs'
  9. export type VideosListCommonPageRouteData = {
  10. sort: VideoSortField
  11. scope: VideoFilterScope
  12. hookParams: ClientFilterHookName
  13. hookResult: ClientFilterHookName
  14. }
  15. @Component({
  16. templateUrl: './videos-list-common-page.component.html'
  17. })
  18. export class VideosListCommonPageComponent implements OnInit, OnDestroy, DisableForReuseHook {
  19. getVideosObservableFunction = this.getVideosObservable.bind(this)
  20. getSyndicationItemsFunction = this.getSyndicationItems.bind(this)
  21. baseRouteBuilderFunction = this.baseRouteBuilder.bind(this)
  22. title: string
  23. titleTooltip: string
  24. groupByDate: boolean
  25. defaultSort: VideoSortField
  26. defaultScope: VideoFilterScope
  27. hookParams: ClientFilterHookName
  28. hookResult: ClientFilterHookName
  29. loadUserVideoPreferences = true
  30. displayFilters = true
  31. disabled = false
  32. private trendingDays: number
  33. private routeSub: Subscription
  34. constructor (
  35. private server: ServerService,
  36. private route: ActivatedRoute,
  37. private videoService: VideoService,
  38. private hooks: HooksService,
  39. private meta: MetaService,
  40. private redirectService: RedirectService
  41. ) {
  42. }
  43. ngOnInit () {
  44. this.trendingDays = this.server.getHTMLConfig().trending.videos.intervalDays
  45. this.routeSub = this.route.params.subscribe(params => {
  46. this.update(params['page'])
  47. })
  48. }
  49. ngOnDestroy () {
  50. if (this.routeSub) this.routeSub.unsubscribe()
  51. }
  52. getVideosObservable (pagination: ComponentPaginationLight, filters: VideoFilters) {
  53. const params = {
  54. ...filters.toVideosAPIObject(),
  55. videoPagination: pagination,
  56. skipCount: true
  57. }
  58. return this.hooks.wrapObsFun(
  59. this.videoService.getVideos.bind(this.videoService),
  60. params,
  61. 'common',
  62. this.hookParams,
  63. this.hookResult
  64. )
  65. }
  66. getSyndicationItems (filters: VideoFilters) {
  67. const result = filters.toVideosAPIObject()
  68. return this.videoService.getVideoFeedUrls(result.sort, result.isLocal)
  69. }
  70. onFiltersChanged (filters: VideoFilters) {
  71. this.buildTitle(filters.scope, filters.sort)
  72. this.updateGroupByDate(filters.sort)
  73. }
  74. baseRouteBuilder (filters: VideoFilters) {
  75. const sanitizedSort = this.getSanitizedSort(filters.sort)
  76. let suffix: string
  77. if (filters.scope === 'local') suffix = 'local'
  78. else if (sanitizedSort === 'publishedAt') suffix = 'recently-added'
  79. else suffix = 'trending'
  80. return [ '/videos', suffix ]
  81. }
  82. disableForReuse () {
  83. this.disabled = true
  84. }
  85. enabledForReuse () {
  86. this.disabled = false
  87. }
  88. update (page: string) {
  89. const data = this.getData(page)
  90. this.hookParams = data.hookParams
  91. this.hookResult = data.hookResult
  92. this.defaultSort = data.sort
  93. this.defaultScope = data.scope
  94. this.buildTitle()
  95. this.updateGroupByDate(this.defaultSort)
  96. this.meta.setTitle(this.title)
  97. }
  98. private getData (page: string) {
  99. if (page === 'trending') return this.generateTrendingData(this.route.snapshot)
  100. if (page === 'local') return this.generateLocalData()
  101. return this.generateRecentlyAddedData()
  102. }
  103. private generateRecentlyAddedData (): VideosListCommonPageRouteData {
  104. return {
  105. sort: '-publishedAt',
  106. scope: 'federated',
  107. hookParams: 'filter:api.recently-added-videos.videos.list.params',
  108. hookResult: 'filter:api.recently-added-videos.videos.list.result'
  109. }
  110. }
  111. private generateLocalData (): VideosListCommonPageRouteData {
  112. return {
  113. sort: '-publishedAt' as VideoSortField,
  114. scope: 'local',
  115. hookParams: 'filter:api.local-videos.videos.list.params',
  116. hookResult: 'filter:api.local-videos.videos.list.result'
  117. }
  118. }
  119. private generateTrendingData (route: ActivatedRouteSnapshot): VideosListCommonPageRouteData {
  120. const sort = route.queryParams['sort'] ?? this.parseTrendingAlgorithm(this.redirectService.getDefaultTrendingAlgorithm())
  121. return {
  122. sort,
  123. scope: 'federated',
  124. hookParams: 'filter:api.trending-videos.videos.list.params',
  125. hookResult: 'filter:api.trending-videos.videos.list.result'
  126. }
  127. }
  128. private parseTrendingAlgorithm (algorithm: string): VideoSortField {
  129. switch (algorithm) {
  130. case 'most-viewed':
  131. return '-trending'
  132. case 'most-liked':
  133. return '-likes'
  134. // We'll automatically apply "best" sort if using "hot" sort with a logged user
  135. case 'best':
  136. return '-hot'
  137. default:
  138. return '-' + algorithm as VideoSortField
  139. }
  140. }
  141. private updateGroupByDate (sort: VideoSortField) {
  142. this.groupByDate = sort === '-publishedAt' || sort === 'publishedAt'
  143. }
  144. private buildTitle (scope: VideoFilterScope = this.defaultScope, sort: VideoSortField = this.defaultSort) {
  145. const sanitizedSort = this.getSanitizedSort(sort)
  146. if (scope === 'local') {
  147. this.title = $localize`Local videos`
  148. this.titleTooltip = $localize`Only videos uploaded on this instance are displayed`
  149. return
  150. }
  151. if (sanitizedSort === 'publishedAt') {
  152. this.title = $localize`Recently added`
  153. this.titleTooltip = undefined
  154. return
  155. }
  156. if ([ 'hot', 'trending', 'likes', 'views' ].includes(sanitizedSort)) {
  157. this.title = $localize`Trending`
  158. if (sanitizedSort === 'hot') this.titleTooltip = $localize`Videos with the most interactions for recent videos`
  159. if (sanitizedSort === 'likes') this.titleTooltip = $localize`Videos that have the most likes`
  160. if (sanitizedSort === 'views') this.titleTooltip = undefined
  161. if (sanitizedSort === 'trending') {
  162. if (this.trendingDays === 1) this.titleTooltip = $localize`Videos with the most views during the last 24 hours`
  163. else this.titleTooltip = $localize`Videos with the most views during the last ${this.trendingDays} days`
  164. }
  165. return
  166. }
  167. }
  168. private getSanitizedSort (sort: VideoSortField) {
  169. return sort.replace(/^-/, '') as VideoSortField
  170. }
  171. }