switchMap(res => from(res.data)),
concatMap(videoChannel => {
return this.videoService.getVideoChannelVideos(videoChannel, this.videosPagination, this.videosSort)
- .pipe(map(data => ({ videoChannel, videos: data.videos })))
+ .pipe(map(data => ({ videoChannel, videos: data.data })))
})
)
.subscribe(({ videoChannel, videos }) => {
return this.videoService
.getAccountVideos(this.account, newPagination, this.sort)
.pipe(
- tap(({ totalVideos }) => {
- this.titlePage = this.i18n('Published {{totalVideos}} videos', { totalVideos })
+ tap(({ total }) => {
+ this.titlePage = this.i18n('Published {{total}} videos', { total })
})
)
}
private loadElements () {
this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination)
- .subscribe(({ totalVideos, videos }) => {
- this.videos = this.videos.concat(videos)
- this.pagination.totalItems = totalVideos
+ .subscribe(({ total, data }) => {
+ this.videos = this.videos.concat(data)
+ this.pagination.totalItems = total
})
}
return this.videoService
.getVideoChannelVideos(this.videoChannel, newPagination, this.sort)
.pipe(
- tap(({ totalVideos }) => {
- this.titlePage = this.i18n('Published {{totalVideos}} videos', { totalVideos })
+ tap(({ total }) => {
+ this.titlePage = this.i18n('Published {{total}} videos', { total })
})
)
}
import { fromEvent } from 'rxjs'
import { ViewportScroller } from '@angular/common'
import { PluginService } from '@app/core/plugins/plugin.service'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-app',
private redirectService: RedirectService,
private screenService: ScreenService,
private hotkeysService: HotkeysService,
- private themeService: ThemeService
+ private themeService: ThemeService,
+ private hooks: HooksService
) { }
get serverVersion () {
await this.pluginService.loadPluginsByScope('common')
- this.pluginService.runHook('action:application.loaded')
+ this.hooks.runAction('action:application.init')
}
private initHotkeys () {
import { ServerConfigResolver } from './routing/server-config-resolver.service'
import { UnloggedGuard } from '@app/core/routing/unlogged-guard.service'
import { PluginService } from '@app/core/plugins/plugin.service'
+import { HooksService } from '@app/core/plugins/hooks.service'
@NgModule({
imports: [
UnloggedGuard,
PluginService,
+ HooksService,
RedirectService,
Notifier,
--- /dev/null
+import { Injectable } from '@angular/core'
+import { PluginService } from '@app/core/plugins/plugin.service'
+import { ClientActionHookName, ClientFilterHookName } from '@shared/models/plugins/client-hook.model'
+import { from, Observable } from 'rxjs'
+import { mergeMap, switchMap } from 'rxjs/operators'
+import { ServerService } from '@app/core/server'
+import { PluginClientScope } from '@shared/models/plugins/plugin-client-scope.type'
+
+type RawFunction<U, T> = (params: U) => T
+type ObservableFunction<U, T> = RawFunction<U, Observable<T>>
+
+@Injectable()
+export class HooksService {
+ constructor (
+ private server: ServerService,
+ private pluginService: PluginService
+ ) { }
+
+ wrapObject<T, U extends ClientFilterHookName> (result: T, hookName: U) {
+ return this.pluginService.runHook(hookName, result)
+ }
+
+ wrapObsFun
+ <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
+ (fun: ObservableFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
+ return from(this.pluginService.ensurePluginsAreLoaded(scope))
+ .pipe(
+ mergeMap(() => this.wrapObject(params, hookParamName)),
+ switchMap(params => fun(params)),
+ mergeMap(result => this.pluginService.runHook(hookResultName, result, params))
+ )
+ }
+
+ async wrapFun<U, T, V extends ClientFilterHookName> (fun: RawFunction<U, T>, params: U, hookName: V) {
+ const result = fun(params)
+
+ return this.pluginService.runHook(hookName, result, params)
+ }
+
+ runAction<T, U extends ClientActionHookName> (hookName: U, params?: T) {
+ this.pluginService.runHook(hookName, params)
+ .catch((err: any) => console.error('Fatal hook error.', { err }))
+ }
+}
import { ServerConfigPlugin } from '@shared/models'
import { ServerService } from '@app/core/server/server.service'
import { ClientScript } from '@shared/models/plugins/plugin-package-json.model'
-import { PluginScope } from '@shared/models/plugins/plugin-scope.type'
import { environment } from '../../../environments/environment'
import { RegisterHookOptions } from '@shared/models/plugins/register-hook.model'
import { ReplaySubject } from 'rxjs'
import { first, shareReplay } from 'rxjs/operators'
+import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks'
+import { ClientHook, ClientHookName } from '@shared/models/plugins/client-hook.model'
+import { PluginClientScope } from '@shared/models/plugins/plugin-client-scope.type'
interface HookStructValue extends RegisterHookOptions {
plugin: ServerConfigPlugin
}
@Injectable()
-export class PluginService {
- pluginsLoaded = new ReplaySubject<boolean>(1)
+export class PluginService implements ClientHook {
+ pluginsBuilt = new ReplaySubject<boolean>(1)
+
+ pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
+ common: new ReplaySubject<boolean>(1),
+ 'video-watch': new ReplaySubject<boolean>(1)
+ }
private plugins: ServerConfigPlugin[] = []
private scopes: { [ scopeName: string ]: PluginInfo[] } = {}
- private loadedPlugins: { [ name: string ]: boolean } = {}
private loadedScripts: { [ script: string ]: boolean } = {}
- private loadedScopes: PluginScope[] = []
+ private loadedScopes: PluginClientScope[] = []
private hooks: { [ name: string ]: HookStructValue[] } = {}
this.buildScopeStruct()
- this.pluginsLoaded.next(true)
+ this.pluginsBuilt.next(true)
})
}
- ensurePluginsAreLoaded () {
- return this.pluginsLoaded.asObservable()
+ ensurePluginsAreBuilt () {
+ return this.pluginsBuilt.asObservable()
+ .pipe(first(), shareReplay())
+ .toPromise()
+ }
+
+ ensurePluginsAreLoaded (scope: PluginClientScope) {
+ return this.pluginsLoaded[scope].asObservable()
.pipe(first(), shareReplay())
.toPromise()
}
}
}
- async loadPluginsByScope (scope: PluginScope, isReload = false) {
+ async loadPluginsByScope (scope: PluginClientScope, isReload = false) {
try {
- await this.ensurePluginsAreLoaded()
+ await this.ensurePluginsAreBuilt()
if (!isReload) this.loadedScopes.push(scope)
}
await Promise.all(promises)
+
+ this.pluginsLoaded[scope].next(true)
} catch (err) {
console.error('Cannot load plugins by scope %s.', scope, err)
}
}
- async runHook (hookName: string, param?: any) {
- let result = param
-
- if (!this.hooks[hookName]) return result
+ async runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T> {
+ if (!this.hooks[hookName]) return Promise.resolve(result)
- const wait = hookName.startsWith('static:')
+ const hookType = getHookType(hookName)
for (const hook of this.hooks[hookName]) {
- try {
- const p = hook.handler(param)
-
- if (wait) {
- result = await p
- } else if (p.catch) {
- p.catch((err: Error) => {
- console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.plugin, hook.clientScript, err)
- })
- }
- } catch (err) {
- console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.plugin, hook.clientScript, err)
- }
+ console.log('Running hook %s of plugin %s.', hookName, hook.plugin.name)
+
+ result = await internalRunHook(hook.handler, hookType, result, params, err => {
+ console.error('Cannot run hook %s of script %s of plugin %s.', hookName, hook.clientScript.script, hook.plugin.name, err)
+ })
}
return result
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { immutableAssign } from '@app/shared/misc/utils'
import { Video } from '@app/shared/video/video.model'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-search',
private metaService: MetaService,
private notifier: Notifier,
private searchService: SearchService,
- private authService: AuthService
+ private authService: AuthService,
+ private hooks: HooksService
) { }
get user () {
search () {
forkJoin([
- this.searchService.searchVideos(this.currentSearch, this.pagination, this.advancedSearch),
- this.searchService.searchVideoChannels(this.currentSearch, immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage }))
+ this.getVideosObs(),
+ this.getVideoChannelObs()
])
.subscribe(
([ videosResult, videoChannelsResult ]) => {
this.results = this.results
.concat(videoChannelsResult.data)
- .concat(videosResult.videos)
- this.pagination.totalItems = videosResult.totalVideos + videoChannelsResult.total
+ .concat(videosResult.data)
+ this.pagination.totalItems = videosResult.total + videoChannelsResult.total
// Focus on channels if there are no enough videos
- if (this.firstSearch === true && videosResult.videos.length < this.pagination.itemsPerPage) {
+ if (this.firstSearch === true && videosResult.data.length < this.pagination.itemsPerPage) {
this.resetPagination()
this.firstSearch = false
err => this.notifier.error(err.message)
)
-
}
onNearOfBottom () {
queryParams: Object.assign({}, this.advancedSearch.toUrlObject(), { search })
})
}
+
+ private getVideosObs () {
+ const params = {
+ search: this.currentSearch,
+ componentPagination: this.pagination,
+ advancedSearch: this.advancedSearch
+ }
+
+ return this.hooks.wrapObsFun(
+ this.searchService.searchVideos.bind(this.searchService),
+ params,
+ 'common',
+ 'filter:api.search.videos.list.params',
+ 'filter:api.search.videos.list.result'
+ )
+ }
+
+ private getVideoChannelObs () {
+ const params = {
+ search: this.currentSearch,
+ componentPagination: immutableAssign(this.pagination, { itemsPerPage: this.channelsPerPage })
+ }
+
+ return this.hooks.wrapObsFun(
+ this.searchService.searchVideoChannels.bind(this.searchService),
+ params,
+ 'common',
+ 'filter:api.search.video-channels.list.params',
+ 'filter:api.search.video-channels.list.result'
+ )
+ }
}
private videoService: VideoService
) {}
- searchVideos (
+ searchVideos (parameters: {
search: string,
componentPagination: ComponentPagination,
advancedSearch: AdvancedSearch
- ): Observable<{ videos: Video[], totalVideos: number }> {
- const url = SearchService.BASE_SEARCH_URL + 'videos'
+ }): Observable<ResultList<Video>> {
+ const { search, componentPagination, advancedSearch } = parameters
+ const url = SearchService.BASE_SEARCH_URL + 'videos'
const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
let params = new HttpParams()
)
}
- searchVideoChannels (
+ searchVideoChannels (parameters: {
search: string,
componentPagination: ComponentPagination
- ): Observable<{ data: VideoChannel[], total: number }> {
- const url = SearchService.BASE_SEARCH_URL + 'video-channels'
+ }): Observable<ResultList<VideoChannel>> {
+ const { search, componentPagination } = parameters
+ const url = SearchService.BASE_SEARCH_URL + 'video-channels'
const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
let params = new HttpParams()
of(object.videos)
.pipe(
switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })),
- map(result => result.videos),
+ map(result => result.data),
tap(videos => {
videosOverviewResult[key].push(immutableAssign(object, { videos }))
})
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date'
+import { ResultList } from '@shared/models'
enum GroupDate {
UNKNOWN = 0,
private groupedDateLabels: { [id in GroupDate]: string }
private groupedDates: { [id: number]: GroupDate } = {}
- abstract getVideosObservable (page: number): Observable<{ videos: Video[], totalVideos: number }>
+ abstract getVideosObservable (page: number): Observable<ResultList<Video>>
abstract generateSyndicationList (): void
}
loadMoreVideos () {
- const observable = this.getVideosObservable(this.pagination.currentPage)
-
- observable.subscribe(
- ({ videos, totalVideos }) => {
- this.pagination.totalItems = totalVideos
- this.videos = this.videos.concat(videos)
+ this.getVideosObservable(this.pagination.currentPage).subscribe(
+ ({ data, total }) => {
+ this.pagination.totalItems = total
+ this.videos = this.videos.concat(data)
if (this.groupByDate) this.buildGroupedDateLabels()
filter?: VideoFilter,
categoryOneOf?: number,
languageOneOf?: string[]
- }): Observable<{ videos: Video[], totalVideos: number }>
+ }): Observable<ResultList<Video>>
}
@Injectable()
return VideoService.BASE_VIDEO_URL + uuid + '/watching'
}
- getVideo (uuid: string): Observable<VideoDetails> {
+ getVideo (options: { videoId: string }): Observable<VideoDetails> {
return this.serverService.localeObservable
.pipe(
switchMap(translations => {
- return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
+ return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + options.videoId)
.pipe(map(videoHash => ({ videoHash, translations })))
}),
map(({ videoHash, translations }) => new VideoDetails(videoHash, translations)),
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
- getMyVideos (videoPagination: ComponentPagination, sort: VideoSortField): Observable<{ videos: Video[], totalVideos: number }> {
+ getMyVideos (videoPagination: ComponentPagination, sort: VideoSortField): Observable<ResultList<Video>> {
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
let params = new HttpParams()
account: Account,
videoPagination: ComponentPagination,
sort: VideoSortField
- ): Observable<{ videos: Video[], totalVideos: number }> {
+ ): Observable<ResultList<Video>> {
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
let params = new HttpParams()
videoChannel: VideoChannel,
videoPagination: ComponentPagination,
sort: VideoSortField
- ): Observable<{ videos: Video[], totalVideos: number }> {
+ ): Observable<ResultList<Video>> {
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
let params = new HttpParams()
getPlaylistVideos (
videoPlaylistId: number | string,
videoPagination: ComponentPagination
- ): Observable<{ videos: Video[], totalVideos: number }> {
+ ): Observable<ResultList<Video>> {
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
let params = new HttpParams()
)
}
- getUserSubscriptionVideos (
+ getUserSubscriptionVideos (parameters: {
videoPagination: ComponentPagination,
sort: VideoSortField
- ): Observable<{ videos: Video[], totalVideos: number }> {
+ }): Observable<ResultList<Video>> {
+ const { videoPagination, sort } = parameters
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
let params = new HttpParams()
filter?: VideoFilter,
categoryOneOf?: number,
languageOneOf?: string[]
- }): Observable<{ videos: Video[], totalVideos: number }> {
+ }): Observable<ResultList<Video>> {
const { videoPagination, sort, filter, categoryOneOf, languageOneOf } = parameters
const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
videos.push(new Video(videoJson, translations))
}
- return { videos, totalVideos }
+ return { total: totalVideos, data: videos }
})
)
}
import { VideoSortField } from '@app/shared/video/sort-field.type'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { I18n } from '@ngx-translate/i18n-polyfill'
+import { ResultList } from '@shared/models'
export type SelectionType = { [ id: number ]: boolean }
@Input() pagination: ComponentPagination
@Input() titlePage: string
@Input() miniatureDisplayOptions: MiniatureDisplayOptions
- @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<{ videos: Video[], totalVideos: number }>
+ @Input() getVideosObservableFunction: (page: number, sort?: VideoSortField) => Observable<ResultList<Video>>
@ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective>
@Output() selectionChange = new EventEmitter<SelectionType>()
resolve (route: ActivatedRouteSnapshot) {
const uuid: string = route.params[ 'uuid' ]
- return this.videoService.getVideo(uuid)
+ return this.videoService.getVideo({ videoId: uuid })
.pipe(
switchMap(video => {
return forkJoin([
)
}
- getVideoCommentThreads (
+ getVideoCommentThreads (parameters: {
videoId: number | string,
componentPagination: ComponentPagination,
sort: VideoSortField
- ): Observable<{ comments: VideoComment[], totalComments: number}> {
+ }): Observable<{ comments: VideoComment[], totalComments: number}> {
+ const { videoId, componentPagination, sort } = parameters
+
const pagination = this.restService.componentPaginationToRestPagination(componentPagination)
let params = new HttpParams()
)
}
- getVideoThreadComments (videoId: number | string, threadId: number): Observable<VideoCommentThreadTree> {
+ getVideoThreadComments (parameters: {
+ videoId: number | string,
+ threadId: number
+ }): Observable<VideoCommentThreadTree> {
+ const { videoId, threadId } = parameters
const url = `${VideoCommentService.BASE_VIDEO_URL + videoId}/comment-threads/${threadId}`
return this.authHttp
import { VideoCommentService } from './video-comment.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { Syndication } from '@app/shared/video/syndication.model'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-video-comments',
private confirmService: ConfirmService,
private videoCommentService: VideoCommentService,
private activatedRoute: ActivatedRoute,
- private i18n: I18n
+ private i18n: I18n,
+ private hooks: HooksService
) {}
ngOnInit () {
viewReplies (commentId: number, highlightThread = false) {
this.threadLoading[commentId] = true
- this.videoCommentService.getVideoThreadComments(this.video.id, commentId)
- .subscribe(
+ const params = {
+ videoId: this.video.id,
+ threadId: commentId
+ }
+
+ const obs = this.hooks.wrapObsFun(
+ this.videoCommentService.getVideoThreadComments.bind(this.videoCommentService),
+ params,
+ 'video-watch',
+ 'filter:api.video-watch.video-thread-replies.list.params',
+ 'filter:api.video-watch.video-thread-replies.list.result'
+ )
+
+ obs.subscribe(
res => {
this.threadComments[commentId] = res
this.threadLoading[commentId] = false
)
}
- loadMoreComments () {
- this.videoCommentService.getVideoCommentThreads(this.video.id, this.componentPagination, this.sort)
- .subscribe(
- res => {
- this.comments = this.comments.concat(res.comments)
- this.componentPagination.totalItems = res.totalComments
- },
+ loadMoreThreads () {
+ const params = {
+ videoId: this.video.id,
+ componentPagination: this.componentPagination,
+ sort: this.sort
+ }
- err => this.notifier.error(err.message)
- )
+ const obs = this.hooks.wrapObsFun(
+ this.videoCommentService.getVideoCommentThreads.bind(this.videoCommentService),
+ params,
+ 'video-watch',
+ 'filter:api.video-watch.video-threads.list.params',
+ 'filter:api.video-watch.video-threads.list.result'
+ )
+
+ obs.subscribe(
+ res => {
+ this.comments = this.comments.concat(res.comments)
+ this.componentPagination.totalItems = res.totalComments
+ },
+
+ err => this.notifier.error(err.message)
+ )
}
onCommentThreadCreated (comment: VideoComment) {
this.componentPagination.currentPage++
if (hasMoreItems(this.componentPagination)) {
- this.loadMoreComments()
+ this.loadMoreThreads()
}
}
this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video.uuid)
- this.loadMoreComments()
+ this.loadMoreThreads()
}
}
loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false) {
this.videoService.getPlaylistVideos(playlist.uuid, this.playlistPagination)
- .subscribe(({ totalVideos, videos }) => {
- this.playlistVideos = this.playlistVideos.concat(videos)
- this.playlistPagination.totalItems = totalVideos
+ .subscribe(({ total, data }) => {
+ this.playlistVideos = this.playlistVideos.concat(data)
+ this.playlistPagination.totalItems = total
- if (totalVideos === 0) {
+ if (total === 0) {
this.noPlaylistVideos = true
return
}
import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component'
import { getStoredTheater } from '../../../assets/player/peertube-player-local-storage'
import { PluginService } from '@app/core/plugins/plugin.service'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-video-watch',
private videoCaptionService: VideoCaptionService,
private i18n: I18n,
private hotkeysService: HotkeysService,
+ private hooks: HooksService,
@Inject(LOCALE_ID) private localeId: string
) {}
this.theaterEnabled = getStoredTheater()
- this.pluginService.runHook('action:video-watch.loaded')
+ this.hooks.runAction('action:video-watch.init')
}
ngOnDestroy () {
if (this.player) this.player.pause()
+ const videoObs = this.hooks.wrapObsFun(
+ this.videoService.getVideo.bind(this.videoService),
+ { videoId },
+ 'video-watch',
+ 'filter:api.video-watch.video.get.params',
+ 'filter:api.video-watch.video.get.result'
+ )
+
// Video did change
forkJoin(
- this.videoService.getVideo(videoId),
+ videoObs,
this.videoCaptionService.listCaptions(videoId)
)
.pipe(
this.setOpenGraphTags()
this.checkUserRating()
+
+ this.hooks.runAction('action:video-watch.video.loaded')
}
private setRating (nextRating: UserVideoRateType) {
private fetchPage (page: number, recommendation: RecommendationInfo): Observable<Video[]> {
const pagination = { currentPage: page, itemsPerPage: this.pageSize + 1 }
const defaultSubscription = this.videos.getVideos({ videoPagination: pagination, sort: '-createdAt' })
- .pipe(map(v => v.videos))
+ .pipe(map(v => v.data))
if (!recommendation.tags || recommendation.tags.length === 0) return defaultSubscription
- return this.searchService.searchVideos('',
- pagination,
- new AdvancedSearch({ tagsOneOf: recommendation.tags.join(','), sort: '-createdAt' })
- ).pipe(
- map(v => v.videos),
- switchMap(videos => {
- if (videos.length <= 1) return defaultSubscription
-
- return of(videos)
- })
- )
+ const params = {
+ search: '',
+ componentPagination: pagination,
+ advancedSearch: new AdvancedSearch({ tagsOneOf: recommendation.tags.join(','), sort: '-createdAt' })
+ }
+
+ return this.searchService.searchVideos(params)
+ .pipe(
+ map(v => v.data),
+ switchMap(videos => {
+ if (videos.length <= 1) return defaultSubscription
+
+ return of(videos)
+ })
+ )
}
}
import { ScreenService } from '@app/shared/misc/screen.service'
import { UserRight } from '../../../../../shared/models/users'
import { Notifier, ServerService } from '@app/core'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-videos-local',
protected notifier: Notifier,
protected authService: AuthService,
protected screenService: ScreenService,
- private videoService: VideoService
+ private videoService: VideoService,
+ private hooks: HooksService
) {
super()
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
-
- return this.videoService.getVideos({
+ const params = {
videoPagination: newPagination,
sort: this.sort,
filter: this.filter,
categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf
- })
+ }
+
+ return this.hooks.wrapObsFun(
+ this.videoService.getVideos.bind(this.videoService),
+ params,
+ 'common',
+ 'filter:api.videos.list.local.params',
+ 'filter:api.videos.list.local.result'
+ )
}
generateSyndicationList () {
import { I18n } from '@ngx-translate/i18n-polyfill'
import { ScreenService } from '@app/shared/misc/screen.service'
import { Notifier, ServerService } from '@app/core'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-videos-recently-added',
protected notifier: Notifier,
protected authService: AuthService,
protected screenService: ScreenService,
- private videoService: VideoService
+ private videoService: VideoService,
+ private hooks: HooksService
) {
super()
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
-
- return this.videoService.getVideos({
+ const params = {
videoPagination: newPagination,
sort: this.sort,
- filter: undefined,
categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf
- })
+ }
+
+ return this.hooks.wrapObsFun(
+ this.videoService.getVideos.bind(this.videoService),
+ params,
+ 'common',
+ 'filter:api.videos.list.recently-added.params',
+ 'filter:api.videos.list.recently-added.result'
+ )
}
generateSyndicationList () {
import { I18n } from '@ngx-translate/i18n-polyfill'
import { ScreenService } from '@app/shared/misc/screen.service'
import { Notifier, ServerService } from '@app/core'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-videos-trending',
protected notifier: Notifier,
protected authService: AuthService,
protected screenService: ScreenService,
- private videoService: VideoService
+ private videoService: VideoService,
+ private hooks: HooksService
) {
super()
}
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
- return this.videoService.getVideos({
+ const params = {
videoPagination: newPagination,
sort: this.sort,
- filter: undefined,
categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf
- })
+ }
+
+ return this.hooks.wrapObsFun(
+ this.videoService.getVideos.bind(this.videoService),
+ params,
+ 'common',
+ 'filter:api.videos.list.trending.params',
+ 'filter:api.videos.list.trending.result'
+ )
}
generateSyndicationList () {
import { ScreenService } from '@app/shared/misc/screen.service'
import { OwnerDisplayType } from '@app/shared/video/video-miniature.component'
import { Notifier, ServerService } from '@app/core'
+import { HooksService } from '@app/core/plugins/hooks.service'
@Component({
selector: 'my-videos-user-subscriptions',
protected notifier: Notifier,
protected authService: AuthService,
protected screenService: ScreenService,
- private videoService: VideoService
+ private videoService: VideoService,
+ private hooks: HooksService
) {
super()
getVideosObservable (page: number) {
const newPagination = immutableAssign(this.pagination, { currentPage: page })
+ const params = {
+ videoPagination: newPagination,
+ sort: this.sort
+ }
- return this.videoService.getUserSubscriptionVideos(newPagination, this.sort)
+ return this.hooks.wrapObsFun(
+ this.videoService.getUserSubscriptionVideos.bind(this.videoService),
+ params,
+ 'common',
+ 'filter:api.videos.list.user-subscriptions.params',
+ 'filter:api.videos.list.user-subscriptions.result'
+ )
}
generateSyndicationList () {
"noImplicitThis": true,
"suppressImplicitAnyIndexErrors":true,
"alwaysStrict": true,
+ "strictBindCallApply": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
}
if (hookType === HookType.ACTION) {
- if (isCatchable(p)) p.catch(err => onError(err))
+ if (isCatchable(p)) p.catch((err: any) => onError(err))
return undefined
}
--- /dev/null
+export type ClientFilterHookName =
+ 'filter:api.videos.list.trending.params' |
+ 'filter:api.videos.list.trending.result' |
+
+ 'filter:api.videos.list.local.params' |
+ 'filter:api.videos.list.local.result' |
+
+ 'filter:api.videos.list.recently-added.params' |
+ 'filter:api.videos.list.recently-added.result' |
+
+ 'filter:api.videos.list.user-subscriptions.params' |
+ 'filter:api.videos.list.user-subscriptions.result' |
+
+ 'filter:api.video-watch.video.get.params' |
+ 'filter:api.video-watch.video.get.result' |
+
+ 'filter:api.video-watch.video-threads.list.params' |
+ 'filter:api.video-watch.video-threads.list.result' |
+
+ 'filter:api.video-watch.video-thread-replies.list.params' |
+ 'filter:api.video-watch.video-thread-replies.list.result' |
+
+ 'filter:api.search.videos.list.params' |
+ 'filter:api.search.videos.list.result' |
+ 'filter:api.search.video-channels.list.params' |
+ 'filter:api.search.video-channels.list.result'
+
+export type ClientActionHookName =
+ 'action:application.init' |
+
+ 'action:video-watch.init' |
+
+ 'action:video-watch.video.loaded'
+
+export type ClientHookName = ClientActionHookName | ClientFilterHookName
+
+export interface ClientHook {
+ runHook <T> (hookName: ClientHookName, result?: T, params?: any): Promise<T>
+}
--- /dev/null
+export type PluginClientScope = 'common' | 'video-watch'
+++ /dev/null
-export type PluginScope = 'common' | 'video-watch'
export type ServerHookName = ServerFilterHookName | ServerActionHookName
export interface ServerHook {
- runHook (hookName: ServerHookName, params?: any)
+ runHook <T> (hookName: ServerHookName, result?: T, params?: any): Promise<T>
}
import { NSFWPolicyType } from '../videos/nsfw-policy.type'
import { ClientScript } from '../plugins/plugin-package-json.model'
+import { PluginClientScope } from '../plugins/plugin-scope.type'
export interface ServerConfigPlugin {
name: string
version: string
description: string
- clientScripts: { [name: string]: ClientScript }
+ clientScripts: { [name in PluginClientScope]: ClientScript }
}
export interface ServerConfigTheme extends ServerConfigPlugin {