diff options
14 files changed, 213 insertions, 52 deletions
diff --git a/client/src/app/core/rest/rest.service.ts b/client/src/app/core/rest/rest.service.ts index 1696e6709..98e45ffc0 100644 --- a/client/src/app/core/rest/rest.service.ts +++ b/client/src/app/core/rest/rest.service.ts | |||
@@ -44,13 +44,21 @@ export class RestService { | |||
44 | return newParams | 44 | return newParams |
45 | } | 45 | } |
46 | 46 | ||
47 | addArrayParams (params: HttpParams, name: string, values: (string | number)[]) { | ||
48 | for (const v of values) { | ||
49 | params = params.append(name, v) | ||
50 | } | ||
51 | |||
52 | return params | ||
53 | } | ||
54 | |||
47 | addObjectParams (params: HttpParams, object: { [ name: string ]: any }) { | 55 | addObjectParams (params: HttpParams, object: { [ name: string ]: any }) { |
48 | for (const name of Object.keys(object)) { | 56 | for (const name of Object.keys(object)) { |
49 | const value = object[name] | 57 | const value = object[name] |
50 | if (value === undefined || value === null) continue | 58 | if (value === undefined || value === null) continue |
51 | 59 | ||
52 | if (Array.isArray(value)) { | 60 | if (Array.isArray(value)) { |
53 | for (const v of value) params = params.append(name, v) | 61 | params = this.addArrayParams(params, name, value) |
54 | } else { | 62 | } else { |
55 | params = params.append(name, value) | 63 | params = params.append(name, value) |
56 | } | 64 | } |
diff --git a/client/src/app/helpers/index.ts b/client/src/app/helpers/index.ts index cc61255ba..beff749ec 100644 --- a/client/src/app/helpers/index.ts +++ b/client/src/app/helpers/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | export * from './locales' | 1 | export * from './locales' |
2 | export * from './constants' | 2 | export * from './constants' |
3 | export * from './i18n-utils' | 3 | export * from './i18n-utils' |
4 | export * from './rxjs' | ||
4 | export * from './utils' | 5 | export * from './utils' |
5 | export * from './zone' | 6 | export * from './zone' |
diff --git a/client/src/app/helpers/rxjs.ts b/client/src/app/helpers/rxjs.ts new file mode 100644 index 000000000..eb051f868 --- /dev/null +++ b/client/src/app/helpers/rxjs.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | import { uniq } from 'lodash-es' | ||
2 | import { asyncScheduler, Observable } from 'rxjs' | ||
3 | import { bufferTime, distinctUntilChanged, filter, map, observeOn, share, switchMap } from 'rxjs/operators' | ||
4 | import { NgZone } from '@angular/core' | ||
5 | import { enterZone, leaveZone } from './zone' | ||
6 | |||
7 | function buildBulkObservable <T extends number | string, R> (options: { | ||
8 | ngZone: NgZone | ||
9 | notifierObservable: Observable<T> | ||
10 | time: number | ||
11 | bulkGet: (params: T[]) => Observable<R> | ||
12 | }) { | ||
13 | const { ngZone, notifierObservable, time, bulkGet } = options | ||
14 | |||
15 | return notifierObservable.pipe( | ||
16 | distinctUntilChanged(), | ||
17 | // We leave Angular zone so Protractor does not get stuck | ||
18 | bufferTime(time, leaveZone(ngZone, asyncScheduler)), | ||
19 | filter(params => params.length !== 0), | ||
20 | map(params => uniq(params)), | ||
21 | observeOn(enterZone(ngZone, asyncScheduler)), | ||
22 | switchMap(params => bulkGet(params)), | ||
23 | share() | ||
24 | ) | ||
25 | } | ||
26 | |||
27 | export { | ||
28 | buildBulkObservable | ||
29 | } | ||
diff --git a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts index 231e52d0a..089728a51 100644 --- a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts +++ b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts | |||
@@ -65,15 +65,15 @@ export class CustomMarkupService { | |||
65 | 65 | ||
66 | for (const selector of Object.keys(this.htmlBuilders)) { | 66 | for (const selector of Object.keys(this.htmlBuilders)) { |
67 | rootElement.querySelectorAll(selector) | 67 | rootElement.querySelectorAll(selector) |
68 | .forEach((e: HTMLElement) => { | 68 | .forEach((e: HTMLElement) => { |
69 | try { | 69 | try { |
70 | const element = this.execHTMLBuilder(selector, e) | 70 | const element = this.execHTMLBuilder(selector, e) |
71 | // Insert as first child | 71 | // Insert as first child |
72 | e.insertBefore(element, e.firstChild) | 72 | e.insertBefore(element, e.firstChild) |
73 | } catch (err) { | 73 | } catch (err) { |
74 | console.error('Cannot inject component %s.', selector, err) | 74 | console.error('Cannot inject component %s.', selector, err) |
75 | } | 75 | } |
76 | }) | 76 | }) |
77 | } | 77 | } |
78 | 78 | ||
79 | const loadedPromises: Promise<boolean>[] = [] | 79 | const loadedPromises: Promise<boolean>[] = [] |
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts index 7043a7ec9..bb099deae 100644 --- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts +++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts | |||
@@ -2,8 +2,9 @@ import { from } from 'rxjs' | |||
2 | import { finalize, map, switchMap, tap } from 'rxjs/operators' | 2 | import { finalize, map, switchMap, tap } from 'rxjs/operators' |
3 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | 3 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' |
4 | import { MarkdownService, Notifier, UserService } from '@app/core' | 4 | import { MarkdownService, Notifier, UserService } from '@app/core' |
5 | import { FindInBulkService } from '@app/shared/shared-search' | ||
5 | import { Video, VideoSortField } from '@shared/models/videos' | 6 | import { Video, VideoSortField } from '@shared/models/videos' |
6 | import { VideoChannel, VideoChannelService, VideoService } from '../../shared-main' | 7 | import { VideoChannel, VideoService } from '../../shared-main' |
7 | import { CustomMarkupComponent } from './shared' | 8 | import { CustomMarkupComponent } from './shared' |
8 | 9 | ||
9 | /* | 10 | /* |
@@ -29,14 +30,14 @@ export class ChannelMiniatureMarkupComponent implements CustomMarkupComponent, O | |||
29 | 30 | ||
30 | constructor ( | 31 | constructor ( |
31 | private markdown: MarkdownService, | 32 | private markdown: MarkdownService, |
32 | private channelService: VideoChannelService, | 33 | private findInBulk: FindInBulkService, |
33 | private videoService: VideoService, | 34 | private videoService: VideoService, |
34 | private userService: UserService, | 35 | private userService: UserService, |
35 | private notifier: Notifier | 36 | private notifier: Notifier |
36 | ) { } | 37 | ) { } |
37 | 38 | ||
38 | ngOnInit () { | 39 | ngOnInit () { |
39 | this.channelService.getVideoChannel(this.name) | 40 | this.findInBulk.getChannel(this.name) |
40 | .pipe( | 41 | .pipe( |
41 | tap(channel => this.channel = channel), | 42 | tap(channel => this.channel = channel), |
42 | switchMap(() => from(this.markdown.textMarkdownToHTML(this.channel.description))), | 43 | switchMap(() => from(this.markdown.textMarkdownToHTML(this.channel.description))), |
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts index ff8cc01db..97d31c4a7 100644 --- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts +++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { finalize } from 'rxjs/operators' | 1 | import { finalize } from 'rxjs/operators' |
2 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | 2 | import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' |
3 | import { Notifier } from '@app/core' | 3 | import { Notifier } from '@app/core' |
4 | import { FindInBulkService } from '@app/shared/shared-search' | ||
4 | import { MiniatureDisplayOptions } from '../../shared-video-miniature' | 5 | import { MiniatureDisplayOptions } from '../../shared-video-miniature' |
5 | import { VideoPlaylist, VideoPlaylistService } from '../../shared-video-playlist' | 6 | import { VideoPlaylist } from '../../shared-video-playlist' |
6 | import { CustomMarkupComponent } from './shared' | 7 | import { CustomMarkupComponent } from './shared' |
7 | 8 | ||
8 | /* | 9 | /* |
@@ -33,12 +34,12 @@ export class PlaylistMiniatureMarkupComponent implements CustomMarkupComponent, | |||
33 | } | 34 | } |
34 | 35 | ||
35 | constructor ( | 36 | constructor ( |
36 | private playlistService: VideoPlaylistService, | 37 | private findInBulkService: FindInBulkService, |
37 | private notifier: Notifier | 38 | private notifier: Notifier |
38 | ) { } | 39 | ) { } |
39 | 40 | ||
40 | ngOnInit () { | 41 | ngOnInit () { |
41 | this.playlistService.getVideoPlaylist(this.uuid) | 42 | this.findInBulkService.getPlaylist(this.uuid) |
42 | .pipe(finalize(() => this.loaded.emit(true))) | 43 | .pipe(finalize(() => this.loaded.emit(true))) |
43 | .subscribe( | 44 | .subscribe( |
44 | playlist => this.playlist = playlist, | 45 | playlist => this.playlist = playlist, |
diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts index 47518abfd..ba61aaf51 100644 --- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts +++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts | |||
@@ -4,6 +4,7 @@ import { AuthService, Notifier } from '@app/core' | |||
4 | import { Video, VideoService } from '../../shared-main' | 4 | import { Video, VideoService } from '../../shared-main' |
5 | import { MiniatureDisplayOptions } from '../../shared-video-miniature' | 5 | import { MiniatureDisplayOptions } from '../../shared-video-miniature' |
6 | import { CustomMarkupComponent } from './shared' | 6 | import { CustomMarkupComponent } from './shared' |
7 | import { FindInBulkService } from '@app/shared/shared-search' | ||
7 | 8 | ||
8 | /* | 9 | /* |
9 | * Markup component that creates a video miniature only | 10 | * Markup component that creates a video miniature only |
@@ -35,7 +36,7 @@ export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnI | |||
35 | 36 | ||
36 | constructor ( | 37 | constructor ( |
37 | private auth: AuthService, | 38 | private auth: AuthService, |
38 | private videoService: VideoService, | 39 | private findInBulk: FindInBulkService, |
39 | private notifier: Notifier | 40 | private notifier: Notifier |
40 | ) { } | 41 | ) { } |
41 | 42 | ||
@@ -50,7 +51,7 @@ export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnI | |||
50 | } | 51 | } |
51 | } | 52 | } |
52 | 53 | ||
53 | this.videoService.getVideo({ videoId: this.uuid }) | 54 | this.findInBulk.getVideo(this.uuid) |
54 | .pipe(finalize(() => this.loaded.emit(true))) | 55 | .pipe(finalize(() => this.loaded.emit(true))) |
55 | .subscribe( | 56 | .subscribe( |
56 | video => this.video = video, | 57 | video => this.video = video, |
diff --git a/client/src/app/shared/shared-custom-markup/shared-custom-markup.module.ts b/client/src/app/shared/shared-custom-markup/shared-custom-markup.module.ts index dccd64709..27e976d13 100644 --- a/client/src/app/shared/shared-custom-markup/shared-custom-markup.module.ts +++ b/client/src/app/shared/shared-custom-markup/shared-custom-markup.module.ts | |||
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core' | |||
3 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' | 3 | import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' |
4 | import { SharedGlobalIconModule } from '../shared-icons' | 4 | import { SharedGlobalIconModule } from '../shared-icons' |
5 | import { SharedMainModule } from '../shared-main' | 5 | import { SharedMainModule } from '../shared-main' |
6 | import { SharedSearchModule } from '../shared-search' | ||
6 | import { SharedVideoMiniatureModule } from '../shared-video-miniature' | 7 | import { SharedVideoMiniatureModule } from '../shared-video-miniature' |
7 | import { SharedVideoPlaylistModule } from '../shared-video-playlist' | 8 | import { SharedVideoPlaylistModule } from '../shared-video-playlist' |
8 | import { CustomMarkupContainerComponent } from './custom-markup-container.component' | 9 | import { CustomMarkupContainerComponent } from './custom-markup-container.component' |
@@ -26,7 +27,8 @@ import { | |||
26 | SharedGlobalIconModule, | 27 | SharedGlobalIconModule, |
27 | SharedVideoMiniatureModule, | 28 | SharedVideoMiniatureModule, |
28 | SharedVideoPlaylistModule, | 29 | SharedVideoPlaylistModule, |
29 | SharedActorImageModule | 30 | SharedActorImageModule, |
31 | SharedSearchModule | ||
30 | ], | 32 | ], |
31 | 33 | ||
32 | declarations: [ | 34 | declarations: [ |
diff --git a/client/src/app/shared/shared-search/find-in-bulk.service.ts b/client/src/app/shared/shared-search/find-in-bulk.service.ts new file mode 100644 index 000000000..0383d8648 --- /dev/null +++ b/client/src/app/shared/shared-search/find-in-bulk.service.ts | |||
@@ -0,0 +1,118 @@ | |||
1 | import * as debug from 'debug' | ||
2 | import { Observable, Subject } from 'rxjs' | ||
3 | import { map } from 'rxjs/operators' | ||
4 | import { Injectable, NgZone } from '@angular/core' | ||
5 | import { buildBulkObservable } from '@app/helpers' | ||
6 | import { ResultList } from '@shared/models/common' | ||
7 | import { Video, VideoChannel } from '../shared-main' | ||
8 | import { VideoPlaylist } from '../shared-video-playlist' | ||
9 | import { SearchService } from './search.service' | ||
10 | |||
11 | const logger = debug('peertube:search:FindInBulkService') | ||
12 | |||
13 | type BulkObservables <P extends number | string, R> = { | ||
14 | notifier: Subject<P> | ||
15 | result: Observable<R> | ||
16 | } | ||
17 | |||
18 | @Injectable() | ||
19 | export class FindInBulkService { | ||
20 | |||
21 | private getVideoInBulk: BulkObservables<string, ResultList<Video>> | ||
22 | private getChannelInBulk: BulkObservables<string, ResultList<VideoChannel>> | ||
23 | private getPlaylistInBulk: BulkObservables<string, ResultList<VideoPlaylist>> | ||
24 | |||
25 | constructor ( | ||
26 | private searchService: SearchService, | ||
27 | private ngZone: NgZone | ||
28 | ) { | ||
29 | this.getVideoInBulk = this.buildBulkObservableObject(this.getVideosInBulk.bind(this)) | ||
30 | this.getChannelInBulk = this.buildBulkObservableObject(this.getChannelsInBulk.bind(this)) | ||
31 | this.getPlaylistInBulk = this.buildBulkObservableObject(this.getPlaylistsInBulk.bind(this)) | ||
32 | } | ||
33 | |||
34 | getVideo (uuid: string): Observable<Video> { | ||
35 | logger('Schedule video fetch for uuid %s.', uuid) | ||
36 | |||
37 | return this.getData({ | ||
38 | observableObject: this.getVideoInBulk, | ||
39 | finder: v => v.uuid === uuid, | ||
40 | param: uuid | ||
41 | }) | ||
42 | } | ||
43 | |||
44 | getChannel (handle: string): Observable<VideoChannel> { | ||
45 | logger('Schedule channel fetch for handle %s.', handle) | ||
46 | |||
47 | return this.getData({ | ||
48 | observableObject: this.getChannelInBulk, | ||
49 | finder: c => c.nameWithHost === handle || c.nameWithHostForced === handle, | ||
50 | param: handle | ||
51 | }) | ||
52 | } | ||
53 | |||
54 | getPlaylist (uuid: string): Observable<VideoPlaylist> { | ||
55 | logger('Schedule playlist fetch for uuid %s.', uuid) | ||
56 | |||
57 | return this.getData({ | ||
58 | observableObject: this.getPlaylistInBulk, | ||
59 | finder: p => p.uuid === uuid, | ||
60 | param: uuid | ||
61 | }) | ||
62 | } | ||
63 | |||
64 | private getData <P extends number | string, R> (options: { | ||
65 | observableObject: BulkObservables<P, ResultList<R>> | ||
66 | param: P | ||
67 | finder: (d: R) => boolean | ||
68 | }) { | ||
69 | const { observableObject, param, finder } = options | ||
70 | |||
71 | return new Observable<R>(obs => { | ||
72 | observableObject.result | ||
73 | .pipe( | ||
74 | map(({ data }) => data), | ||
75 | map(data => data.find(finder)) | ||
76 | ) | ||
77 | .subscribe(result => { | ||
78 | obs.next(result) | ||
79 | obs.complete() | ||
80 | }) | ||
81 | |||
82 | observableObject.notifier.next(param) | ||
83 | }) | ||
84 | } | ||
85 | |||
86 | private getVideosInBulk (uuids: string[]) { | ||
87 | logger('Fetching videos %s.', uuids.join(', ')) | ||
88 | |||
89 | return this.searchService.searchVideos({ uuids }) | ||
90 | } | ||
91 | |||
92 | private getChannelsInBulk (handles: string[]) { | ||
93 | logger('Fetching channels %s.', handles.join(', ')) | ||
94 | |||
95 | return this.searchService.searchVideoChannels({ handles }) | ||
96 | } | ||
97 | |||
98 | private getPlaylistsInBulk (uuids: string[]) { | ||
99 | logger('Fetching playlists %s.', uuids.join(', ')) | ||
100 | |||
101 | return this.searchService.searchVideoPlaylists({ uuids }) | ||
102 | } | ||
103 | |||
104 | private buildBulkObservableObject <T extends number | string, R> (bulkGet: (params: T[]) => Observable<R>) { | ||
105 | const notifier = new Subject<T>() | ||
106 | |||
107 | return { | ||
108 | notifier, | ||
109 | |||
110 | result: buildBulkObservable({ | ||
111 | time: 500, | ||
112 | bulkGet, | ||
113 | ngZone: this.ngZone, | ||
114 | notifierObservable: notifier.asObservable() | ||
115 | }) | ||
116 | } | ||
117 | } | ||
118 | } | ||
diff --git a/client/src/app/shared/shared-search/index.ts b/client/src/app/shared/shared-search/index.ts index f687f6767..0235893c4 100644 --- a/client/src/app/shared/shared-search/index.ts +++ b/client/src/app/shared/shared-search/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export * from './advanced-search.model' | 1 | export * from './advanced-search.model' |
2 | export * from './find-in-bulk.service' | ||
2 | export * from './search.service' | 3 | export * from './search.service' |
3 | export * from './shared-search.module' | 4 | export * from './shared-search.module' |
diff --git a/client/src/app/shared/shared-search/search.service.ts b/client/src/app/shared/shared-search/search.service.ts index a1603da98..fdfab0e0e 100644 --- a/client/src/app/shared/shared-search/search.service.ts +++ b/client/src/app/shared/shared-search/search.service.ts | |||
@@ -32,11 +32,12 @@ export class SearchService { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | searchVideos (parameters: { | 34 | searchVideos (parameters: { |
35 | search: string | 35 | search?: string |
36 | componentPagination?: ComponentPaginationLight | 36 | componentPagination?: ComponentPaginationLight |
37 | advancedSearch?: AdvancedSearch | 37 | advancedSearch?: AdvancedSearch |
38 | uuids?: string[] | ||
38 | }): Observable<ResultList<Video>> { | 39 | }): Observable<ResultList<Video>> { |
39 | const { search, componentPagination, advancedSearch } = parameters | 40 | const { search, uuids, componentPagination, advancedSearch } = parameters |
40 | 41 | ||
41 | const url = SearchService.BASE_SEARCH_URL + 'videos' | 42 | const url = SearchService.BASE_SEARCH_URL + 'videos' |
42 | let pagination: RestPagination | 43 | let pagination: RestPagination |
@@ -49,6 +50,7 @@ export class SearchService { | |||
49 | params = this.restService.addRestGetParams(params, pagination) | 50 | params = this.restService.addRestGetParams(params, pagination) |
50 | 51 | ||
51 | if (search) params = params.append('search', search) | 52 | if (search) params = params.append('search', search) |
53 | if (uuids) params = this.restService.addArrayParams(params, 'uuids', uuids) | ||
52 | 54 | ||
53 | if (advancedSearch) { | 55 | if (advancedSearch) { |
54 | const advancedSearchObject = advancedSearch.toVideosAPIObject() | 56 | const advancedSearchObject = advancedSearch.toVideosAPIObject() |
@@ -64,11 +66,12 @@ export class SearchService { | |||
64 | } | 66 | } |
65 | 67 | ||
66 | searchVideoChannels (parameters: { | 68 | searchVideoChannels (parameters: { |
67 | search: string | 69 | search?: string |
68 | advancedSearch?: AdvancedSearch | 70 | advancedSearch?: AdvancedSearch |
69 | componentPagination?: ComponentPaginationLight | 71 | componentPagination?: ComponentPaginationLight |
72 | handles?: string[] | ||
70 | }): Observable<ResultList<VideoChannel>> { | 73 | }): Observable<ResultList<VideoChannel>> { |
71 | const { search, advancedSearch, componentPagination } = parameters | 74 | const { search, advancedSearch, componentPagination, handles } = parameters |
72 | 75 | ||
73 | const url = SearchService.BASE_SEARCH_URL + 'video-channels' | 76 | const url = SearchService.BASE_SEARCH_URL + 'video-channels' |
74 | 77 | ||
@@ -81,6 +84,7 @@ export class SearchService { | |||
81 | params = this.restService.addRestGetParams(params, pagination) | 84 | params = this.restService.addRestGetParams(params, pagination) |
82 | 85 | ||
83 | if (search) params = params.append('search', search) | 86 | if (search) params = params.append('search', search) |
87 | if (handles) params = this.restService.addArrayParams(params, 'handles', handles) | ||
84 | 88 | ||
85 | if (advancedSearch) { | 89 | if (advancedSearch) { |
86 | const advancedSearchObject = advancedSearch.toChannelAPIObject() | 90 | const advancedSearchObject = advancedSearch.toChannelAPIObject() |
@@ -96,11 +100,12 @@ export class SearchService { | |||
96 | } | 100 | } |
97 | 101 | ||
98 | searchVideoPlaylists (parameters: { | 102 | searchVideoPlaylists (parameters: { |
99 | search: string | 103 | search?: string |
100 | advancedSearch?: AdvancedSearch | 104 | advancedSearch?: AdvancedSearch |
101 | componentPagination?: ComponentPaginationLight | 105 | componentPagination?: ComponentPaginationLight |
106 | uuids?: string[] | ||
102 | }): Observable<ResultList<VideoPlaylist>> { | 107 | }): Observable<ResultList<VideoPlaylist>> { |
103 | const { search, advancedSearch, componentPagination } = parameters | 108 | const { search, advancedSearch, componentPagination, uuids } = parameters |
104 | 109 | ||
105 | const url = SearchService.BASE_SEARCH_URL + 'video-playlists' | 110 | const url = SearchService.BASE_SEARCH_URL + 'video-playlists' |
106 | 111 | ||
@@ -113,6 +118,7 @@ export class SearchService { | |||
113 | params = this.restService.addRestGetParams(params, pagination) | 118 | params = this.restService.addRestGetParams(params, pagination) |
114 | 119 | ||
115 | if (search) params = params.append('search', search) | 120 | if (search) params = params.append('search', search) |
121 | if (uuids) params = this.restService.addArrayParams(params, 'uuids', uuids) | ||
116 | 122 | ||
117 | if (advancedSearch) { | 123 | if (advancedSearch) { |
118 | const advancedSearchObject = advancedSearch.toPlaylistAPIObject() | 124 | const advancedSearchObject = advancedSearch.toPlaylistAPIObject() |
diff --git a/client/src/app/shared/shared-search/shared-search.module.ts b/client/src/app/shared/shared-search/shared-search.module.ts index be4ef5e3f..8b5492400 100644 --- a/client/src/app/shared/shared-search/shared-search.module.ts +++ b/client/src/app/shared/shared-search/shared-search.module.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { NgModule } from '@angular/core' | 1 | import { NgModule } from '@angular/core' |
2 | import { SharedMainModule } from '../shared-main' | 2 | import { SharedMainModule } from '../shared-main' |
3 | import { SharedVideoPlaylistModule } from '../shared-video-playlist' | 3 | import { SharedVideoPlaylistModule } from '../shared-video-playlist' |
4 | import { FindInBulkService } from './find-in-bulk.service' | ||
4 | import { SearchService } from './search.service' | 5 | import { SearchService } from './search.service' |
5 | 6 | ||
6 | @NgModule({ | 7 | @NgModule({ |
@@ -16,6 +17,7 @@ import { SearchService } from './search.service' | |||
16 | ], | 17 | ], |
17 | 18 | ||
18 | providers: [ | 19 | providers: [ |
20 | FindInBulkService, | ||
19 | SearchService | 21 | SearchService |
20 | ] | 22 | ] |
21 | }) | 23 | }) |
diff --git a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts index eb1fdf91c..bb44660d2 100644 --- a/client/src/app/shared/shared-user-subscription/user-subscription.service.ts +++ b/client/src/app/shared/shared-user-subscription/user-subscription.service.ts | |||
@@ -1,11 +1,10 @@ | |||
1 | import * as debug from 'debug' | 1 | import * as debug from 'debug' |
2 | import { uniq } from 'lodash-es' | 2 | import { merge, Observable, of, ReplaySubject, Subject } from 'rxjs' |
3 | import { asyncScheduler, merge, Observable, of, ReplaySubject, Subject } from 'rxjs' | 3 | import { catchError, filter, map, switchMap, tap } from 'rxjs/operators' |
4 | import { bufferTime, catchError, filter, map, observeOn, share, switchMap, tap } from 'rxjs/operators' | ||
5 | import { HttpClient, HttpParams } from '@angular/common/http' | 4 | import { HttpClient, HttpParams } from '@angular/common/http' |
6 | import { Injectable, NgZone } from '@angular/core' | 5 | import { Injectable, NgZone } from '@angular/core' |
7 | import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' | 6 | import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' |
8 | import { enterZone, leaveZone } from '@app/helpers' | 7 | import { buildBulkObservable } from '@app/helpers' |
9 | import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' | 8 | import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' |
10 | import { ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models' | 9 | import { ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models' |
11 | import { environment } from '../../../environments/environment' | 10 | import { environment } from '../../../environments/environment' |
@@ -35,15 +34,12 @@ export class UserSubscriptionService { | |||
35 | private ngZone: NgZone | 34 | private ngZone: NgZone |
36 | ) { | 35 | ) { |
37 | this.existsObservable = merge( | 36 | this.existsObservable = merge( |
38 | this.existsSubject.pipe( | 37 | buildBulkObservable({ |
39 | // We leave Angular zone so Protractor does not get stuck | 38 | time: 500, |
40 | bufferTime(500, leaveZone(this.ngZone, asyncScheduler)), | 39 | ngZone: this.ngZone, |
41 | filter(uris => uris.length !== 0), | 40 | notifierObservable: this.existsSubject, |
42 | map(uris => uniq(uris)), | 41 | bulkGet: this.doSubscriptionsExist.bind(this) |
43 | observeOn(enterZone(this.ngZone, asyncScheduler)), | 42 | }), |
44 | switchMap(uris => this.doSubscriptionsExist(uris)), | ||
45 | share() | ||
46 | ), | ||
47 | 43 | ||
48 | this.myAccountSubscriptionCacheSubject | 44 | this.myAccountSubscriptionCacheSubject |
49 | ) | 45 | ) |
diff --git a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts index 1b87e0b2a..a3f1393ff 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist.service.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist.service.ts | |||
@@ -1,11 +1,10 @@ | |||
1 | import * as debug from 'debug' | 1 | import * as debug from 'debug' |
2 | import { uniq } from 'lodash-es' | 2 | import { merge, Observable, of, ReplaySubject, Subject } from 'rxjs' |
3 | import { asyncScheduler, merge, Observable, of, ReplaySubject, Subject } from 'rxjs' | 3 | import { catchError, filter, map, share, switchMap, tap } from 'rxjs/operators' |
4 | import { bufferTime, catchError, filter, map, observeOn, share, switchMap, tap, distinctUntilChanged } from 'rxjs/operators' | ||
5 | import { HttpClient, HttpParams } from '@angular/common/http' | 4 | import { HttpClient, HttpParams } from '@angular/common/http' |
6 | import { Injectable, NgZone } from '@angular/core' | 5 | import { Injectable, NgZone } from '@angular/core' |
7 | import { AuthUser, ComponentPaginationLight, RestExtractor, RestService, ServerService } from '@app/core' | 6 | import { AuthUser, ComponentPaginationLight, RestExtractor, RestService, ServerService } from '@app/core' |
8 | import { enterZone, leaveZone, objectToFormData } from '@app/helpers' | 7 | import { buildBulkObservable, objectToFormData } from '@app/helpers' |
9 | import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' | 8 | import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' |
10 | import { | 9 | import { |
11 | ResultList, | 10 | ResultList, |
@@ -52,16 +51,12 @@ export class VideoPlaylistService { | |||
52 | private ngZone: NgZone | 51 | private ngZone: NgZone |
53 | ) { | 52 | ) { |
54 | this.videoExistsInPlaylistObservable = merge( | 53 | this.videoExistsInPlaylistObservable = merge( |
55 | this.videoExistsInPlaylistNotifier.pipe( | 54 | buildBulkObservable({ |
56 | distinctUntilChanged(), | 55 | time: 500, |
57 | // We leave Angular zone so Protractor does not get stuck | 56 | ngZone: this.ngZone, |
58 | bufferTime(500, leaveZone(this.ngZone, asyncScheduler)), | 57 | bulkGet: this.doVideosExistInPlaylist.bind(this), |
59 | filter(videoIds => videoIds.length !== 0), | 58 | notifierObservable: this.videoExistsInPlaylistNotifier |
60 | map(videoIds => uniq(videoIds)), | 59 | }), |
61 | observeOn(enterZone(this.ngZone, asyncScheduler)), | ||
62 | switchMap(videoIds => this.doVideosExistInPlaylist(videoIds)), | ||
63 | share() | ||
64 | ), | ||
65 | 60 | ||
66 | this.videoExistsInPlaylistCacheSubject | 61 | this.videoExistsInPlaylistCacheSubject |
67 | ) | 62 | ) |