import * as debug from 'debug'
import { Subject, Subscription } from 'rxjs'
import { debounceTime, filter } from 'rxjs/operators'
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core'
import { AuthService, DisableForReuseHook, Notifier } from '@app/core'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { Video, VideoExistInPlaylist, VideoPlaylistCreate, VideoPlaylistElementCreate, VideoPlaylistPrivacy } from '@shared/models'
import { secondsToTime } from '../../../assets/player/utils'
import { VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR } from '../form-validators/video-playlist-validators'
import { CachedPlaylist, VideoPlaylistService } from './video-playlist.service'
const logger = debug('peertube:playlists:VideoAddToPlaylistComponent')
type PlaylistSummary = {
id: number
inPlaylist: boolean
displayName: string
playlistElementId?: number
startTimestamp?: number
stopTimestamp?: number
}
@Component({
selector: 'my-video-add-to-playlist',
styleUrls: [ './video-add-to-playlist.component.scss' ],
templateUrl: './video-add-to-playlist.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, OnChanges, OnDestroy, DisableForReuseHook {
@Input() video: Video
@Input() currentVideoTimestamp: number
@Input() lazyLoad = false
isNewPlaylistBlockOpened = false
videoPlaylistSearch: string
videoPlaylistSearchChanged = new Subject<string>()
videoPlaylists: PlaylistSummary[] = []
timestampOptions: {
startTimestampEnabled: boolean
startTimestamp: number
stopTimestampEnabled: boolean
stopTimestamp: number
}
displayOptions = false
private disabled = false
private listenToPlaylistChangeSub: Subscription
private playlistsData: CachedPlaylist[] = []
constructor (
protected formValidatorService: FormValidatorService,
private authService: AuthService,
private notifier: Notifier,
private videoPlaylistService: VideoPlaylistService,
private cd: ChangeDetectorRef
) {
super()
}
get user () {
return this.authService.getUser()
}
ngOnInit () {
this.buildForm({
displayName: VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR
})
this.videoPlaylistService.listenToMyAccountPlaylistsChange()
.subscribe(result => {
this.playlistsData = result.data
this.videoPlaylistService.runPlaylistCheck(this.video.id)
})
this.videoPlaylistSearchChanged
.pipe(debounceTime(500))
.subscribe(() => this.load())
if (this.lazyLoad === false) this.load()
}
ngOnChanges (simpleChanges: SimpleChanges) {
if (simpleChanges['video']) {
this.reload()
}
}
ngOnDestroy () {
this.unsubscribePlaylistChanges()
}
disableForReuse () {
this.disabled = true
}
enabledForReuse () {
this.disabled = false
}
reload () {
logger('Reloading component')
this.videoPlaylists = []
this.videoPlaylistSearch = undefined
this.resetOptions(true)
this.load()
this.cd.markForCheck()
}
load () {
logger('Loading component')
this.listenToPlaylistChanges()
this.videoPlaylistService.listMyPlaylistWithCache(this.user, this.videoPlaylistSearch)
.subscribe(playlistsResult => {
this.playlistsData = playlistsResult.data
this.videoPlaylistService.runPlaylistCheck(this.video.id)
})
}
openChange (opened: boolean) {
if (opened === false) {
this.isNewPlaylistBlockOpened = false
this.displayOptions = false
}
}
openCreateBlock (event: Event) {
event.preventDefault()
this.isNewPlaylistBlockOpened = true
}
togglePlaylist (event: Event, playlist: PlaylistSummary) {
event.preventDefault()
if (playlist.inPlaylist === true) {
this.removeVideoFromPlaylist(playlist)
} else {
this.addVideoInPlaylist(playlist)
}
playlist.inPlaylist = !playlist.inPlaylist
this.resetOptions()
this.cd.markForCheck()
}
createPlaylist () {
const displayName = this.form.value[ 'displayName' ]
const videoPlaylistCreate: VideoPlaylistCreate = {
displayName,
privacy: VideoPlaylistPrivacy.PRIVATE
}
this.videoPlaylistService.createVideoPlaylist(videoPlaylistCreate).subscribe(
() => {
this.isNewPlaylistBlockOpened = false
this.cd.markForCheck()
},
err => this.notifier.error(err.message)
)
}
resetOptions (resetTimestamp = false) {
this.displayOptions = false
this.timestampOptions = {} as any
this.timestampOptions.startTimestampEnabled = false
this.timestampOptions.stopTimestampEnabled = false
if (resetTimestamp) {
this.timestampOptions.startTimestamp = 0
this.timestampOptions.stopTimestamp = this.video.duration
}
}
formatTimestamp (playlist: PlaylistSummary) {
const start = playlist.startTimestamp ? secondsToTime(playlist.startTimestamp) : ''
const stop = playlist.stopTimestamp ? secondsToTime(playlist.stopTimestamp) : ''
return `(${start}-${stop})`
}
onVideoPlaylistSearchChanged () {
this.videoPlaylistSearchChanged.next()
}
private removeVideoFromPlaylist (playlist: PlaylistSummary) {
if (!playlist.playlistElementId) return
this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, playlist.playlistElementId, this.video.id)
.subscribe(
() => {
this.notifier.success($localize`Video removed from ${playlist.displayName}`)
},
err => {
this.notifier.error(err.message)
},
() => this.cd.markForCheck()
)
}
private listenToPlaylistChanges () {
this.unsubscribePlaylistChanges()
this.listenToPlaylistChangeSub = this.videoPlaylistService.listenToVideoPlaylistChange(this.video.id)
.pipe(filter(() => this.disabled === false))
.subscribe(existResult => this.rebuildPlaylists(existResult))
}
private unsubscribePlaylistChanges () {
if (this.listenToPlaylistChangeSub) {
this.listenToPlaylistChangeSub.unsubscribe()
this.listenToPlaylistChangeSub = undefined
}
}
private rebuildPlaylists (existResult: VideoExistInPlaylist[]) {
logger('Got existing results for %d.', this.video.id, existResult)
this.videoPlaylists = []
for (const playlist of this.playlistsData) {
const existingPlaylist = existResult.find(p => p.playlistId === playlist.id)
this.videoPlaylists.push({
id: playlist.id,
displayName: playlist.displayName,
inPlaylist: !!existingPlaylist,
playlistElementId: existingPlaylist ? existingPlaylist.playlistElementId : undefined,
startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined,
stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined
})
}
logger('Rebuilt playlist state for video %d.', this.video.id, this.videoPlaylists)
this.cd.markForCheck()
}
private addVideoInPlaylist (playlist: PlaylistSummary) {
const body: VideoPlaylistElementCreate = { videoId: this.video.id }
if (this.timestampOptions.startTimestampEnabled) body.startTimestamp = this.timestampOptions.startTimestamp
if (this.timestampOptions.stopTimestampEnabled) body.stopTimestamp = this.timestampOptions.stopTimestamp
this.videoPlaylistService.addVideoInPlaylist(playlist.id, body)
.subscribe(
() => {
const message = body.startTimestamp || body.stopTimestamp
? $localize`Video added in ${playlist.displayName} at timestamps ${this.formatTimestamp(playlist)}`
: $localize`Video added in ${playlist.displayName}`
this.notifier.success(message)
},
err => {
this.notifier.error(err.message)
},
() => this.cd.markForCheck()
)
}
}