aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-08-02 15:29:09 +0200
committerChocobozzz <me@florianbigard.com>2021-08-02 15:40:09 +0200
commit3da38d6e9f8d600476be276666ac7223aa5f172c (patch)
treedaec25cccb900a0f90fc9d2273099683b42d8551
parent200eaf5152ca72fe6b05a49caf819e22bd045b37 (diff)
downloadPeerTube-3da38d6e9f8d600476be276666ac7223aa5f172c.tar.gz
PeerTube-3da38d6e9f8d600476be276666ac7223aa5f172c.tar.zst
PeerTube-3da38d6e9f8d600476be276666ac7223aa5f172c.zip
Fetch things in bulk for the homepage
-rw-r--r--client/src/app/core/rest/rest.service.ts10
-rw-r--r--client/src/app/helpers/index.ts1
-rw-r--r--client/src/app/helpers/rxjs.ts29
-rw-r--r--client/src/app/shared/shared-custom-markup/custom-markup.service.ts18
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts7
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/playlist-miniature-markup.component.ts7
-rw-r--r--client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts5
-rw-r--r--client/src/app/shared/shared-custom-markup/shared-custom-markup.module.ts4
-rw-r--r--client/src/app/shared/shared-search/find-in-bulk.service.ts118
-rw-r--r--client/src/app/shared/shared-search/index.ts1
-rw-r--r--client/src/app/shared/shared-search/search.service.ts18
-rw-r--r--client/src/app/shared/shared-search/shared-search.module.ts2
-rw-r--r--client/src/app/shared/shared-user-subscription/user-subscription.service.ts22
-rw-r--r--client/src/app/shared/shared-video-playlist/video-playlist.service.ts23
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 @@
1export * from './locales' 1export * from './locales'
2export * from './constants' 2export * from './constants'
3export * from './i18n-utils' 3export * from './i18n-utils'
4export * from './rxjs'
4export * from './utils' 5export * from './utils'
5export * from './zone' 6export * 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 @@
1import { uniq } from 'lodash-es'
2import { asyncScheduler, Observable } from 'rxjs'
3import { bufferTime, distinctUntilChanged, filter, map, observeOn, share, switchMap } from 'rxjs/operators'
4import { NgZone } from '@angular/core'
5import { enterZone, leaveZone } from './zone'
6
7function 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
27export {
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'
2import { finalize, map, switchMap, tap } from 'rxjs/operators' 2import { finalize, map, switchMap, tap } from 'rxjs/operators'
3import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 3import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
4import { MarkdownService, Notifier, UserService } from '@app/core' 4import { MarkdownService, Notifier, UserService } from '@app/core'
5import { FindInBulkService } from '@app/shared/shared-search'
5import { Video, VideoSortField } from '@shared/models/videos' 6import { Video, VideoSortField } from '@shared/models/videos'
6import { VideoChannel, VideoChannelService, VideoService } from '../../shared-main' 7import { VideoChannel, VideoService } from '../../shared-main'
7import { CustomMarkupComponent } from './shared' 8import { 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 @@
1import { finalize } from 'rxjs/operators' 1import { finalize } from 'rxjs/operators'
2import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' 2import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
3import { Notifier } from '@app/core' 3import { Notifier } from '@app/core'
4import { FindInBulkService } from '@app/shared/shared-search'
4import { MiniatureDisplayOptions } from '../../shared-video-miniature' 5import { MiniatureDisplayOptions } from '../../shared-video-miniature'
5import { VideoPlaylist, VideoPlaylistService } from '../../shared-video-playlist' 6import { VideoPlaylist } from '../../shared-video-playlist'
6import { CustomMarkupComponent } from './shared' 7import { 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'
4import { Video, VideoService } from '../../shared-main' 4import { Video, VideoService } from '../../shared-main'
5import { MiniatureDisplayOptions } from '../../shared-video-miniature' 5import { MiniatureDisplayOptions } from '../../shared-video-miniature'
6import { CustomMarkupComponent } from './shared' 6import { CustomMarkupComponent } from './shared'
7import { 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'
3import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module' 3import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
4import { SharedGlobalIconModule } from '../shared-icons' 4import { SharedGlobalIconModule } from '../shared-icons'
5import { SharedMainModule } from '../shared-main' 5import { SharedMainModule } from '../shared-main'
6import { SharedSearchModule } from '../shared-search'
6import { SharedVideoMiniatureModule } from '../shared-video-miniature' 7import { SharedVideoMiniatureModule } from '../shared-video-miniature'
7import { SharedVideoPlaylistModule } from '../shared-video-playlist' 8import { SharedVideoPlaylistModule } from '../shared-video-playlist'
8import { CustomMarkupContainerComponent } from './custom-markup-container.component' 9import { 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 @@
1import * as debug from 'debug'
2import { Observable, Subject } from 'rxjs'
3import { map } from 'rxjs/operators'
4import { Injectable, NgZone } from '@angular/core'
5import { buildBulkObservable } from '@app/helpers'
6import { ResultList } from '@shared/models/common'
7import { Video, VideoChannel } from '../shared-main'
8import { VideoPlaylist } from '../shared-video-playlist'
9import { SearchService } from './search.service'
10
11const logger = debug('peertube:search:FindInBulkService')
12
13type BulkObservables <P extends number | string, R> = {
14 notifier: Subject<P>
15 result: Observable<R>
16}
17
18@Injectable()
19export 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 @@
1export * from './advanced-search.model' 1export * from './advanced-search.model'
2export * from './find-in-bulk.service'
2export * from './search.service' 3export * from './search.service'
3export * from './shared-search.module' 4export * 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 @@
1import { NgModule } from '@angular/core' 1import { NgModule } from '@angular/core'
2import { SharedMainModule } from '../shared-main' 2import { SharedMainModule } from '../shared-main'
3import { SharedVideoPlaylistModule } from '../shared-video-playlist' 3import { SharedVideoPlaylistModule } from '../shared-video-playlist'
4import { FindInBulkService } from './find-in-bulk.service'
4import { SearchService } from './search.service' 5import { 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 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import { uniq } from 'lodash-es' 2import { merge, Observable, of, ReplaySubject, Subject } from 'rxjs'
3import { asyncScheduler, merge, Observable, of, ReplaySubject, Subject } from 'rxjs' 3import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'
4import { bufferTime, catchError, filter, map, observeOn, share, switchMap, tap } from 'rxjs/operators'
5import { HttpClient, HttpParams } from '@angular/common/http' 4import { HttpClient, HttpParams } from '@angular/common/http'
6import { Injectable, NgZone } from '@angular/core' 5import { Injectable, NgZone } from '@angular/core'
7import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core' 6import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core'
8import { enterZone, leaveZone } from '@app/helpers' 7import { buildBulkObservable } from '@app/helpers'
9import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main' 8import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
10import { ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models' 9import { ResultList, VideoChannel as VideoChannelServer, VideoSortField } from '@shared/models'
11import { environment } from '../../../environments/environment' 10import { 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 @@
1import * as debug from 'debug' 1import * as debug from 'debug'
2import { uniq } from 'lodash-es' 2import { merge, Observable, of, ReplaySubject, Subject } from 'rxjs'
3import { asyncScheduler, merge, Observable, of, ReplaySubject, Subject } from 'rxjs' 3import { catchError, filter, map, share, switchMap, tap } from 'rxjs/operators'
4import { bufferTime, catchError, filter, map, observeOn, share, switchMap, tap, distinctUntilChanged } from 'rxjs/operators'
5import { HttpClient, HttpParams } from '@angular/common/http' 4import { HttpClient, HttpParams } from '@angular/common/http'
6import { Injectable, NgZone } from '@angular/core' 5import { Injectable, NgZone } from '@angular/core'
7import { AuthUser, ComponentPaginationLight, RestExtractor, RestService, ServerService } from '@app/core' 6import { AuthUser, ComponentPaginationLight, RestExtractor, RestService, ServerService } from '@app/core'
8import { enterZone, leaveZone, objectToFormData } from '@app/helpers' 7import { buildBulkObservable, objectToFormData } from '@app/helpers'
9import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main' 8import { Account, AccountService, VideoChannel, VideoChannelService } from '@app/shared/shared-main'
10import { 9import {
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 )