X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=client%2Fsrc%2Fassets%2Fplayer%2Fpeertube-videojs-plugin.ts;h=4a280b7efed111f6153011941697209e31705b09;hb=3b019808ef529cacce7f40706441670309e231d1;hp=2330f476f0df03936c5f7c89cbcee5539a000c67;hpb=71e318b4fe66175d03c7c82357d60062eb68af81;p=github%2FChocobozzz%2FPeerTube.git diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index 2330f476f..4a280b7ef 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts @@ -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