]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/standalone/videos/embed.ts
fix lint test
[github/Chocobozzz/PeerTube.git] / client / src / standalone / videos / embed.ts
index e9baf64d0270576dab01c31cc321b4bffe0a4a0c..7b269eeb9a627f52166e5654731ea6839fd91dc0 100644 (file)
@@ -17,28 +17,31 @@ import 'core-js/es6/set'
 // For google bot that uses Chrome 41 and does not understand fetch
 import 'whatwg-fetch'
 
-import * as vjs from 'video.js'
+// FIXME: something weird with our path definition in tsconfig and typings
+// @ts-ignore
+import vjs from 'video.js'
+
 import * as Channel from 'jschannel'
 
-import { VideoDetails } from '../../../../shared'
-import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player'
-import { PeerTubeResolution } from '../player/definitions';
+import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared'
+import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player'
+import { PeerTubeResolution } from '../player/definitions'
+import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
+import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
 
 /**
- * Embed API exposes control of the embed player to the outside world via 
+ * Embed API exposes control of the embed player to the outside world via
  * JSChannels and window.postMessage
  */
 class PeerTubeEmbedApi {
-  constructor(
-    private embed : PeerTubeEmbed
-  ) {
-  }
-
-  private channel : Channel.MessagingChannel
+  private channel: Channel.MessagingChannel
   private isReady = false
-  private resolutions : PeerTubeResolution[] = null
+  private resolutions: PeerTubeResolution[] = null
+
+  constructor (private embed: PeerTubeEmbed) {
+  }
 
-  initialize() {
+  initialize () {
     this.constructChannel()
     this.setupStateTracking()
 
@@ -46,14 +49,14 @@ class PeerTubeEmbedApi {
 
     this.notifyReady()
   }
-  
-  private get element() {
+
+  private get element () {
     return this.embed.videoElement
   }
 
-  private constructChannel() {
+  private constructChannel () {
     let channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope })
-    
+
     channel.bind('play', (txn, params) => this.embed.player.play())
     channel.bind('pause', (txn, params) => this.embed.player.pause())
     channel.bind('seek', (txn, time) => this.embed.player.currentTime(time))
@@ -69,9 +72,8 @@ class PeerTubeEmbedApi {
     this.channel = channel
   }
 
-  private setResolution(resolutionId : number) {
-    if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) 
-      return
+  private setResolution (resolutionId: number) {
+    if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return
 
     // Auto resolution
     if (resolutionId === -1) {
@@ -86,14 +88,13 @@ class PeerTubeEmbedApi {
   /**
    * Let the host know that we're ready to go!
    */
-  private notifyReady() {
+  private notifyReady () {
     this.isReady = true
     this.channel.notify({ method: 'ready', params: true })
   }
 
-  private setupStateTracking() {
-    
-    let currentState : 'playing' | 'paused' | 'unstarted' = 'unstarted'
+  private setupStateTracking () {
+    let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted'
 
     setInterval(() => {
       let position = this.element.currentTime
@@ -104,7 +105,7 @@ class PeerTubeEmbedApi {
         params: {
           position,
           volume,
-          playbackState: currentState,
+          playbackState: currentState
         }
       })
     }, 500)
@@ -125,7 +126,7 @@ class PeerTubeEmbedApi {
     this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions())
   }
 
-  private loadResolutions() {
+  private loadResolutions () {
     let resolutions = []
     let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId()
 
@@ -152,30 +153,28 @@ class PeerTubeEmbedApi {
 }
 
 class PeerTubeEmbed {
-  constructor(
-    private videoContainerId : string
-  ) {
-    this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
-  }
-
-  videoElement : HTMLVideoElement
-  player : any
-  playerOptions : any
-  api : PeerTubeEmbedApi = null
-  autoplay : boolean = false
-  controls : boolean = true
-  muted : boolean = false
-  loop : boolean = false
-  enableApi : boolean = false
-  startTime : number = 0
-  scope : string = 'peertube'
-
-  static async main() {
+  videoElement: HTMLVideoElement
+  player: any
+  playerOptions: any
+  api: PeerTubeEmbedApi = null
+  autoplay = false
+  controls = true
+  muted = false
+  loop = false
+  enableApi = false
+  startTime: number | string = 0
+  scope = 'peertube'
+
+  static async main () {
     const videoContainerId = 'video-container'
     const embed = new PeerTubeEmbed(videoContainerId)
     await embed.init()
   }
-  
+
+  constructor (private videoContainerId: string) {
+    this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement
+  }
+
   getVideoUrl (id: string) {
     return window.location.origin + '/api/v1/videos/' + id
   }
@@ -184,13 +183,17 @@ class PeerTubeEmbed {
     return fetch(this.getVideoUrl(videoId))
   }
 
+  loadVideoCaptions (videoId: string): Promise<Response> {
+    return fetch(this.getVideoUrl(videoId) + '/captions')
+  }
+
   removeElement (element: HTMLElement) {
     element.parentElement.removeChild(element)
   }
 
-  displayError (videoElement: HTMLVideoElement, text: string) {
+  displayError (text: string) {
     // Remove video element
-    this.removeElement(videoElement)
+    if (this.videoElement) this.removeElement(this.videoElement)
 
     document.title = 'Sorry - ' + text
 
@@ -201,14 +204,14 @@ class PeerTubeEmbed {
     errorText.innerHTML = text
   }
 
-  videoNotFound (videoElement: HTMLVideoElement) {
+  videoNotFound () {
     const text = 'This video does not exist.'
-    this.displayError(videoElement, text)
+    this.displayError(text)
   }
 
-  videoFetchError (videoElement: HTMLVideoElement) {
+  videoFetchError () {
     const text = 'We cannot fetch the video. Please try again later.'
-    this.displayError(videoElement, text)
+    this.displayError(text)
   }
 
   getParamToggle (params: URLSearchParams, name: string, defaultValue: boolean) {
@@ -219,15 +222,7 @@ class PeerTubeEmbed {
     return params.has(name) ? params.get(name) : defaultValue
   }
 
-  private initializeApi() {
-    if (!this.enableApi)
-      return
-
-    this.api = new PeerTubeEmbedApi(this)
-    this.api.initialize()
-  }
-
-  async init() {
+  async init () {
     try {
       await this.initCore()
     } catch (e) {
@@ -235,7 +230,14 @@ class PeerTubeEmbed {
     }
   }
 
-  private loadParams() {
+  private initializeApi () {
+    if (!this.enableApi) return
+
+    this.api = new PeerTubeEmbedApi(this)
+    this.api.initialize()
+  }
+
+  private loadParams () {
     try {
       let params = new URL(window.location.toString()).searchParams
 
@@ -247,30 +249,40 @@ class PeerTubeEmbed {
       this.scope = this.getParamString(params, 'scope', this.scope)
 
       const startTimeParamString = params.get('start')
-      const startTimeParamNumber = parseInt(startTimeParamString, 10)
-      if (isNaN(startTimeParamNumber) === false) 
-        this.startTime = startTimeParamNumber
+      if (startTimeParamString) this.startTime = startTimeParamString
     } catch (err) {
       console.error('Cannot get params from URL.', err)
     }
   }
 
-  private async initCore() {
+  private async initCore () {
     const urlParts = window.location.href.split('/')
-    const lastPart = urlParts[urlParts.length - 1]
-    const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[0]
+    const lastPart = urlParts[ urlParts.length - 1 ]
+    const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
 
-    await loadLocale(window.location.origin, vjs, navigator.language)
-    let response = await this.loadVideoInfo(videoId)
+    const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([
+      loadLocaleInVideoJS(window.location.origin, vjs, navigator.language),
+      getServerTranslations(window.location.origin, navigator.language),
+      this.loadVideoInfo(videoId),
+      this.loadVideoCaptions(videoId)
+    ])
 
-    if (!response.ok) {
-      if (response.status === 404) 
-        return this.videoNotFound(this.videoElement)
+    if (!videoResponse.ok) {
+      if (videoResponse.status === 404) return this.videoNotFound()
 
-      return this.videoFetchError(this.videoElement)
+      return this.videoFetchError()
     }
 
-    const videoInfo: VideoDetails = await response.json()
+    const videoInfo: VideoDetails = await videoResponse.json()
+    let videoCaptions: VideoJSCaption[] = []
+    if (captionsResponse.ok) {
+      const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
+      videoCaptions = data.map(c => ({
+        label: peertubeTranslate(c.language.label, serverTranslations),
+        language: c.language.id,
+        src: window.location.origin + c.captionPath
+      }))
+    }
 
     this.loadParams()
 
@@ -279,8 +291,9 @@ class PeerTubeEmbed {
       controls: this.controls,
       muted: this.muted,
       loop: this.loop,
-      startTime : this.startTime,
+      startTime: this.startTime,
 
+      videoCaptions,
       inactivityTimeout: 1500,
       videoViewUrl: this.getVideoUrl(videoId) + '/views',
       playerElement: this.videoElement,
@@ -294,19 +307,32 @@ class PeerTubeEmbed {
 
     this.playerOptions = videojsOptions
     this.player = vjs(this.videoContainerId, videojsOptions, () => {
+      this.player.on('customError', (event: any, data: any) => this.handleError(data.err))
 
-      window['videojsPlayer'] = this.player
+      window[ 'videojsPlayer' ] = this.player
 
       if (this.controls) {
-        (this.player as any).dock({
+        this.player.dock({
           title: videoInfo.name,
           description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
         })
       }
+
       addContextMenu(this.player, window.location.origin + videoInfo.embedPath)
+
       this.initializeApi()
     })
   }
+
+  private handleError (err: Error) {
+    if (err.message.indexOf('from xs param') !== -1) {
+      this.player.dispose()
+      this.videoElement = null
+      this.displayError('This video is not available because the remote instance is not responding.')
+      return
+    }
+  }
 }
 
 PeerTubeEmbed.main()
+  .catch(err => console.error('Cannot init embed.', err))