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