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