]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/assets/player/peertube-plugin.ts
Fix viewers for lives
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / peertube-plugin.ts
index 0bd607697fc43cc8b6f263b18aa028ede7ad0b3b..75a6e662ebec2c260f5e185efc28b24ebbaacb10 100644 (file)
@@ -1,8 +1,11 @@
-// FIXME: something weird with our path definition in tsconfig and typings
-// @ts-ignore
-import * as videojs from 'video.js'
+import videojs from 'video.js'
 import './videojs-components/settings-menu-button'
-import { PeerTubePluginOptions, UserWatching, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
+import {
+  PeerTubePluginOptions,
+  ResolutionUpdateData,
+  UserWatching,
+  VideoJSCaption
+} from './peertube-videojs-typings'
 import { isMobile, timeToInt } from './utils'
 import {
   getStoredLastSubtitle,
@@ -13,37 +16,63 @@ import {
   saveVolumeInStore
 } from './peertube-player-local-storage'
 
-const Plugin: VideoJSComponentInterface = videojs.getPlugin('plugin')
+const Plugin = videojs.getPlugin('plugin')
+
 class PeerTubePlugin extends Plugin {
-  private readonly autoplay: boolean = false
-  private readonly startTime: number = 0
   private readonly videoViewUrl: string
   private readonly videoDuration: number
   private readonly CONSTANTS = {
     USER_WATCHING_VIDEO_INTERVAL: 5000 // Every 5 seconds, notify the user is watching the video
   }
 
-  private player: any
   private videoCaptions: VideoJSCaption[]
   private defaultSubtitle: string
 
   private videoViewInterval: any
   private userWatchingVideoInterval: any
-  private qualityObservationTimer: any
+  private lastResolutionChange: ResolutionUpdateData
+
+  private isLive: boolean
 
-  constructor (player: videojs.Player, options: PeerTubePluginOptions) {
-    super(player, options)
+  private menuOpened = false
+  private mouseInControlBar = false
+  private readonly savedInactivityTimeout: number
+
+  constructor (player: videojs.Player, options?: PeerTubePluginOptions) {
+    super(player)
 
-    this.startTime = timeToInt(options.startTime)
     this.videoViewUrl = options.videoViewUrl
     this.videoDuration = options.videoDuration
     this.videoCaptions = options.videoCaptions
+    this.isLive = options.isLive
+
+    this.savedInactivityTimeout = player.options_.inactivityTimeout
 
-    if (this.autoplay === true) this.player.addClass('vjs-has-autoplay')
+    if (options.autoplay) this.player.addClass('vjs-has-autoplay')
+
+    this.player.on('autoplay-failure', () => {
+      this.player.removeClass('vjs-has-autoplay')
+    })
 
     this.player.ready(() => {
       const playerOptions = this.player.options_
 
+      if (options.mode === 'webtorrent') {
+        this.player.webtorrent().on('resolutionChange', (_: any, d: any) => this.handleResolutionChange(d))
+        this.player.webtorrent().on('autoResolutionChange', (_: any, d: any) => this.trigger('autoResolutionChange', d))
+      }
+
+      if (options.mode === 'p2p-media-loader') {
+        this.player.p2pMediaLoader().on('resolutionChange', (_: any, d: any) => this.handleResolutionChange(d))
+      }
+
+      this.player.tech(true).on('loadedqualitydata', () => {
+        setTimeout(() => {
+          // Replay a resolution change, now we loaded all quality data
+          if (this.lastResolutionChange) this.handleResolutionChange(this.lastResolutionChange)
+        }, 0)
+      })
+
       const volume = getStoredVolume()
       if (volume !== undefined) this.player.volume(volume)
 
@@ -57,8 +86,22 @@ class PeerTubePlugin extends Plugin {
         saveMuteInStore(this.player.muted())
       })
 
+      if (options.stopTime) {
+        const stopTime = timeToInt(options.stopTime)
+        const self = this
+
+        this.player.on('timeupdate', function onTimeUpdate () {
+          if (self.player.currentTime() > stopTime) {
+            self.player.pause()
+            self.player.trigger('stopped')
+
+            self.player.off('timeupdate', onTimeUpdate)
+          }
+        })
+      }
+
       this.player.textTracks().on('change', () => {
-        const showing = this.player.textTracks().tracks_.find((t: { kind: string, mode: string }) => {
+        const showing = this.player.textTracks().tracks_.find(t => {
           return t.kind === 'captions' && t.mode === 'showing'
         })
 
@@ -82,11 +125,18 @@ class PeerTubePlugin extends Plugin {
   }
 
   dispose () {
-    clearTimeout(this.qualityObservationTimer)
+    if (this.videoViewInterval) clearInterval(this.videoViewInterval)
+    if (this.userWatchingVideoInterval) clearInterval(this.userWatchingVideoInterval)
+  }
 
-    clearInterval(this.videoViewInterval)
+  onMenuOpen () {
+    this.menuOpened = false
+    this.alterInactivity()
+  }
 
-    if (this.userWatchingVideoInterval) clearInterval(this.userWatchingVideoInterval)
+  onMenuClosed () {
+    this.menuOpened = true
+    this.alterInactivity()
   }
 
   private initializePlayer () {
@@ -96,7 +146,7 @@ class PeerTubePlugin extends Plugin {
 
     this.initCaptions()
 
-    this.alterInactivity()
+    this.listenControlBarMouse()
   }
 
   private runViewAdd () {
@@ -105,7 +155,9 @@ class PeerTubePlugin extends Plugin {
     // After 30 seconds (or 3/4 of the video), add a view to the video
     let minSecondsToView = 30
 
-    if (this.videoDuration < minSecondsToView) minSecondsToView = (this.videoDuration * 3) / 4
+    if (!this.isLive && this.videoDuration < minSecondsToView) {
+      minSecondsToView = (this.videoDuration * 3) / 4
+    }
 
     let secondsViewed = 0
     this.videoViewInterval = setInterval(() => {
@@ -113,7 +165,12 @@ class PeerTubePlugin extends Plugin {
         secondsViewed += 1
 
         if (secondsViewed > minSecondsToView) {
-          this.clearVideoViewInterval()
+          // Restart the loop if this is a live
+          if (this.isLive) {
+            secondsViewed = 0
+          } else {
+            this.clearVideoViewInterval()
+          }
 
           this.addViewToVideo().catch(err => console.error(err))
         }
@@ -158,23 +215,48 @@ class PeerTubePlugin extends Plugin {
     return fetch(url, { method: 'PUT', body, headers })
   }
 
-  private alterInactivity () {
-    let saveInactivityTimeout: number
+  private handleResolutionChange (data: ResolutionUpdateData) {
+    this.lastResolutionChange = data
+
+    const qualityLevels = this.player.qualityLevels()
 
-    const disableInactivity = () => {
-      saveInactivityTimeout = this.player.options_.inactivityTimeout
-      this.player.options_.inactivityTimeout = 0
+    for (let i = 0; i < qualityLevels.length; i++) {
+      if (qualityLevels[i].height === data.resolutionId) {
+        data.id = qualityLevels[i].id
+        break
+      }
     }
-    const enableInactivity = () => {
-      this.player.options_.inactivityTimeout = saveInactivityTimeout
+
+    this.trigger('resolutionChange', data)
+  }
+
+  private listenControlBarMouse () {
+    this.player.controlBar.on('mouseenter', () => {
+      this.mouseInControlBar = true
+      this.alterInactivity()
+    })
+
+    this.player.controlBar.on('mouseleave', () => {
+      this.mouseInControlBar = false
+      this.alterInactivity()
+    })
+  }
+
+  private alterInactivity () {
+    if (this.menuOpened) {
+      this.player.options_.inactivityTimeout = this.savedInactivityTimeout
+      return
     }
 
-    const settingsDialog = this.player.children_.find((c: any) => c.name_ === 'SettingsDialog')
+    if (!this.mouseInControlBar && !this.isTouchEnabled()) {
+      this.player.options_.inactivityTimeout = 1
+    }
+  }
 
-    this.player.controlBar.on('mouseenter', () => disableInactivity())
-    settingsDialog.on('mouseenter', () => disableInactivity())
-    this.player.controlBar.on('mouseleave', () => enableInactivity())
-    settingsDialog.on('mouseleave', () => enableInactivity())
+  private isTouchEnabled () {
+    return ('ontouchstart' in window) ||
+      navigator.maxTouchPoints > 0 ||
+      navigator.msMaxTouchPoints > 0
   }
 
   private initCaptions () {
@@ -194,7 +276,7 @@ class PeerTubePlugin extends Plugin {
 
   // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
   private initSmoothProgressBar () {
-    const SeekBar = videojsUntyped.getComponent('SeekBar')
+    const SeekBar = videojs.getComponent('SeekBar') as any
     SeekBar.prototype.getPercent = function getPercent () {
       // Allows for smooth scrubbing, when player can't keep up.
       // const time = (this.player_.scrubbing()) ?