aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/assets/player/peertube-videojs-plugin.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-09-17 15:00:46 +0200
committerChocobozzz <me@florianbigard.com>2018-09-17 15:01:04 +0200
commitb335ccec49b450052e3520f66f9acb6670e669f8 (patch)
tree87099007da9f812621904a474f8a10085b3a1653 /client/src/assets/player/peertube-videojs-plugin.ts
parentc07b6041111daa6dd5d611f31e31819db5992ba8 (diff)
downloadPeerTube-b335ccec49b450052e3520f66f9acb6670e669f8.tar.gz
PeerTube-b335ccec49b450052e3520f66f9acb6670e669f8.tar.zst
PeerTube-b335ccec49b450052e3520f66f9acb6670e669f8.zip
Fix ios player playback/subtitles menu
Diffstat (limited to 'client/src/assets/player/peertube-videojs-plugin.ts')
-rw-r--r--client/src/assets/player/peertube-videojs-plugin.ts201
1 files changed, 102 insertions, 99 deletions
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts
index 4b0677fab..36b80bd72 100644
--- a/client/src/assets/player/peertube-videojs-plugin.ts
+++ b/client/src/assets/player/peertube-videojs-plugin.ts
@@ -4,7 +4,7 @@ import { VideoFile } from '../../../../shared/models/videos/video.model'
4import { renderVideo } from './video-renderer' 4import { renderVideo } from './video-renderer'
5import './settings-menu-button' 5import './settings-menu-button'
6import { PeertubePluginOptions, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' 6import { PeertubePluginOptions, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
7import { isMobile, videoFileMaxByResolution, videoFileMinByResolution, timeToInt } from './utils' 7import { isMobile, timeToInt, videoFileMaxByResolution, videoFileMinByResolution } from './utils'
8import * as CacheChunkStore from 'cache-chunk-store' 8import * as CacheChunkStore from 'cache-chunk-store'
9import { PeertubeChunkStore } from './peertube-chunk-store' 9import { PeertubeChunkStore } from './peertube-chunk-store'
10import { 10import {
@@ -83,11 +83,6 @@ class PeerTubePlugin extends Plugin {
83 this.videoCaptions = options.videoCaptions 83 this.videoCaptions = options.videoCaptions
84 84
85 this.savePlayerSrcFunction = this.player.src 85 this.savePlayerSrcFunction = this.player.src
86 // Hack to "simulate" src link in video.js >= 6
87 // Without this, we can't play the video after pausing it
88 // https://github.com/videojs/video.js/blob/master/src/js/player.js#L1633
89 this.player.src = () => true
90
91 this.playerElement = options.playerElement 86 this.playerElement = options.playerElement
92 87
93 if (this.autoplay === true) this.player.addClass('vjs-has-autoplay') 88 if (this.autoplay === true) this.player.addClass('vjs-has-autoplay')
@@ -104,9 +99,7 @@ class PeerTubePlugin extends Plugin {
104 99
105 this.player.one('play', () => { 100 this.player.one('play', () => {
106 // Don't run immediately scheduler, wait some seconds the TCP connections are made 101 // Don't run immediately scheduler, wait some seconds the TCP connections are made
107 this.runAutoQualitySchedulerTimer = setTimeout(() => { 102 this.runAutoQualitySchedulerTimer = setTimeout(() => this.runAutoQualityScheduler(), this.CONSTANTS.AUTO_QUALITY_SCHEDULER)
108 this.runAutoQualityScheduler()
109 }, this.CONSTANTS.AUTO_QUALITY_SCHEDULER)
110 }) 103 })
111 }) 104 })
112 105
@@ -167,6 +160,9 @@ class PeerTubePlugin extends Plugin {
167 // Do not display error to user because we will have multiple fallback 160 // Do not display error to user because we will have multiple fallback
168 this.disableErrorDisplay() 161 this.disableErrorDisplay()
169 162
163 // Hack to "simulate" src link in video.js >= 6
164 // Without this, we can't play the video after pausing it
165 // https://github.com/videojs/video.js/blob/master/src/js/player.js#L1633
170 this.player.src = () => true 166 this.player.src = () => true
171 const oldPlaybackRate = this.player.playbackRate() 167 const oldPlaybackRate = this.player.playbackRate()
172 168
@@ -181,7 +177,66 @@ class PeerTubePlugin extends Plugin {
181 this.trigger('videoFileUpdate') 177 this.trigger('videoFileUpdate')
182 } 178 }
183 179
184 addTorrent ( 180 updateResolution (resolutionId: number, delay = 0) {
181 // Remember player state
182 const currentTime = this.player.currentTime()
183 const isPaused = this.player.paused()
184
185 // Remove poster to have black background
186 this.playerElement.poster = ''
187
188 // Hide bigPlayButton
189 if (!isPaused) {
190 this.player.bigPlayButton.hide()
191 }
192
193 const newVideoFile = this.videoFiles.find(f => f.resolution.id === resolutionId)
194 const options = {
195 forcePlay: false,
196 delay,
197 seek: currentTime + (delay / 1000)
198 }
199 this.updateVideoFile(newVideoFile, options)
200 }
201
202 flushVideoFile (videoFile: VideoFile, destroyRenderer = true) {
203 if (videoFile !== undefined && this.webtorrent.get(videoFile.magnetUri)) {
204 if (destroyRenderer === true && this.renderer && this.renderer.destroy) this.renderer.destroy()
205
206 this.webtorrent.remove(videoFile.magnetUri)
207 console.log('Removed ' + videoFile.magnetUri)
208 }
209 }
210
211 isAutoResolutionOn () {
212 return this.autoResolution
213 }
214
215 enableAutoResolution () {
216 this.autoResolution = true
217 this.trigger('autoResolutionUpdate')
218 }
219
220 disableAutoResolution (forbid = false) {
221 if (forbid === true) this.forbidAutoResolution = true
222
223 this.autoResolution = false
224 this.trigger('autoResolutionUpdate')
225 }
226
227 isAutoResolutionForbidden () {
228 return this.forbidAutoResolution === true
229 }
230
231 getCurrentVideoFile () {
232 return this.currentVideoFile
233 }
234
235 getTorrent () {
236 return this.torrent
237 }
238
239 private addTorrent (
185 magnetOrTorrentUrl: string, 240 magnetOrTorrentUrl: string,
186 previousVideoFile: VideoFile, 241 previousVideoFile: VideoFile,
187 options: { 242 options: {
@@ -205,26 +260,15 @@ class PeerTubePlugin extends Plugin {
205 260
206 if (oldTorrent) { 261 if (oldTorrent) {
207 // Pause the old torrent 262 // Pause the old torrent
208 oldTorrent.pause() 263 this.stopTorrent(oldTorrent)
209 // Pause does not remove actual peers (in particular the webseed peer)
210 oldTorrent.removePeer(oldTorrent['ws'])
211 264
212 // We use a fake renderer so we download correct pieces of the next file 265 // We use a fake renderer so we download correct pieces of the next file
213 if (options.delay) { 266 if (options.delay) this.renderFileInFakeElement(torrent.files[ 0 ], options.delay)
214 const fakeVideoElem = document.createElement('video')
215 renderVideo(torrent.files[0], fakeVideoElem, { autoplay: false, controls: false }, (err, renderer) => {
216 this.fakeRenderer = renderer
217
218 if (err) console.error('Cannot render new torrent in fake video element.', err)
219
220 // Load the future file at the correct time
221 fakeVideoElem.currentTime = this.player.currentTime() + (options.delay / 2000)
222 })
223 }
224 } 267 }
225 268
226 // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution) 269 // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution)
227 this.addTorrentDelay = setTimeout(() => { 270 this.addTorrentDelay = setTimeout(() => {
271 // We don't need the fake renderer anymore
228 this.destroyFakeRenderer() 272 this.destroyFakeRenderer()
229 273
230 const paused = this.player.paused() 274 const paused = this.player.paused()
@@ -232,7 +276,7 @@ class PeerTubePlugin extends Plugin {
232 this.flushVideoFile(previousVideoFile) 276 this.flushVideoFile(previousVideoFile)
233 277
234 const renderVideoOptions = { autoplay: false, controls: true } 278 const renderVideoOptions = { autoplay: false, controls: true }
235 renderVideo(torrent.files[0], this.playerElement, renderVideoOptions,(err, renderer) => { 279 renderVideo(torrent.files[ 0 ], this.playerElement, renderVideoOptions, (err, renderer) => {
236 this.renderer = renderer 280 this.renderer = renderer
237 281
238 if (err) return this.fallbackToHttp(done) 282 if (err) return this.fallbackToHttp(done)
@@ -265,7 +309,7 @@ class PeerTubePlugin extends Plugin {
265 if (err.message.indexOf('incorrect info hash') !== -1) { 309 if (err.message.indexOf('incorrect info hash') !== -1) {
266 console.error('Incorrect info hash detected, falling back to torrent file.') 310 console.error('Incorrect info hash detected, falling back to torrent file.')
267 const newOptions = { forcePlay: true, seek: options.seek } 311 const newOptions = { forcePlay: true, seek: options.seek }
268 return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done) 312 return this.addTorrent(this.torrent[ 'xs' ], previousVideoFile, newOptions, done)
269 } 313 }
270 314
271 // Remote instance is down 315 // Remote instance is down
@@ -277,65 +321,6 @@ class PeerTubePlugin extends Plugin {
277 }) 321 })
278 } 322 }
279 323
280 updateResolution (resolutionId: number, delay = 0) {
281 // Remember player state
282 const currentTime = this.player.currentTime()
283 const isPaused = this.player.paused()
284
285 // Remove poster to have black background
286 this.playerElement.poster = ''
287
288 // Hide bigPlayButton
289 if (!isPaused) {
290 this.player.bigPlayButton.hide()
291 }
292
293 const newVideoFile = this.videoFiles.find(f => f.resolution.id === resolutionId)
294 const options = {
295 forcePlay: false,
296 delay,
297 seek: currentTime + (delay / 1000)
298 }
299 this.updateVideoFile(newVideoFile, options)
300 }
301
302 flushVideoFile (videoFile: VideoFile, destroyRenderer = true) {
303 if (videoFile !== undefined && this.webtorrent.get(videoFile.magnetUri)) {
304 if (destroyRenderer === true && this.renderer && this.renderer.destroy) this.renderer.destroy()
305
306 this.webtorrent.remove(videoFile.magnetUri)
307 console.log('Removed ' + videoFile.magnetUri)
308 }
309 }
310
311 isAutoResolutionOn () {
312 return this.autoResolution
313 }
314
315 enableAutoResolution () {
316 this.autoResolution = true
317 this.trigger('autoResolutionUpdate')
318 }
319
320 disableAutoResolution (forbid = false) {
321 if (forbid === true) this.forbidAutoResolution = true
322
323 this.autoResolution = false
324 this.trigger('autoResolutionUpdate')
325 }
326
327 isAutoResolutionForbidden () {
328 return this.forbidAutoResolution === true
329 }
330
331 getCurrentVideoFile () {
332 return this.currentVideoFile
333 }
334
335 getTorrent () {
336 return this.torrent
337 }
338
339 private tryToPlay (done?: Function) { 324 private tryToPlay (done?: Function) {
340 if (!done) done = function () { /* empty */ } 325 if (!done) done = function () { /* empty */ }
341 326
@@ -435,22 +420,22 @@ class PeerTubePlugin extends Plugin {
435 if (this.autoplay === true) { 420 if (this.autoplay === true) {
436 this.player.posterImage.hide() 421 this.player.posterImage.hide()
437 422
438 this.updateVideoFile(undefined, { forcePlay: true, seek: this.startTime }) 423 return this.updateVideoFile(undefined, { forcePlay: true, seek: this.startTime })
439 } else { 424 }
440 // Don't try on iOS that does not support MediaSource
441 if (this.isIOS()) {
442 this.currentVideoFile = this.pickAverageVideoFile()
443 return this.fallbackToHttp(undefined, false)
444 }
445 425
446 // Proxy first play 426 // Don't try on iOS that does not support MediaSource
447 const oldPlay = this.player.play.bind(this.player) 427 if (this.isIOS()) {
448 this.player.play = () => { 428 this.currentVideoFile = this.pickAverageVideoFile()
449 this.player.addClass('vjs-has-big-play-button-clicked') 429 return this.fallbackToHttp(undefined, false)
450 this.player.play = oldPlay 430 }
451 431
452 this.updateVideoFile(undefined, { forcePlay: true, seek: this.startTime }) 432 // Proxy first play
453 } 433 const oldPlay = this.player.play.bind(this.player)
434 this.player.play = () => {
435 this.player.addClass('vjs-has-big-play-button-clicked')
436 this.player.play = oldPlay
437
438 this.updateVideoFile(undefined, { forcePlay: true, seek: this.startTime })
454 } 439 }
455 } 440 }
456 441
@@ -607,6 +592,24 @@ class PeerTubePlugin extends Plugin {
607 return this.videoFiles[Math.floor(this.videoFiles.length / 2)] 592 return this.videoFiles[Math.floor(this.videoFiles.length / 2)]
608 } 593 }
609 594
595 private stopTorrent (torrent: WebTorrent.Torrent) {
596 torrent.pause()
597 // Pause does not remove actual peers (in particular the webseed peer)
598 torrent.removePeer(torrent[ 'ws' ])
599 }
600
601 private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {
602 const fakeVideoElem = document.createElement('video')
603 renderVideo(file, fakeVideoElem, { autoplay: false, controls: false }, (err, renderer) => {
604 this.fakeRenderer = renderer
605
606 if (err) console.error('Cannot render new torrent in fake video element.', err)
607
608 // Load the future file at the correct time (in delay MS - 2 seconds)
609 fakeVideoElem.currentTime = this.player.currentTime() + (delay - 2000)
610 })
611 }
612
610 private destroyFakeRenderer () { 613 private destroyFakeRenderer () {
611 if (this.fakeRenderer) { 614 if (this.fakeRenderer) {
612 if (this.fakeRenderer.destroy) { 615 if (this.fakeRenderer.destroy) {