]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - client/src/assets/player/shared/peertube/peertube-plugin.ts
Bumped to version v5.2.1
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / shared / peertube / peertube-plugin.ts
index 1dc3e3de096869c61367bf3c19111368a25892ee..af21477491444b5192183a1b528d43f7ef89317b 100644 (file)
@@ -1,7 +1,9 @@
 import debug from 'debug'
 import videojs from 'video.js'
+import { logger } from '@root-helpers/logger'
 import { isMobile } from '@root-helpers/web-browser'
 import { timeToInt } from '@shared/core-utils'
+import { VideoView, VideoViewEvent } from '@shared/models/videos'
 import {
   getStoredLastSubtitle,
   getStoredMute,
@@ -11,27 +13,26 @@ import {
   saveVideoWatchHistory,
   saveVolumeInStore
 } from '../../peertube-player-local-storage'
-import { PeerTubePluginOptions, UserWatching, VideoJSCaption } from '../../types'
+import { PeerTubePluginOptions, VideoJSCaption } from '../../types'
 import { SettingsButton } from '../settings/settings-menu-button'
 
-const logger = debug('peertube:player:peertube')
+const debugLogger = debug('peertube:player:peertube')
 
 const Plugin = videojs.getPlugin('plugin')
 
 class PeerTubePlugin extends Plugin {
   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 readonly authorizationHeader: () => string
+
+  private readonly videoUUID: string
+  private readonly startTime: number
+
+  private readonly videoViewIntervalMs: number
 
   private videoCaptions: VideoJSCaption[]
   private defaultSubtitle: string
 
   private videoViewInterval: any
-  private userWatchingVideoInterval: any
-
-  private isLive: boolean
 
   private menuOpened = false
   private mouseInControlBar = false
@@ -42,12 +43,15 @@ class PeerTubePlugin extends Plugin {
     super(player)
 
     this.videoViewUrl = options.videoViewUrl
-    this.videoDuration = options.videoDuration
+    this.authorizationHeader = options.authorizationHeader
+    this.videoUUID = options.videoUUID
+    this.startTime = timeToInt(options.startTime)
+    this.videoViewIntervalMs = options.videoViewIntervalMs
+
     this.videoCaptions = options.videoCaptions
-    this.isLive = options.isLive
     this.initialInactivityTimeout = this.player.options_.inactivityTimeout
 
-    if (options.autoplay) this.player.addClass('vjs-has-autoplay')
+    if (options.autoplay !== false) this.player.addClass('vjs-has-autoplay')
 
     this.player.on('autoplay-failure', () => {
       this.player.removeClass('vjs-has-autoplay')
@@ -101,15 +105,12 @@ class PeerTubePlugin extends Plugin {
       this.player.duration(options.videoDuration)
 
       this.initializePlayer()
-      this.runViewAdd()
-
-      this.runUserWatchVideo(options.userWatching, options.videoUUID)
+      this.runUserViewing()
     })
   }
 
   dispose () {
     if (this.videoViewInterval) clearInterval(this.videoViewInterval)
-    if (this.userWatchingVideoInterval) clearInterval(this.userWatchingVideoInterval)
   }
 
   onMenuOpened () {
@@ -123,6 +124,32 @@ class PeerTubePlugin extends Plugin {
   }
 
   displayFatalError () {
+    this.player.loadingSpinner.hide()
+
+    const buildModal = (error: MediaError) => {
+      const localize = this.player.localize.bind(this.player)
+
+      const wrapper = document.createElement('div')
+      const header = document.createElement('h1')
+      header.innerText = localize('Failed to play video')
+      wrapper.appendChild(header)
+      const desc = document.createElement('div')
+      desc.innerText = localize('The video failed to play due to technical issues.')
+      wrapper.appendChild(desc)
+      const details = document.createElement('p')
+      details.classList.add('error-details')
+      details.innerText = error.message
+      wrapper.appendChild(details)
+
+      return wrapper
+    }
+
+    const modal = this.player.createModal(buildModal(this.player.error()), {
+      temporary: false,
+      uncloseable: true
+    })
+    modal.addClass('vjs-custom-error-display')
+
     this.player.addClass('vjs-error-display-enabled')
   }
 
@@ -142,76 +169,67 @@ class PeerTubePlugin extends Plugin {
     this.listenFullScreenChange()
   }
 
-  private runViewAdd () {
-    this.clearVideoViewInterval()
+  // ---------------------------------------------------------------------------
 
-    // After 30 seconds (or 3/4 of the video), add a view to the video
-    let minSecondsToView = 30
+  private runUserViewing () {
+    let lastCurrentTime = this.startTime
+    let lastViewEvent: VideoViewEvent
 
-    if (!this.isLive && this.videoDuration < minSecondsToView) {
-      minSecondsToView = (this.videoDuration * 3) / 4
-    }
+    this.player.one('play', () => {
+      this.notifyUserIsWatching(this.startTime, lastViewEvent)
+    })
 
-    let secondsViewed = 0
-    this.videoViewInterval = setInterval(() => {
-      if (this.player && !this.player.paused()) {
-        secondsViewed += 1
-
-        if (secondsViewed > minSecondsToView) {
-          // Restart the loop if this is a live
-          if (this.isLive) {
-            secondsViewed = 0
-          } else {
-            this.clearVideoViewInterval()
-          }
+    this.player.on('seeked', () => {
+      const diff = Math.floor(this.player.currentTime()) - lastCurrentTime
 
-          this.addViewToVideo().catch(err => console.error(err))
-        }
-      }
-    }, 1000)
-  }
+      // Don't take into account small forwards
+      if (diff > 0 && diff < 3) return
+
+      lastViewEvent = 'seek'
+    })
+
+    this.player.one('ended', () => {
+      const currentTime = Math.floor(this.player.duration())
+      lastCurrentTime = currentTime
 
-  private runUserWatchVideo (options: UserWatching, videoUUID: string) {
-    let lastCurrentTime = 0
+      this.notifyUserIsWatching(currentTime, lastViewEvent)
 
-    this.userWatchingVideoInterval = setInterval(() => {
+      lastViewEvent = undefined
+    })
+
+    this.videoViewInterval = setInterval(() => {
       const currentTime = Math.floor(this.player.currentTime())
 
-      if (currentTime - lastCurrentTime >= 1) {
-        lastCurrentTime = currentTime
+      // No need to update
+      if (currentTime === lastCurrentTime) return
 
-        if (options) {
-          this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader)
-            .catch(err => console.error('Cannot notify user is watching.', err))
-        } else {
-          saveVideoWatchHistory(videoUUID, currentTime)
-        }
-      }
-    }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL)
-  }
+      lastCurrentTime = currentTime
 
-  private clearVideoViewInterval () {
-    if (this.videoViewInterval !== undefined) {
-      clearInterval(this.videoViewInterval)
-      this.videoViewInterval = undefined
-    }
+      this.notifyUserIsWatching(currentTime, lastViewEvent)
+        .catch(err => logger.error('Cannot notify user is watching.', err))
+
+      lastViewEvent = undefined
+    }, this.videoViewIntervalMs)
   }
 
-  private addViewToVideo () {
-    if (!this.videoViewUrl) return Promise.resolve(undefined)
+  private notifyUserIsWatching (currentTime: number, viewEvent: VideoViewEvent) {
+    // Server won't save history, so save the video position in local storage
+    if (!this.authorizationHeader()) {
+      saveVideoWatchHistory(this.videoUUID, currentTime)
+    }
 
-    return fetch(this.videoViewUrl, { method: 'POST' })
-  }
+    if (!this.videoViewUrl) return Promise.resolve(true)
 
-  private notifyUserIsWatching (currentTime: number, url: string, authorizationHeader: string) {
-    const body = new URLSearchParams()
-    body.append('currentTime', currentTime.toString())
+    const body: VideoView = { currentTime, viewEvent }
 
-    const headers = new Headers({ Authorization: authorizationHeader })
+    const headers = new Headers({ 'Content-type': 'application/json; charset=UTF-8' })
+    if (this.authorizationHeader()) headers.set('Authorization', this.authorizationHeader())
 
-    return fetch(url, { method: 'PUT', body, headers })
+    return fetch(this.videoViewUrl, { method: 'POST', body: JSON.stringify(body), headers })
   }
 
+  // ---------------------------------------------------------------------------
+
   private listenFullScreenChange () {
     this.player.on('fullscreenchange', () => {
       if (this.player.isFullscreen()) this.player.focus()
@@ -257,7 +275,7 @@ class PeerTubePlugin extends Plugin {
     (this.player as any).cache_.inactivityTimeout = timeout
     this.player.options_.inactivityTimeout = timeout
 
-    logger('Set player inactivity to ' + timeout)
+    debugLogger('Set player inactivity to ' + timeout)
   }
 
   private initCaptions () {