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, VideoPlaylistValidatorsService } from '@app/shared/shared-forms'
7 import { I18n } from '@ngx-translate/i18n-polyfill'
8 import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models'
9 import { secondsToTime } from '../../../assets/player/utils'
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,
57 private videoPlaylistService: VideoPlaylistService,
58 private videoPlaylistValidatorsService: VideoPlaylistValidatorsService,
59 private cd: ChangeDetectorRef
65 return this.authService.getUser()
70 displayName: this.videoPlaylistValidatorsService.VIDEO_PLAYLIST_DISPLAY_NAME
73 this.videoPlaylistService.listenToMyAccountPlaylistsChange()
74 .subscribe(result => {
75 this.playlistsData = result.data
77 this.videoPlaylistService.runPlaylistCheck(this.video.id)
80 this.videoPlaylistSearchChanged
81 .pipe(debounceTime(500))
82 .subscribe(() => this.load())
84 if (this.lazyLoad === false) this.load()
87 ngOnChanges (simpleChanges: SimpleChanges) {
88 if (simpleChanges['video']) {
94 this.unsubscribePlaylistChanges()
102 this.disabled = false
106 logger('Reloading component')
108 this.videoPlaylists = []
109 this.videoPlaylistSearch = undefined
111 this.resetOptions(true)
114 this.cd.markForCheck()
118 logger('Loading component')
120 this.listenToPlaylistChanges()
122 this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch)
123 .subscribe(playlistsResult => {
124 this.playlistsData = playlistsResult.data
126 this.videoPlaylistService.runPlaylistCheck(this.video.id)
130 openChange (opened: boolean) {
131 if (opened === false) {
132 this.isNewPlaylistBlockOpened = false
133 this.displayOptions = false
137 openCreateBlock (event: Event) {
138 event.preventDefault()
140 this.isNewPlaylistBlockOpened = true
143 togglePlaylist (event: Event, playlist: PlaylistSummary) {
144 event.preventDefault()
146 if (playlist.inPlaylist === true) {
147 this.removeVideoFromPlaylist(playlist)
149 this.addVideoInPlaylist(playlist)
152 playlist.inPlaylist = !playlist.inPlaylist
155 this.cd.markForCheck()
159 const displayName = this.form.value[ 'displayName' ]
161 const videoPlaylistCreate: VideoPlaylistCreate = {
163 privacy: VideoPlaylistPrivacy.PRIVATE
166 this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
168 this.isNewPlaylistBlockOpened = false
170 this.cd.markForCheck()
173 err => this.notifier.error(err.message)
177 resetOptions (resetTimestamp = false) {
178 this.displayOptions = false
180 this.timestampOptions = {} as any
181 this.timestampOptions.startTimestampEnabled = false
182 this.timestampOptions.stopTimestampEnabled = false
184 if (resetTimestamp) {
185 this.timestampOptions.startTimestamp = 0
186 this.timestampOptions.stopTimestamp = this.video.duration
190 formatTimestamp (playlist: PlaylistSummary) {
191 const start = playlist.startTimestamp ? secondsToTime(playlist.startTimestamp) : ''
192 const stop = playlist.stopTimestamp ? secondsToTime(playlist.stopTimestamp) : ''
194 return `(${start}-${stop})`
197 onVideoPlaylistSearchChanged () {
198 this.videoPlaylistSearchChanged.next()
201 private removeVideoFromPlaylist (playlist: PlaylistSummary) {
202 if (!playlist.playlistElementId) return
204 this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id)
207 this.notifier.success(this.i18n('Video removed from {{name}}', { name: playlist.displayName }))
211 this.notifier.error(err.message)
214 () => this.cd.markForCheck()
218 private listenToPlaylistChanges () {
219 this.unsubscribePlaylistChanges()
221 this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id)
222 .pipe(filter(() => this.disabled === false))
223 .subscribe(existResult => this.rebuildPlaylists(existResult))
226 private unsubscribePlaylistChanges () {
227 if (this.listenToPlaylistChangeSub) {
228 this.listenToPlaylistChangeSub.unsubscribe()
229 this.listenToPlaylistChangeSub = undefined
233 private rebuildPlaylists (existResult: VideoExistInPlaylist[]) {
234 logger('Got existing results for %d.', this.video.id, existResult)
236 this.videoPlaylists = []
237 for (const playlist of this.playlistsData) {
238 const existingPlaylist = existResult.find(p => p.playlistId === playlist.id)
240 this.videoPlaylists.push({
242 displayName: playlist.displayName,
243 inPlaylist: !!existingPlaylist,
244 playlistElementId: existingPlaylist ? existingPlaylist.playlistElementId : undefined,
245 startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined,
246 stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined
250 logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists)
252 this.cd.markForCheck()
255 private addVideoInPlaylist (playlist: PlaylistSummary) {
256 const body: VideoPlaylistElementCreate = { videoId: this.video.id }
258 if (this.timestampOptions.startTimestampEnabled) body.startTimestamp = this.timestampOptions.startTimestamp
259 if (this.timestampOptions.stopTimestampEnabled) body.stopTimestamp = this.timestampOptions.stopTimestamp
261 this.videoPlaylistService.addVideoInPlaylist(playlist.id, body)
264 const message = body.startTimestamp || body.stopTimestamp
265 ? this.i18n('Video added in {{n}} at timestamps {{t}}', { n: playlist.displayName, t: this.formatTimestamp(playlist) })
266 : this.i18n('Video added in {{n}}', { n: playlist.displayName })
268 this.notifier.success(message)
272 this.notifier.error(err.message)
275 () => this.cd.markForCheck()