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'
10 export type VideosListCommonPageRouteData = {
13 scope: VideoFilterScope
14 hookParams: ClientFilterHookName
15 hookResult: ClientFilterHookName
19 templateUrl: './videos-list-common-page.component.html'
21 export class VideosListCommonPageComponent implements OnInit, OnDestroy, DisableForReuseHook {
22 getVideosObservableFunction = this.getVideosObservable.bind(this)
23 getSyndicationItemsFunction = this.getSyndicationItems.bind(this)
24 baseRouteBuilderFunction = this.baseRouteBuilder.bind(this)
31 defaultSort: VideoSortField
32 defaultScope: VideoFilterScope
34 hookParams: ClientFilterHookName
35 hookResult: ClientFilterHookName
37 loadUserVideoPreferences = true
43 private trendingDays: number
44 private routeSub: Subscription
47 private server: ServerService,
48 private route: ActivatedRoute,
49 private videoService: VideoService,
50 private hooks: HooksService,
51 private meta: MetaService,
52 private redirectService: RedirectService
57 this.trendingDays = this.server.getHTMLConfig().trending.videos.intervalDays
59 this.routeSub = this.route.params.subscribe(params => {
60 this.update(params['page'])
65 if (this.routeSub) this.routeSub.unsubscribe()
68 getVideosObservable (pagination: ComponentPaginationLight, filters: VideoFilters) {
70 ...filters.toVideosAPIObject(),
72 videoPagination: pagination,
76 return this.hooks.wrapObsFun(
77 this.videoService.getVideos.bind(this.videoService),
85 getSyndicationItems (filters: VideoFilters) {
86 const result = filters.toVideosAPIObject()
88 return this.videoService.getVideoFeedUrls(result.sort, result.isLocal)
91 onFiltersChanged (filters: VideoFilters) {
92 this.buildTitle(filters.scope, filters.sort)
93 this.updateGroupByDate(filters.sort)
96 baseRouteBuilder (filters: VideoFilters) {
97 const sanitizedSort = this.getSanitizedSort(filters.sort)
101 if (filters.scope === 'local') suffix = 'local'
102 else if (sanitizedSort === 'publishedAt') suffix = 'recently-added'
103 else suffix = 'trending'
105 return [ '/videos', suffix ]
113 this.disabled = false
116 update (page: string) {
117 const data = this.getData(page)
119 this.hookParams = data.hookParams
120 this.hookResult = data.hookResult
122 this.defaultSort = data.sort
123 this.defaultScope = data.scope
126 this.updateGroupByDate(this.defaultSort)
128 this.meta.setTitle(this.title)
131 private getData (page: string) {
132 if (page === 'trending') return this.generateTrendingData(this.route.snapshot)
134 if (page === 'local') return this.generateLocalData()
136 return this.generateRecentlyAddedData()
139 private generateRecentlyAddedData (): VideosListCommonPageRouteData {
141 sort: '-publishedAt',
143 hookParams: 'filter:api.recently-added-videos.videos.list.params',
144 hookResult: 'filter:api.recently-added-videos.videos.list.result'
148 private generateLocalData (): VideosListCommonPageRouteData {
150 sort: '-publishedAt' as VideoSortField,
152 hookParams: 'filter:api.local-videos.videos.list.params',
153 hookResult: 'filter:api.local-videos.videos.list.result'
157 private generateTrendingData (route: ActivatedRouteSnapshot): VideosListCommonPageRouteData {
158 const sort = route.queryParams['sort'] ?? this.parseTrendingAlgorithm(this.redirectService.getDefaultTrendingAlgorithm())
163 hookParams: 'filter:api.trending-videos.videos.list.params',
164 hookResult: 'filter:api.trending-videos.videos.list.result'
168 private parseTrendingAlgorithm (algorithm: string): VideoSortField {
177 return '-' + algorithm as VideoSortField
181 private updateGroupByDate (sort: VideoSortField) {
182 this.groupByDate = sort === '-publishedAt' || sort === 'publishedAt'
185 private buildTitle (scope: VideoFilterScope = this.defaultScope, sort: VideoSortField = this.defaultSort) {
186 const sanitizedSort = this.getSanitizedSort(sort)
188 if (scope === 'local') {
189 this.title = $localize`Local videos`
190 this.titleTooltip = $localize`Only videos uploaded on this instance are displayed`
194 if (sanitizedSort === 'publishedAt') {
195 this.title = $localize`Recently added`
196 this.titleTooltip = undefined
200 if ([ 'best', 'hot', 'trending', 'likes' ].includes(sanitizedSort)) {
201 this.title = $localize`Trending`
203 if (sanitizedSort === 'best') this.titleTooltip = $localize`Videos with the most interactions for recent videos, minus user history`
204 if (sanitizedSort === 'hot') this.titleTooltip = $localize`Videos with the most interactions for recent videos`
205 if (sanitizedSort === 'likes') this.titleTooltip = $localize`Videos that have the most likes`
207 if (sanitizedSort === 'trending') {
208 if (this.trendingDays === 1) this.titleTooltip = $localize`Videos with the most views during the last 24 hours`
209 else this.titleTooltip = $localize`Videos with the most views during the last ${this.trendingDays} days`
216 private getSanitizedSort (sort: VideoSortField) {
217 return sort.replace(/^-/, '') as VideoSortField