+ const serverTranslations = await this.translationsPromise
+
+ if (videoResponse.status === 404) {
+ this.videoNotFound(serverTranslations)
+ return undefined
+ }
+
+ this.videoFetchError(serverTranslations)
+ return undefined
+ }
+
+ const captionsPromise = this.loadVideoCaptions(videoId)
+
+ return { captionsPromise, videoResponse }
+ }
+
+ private async buildPlaylistManager () {
+ const translations = await this.translationsPromise
+
+ this.player.upnext({
+ timeout: 10000, // 10s
+ headText: peertubeTranslate('Up Next', translations),
+ cancelText: peertubeTranslate('Cancel', translations),
+ suspendedText: peertubeTranslate('Autoplay is suspended', translations),
+ getTitle: () => this.nextVideoTitle(),
+ next: () => this.playNextVideo(),
+ condition: () => !!this.getNextPlaylistElement(),
+ suspended: () => false
+ })
+ }
+
+ private async loadVideoAndBuildPlayer (uuid: string) {
+ const res = await this.loadVideo(uuid)
+ if (res === undefined) return
+
+ return this.buildVideoPlayer(res.videoResponse, res.captionsPromise)
+ }
+
+ private nextVideoTitle () {
+ const next = this.getNextPlaylistElement()
+ if (!next) return ''
+
+ return next.video.name
+ }
+
+ private getNextPlaylistElement (position?: number): VideoPlaylistElement {
+ if (!position) position = this.currentPlaylistElement.position + 1
+
+ if (position > this.playlist.videosLength) {
+ return undefined
+ }
+
+ const next = this.playlistElements.find(e => e.position === position)
+
+ if (!next || !next.video) {
+ return this.getNextPlaylistElement(position + 1)
+ }
+
+ return next
+ }
+
+ private getPreviousPlaylistElement (position?: number): VideoPlaylistElement {
+ if (!position) position = this.currentPlaylistElement.position - 1
+
+ if (position < 1) {
+ return undefined
+ }
+
+ const prev = this.playlistElements.find(e => e.position === position)
+
+ if (!prev || !prev.video) {
+ return this.getNextPlaylistElement(position - 1)
+ }
+
+ return prev
+ }