+ this.userWatchingVideoInterval = setInterval(() => {
+ const currentTime = Math.floor(this.player.currentTime())
+
+ if (currentTime - lastCurrentTime >= 1) {
+ lastCurrentTime = currentTime
+
+ this.notifyUserIsWatching(currentTime, options.url, options.authorizationHeader)
+ .catch(err => console.error('Cannot notify user is watching.', err))
+ }
+ }, this.CONSTANTS.USER_WATCHING_VIDEO_INTERVAL)
+ }
+
+ private clearVideoViewInterval () {
+ if (this.videoViewInterval !== undefined) {
+ clearInterval(this.videoViewInterval)
+ this.videoViewInterval = undefined
+ }
+ }
+
+ private addViewToVideo () {
+ if (!this.videoViewUrl) return Promise.resolve(undefined)
+
+ return fetch(this.videoViewUrl, { method: 'POST' })
+ }
+
+ private notifyUserIsWatching (currentTime: number, url: string, authorizationHeader: string) {
+ const body = new URLSearchParams()
+ body.append('currentTime', currentTime.toString())
+
+ const headers = new Headers({ 'Authorization': authorizationHeader })
+
+ return fetch(url, { method: 'PUT', body, headers })
+ }
+
+ private fallbackToHttp (done?: Function, play = true) {
+ this.disableAutoResolution(true)
+
+ this.flushVideoFile(this.currentVideoFile, true)
+ this.torrent = null
+
+ // Enable error display now this is our last fallback
+ this.player.one('error', () => this.enableErrorDisplay())
+
+ const httpUrl = this.currentVideoFile.fileUrl
+ this.player.src = this.savePlayerSrcFunction
+ this.player.src(httpUrl)
+ if (play) this.tryToPlay()
+
+ if (done) return done()
+ }
+
+ private handleError (err: Error | string) {
+ return this.player.trigger('customError', { err })
+ }
+
+ private enableErrorDisplay () {
+ this.player.addClass('vjs-error-display-enabled')
+ }
+
+ private disableErrorDisplay () {
+ this.player.removeClass('vjs-error-display-enabled')
+ }
+
+ private isIOS () {
+ return !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)
+ }
+
+ private alterInactivity () {
+ let saveInactivityTimeout: number
+
+ const disableInactivity = () => {
+ saveInactivityTimeout = this.player.options_.inactivityTimeout
+ this.player.options_.inactivityTimeout = 0
+ }
+ const enableInactivity = () => {
+ this.player.options_.inactivityTimeout = saveInactivityTimeout
+ }
+
+ const settingsDialog = this.player.children_.find(c => c.name_ === 'SettingsDialog')
+
+ this.player.controlBar.on('mouseenter', () => disableInactivity())
+ settingsDialog.on('mouseenter', () => disableInactivity())
+ this.player.controlBar.on('mouseleave', () => enableInactivity())
+ settingsDialog.on('mouseleave', () => enableInactivity())
+ }
+
+ private pickAverageVideoFile () {
+ if (this.videoFiles.length === 1) return this.videoFiles[0]
+
+ return this.videoFiles[Math.floor(this.videoFiles.length / 2)]
+ }
+
+ private stopTorrent (torrent: WebTorrent.Torrent) {
+ torrent.pause()
+ // Pause does not remove actual peers (in particular the webseed peer)
+ torrent.removePeer(torrent[ 'ws' ])
+ }
+
+ private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {
+ this.destoyingFakeRenderer = 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) {
+ console.error('Cannot render new torrent in fake video element.', err)
+ }
+
+ // Load the future file at the correct time (in delay MS - 2 seconds)
+ fakeVideoElem.currentTime = this.player.currentTime() + (delay - 2000)
+ })
+ }
+
+ private destroyFakeRenderer () {
+ if (this.fakeRenderer) {
+ this.destoyingFakeRenderer = true
+
+ if (this.fakeRenderer.destroy) {
+ try {
+ this.fakeRenderer.destroy()
+ } catch (err) {
+ console.log('Cannot destroy correctly fake renderer.', err)
+ }
+ }
+ this.fakeRenderer = undefined
+ }
+ }
+
+ private initCaptions () {
+ for (const caption of this.videoCaptions) {
+ this.player.addRemoteTextTrack({
+ kind: 'captions',
+ label: caption.label,
+ language: caption.language,
+ id: caption.language,
+ src: caption.src
+ }, false)
+ }
+ }
+
+ // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
+ private initSmoothProgressBar () {
+ const SeekBar = videojsUntyped.getComponent('SeekBar')
+ SeekBar.prototype.getPercent = function getPercent () {
+ // Allows for smooth scrubbing, when player can't keep up.
+ // const time = (this.player_.scrubbing()) ?
+ // this.player_.getCache().currentTime :
+ // this.player_.currentTime()
+ const time = this.player_.currentTime()
+ const percent = time / this.player_.duration()
+ return percent >= 1 ? 1 : percent
+ }
+ SeekBar.prototype.handleMouseMove = function handleMouseMove (event) {
+ let newTime = this.calculateDistance(event) * this.player_.duration()
+ if (newTime === this.player_.duration()) {
+ newTime = newTime - 0.1
+ }
+ this.player_.currentTime(newTime)
+ this.update()
+ }