]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/standalone/videos/embed.ts
Handle playlist oembed
[github/Chocobozzz/PeerTube.git] / client / src / standalone / videos / embed.ts
index 71bd04e764378dca8cc525deadf1fb2f14598562..8d1720f7565f5775663d91924dfa248f99ea3f24 100644 (file)
@@ -143,8 +143,11 @@ export class PeerTubeEmbed {
     return fetch(this.getPlaylistUrl(playlistId))
   }
 
-  loadPlaylistElements (playlistId: string): Promise<Response> {
-    return fetch(this.getPlaylistUrl(playlistId) + '/videos')
+  loadPlaylistElements (playlistId: string, start = 0): Promise<Response> {
+    const url = new URL(this.getPlaylistUrl(playlistId) + '/videos')
+    url.search = new URLSearchParams({ start: '' + start, count: '100' }).toString()
+
+    return fetch(url.toString())
   }
 
   loadConfig (): Promise<ServerConfig> {
@@ -176,6 +179,8 @@ export class PeerTubeEmbed {
 
     const errorText = document.getElementById('error-content')
     errorText.innerHTML = translatedText
+
+    this.wrapperElement.style.display = 'none'
   }
 
   videoNotFound (translations?: Translations) {
@@ -206,6 +211,36 @@ export class PeerTubeEmbed {
     return params.has(name) ? params.get(name) : defaultValue
   }
 
+  async playNextVideo () {
+    const next = this.getNextPlaylistElement()
+    if (!next) {
+      console.log('Next element not found in playlist.')
+      return
+    }
+
+    this.currentPlaylistElement = next
+
+    return this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
+  }
+
+  async playPreviousVideo () {
+    const previous = this.getPreviousPlaylistElement()
+    if (!previous) {
+      console.log('Previous element not found in playlist.')
+      return
+    }
+
+    this.currentPlaylistElement = previous
+
+    await this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
+  }
+
+  getCurrentPosition () {
+    if (!this.currentPlaylistElement) return -1
+
+    return this.currentPlaylistElement.position
+  }
+
   async init () {
     try {
       this.userTokens = Tokens.load()
@@ -257,6 +292,28 @@ export class PeerTubeEmbed {
     }
   }
 
+  private async loadAllPlaylistVideos (playlistId: string, baseResult: ResultList<VideoPlaylistElement>) {
+    let elements = baseResult.data
+    let total = baseResult.total
+    let i = 0
+
+    while (total > elements.length && i < 10) {
+      const result = await this.loadPlaylistElements(playlistId, elements.length)
+
+      const json = await result.json() as ResultList<VideoPlaylistElement>
+      total = json.total
+
+      elements = elements.concat(json.data)
+      i++
+    }
+
+    if (i === 10) {
+      console.error('Cannot fetch all playlists elements, there are too many!')
+    }
+
+    return elements
+  }
+
   private async loadPlaylist (playlistId: string) {
     const playlistPromise = this.loadPlaylistInfo(playlistId)
     const playlistElementsPromise = this.loadPlaylistElements(playlistId)
@@ -309,22 +366,14 @@ export class PeerTubeEmbed {
       cancelText: peertubeTranslate('Cancel', translations),
       suspendedText: peertubeTranslate('Autoplay is suspended', translations),
       getTitle: () => this.nextVideoTitle(),
-      next: () => this.autoplayNext(),
+      next: () => this.playNextVideo(),
       condition: () => !!this.getNextPlaylistElement(),
       suspended: () => false
     })
   }
 
-  private async autoplayNext () {
-    const next = this.getNextPlaylistElement()
-    if (!next) {
-      console.log('Next element not found in playlist.')
-      return
-    }
-
-    this.currentPlaylistElement = next
-
-    const res = await this.loadVideo(this.currentPlaylistElement.video.uuid)
+  private async loadVideoAndBuildPlayer (uuid: string) {
+    const res = await this.loadVideo(uuid)
     if (res === undefined) return
 
     return this.buildVideoPlayer(res.videoResponse, res.captionsPromise)
@@ -353,6 +402,22 @@ export class PeerTubeEmbed {
     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
+  }
+
   private async buildVideoPlayer (videoResponse: Response, captionsPromise: Promise<Response>) {
     let alreadyHadPlayer = false
 
@@ -373,7 +438,7 @@ export class PeerTubeEmbed {
         return videoInfo
       })
 
-    const [ videoInfo, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
+    const [ videoInfoTmp, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
       videoInfoPromise,
       this.translationsPromise,
       captionsPromise,
@@ -381,11 +446,29 @@ export class PeerTubeEmbed {
       this.PeertubePlayerManagerModulePromise
     ])
 
+    const videoInfo: VideoDetails = videoInfoTmp
+
     const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager
     const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse)
 
     this.loadParams(videoInfo)
 
+    const playlistPlugin = this.currentPlaylistElement
+      ? {
+        elements: this.playlistElements,
+        playlist: this.playlist,
+
+        getCurrentPosition: () => this.currentPlaylistElement.position,
+
+        onItemClicked: (videoPlaylistElement: VideoPlaylistElement) => {
+          this.currentPlaylistElement = videoPlaylistElement
+
+          this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
+            .catch(err => console.error(err))
+        }
+      }
+      : undefined
+
     const options: PeertubePlayerManagerOptions = {
       common: {
         // Autoplay in playlist mode
@@ -393,12 +476,20 @@ export class PeerTubeEmbed {
         controls: this.controls,
         muted: this.muted,
         loop: this.loop,
+
         captions: videoCaptions.length !== 0,
-        startTime: this.startTime,
-        stopTime: this.stopTime,
         subtitle: this.subtitle,
 
-        nextVideo: () => this.autoplayNext(),
+        startTime: this.playlist ? this.currentPlaylistElement.startTimestamp : this.startTime,
+        stopTime: this.playlist ? this.currentPlaylistElement.stopTimestamp : this.stopTime,
+
+        nextVideo: this.playlist ? () => this.playNextVideo() : undefined,
+        hasNextVideo: this.playlist ? () => !!this.getNextPlaylistElement() : undefined,
+
+        previousVideo: this.playlist ? () => this.playPreviousVideo() : undefined,
+        hasPreviousVideo: this.playlist ? () => !!this.getPreviousPlaylistElement() : undefined,
+
+        playlist: playlistPlugin,
 
         videoCaptions,
         inactivityTimeout: 2500,
@@ -452,6 +543,12 @@ export class PeerTubeEmbed {
 
     if (this.isPlaylistEmbed()) {
       await this.buildPlaylistManager()
+
+      this.player.playlist().updateSelected()
+
+      this.player.on('stopped', () => {
+        this.playNextVideo()
+      })
     }
   }
 
@@ -472,18 +569,36 @@ export class PeerTubeEmbed {
       this.playlist = await res.playlistResponse.json()
 
       const playlistElementResult = await res.videosResponse.json()
-      this.playlistElements = playlistElementResult.data
+      this.playlistElements = await this.loadAllPlaylistVideos(playlistId, playlistElementResult)
+
+      const params = new URL(window.location.toString()).searchParams
+      const playlistPositionParam = this.getParamString(params, 'playlistPosition')
+
+      let position = 1
+
+      if (playlistPositionParam) {
+        position = parseInt(playlistPositionParam + '', 10)
+      }
+
+      this.currentPlaylistElement = this.playlistElements.find(e => e.position === position)
+      if (!this.currentPlaylistElement || !this.currentPlaylistElement.video) {
+        console.error('Current playlist element is not valid.', this.currentPlaylistElement)
+        this.currentPlaylistElement = this.getNextPlaylistElement()
+      }
+
+      if (!this.currentPlaylistElement) {
+        console.error('This playlist does not have any valid element.')
+        const serverTranslations = await this.translationsPromise
+        this.playlistFetchError(serverTranslations)
+        return
+      }
 
-      this.currentPlaylistElement = this.playlistElements[0]
       videoId = this.currentPlaylistElement.video.uuid
     } else {
       videoId = this.getResourceId()
     }
 
-    const res = await this.loadVideo(videoId)
-    if (res === undefined) return
-
-    return this.buildVideoPlayer(res.videoResponse, res.captionsPromise)
+    return this.loadVideoAndBuildPlayer(videoId)
   }
 
   private handleError (err: Error, translations?: { [ id: string ]: string }) {