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