]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/app/+videos/+video-watch/video-watch-playlist.component.ts
Refactor next/prev logic
[github/Chocobozzz/PeerTube.git] / client / src / app / +videos / +video-watch / video-watch-playlist.component.ts
1
2 import { Component, EventEmitter, Input, Output } from '@angular/core'
3 import { Router } from '@angular/router'
4 import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core'
5 import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6 import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
7 import { VideoPlaylistPrivacy } from '@shared/models'
8
9 @Component({
10 selector: 'my-video-watch-playlist',
11 templateUrl: './video-watch-playlist.component.html',
12 styleUrls: [ './video-watch-playlist.component.scss' ]
13 })
14 export class VideoWatchPlaylistComponent {
15 static LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'auto_play_video_playlist'
16 static SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'loop_playlist'
17
18 @Input() playlist: VideoPlaylist
19
20 @Output() videoFound = new EventEmitter<string>()
21
22 playlistElements: VideoPlaylistElement[] = []
23 playlistPagination: ComponentPagination = {
24 currentPage: 1,
25 itemsPerPage: 30,
26 totalItems: null
27 }
28
29 autoPlayNextVideoPlaylist: boolean
30 autoPlayNextVideoPlaylistSwitchText = ''
31 loopPlaylist: boolean
32 loopPlaylistSwitchText = ''
33 noPlaylistVideos = false
34
35 currentPlaylistPosition: number
36
37 constructor (
38 private userService: UserService,
39 private auth: AuthService,
40 private notifier: Notifier,
41 private videoPlaylist: VideoPlaylistService,
42 private localStorageService: LocalStorageService,
43 private sessionStorageService: SessionStorageService,
44 private router: Router
45 ) {
46 // defaults to true
47 this.autoPlayNextVideoPlaylist = this.auth.isLoggedIn()
48 ? this.auth.getUser().autoPlayNextVideoPlaylist
49 : this.localStorageService.getItem(VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) !== 'false'
50
51 this.setAutoPlayNextVideoPlaylistSwitchText()
52
53 // defaults to false
54 this.loopPlaylist = this.sessionStorageService.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
55 this.setLoopPlaylistSwitchText()
56 }
57
58 onPlaylistVideosNearOfBottom (position?: number) {
59 // Last page
60 if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return
61
62 this.playlistPagination.currentPage += 1
63 this.loadPlaylistElements(this.playlist, false, position)
64 }
65
66 onElementRemoved (playlistElement: VideoPlaylistElement) {
67 this.playlistElements = this.playlistElements.filter(e => e.id !== playlistElement.id)
68
69 this.playlistPagination.totalItems--
70 }
71
72 isPlaylistOwned () {
73 return this.playlist.isLocal === true &&
74 this.auth.isLoggedIn() &&
75 this.playlist.ownerAccount.name === this.auth.getUser().username
76 }
77
78 isUnlistedPlaylist () {
79 return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED
80 }
81
82 isPrivatePlaylist () {
83 return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE
84 }
85
86 isPublicPlaylist () {
87 return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC
88 }
89
90 loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false, position?: number) {
91 this.videoPlaylist.getPlaylistVideos(playlist.uuid, this.playlistPagination)
92 .subscribe(({ total, data }) => {
93 this.playlistElements = this.playlistElements.concat(data)
94 this.playlistPagination.totalItems = total
95
96 const firstAvailableVideo = this.playlistElements.find(e => !!e.video)
97 if (!firstAvailableVideo) {
98 this.noPlaylistVideos = true
99 return
100 }
101
102 if (position) this.updatePlaylistIndex(position)
103
104 if (redirectToFirst) {
105 const extras = {
106 queryParams: {
107 start: firstAvailableVideo.startTimestamp,
108 stop: firstAvailableVideo.stopTimestamp,
109 playlistPosition: firstAvailableVideo.position
110 },
111 replaceUrl: true
112 }
113 this.router.navigate([], extras)
114 }
115 })
116 }
117
118 updatePlaylistIndex (position: number) {
119 if (this.playlistElements.length === 0 || !position) return
120
121 for (const playlistElement of this.playlistElements) {
122 // >= if the previous videos were not valid
123 if (playlistElement.video && playlistElement.position >= position) {
124 this.currentPlaylistPosition = playlistElement.position
125
126 this.videoFound.emit(playlistElement.video.uuid)
127
128 setTimeout(() => {
129 document.querySelector('.element-' + this.currentPlaylistPosition).scrollIntoView(false)
130 }, 0)
131
132 return
133 }
134 }
135
136 // Load more videos to find our video
137 this.onPlaylistVideosNearOfBottom(position)
138 }
139
140 navigateToPreviousPlaylistVideo () {
141 const previous = this.findPlaylistVideo(this.currentPlaylistPosition - 1, 'previous')
142 if (!previous) return
143
144 const start = previous.startTimestamp
145 const stop = previous.stopTimestamp
146 this.router.navigate([],{ queryParams: { playlistPosition: previous.position, start, stop } })
147 }
148
149 findPlaylistVideo (position: number, type: 'previous' | 'next'): VideoPlaylistElement {
150 if (
151 (type === 'next' && position > this.playlistPagination.totalItems) ||
152 (type === 'previous' && position < 1)
153 ) {
154 // End of the playlist: end the recursion if we're not in the loop mode
155 if (!this.loopPlaylist) return
156
157 // Loop mode
158 position = type === 'previous'
159 ? this.playlistPagination.totalItems
160 : 1
161 }
162
163 const found = this.playlistElements.find(e => e.position === position)
164 if (found && found.video) return found
165
166 const newPosition = type === 'previous'
167 ? position - 1
168 : position + 1
169
170 return this.findPlaylistVideo(newPosition, type)
171 }
172
173 navigateToNextPlaylistVideo () {
174 const next = this.findPlaylistVideo(this.currentPlaylistPosition + 1, 'next')
175 if (!next) return
176
177 const start = next.startTimestamp
178 const stop = next.stopTimestamp
179 this.router.navigate([],{ queryParams: { playlistPosition: next.position, start, stop } })
180 }
181
182 switchAutoPlayNextVideoPlaylist () {
183 this.autoPlayNextVideoPlaylist = !this.autoPlayNextVideoPlaylist
184 this.setAutoPlayNextVideoPlaylistSwitchText()
185
186 peertubeLocalStorage.setItem(
187 VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
188 this.autoPlayNextVideoPlaylist.toString()
189 )
190
191 if (this.auth.isLoggedIn()) {
192 const details = {
193 autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist
194 }
195
196 this.userService.updateMyProfile(details).subscribe(
197 () => {
198 this.auth.refreshUserInformation()
199 },
200 err => this.notifier.error(err.message)
201 )
202 }
203 }
204
205 switchLoopPlaylist () {
206 this.loopPlaylist = !this.loopPlaylist
207 this.setLoopPlaylistSwitchText()
208
209 peertubeSessionStorage.setItem(
210 VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
211 this.loopPlaylist.toString()
212 )
213 }
214
215 private setAutoPlayNextVideoPlaylistSwitchText () {
216 this.autoPlayNextVideoPlaylistSwitchText = this.autoPlayNextVideoPlaylist
217 ? $localize`Stop autoplaying next video`
218 : $localize`Autoplay next video`
219 }
220
221 private setLoopPlaylistSwitchText () {
222 this.loopPlaylistSwitchText = this.loopPlaylist
223 ? $localize`Stop looping playlist videos`
224 : $localize`Loop playlist videos`
225 }
226 }