1 import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
2 import { CachedPlaylist, VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
3 import { AuthService, Notifier } from '@app/core'
4 import { Subject, Subscription } from 'rxjs'
5 import { debounceTime, filter } from 'rxjs/operators'
6 import { Video, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models'
7 import { FormReactive, FormValidatorService, VideoPlaylistValidatorsService } from '@app/shared/forms'
8 import { I18n } from '@ngx-translate/i18n-polyfill'
9 import { secondsToTime } from '../../../assets/player/utils'
10 import * as debug from 'debug'
11 import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
12 import { VideoExistInPlaylist } from '@shared/models/videos/playlist/video-exist-in-playlist.model'
14 const logger = debug('peertube:playlists:VideoAddToPlaylistComponent')
16 type PlaylistSummary = {
21 playlistElementId?: number
22 startTimestamp?: number
23 stopTimestamp?: number
27 selector: 'my-video-add-to-playlist',
28 styleUrls: [ './video-add-to-playlist.component.scss' ],
29 templateUrl: './video-add-to-playlist.component.html',
30 changeDetection: ChangeDetectionStrategy.OnPush
32 export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, OnChanges, OnDestroy, DisableForReuseHook {
34 @Input() currentVideoTimestamp: number
35 @Input() lazyLoad = false
37 isNewPlaylistBlockOpened = false
38 videoPlaylistSearch: string
39 videoPlaylistSearchChanged = new Subject<string>()
40 videoPlaylists: PlaylistSummary[] = []
42 startTimestampEnabled: boolean
43 startTimestamp: number
44 stopTimestampEnabled: boolean
47 displayOptions = false
49 private disabled = false
51 private listenToPlaylistChangeSub: Subscription
52 private playlistsData: CachedPlaylist[] = []
55 protected formValidatorService: FormValidatorService,
56 private authService: AuthService,
57 private notifier: Notifier,
59 private videoPlaylistService: VideoPlaylistService,
60 private videoPlaylistValidatorsService: VideoPlaylistValidatorsService,
61 private cd: ChangeDetectorRef
67 return this.authService.getUser()
72 displayName: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_DISPLAY_NAME
75 this.videoPlaylistService.listenToMyAccountPlaylistsChange()
76 .subscribe(result => {
77 this.playlistsData = result.data
79 this.videoPlaylistService.runPlaylistCheck(this.video.id)
82 this.videoPlaylistSearchChanged
83 .pipe(debounceTime(500))
84 .subscribe(() => this.load())
86 if (this.lazyLoad === false) this.load()
89 ngOnChanges (simpleChanges: SimpleChanges) {
90 if (simpleChanges['video']) {
96 this.unsubscribePlaylistChanges()
104 this.disabled = false
108 logger('Reloading component')
110 this.videoPlaylists = []
111 this.videoPlaylistSearch = undefined
113 this.resetOptions(true)
116 this.cd.markForCheck()
120 logger('Loading component')
122 this.listenToPlaylistChanges()
124 this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch)
125 .subscribe(playlistsResult => {
126 this.playlistsData = playlistsResult.data
128 this.videoPlaylistService.runPlaylistCheck(this.video.id)
132 openChange (opened: boolean) {
133 if (opened === false) {
134 this.isNewPlaylistBlockOpened = false
135 this.displayOptions = false
139 openCreateBlock (event: Event) {
140 event.preventDefault()
142 this.isNewPlaylistBlockOpened = true
145 togglePlaylist (event: Event, playlist: PlaylistSummary) {
146 event.preventDefault()
148 if (playlist.inPlaylist === true) {
149 this.removeVideoFromPlaylist(playlist)
151 this.addVideoInPlaylist(playlist)
154 playlist.inPlaylist = !playlist.inPlaylist
157 this.cd.markForCheck()
161 const displayName = this.form.value[ 'displayName' ]
163 const videoPlaylistCreate: VideoPlaylistCreate = {
165 privacy: VideoPlaylistPrivacy.PRIVATE
168 this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
170 this.isNewPlaylistBlockOpened = false
172 this.cd.markForCheck()
175 err => this.notifier.error(err.message)
179 resetOptions (resetTimestamp = false) {
180 this.displayOptions = false
182 this.timestampOptions = {} as any
183 this.timestampOptions.startTimestampEnabled = false
184 this.timestampOptions.stopTimestampEnabled = false
186 if (resetTimestamp) {
187 this.timestampOptions.startTimestamp = 0
188 this.timestampOptions.stopTimestamp = this.video.duration
192 formatTimestamp (playlist: PlaylistSummary) {
193 const start = playlist.startTimestamp ? secondsToTime(playlist.startTimestamp) : ''
194 const stop = playlist.stopTimestamp ? secondsToTime(playlist.stopTimestamp) : ''
196 return `(${start}-${stop})`
199 onVideoPlaylistSearchChanged () {
200 this.videoPlaylistSearchChanged.next()
203 private removeVideoFromPlaylist (playlist: PlaylistSummary) {
204 if (!playlist.playlistElementId) return
206 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id)
209 this.notifier.success(this.i18n('Video removed from {{name}}', { name: playlist.displayName }))
213 this.notifier.error(err.message)
216 () => this.cd.markForCheck()
220 private listenToPlaylistChanges () {
221 this.unsubscribePlaylistChanges()
223 this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id)
224 .pipe(filter(() => this.disabled === false))
225 .subscribe(existResult => this.rebuildPlaylists(existResult))
228 private unsubscribePlaylistChanges () {
229 if (this.listenToPlaylistChangeSub) {
230 this.listenToPlaylistChangeSub.unsubscribe()
231 this.listenToPlaylistChangeSub = undefined
235 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) {
236 logger('Got existing results for %d.', this.video.id, existResult)
238 this.videoPlaylists = []
239 for (const playlist of this.playlistsData) {
240 const existingPlaylist = existResult.find(p => p.playlistId === playlist.id)
242 this.videoPlaylists.push({
244 displayName: playlist.displayName,
245 inPlaylist: !!existingPlaylist,
246 playlistElementId: existingPlaylist ? existingPlaylist.playlistElementId : undefined,
247 startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined,
248 stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined
252 logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists)
254 this.cd.markForCheck()
257 private addVideoInPlaylist (playlist: PlaylistSummary) {
258 const body: VideoPlaylistElementCreate = { videoId: this.video.id }
260 if (this.timestampOptions.startTimestampEnabled) body.startTimestamp = this.timestampOptions.startTimestamp
261 if (this.timestampOptions.stopTimestampEnabled) body.stopTimestamp = this.timestampOptions.stopTimestamp
263 this.videoPlaylistService.addVideoInPlaylist(playlist.id, body)
266 const message = body.startTimestamp || body.stopTimestamp
267 ? this.i18n('Video added in {{n}} at timestamps {{t}}', { n: playlist.displayName, t: this.formatTimestamp(playlist) })
268 : this.i18n('Video added in {{n}}', { n: playlist.displayName })
270 this.notifier.success(message)
274 this.notifier.error(err.message)
277 () => this.cd.markForCheck()