1 import * as debug from 'debug'
2 import { Subject, Subscription } from 'rxjs'
3 import { debounceTime, filter } from 'rxjs/operators'
4 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
5 import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
6 import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
7 import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models'
8 import { secondsToTime } from '../../../assets/player/utils'
9 import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
10 import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
12 const logger = debug('peertube:playlists:VideoAddToPlaylistComponent')
14 type PlaylistSummary = {
19 playlistElementId?: number
20 startTimestamp?: number
21 stopTimestamp?: number
25 selector: 'my-video-add-to-playlist',
26 styleUrls: [ './video-add-to-playlist.component.scss' ],
27 templateUrl: './video-add-to-playlist.component.html',
28 changeDetection: ChangeDetectionStrategy.OnPush
30 export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, OnChanges, OnDestroy, DisableForReuseHook {
32 @Input() currentVideoTimestamp: number
33 @Input() lazyLoad = false
35 isNewPlaylistBlockOpened = false
36 videoPlaylistSearch: string
37 videoPlaylistSearchChanged = new Subject<string>()
38 videoPlaylists: PlaylistSummary[] = []
40 startTimestampEnabled: boolean
41 startTimestamp: number
42 stopTimestampEnabled: boolean
45 displayOptions = false
47 private disabled = false
49 private listenToPlaylistChangeSub: Subscription
50 private playlistsData: CachedPlaylist[] = []
53 protected formValidatorService: FormValidatorService,
54 private authService: AuthService,
55 private notifier: Notifier,
56 private videoPlaylistService: VideoPlaylistService,
57 private cd: ChangeDetectorRef
63 return this.authService.getUser()
68 displayName: VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR
71 this.videoPlaylistService.listenToMyAccountPlaylistsChange()
72 .subscribe(result => {
73 this.playlistsData = result.data
75 this.videoPlaylistService.runPlaylistCheck(this.video.id)
78 this.videoPlaylistSearchChanged
79 .pipe(debounceTime(500))
80 .subscribe(() => this.load())
82 if (this.lazyLoad === false) this.load()
85 ngOnChanges (simpleChanges: SimpleChanges) {
86 if (simpleChanges['video']) {
92 this.unsubscribePlaylistChanges()
100 this.disabled = false
104 logger('Reloading component')
106 this.videoPlaylists = []
107 this.videoPlaylistSearch = undefined
109 this.resetOptions(true)
112 this.cd.markForCheck()
116 logger('Loading component')
118 this.listenToPlaylistChanges()
120 this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch)
121 .subscribe(playlistsResult => {
122 this.playlistsData = playlistsResult.data
124 this.videoPlaylistService.runPlaylistCheck(this.video.id)
128 openChange (opened: boolean) {
129 if (opened === false) {
130 this.isNewPlaylistBlockOpened = false
131 this.displayOptions = false
135 openCreateBlock (event: Event) {
136 event.preventDefault()
138 this.isNewPlaylistBlockOpened = true
141 togglePlaylist (event: Event, playlist: PlaylistSummary) {
142 event.preventDefault()
144 if (playlist.inPlaylist === true) {
145 this.removeVideoFromPlaylist(playlist)
147 this.addVideoInPlaylist(playlist)
150 playlist.inPlaylist = !playlist.inPlaylist
153 this.cd.markForCheck()
157 const displayName = this.form.value[ 'displayName' ]
159 const videoPlaylistCreate: VideoPlaylistCreate = {
161 privacy: VideoPlaylistPrivacy.PRIVATE
164 this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
166 this.isNewPlaylistBlockOpened = false
168 this.cd.markForCheck()
171 err => this.notifier.error(err.message)
175 resetOptions (resetTimestamp = false) {
176 this.displayOptions = false
178 this.timestampOptions = {} as any
179 this.timestampOptions.startTimestampEnabled = false
180 this.timestampOptions.stopTimestampEnabled = false
182 if (resetTimestamp) {
183 this.timestampOptions.startTimestamp = 0
184 this.timestampOptions.stopTimestamp = this.video.duration
188 formatTimestamp (playlist: PlaylistSummary) {
189 const start = playlist.startTimestamp ? secondsToTime(playlist.startTimestamp) : ''
190 const stop = playlist.stopTimestamp ? secondsToTime(playlist.stopTimestamp) : ''
192 return `(${start}-${stop})`
195 onVideoPlaylistSearchChanged () {
196 this.videoPlaylistSearchChanged.next()
199 private removeVideoFromPlaylist (playlist: PlaylistSummary) {
200 if (!playlist.playlistElementId) return
202 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id)
205 this.notifier.success($localize`Video removed from ${playlist.displayName}`)
209 this.notifier.error(err.message)
212 () => this.cd.markForCheck()
216 private listenToPlaylistChanges () {
217 this.unsubscribePlaylistChanges()
219 this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id)
220 .pipe(filter(() => this.disabled === false))
221 .subscribe(existResult => this.rebuildPlaylists(existResult))
224 private unsubscribePlaylistChanges () {
225 if (this.listenToPlaylistChangeSub) {
226 this.listenToPlaylistChangeSub.unsubscribe()
227 this.listenToPlaylistChangeSub = undefined
231 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) {
232 logger('Got existing results for %d.', this.video.id, existResult)
234 this.videoPlaylists = []
235 for (const playlist of this.playlistsData) {
236 const existingPlaylist = existResult.find(p => p.playlistId === playlist.id)
238 this.videoPlaylists.push({
240 displayName: playlist.displayName,
241 inPlaylist: !!existingPlaylist,
242 playlistElementId: existingPlaylist ? existingPlaylist.playlistElementId : undefined,
243 startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined,
244 stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined
248 logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists)
250 this.cd.markForCheck()
253 private addVideoInPlaylist (playlist: PlaylistSummary) {
254 const body: VideoPlaylistElementCreate = { videoId: this.video.id }
256 if (this.timestampOptions.startTimestampEnabled) body.startTimestamp = this.timestampOptions.startTimestamp
257 if (this.timestampOptions.stopTimestampEnabled) body.stopTimestamp = this.timestampOptions.stopTimestamp
259 this.videoPlaylistService.addVideoInPlaylist(playlist.id, body)
262 const message = body.startTimestamp || body.stopTimestamp
263 ? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(playlist)}`
264 : $localize`Video added in ${playlist.displayName}`
266 this.notifier.success(message)
270 this.notifier.error(err.message)
273 () => this.cd.markForCheck()