1 import { Component, EventEmitter, Input, Output } from '@angular/core'
2 import { Router } from '@angular/router'
3 import { AuthService, ComponentPagination, HooksService, Notifier, SessionStorageService, UserService } from '@app/core'
4 import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
5 import { peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
6 import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
7 import { VideoPlaylistPrivacy } from '@shared/models'
10 selector: 'my-video-watch-playlist',
11 templateUrl: './video-watch-playlist.component.html',
12 styleUrls: [ './video-watch-playlist.component.scss' ]
14 export class VideoWatchPlaylistComponent {
15 static SESSION_STORAGE_LOOP_PLAYLIST = 'loop_playlist'
17 @Input() playlist: VideoPlaylist
19 @Output() videoFound = new EventEmitter<string>()
21 playlistElements: VideoPlaylistElement[] = []
22 playlistPagination: ComponentPagination = {
28 autoPlayNextVideoPlaylist: boolean
29 autoPlayNextVideoPlaylistSwitchText = ''
31 loopPlaylistSwitchText = ''
32 noPlaylistVideos = false
34 currentPlaylistPosition: number
37 private hooks: HooksService,
38 private userService: UserService,
39 private auth: AuthService,
40 private notifier: Notifier,
41 private videoPlaylist: VideoPlaylistService,
42 private sessionStorage: SessionStorageService,
43 private router: Router
45 this.userService.getAnonymousOrLoggedUser()
46 .subscribe(user => this.autoPlayNextVideoPlaylist = user.autoPlayNextVideoPlaylist)
48 this.setAutoPlayNextVideoPlaylistSwitchText()
50 this.loopPlaylist = getBoolOrDefault(this.sessionStorage.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_LOOP_PLAYLIST), false)
51 this.setLoopPlaylistSwitchText()
54 onPlaylistVideosNearOfBottom (position?: number) {
56 if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return
58 this.playlistPagination.currentPage += 1
59 this.loadPlaylistElements(this.playlist, false, position)
62 onElementRemoved (playlistElement: VideoPlaylistElement) {
63 this.playlistElements = this.playlistElements.filter(e => e.id !== playlistElement.id)
65 this.playlistPagination.totalItems--
69 return this.playlist.isLocal === true &&
70 this.auth.isLoggedIn() &&
71 this.playlist.ownerAccount.name === this.auth.getUser().username
74 isUnlistedPlaylist () {
75 return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED
78 isPrivatePlaylist () {
79 return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE
83 return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC
86 loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false, position?: number) {
87 const obs = this.hooks.wrapObsFun(
88 this.videoPlaylist.getPlaylistVideos.bind(this.videoPlaylist),
89 { videoPlaylistId: playlist.uuid, componentPagination: this.playlistPagination },
91 'filter:api.video-watch.video-playlist-elements.get.params',
92 'filter:api.video-watch.video-playlist-elements.get.result'
95 obs.subscribe(({ total, data: playlistElements }) => {
96 this.playlistElements = this.playlistElements.concat(playlistElements)
97 this.playlistPagination.totalItems = total
99 const firstAvailableVideo = this.playlistElements.find(e => !!e.video)
100 if (!firstAvailableVideo) {
101 this.noPlaylistVideos = true
105 if (position) this.updatePlaylistIndex(position)
107 if (redirectToFirst) {
110 start: firstAvailableVideo.startTimestamp,
111 stop: firstAvailableVideo.stopTimestamp,
112 playlistPosition: firstAvailableVideo.position
116 this.router.navigate([], extras)
121 updatePlaylistIndex (position: number) {
122 if (this.playlistElements.length === 0 || !position) return
124 // Handle the reverse index
125 if (position < 0) position = this.playlist.videosLength + position + 1
127 for (const playlistElement of this.playlistElements) {
128 // >= if the previous videos were not valid
129 if (playlistElement.video && playlistElement.position >= position) {
130 this.currentPlaylistPosition = playlistElement.position
132 this.videoFound.emit(playlistElement.video.uuid)
135 document.querySelector('.element-' + this.currentPlaylistPosition).scrollIntoView(false)
142 // Load more videos to find our video
143 this.onPlaylistVideosNearOfBottom(position)
146 navigateToPreviousPlaylistVideo () {
147 const previous = this.findPlaylistVideo(this.currentPlaylistPosition - 1, 'previous')
148 if (!previous) return
150 const start = previous.startTimestamp
151 const stop = previous.stopTimestamp
152 this.router.navigate([], { queryParams: { playlistPosition: previous.position, start, stop } })
155 findPlaylistVideo (position: number, type: 'previous' | 'next'): VideoPlaylistElement {
157 (type === 'next' && position > this.playlistPagination.totalItems) ||
158 (type === 'previous' && position < 1)
160 // End of the playlist: end the recursion if we're not in the loop mode
161 if (!this.loopPlaylist) return
164 position = type === 'previous'
165 ? this.playlistPagination.totalItems
169 const found = this.playlistElements.find(e => e.position === position)
170 if (found?.video) return found
172 const newPosition = type === 'previous'
176 return this.findPlaylistVideo(newPosition, type)
179 navigateToNextPlaylistVideo () {
180 const next = this.findPlaylistVideo(this.currentPlaylistPosition + 1, 'next')
183 const start = next.startTimestamp
184 const stop = next.stopTimestamp
185 this.router.navigate([], { queryParams: { playlistPosition: next.position, start, stop } })
188 switchAutoPlayNextVideoPlaylist () {
189 this.autoPlayNextVideoPlaylist = !this.autoPlayNextVideoPlaylist
190 this.setAutoPlayNextVideoPlaylistSwitchText()
192 const details = { autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist }
194 if (this.auth.isLoggedIn()) {
195 this.userService.updateMyProfile(details)
198 this.auth.refreshUserInformation()
201 error: err => this.notifier.error(err.message)
204 this.userService.updateMyAnonymousProfile(details)
208 switchLoopPlaylist () {
209 this.loopPlaylist = !this.loopPlaylist
210 this.setLoopPlaylistSwitchText()
212 peertubeSessionStorage.setItem(
213 VideoWatchPlaylistComponent.SESSION_STORAGE_LOOP_PLAYLIST,
214 this.loopPlaylist.toString()
218 private setAutoPlayNextVideoPlaylistSwitchText () {
219 this.autoPlayNextVideoPlaylistSwitchText = this.autoPlayNextVideoPlaylist
220 ? $localize`Stop autoplaying next video`
221 : $localize`Autoplay next video`
224 private setLoopPlaylistSwitchText () {
225 this.loopPlaylistSwitchText = this.loopPlaylist
226 ? $localize`Stop looping playlist videos`
227 : $localize`Loop playlist videos`