]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/assets/player/peertube-videojs-plugin.ts
Set last subtitle or subtitle in URL
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / peertube-videojs-plugin.ts
index 2330f476f0df03936c5f7c89cbcee5539a000c67..4a280b7efed111f6153011941697209e31705b09 100644 (file)
@@ -1,21 +1,34 @@
+// FIXME: something weird with our path definition in tsconfig and typings
+// @ts-ignore
 import * as videojs from 'video.js'
+
 import * as WebTorrent from 'webtorrent'
 import { VideoFile } from '../../../../shared/models/videos/video.model'
 import { renderVideo } from './video-renderer'
 import './settings-menu-button'
 import { PeertubePluginOptions, UserWatching, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
 import { isMobile, timeToInt, videoFileMaxByResolution, videoFileMinByResolution } from './utils'
-import * as CacheChunkStore from 'cache-chunk-store'
 import { PeertubeChunkStore } from './peertube-chunk-store'
 import {
   getAverageBandwidthInStore,
+  getStoredLastSubtitle,
   getStoredMute,
   getStoredVolume,
+  getStoredWebTorrentEnabled,
   saveAverageBandwidth,
+  saveLastSubtitle,
   saveMuteInStore,
   saveVolumeInStore
 } from './peertube-player-local-storage'
 
+const CacheChunkStore = require('cache-chunk-store')
+
+type PlayOptions = {
+  forcePlay?: boolean,
+  seek?: number,
+  delay?: number
+}
+
 const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
 class PeerTubePlugin extends Plugin {
   private readonly playerElement: HTMLVideoElement
@@ -56,22 +69,24 @@ class PeerTubePlugin extends Plugin {
   private currentVideoFile: VideoFile
   private torrent: WebTorrent.Torrent
   private videoCaptions: VideoJSCaption[]
+  private defaultSubtitle: string
 
-  private renderer
-  private fakeRenderer
-  private destoyingFakeRenderer = false
+  private renderer: any
+  private fakeRenderer: any
+  private destroyingFakeRenderer = false
 
   private autoResolution = true
   private forbidAutoResolution = false
   private isAutoResolutionObservation = false
+  private playerRefusedP2P = false
 
-  private videoViewInterval
-  private torrentInfoInterval
-  private autoQualityInterval
-  private userWatchingVideoInterval
-  private addTorrentDelay
-  private qualityObservationTimer
-  private runAutoQualitySchedulerTimer
+  private videoViewInterval: any
+  private torrentInfoInterval: any
+  private autoQualityInterval: any
+  private userWatchingVideoInterval: any
+  private addTorrentDelay: any
+  private qualityObservationTimer: any
+  private runAutoQualitySchedulerTimer: any
 
   private downloadSpeeds: number[] = []
 
@@ -80,6 +95,7 @@ class PeerTubePlugin extends Plugin {
 
     // Disable auto play on iOS
     this.autoplay = options.autoplay && this.isIOS() === false
+    this.playerRefusedP2P = !getStoredWebTorrentEnabled()
 
     this.startTime = timeToInt(options.startTime)
     this.videoFiles = options.videoFiles
@@ -93,11 +109,36 @@ class PeerTubePlugin extends Plugin {
     if (this.autoplay === true) this.player.addClass('vjs-has-autoplay')
 
     this.player.ready(() => {
+      const playerOptions = this.player.options_
+
       const volume = getStoredVolume()
       if (volume !== undefined) this.player.volume(volume)
-      const muted = getStoredMute()
+
+      const muted = playerOptions.muted !== undefined ? playerOptions.muted : getStoredMute()
       if (muted !== undefined) this.player.muted(muted)
 
+      this.defaultSubtitle = options.subtitle || getStoredLastSubtitle()
+
+      this.player.on('volumechange', () => {
+        saveVolumeInStore(this.player.volume())
+        saveMuteInStore(this.player.muted())
+      })
+
+      this.player.textTracks().on('change', () => {
+        const showing = this.player.textTracks().tracks_.find((t: { kind: string, mode: string }) => {
+          return t.kind === 'captions' && t.mode === 'showing'
+        })
+
+        if (!showing) {
+          saveLastSubtitle('off')
+          return
+        }
+
+        saveLastSubtitle(showing.language)
+      })
+
+      this.player.duration(options.videoDuration)
+
       this.initializePlayer()
       this.runTorrentInfoScheduler()
       this.runViewAdd()
@@ -109,11 +150,6 @@ class PeerTubePlugin extends Plugin {
         this.runAutoQualitySchedulerTimer = setTimeout(() => this.runAutoQualityScheduler(), this.CONSTANTS.AUTO_QUALITY_SCHEDULER)
       })
     })
-
-    this.player.on('volumechange', () => {
-      saveVolumeInStore(this.player.volume())
-      saveMuteInStore(this.player.muted())
-    })
   }
 
   dispose () {
@@ -178,6 +214,15 @@ class PeerTubePlugin extends Plugin {
     const previousVideoFile = this.currentVideoFile
     this.currentVideoFile = videoFile
 
+    // Don't try on iOS that does not support MediaSource
+    // Or don't use P2P if webtorrent is disabled
+    if (this.isIOS() || this.playerRefusedP2P) {
+      return this.fallbackToHttp(options, () => {
+        this.player.playbackRate(oldPlaybackRate)
+        return done()
+      })
+    }
+
     this.addTorrent(this.currentVideoFile.magnetUri, previousVideoFile, options, () => {
       this.player.playbackRate(oldPlaybackRate)
       return done()
@@ -248,18 +293,14 @@ class PeerTubePlugin extends Plugin {
   private addTorrent (
     magnetOrTorrentUrl: string,
     previousVideoFile: VideoFile,
-    options: {
-      forcePlay?: boolean,
-      seek?: number,
-      delay?: number
-    },
+    options: PlayOptions,
     done: Function
   ) {
     console.log('Adding ' + magnetOrTorrentUrl + '.')
 
     const oldTorrent = this.torrent
     const torrentOptions = {
-      store: (chunkLength, storeOpts) => new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), {
+      store: (chunkLength: number, storeOpts: any) => new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), {
         max: 100
       })
     }
@@ -284,11 +325,14 @@ class PeerTubePlugin extends Plugin {
 
         this.flushVideoFile(previousVideoFile)
 
+        // Update progress bar (just for the UI), do not wait rendering
+        if (options.seek) this.player.currentTime(options.seek)
+
         const renderVideoOptions = { autoplay: false, controls: true }
         renderVideo(torrent.files[ 0 ], this.playerElement, renderVideoOptions, (err, renderer) => {
           this.renderer = renderer
 
-          if (err) return this.fallbackToHttp(done)
+          if (err) return this.fallbackToHttp(options, done)
 
           return this.tryToPlay(err => {
             if (err) return done(err)
@@ -296,13 +340,13 @@ class PeerTubePlugin extends Plugin {
             if (options.seek) this.seek(options.seek)
             if (options.forcePlay === false && paused === true) this.player.pause()
 
-            return done(err)
+            return done()
           })
         })
       }, options.delay || 0)
     })
 
-    this.torrent.on('error', err => console.error(err))
+    this.torrent.on('error', (err: any) => console.error(err))
 
     this.torrent.on('warning', (err: any) => {
       // We don't support HTTP tracker but we don't care -> we use the web socket tracker
@@ -330,13 +374,13 @@ class PeerTubePlugin extends Plugin {
     })
   }
 
-  private tryToPlay (done?: Function) {
+  private tryToPlay (done?: (err?: Error) => void) {
     if (!done) done = function () { /* empty */ }
 
     const playPromise = this.player.play()
     if (playPromise !== undefined) {
       return playPromise.then(done)
-                        .catch(err => {
+                        .catch((err: Error) => {
                           if (err.message.indexOf('The play() request was interrupted by a call to pause()') !== -1) {
                             return
                           }
@@ -432,12 +476,6 @@ class PeerTubePlugin extends Plugin {
       return this.updateVideoFile(undefined, { forcePlay: true, seek: this.startTime })
     }
 
-    // Don't try on iOS that does not support MediaSource
-    if (this.isIOS()) {
-      this.currentVideoFile = this.pickAverageVideoFile()
-      return this.fallbackToHttp(undefined, false)
-    }
-
     // Proxy first play
     const oldPlay = this.player.play.bind(this.player)
     this.player.play = () => {
@@ -567,7 +605,9 @@ class PeerTubePlugin extends Plugin {
     return fetch(url, { method: 'PUT', body, headers })
   }
 
-  private fallbackToHttp (done?: Function, play = true) {
+  private fallbackToHttp (options: PlayOptions, done?: Function) {
+    const paused = this.player.paused()
+
     this.disableAutoResolution(true)
 
     this.flushVideoFile(this.currentVideoFile, true)
@@ -579,9 +619,15 @@ class PeerTubePlugin extends Plugin {
     const httpUrl = this.currentVideoFile.fileUrl
     this.player.src = this.savePlayerSrcFunction
     this.player.src(httpUrl)
-    if (play) this.tryToPlay()
 
-    if (done) return done()
+    return this.tryToPlay(err => {
+      if (err && done) return done(err)
+
+      if (options.seek) this.seek(options.seek)
+      if (options.forcePlay === false && paused === true) this.player.pause()
+
+      if (done) return done()
+    })
   }
 
   private handleError (err: Error | string) {
@@ -611,7 +657,7 @@ class PeerTubePlugin extends Plugin {
       this.player.options_.inactivityTimeout = saveInactivityTimeout
     }
 
-    const settingsDialog = this.player.children_.find(c => c.name_ === 'SettingsDialog')
+    const settingsDialog = this.player.children_.find((c: any) => c.name_ === 'SettingsDialog')
 
     this.player.controlBar.on('mouseenter', () => disableInactivity())
     settingsDialog.on('mouseenter', () => disableInactivity())
@@ -632,14 +678,14 @@ class PeerTubePlugin extends Plugin {
   }
 
   private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {
-    this.destoyingFakeRenderer = false
+    this.destroyingFakeRenderer = false
 
     const fakeVideoElem = document.createElement('video')
     renderVideo(file, fakeVideoElem, { autoplay: false, controls: false }, (err, renderer) => {
       this.fakeRenderer = renderer
 
       // The renderer returns an error when we destroy it, so skip them
-      if (this.destoyingFakeRenderer === false && err) {
+      if (this.destroyingFakeRenderer === false && err) {
         console.error('Cannot render new torrent in fake video element.', err)
       }
 
@@ -650,7 +696,7 @@ class PeerTubePlugin extends Plugin {
 
   private destroyFakeRenderer () {
     if (this.fakeRenderer) {
-      this.destoyingFakeRenderer = true
+      this.destroyingFakeRenderer = true
 
       if (this.fakeRenderer.destroy) {
         try {
@@ -670,7 +716,8 @@ class PeerTubePlugin extends Plugin {
         label: caption.label,
         language: caption.language,
         id: caption.language,
-        src: caption.src
+        src: caption.src,
+        default: this.defaultSubtitle === caption.language
       }, false)
     }
   }
@@ -687,7 +734,7 @@ class PeerTubePlugin extends Plugin {
       const percent = time / this.player_.duration()
       return percent >= 1 ? 1 : percent
     }
-    SeekBar.prototype.handleMouseMove = function handleMouseMove (event) {
+    SeekBar.prototype.handleMouseMove = function handleMouseMove (event: any) {
       let newTime = this.calculateDistance(event) * this.player_.duration()
       if (newTime === this.player_.duration()) {
         newTime = newTime - 0.1