aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts')
-rw-r--r--client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts223
1 files changed, 170 insertions, 53 deletions
diff --git a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
index 41f16e0bf..b6a3408c7 100644
--- a/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
+++ b/client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
@@ -4,21 +4,27 @@ import { debounceTime, filter } from 'rxjs/operators'
4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core' 4import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
5import { AuthService, DisableForReuseHook, Notifier } from '@app/core' 5import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' 6import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models' 7import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy, VideoPlaylistElementUpdate } from '@shared/models'
8import { secondsToTime } from '../../../assets/player/utils' 8import { secondsToTime } from '../../../assets/player/utils'
9import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators' 9import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
10import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service' 10import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
11import { invoke, last } from 'lodash'
11 12
12const logger = debug('peertube:playlists:VideoAddToPlaylistComponent') 13const logger = debug('peertube:playlists:VideoAddToPlaylistComponent')
13 14
15type PlaylistElement = {
16 enabled: boolean
17 playlistElementId?: number
18 startTimestamp?: number
19 stopTimestamp?: number
20}
21
14type PlaylistSummary = { 22type PlaylistSummary = {
15 id: number 23 id: number
16 inPlaylist: boolean
17 displayName: string 24 displayName: string
25 optionalRowDisplayed: boolean
18 26
19 playlistElementId?: number 27 elements: PlaylistElement[]
20 startTimestamp?: number
21 stopTimestamp?: number
22} 28}
23 29
24@Component({ 30@Component({
@@ -33,16 +39,11 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
33 @Input() lazyLoad = false 39 @Input() lazyLoad = false
34 40
35 isNewPlaylistBlockOpened = false 41 isNewPlaylistBlockOpened = false
42
36 videoPlaylistSearch: string 43 videoPlaylistSearch: string
37 videoPlaylistSearchChanged = new Subject<string>() 44 videoPlaylistSearchChanged = new Subject<string>()
45
38 videoPlaylists: PlaylistSummary[] = [] 46 videoPlaylists: PlaylistSummary[] = []
39 timestampOptions: {
40 startTimestampEnabled: boolean
41 startTimestamp: number
42 stopTimestampEnabled: boolean
43 stopTimestamp: number
44 }
45 displayOptions = false
46 47
47 private disabled = false 48 private disabled = false
48 49
@@ -106,7 +107,6 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
106 this.videoPlaylists = [] 107 this.videoPlaylists = []
107 this.videoPlaylistSearch = undefined 108 this.videoPlaylistSearch = undefined
108 109
109 this.resetOptions(true)
110 this.load() 110 this.load()
111 111
112 this.cd.markForCheck() 112 this.cd.markForCheck()
@@ -115,7 +115,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
115 load () { 115 load () {
116 logger('Loading component') 116 logger('Loading component')
117 117
118 this.listenToPlaylistChanges() 118 this.listenToVideoPlaylistChange()
119 119
120 this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch) 120 this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch)
121 .subscribe(playlistsResult => { 121 .subscribe(playlistsResult => {
@@ -128,7 +128,6 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
128 openChange (opened: boolean) { 128 openChange (opened: boolean) {
129 if (opened === false) { 129 if (opened === false) {
130 this.isNewPlaylistBlockOpened = false 130 this.isNewPlaylistBlockOpened = false
131 this.displayOptions = false
132 } 131 }
133 } 132 }
134 133
@@ -138,17 +137,49 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
138 this.isNewPlaylistBlockOpened = true 137 this.isNewPlaylistBlockOpened = true
139 } 138 }
140 139
141 togglePlaylist (event: Event, playlist: PlaylistSummary) { 140 toggleMainPlaylist (e: Event, playlist: PlaylistSummary) {
142 event.preventDefault() 141 e.preventDefault()
142
143 if (this.isPresentMultipleTimes(playlist) || playlist.optionalRowDisplayed) return
143 144
144 if (playlist.inPlaylist === true) { 145 if (playlist.elements.length === 0) {
145 this.removeVideoFromPlaylist(playlist) 146 const element: PlaylistElement = {
147 enabled: true,
148 playlistElementId: undefined,
149 startTimestamp: 0,
150 stopTimestamp: this.video.duration
151 }
152
153 this.addVideoInPlaylist(playlist, element)
146 } else { 154 } else {
147 this.addVideoInPlaylist(playlist) 155 this.removeVideoFromPlaylist(playlist, playlist.elements[0].playlistElementId)
156 playlist.elements = []
148 } 157 }
149 158
150 playlist.inPlaylist = !playlist.inPlaylist 159 this.cd.markForCheck()
151 this.resetOptions() 160 }
161
162 toggleOptionalPlaylist (e: Event, playlist: PlaylistSummary, element: PlaylistElement, startTimestamp: number, stopTimestamp: number) {
163 e.preventDefault()
164
165 if (element.enabled) {
166 this.removeVideoFromPlaylist(playlist, element.playlistElementId)
167 element.enabled = false
168
169 // Hide optional rows pane when the user unchecked all the playlists
170 if (this.isPrimaryCheckboxChecked(playlist) === false) {
171 playlist.optionalRowDisplayed = false
172 }
173 } else {
174 const element: PlaylistElement = {
175 enabled: true,
176 playlistElementId: undefined,
177 startTimestamp,
178 stopTimestamp
179 }
180
181 this.addVideoInPlaylist(playlist, element)
182 }
152 183
153 this.cd.markForCheck() 184 this.cd.markForCheck()
154 } 185 }
@@ -172,34 +203,99 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
172 ) 203 )
173 } 204 }
174 205
175 resetOptions (resetTimestamp = false) { 206 onVideoPlaylistSearchChanged () {
176 this.displayOptions = false 207 this.videoPlaylistSearchChanged.next()
208 }
177 209
178 this.timestampOptions = {} as any 210 isPrimaryCheckboxChecked (playlist: PlaylistSummary) {
179 this.timestampOptions.startTimestampEnabled = false 211 return playlist.elements.filter(e => e.enabled)
180 this.timestampOptions.stopTimestampEnabled = false 212 .length !== 0
213 }
181 214
182 if (resetTimestamp) { 215 toggleOptionalRow (playlist: PlaylistSummary) {
183 this.timestampOptions.startTimestamp = 0 216 playlist.optionalRowDisplayed = !playlist.optionalRowDisplayed
184 this.timestampOptions.stopTimestamp = this.video.duration 217
185 } 218 this.cd.markForCheck()
186 } 219 }
187 220
188 formatTimestamp (playlist: PlaylistSummary) { 221 getPrimaryInputName (playlist: PlaylistSummary) {
189 const start = playlist.startTimestamp ? secondsToTime(playlist.startTimestamp) : '' 222 return 'in-playlist-primary-' + playlist.id
190 const stop = playlist.stopTimestamp ? secondsToTime(playlist.stopTimestamp) : '' 223 }
191 224
192 return `(${start}-${stop})` 225 getOptionalInputName (playlist: PlaylistSummary, element?: PlaylistElement) {
226 const suffix = element
227 ? '-' + element.playlistElementId
228 : ''
229
230 return 'in-playlist-optional-' + playlist.id + suffix
193 } 231 }
194 232
195 onVideoPlaylistSearchChanged () { 233 buildOptionalRowElements (playlist: PlaylistSummary) {
196 this.videoPlaylistSearchChanged.next() 234 const elements = playlist.elements
235
236 const lastElement = elements.length === 0
237 ? undefined
238 : elements[elements.length - 1]
239
240 // Build an empty last element
241 if (!lastElement || lastElement.enabled === true) {
242 elements.push({
243 enabled: false,
244 startTimestamp: 0,
245 stopTimestamp: this.video.duration
246 })
247 }
248
249 return elements
250 }
251
252 isPresentMultipleTimes (playlist: PlaylistSummary) {
253 return playlist.elements.filter(e => e.enabled === true).length > 1
254 }
255
256 onElementTimestampUpdate (playlist: PlaylistSummary, element: PlaylistElement) {
257 if (!element.playlistElementId || element.enabled === false) return
258
259 const body: VideoPlaylistElementUpdate = {
260 startTimestamp: element.startTimestamp,
261 stopTimestamp: element.stopTimestamp
262 }
263
264 this.videoPlaylistService.updateVideoOfPlaylist(playlist.id, element.playlistElementId, body, this.video.id)
265 .subscribe(
266 () => {
267 this.notifier.success($localize`Timestamps updated`)
268 },
269
270 err => {
271 this.notifier.error(err.message)
272 },
273
274 () => this.cd.markForCheck()
275 )
197 } 276 }
198 277
199 private removeVideoFromPlaylist (playlist: PlaylistSummary) { 278 private isOptionalRowDisplayed (playlist: PlaylistSummary) {
200 if (!playlist.playlistElementId) return 279 const elements = playlist.elements.filter(e => e.enabled)
280
281 if (elements.length > 1) return true
201 282
202 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id) 283 if (elements.length === 1) {
284 const element = elements[0]
285
286 if (
287 (element.startTimestamp && element.startTimestamp !== 0) ||
288 (element.stopTimestamp && element.stopTimestamp !== this.video.duration)
289 ) {
290 return true
291 }
292 }
293
294 return false
295 }
296
297 private removeVideoFromPlaylist (playlist: PlaylistSummary, elementId: number) {
298 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, elementId, this.video.id)
203 .subscribe( 299 .subscribe(
204 () => { 300 () => {
205 this.notifier.success($localize`Video removed from ${playlist.displayName}`) 301 this.notifier.success($localize`Video removed from ${playlist.displayName}`)
@@ -213,7 +309,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
213 ) 309 )
214 } 310 }
215 311
216 private listenToPlaylistChanges () { 312 private listenToVideoPlaylistChange () {
217 this.unsubscribePlaylistChanges() 313 this.unsubscribePlaylistChanges()
218 314
219 this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id) 315 this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id)
@@ -231,18 +327,30 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
231 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) { 327 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) {
232 logger('Got existing results for %d.', this.video.id, existResult) 328 logger('Got existing results for %d.', this.video.id, existResult)
233 329
330 const oldPlaylists = this.videoPlaylists
331
234 this.videoPlaylists = [] 332 this.videoPlaylists = []
235 for (const playlist of this.playlistsData) { 333 for (const playlist of this.playlistsData) {
236 const existingPlaylist = existResult.find(p => p.playlistId === playlist.id) 334 const existingPlaylists = existResult.filter(p => p.playlistId === playlist.id)
237 335
238 this.videoPlaylists.push({ 336 const playlistSummary = {
239 id: playlist.id, 337 id: playlist.id,
338 optionalRowDisplayed: false,
240 displayName: playlist.displayName, 339 displayName: playlist.displayName,
241 inPlaylist: !!existingPlaylist, 340 elements: existingPlaylists.map(e => ({
242 playlistElementId: existingPlaylist ? existingPlaylist.playlistElementId : undefined, 341 enabled: true,
243 startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined, 342 playlistElementId: e.playlistElementId,
244 stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined 343 startTimestamp: e.startTimestamp || 0,
245 }) 344 stopTimestamp: e.stopTimestamp || this.video.duration
345 }))
346 }
347
348 const oldPlaylist = oldPlaylists.find(p => p.id === playlist.id)
349 playlistSummary.optionalRowDisplayed = oldPlaylist
350 ? oldPlaylist.optionalRowDisplayed
351 : this.isOptionalRowDisplayed(playlistSummary)
352
353 this.videoPlaylists.push(playlistSummary)
246 } 354 }
247 355
248 logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists) 356 logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists)
@@ -250,20 +358,22 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
250 this.cd.markForCheck() 358 this.cd.markForCheck()
251 } 359 }
252 360
253 private addVideoInPlaylist (playlist: PlaylistSummary) { 361 private addVideoInPlaylist (playlist: PlaylistSummary, element: PlaylistElement) {
254 const body: VideoPlaylistElementCreate = { videoId: this.video.id } 362 const body: VideoPlaylistElementCreate = { videoId: this.video.id }
255 363
256 if (this.timestampOptions.startTimestampEnabled) body.startTimestamp = this.timestampOptions.startTimestamp 364 if (element.startTimestamp) body.startTimestamp = element.startTimestamp
257 if (this.timestampOptions.stopTimestampEnabled) body.stopTimestamp = this.timestampOptions.stopTimestamp 365 if (element.stopTimestamp && element.stopTimestamp !== this.video.duration) body.stopTimestamp = element.stopTimestamp
258 366
259 this.videoPlaylistService.addVideoInPlaylist(playlist.id, body) 367 this.videoPlaylistService.addVideoInPlaylist(playlist.id, body)
260 .subscribe( 368 .subscribe(
261 () => { 369 res => {
262 const message = body.startTimestamp || body.stopTimestamp 370 const message = body.startTimestamp || body.stopTimestamp
263 ? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(playlist)}` 371 ? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(element)}`
264 : $localize`Video added in ${playlist.displayName}` 372 : $localize`Video added in ${playlist.displayName}`
265 373
266 this.notifier.success(message) 374 this.notifier.success(message)
375
376 if (element) element.playlistElementId = res.videoPlaylistElement.id
267 }, 377 },
268 378
269 err => { 379 err => {
@@ -273,4 +383,11 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
273 () => this.cd.markForCheck() 383 () => this.cd.markForCheck()
274 ) 384 )
275 } 385 }
386
387 private formatTimestamp (element: PlaylistElement) {
388 const start = element.startTimestamp ? secondsToTime(element.startTimestamp) : ''
389 const stop = element.stopTimestamp ? secondsToTime(element.stopTimestamp) : ''
390
391 return `(${start}-${stop})`
392 }
276} 393}