diff options
-rw-r--r-- | client/src/assets/player/peertube-videojs-plugin.ts | 201 | ||||
-rw-r--r-- | client/src/assets/player/settings-menu-item.ts | 7 | ||||
-rw-r--r-- | client/src/sass/application.scss | 2 | ||||
-rw-r--r-- | client/src/sass/player/index.scss (renamed from client/src/sass/player/player.scss) | 0 | ||||
-rw-r--r-- | client/src/sass/player/peertube-skin.scss | 5 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.scss | 2 | ||||
-rw-r--r-- | package.json | 2 |
7 files changed, 111 insertions, 108 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' | |||
4 | import { renderVideo } from './video-renderer' | 4 | import { renderVideo } from './video-renderer' |
5 | import './settings-menu-button' | 5 | import './settings-menu-button' |
6 | import { PeertubePluginOptions, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' | 6 | import { PeertubePluginOptions, VideoJSCaption, VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' |
7 | import { isMobile, videoFileMaxByResolution, videoFileMinByResolution, timeToInt } from './utils' | 7 | import { isMobile, timeToInt, videoFileMaxByResolution, videoFileMinByResolution } from './utils' |
8 | import * as CacheChunkStore from 'cache-chunk-store' | 8 | import * as CacheChunkStore from 'cache-chunk-store' |
9 | import { PeertubeChunkStore } from './peertube-chunk-store' | 9 | import { PeertubeChunkStore } from './peertube-chunk-store' |
10 | import { | 10 | import { |
@@ -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) { |
diff --git a/client/src/assets/player/settings-menu-item.ts b/client/src/assets/player/settings-menu-item.ts index 6e2224e20..f6cf6d0f3 100644 --- a/client/src/assets/player/settings-menu-item.ts +++ b/client/src/assets/player/settings-menu-item.ts | |||
@@ -38,8 +38,11 @@ class SettingsMenuItem extends MenuItem { | |||
38 | this.eventHandlers() | 38 | this.eventHandlers() |
39 | 39 | ||
40 | player.ready(() => { | 40 | player.ready(() => { |
41 | this.build() | 41 | // Voodoo magic for IOS |
42 | this.reset() | 42 | setTimeout(() => { |
43 | this.build() | ||
44 | this.reset() | ||
45 | }, 0) | ||
43 | }) | 46 | }) |
44 | } | 47 | } |
45 | 48 | ||
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 8d2bfb077..c1135cd02 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -9,7 +9,7 @@ $icon-font-path: '../../node_modules/@neos21/bootstrap3-glyphicons/assets/fonts/ | |||
9 | @import '~video.js/dist/video-js.css'; | 9 | @import '~video.js/dist/video-js.css'; |
10 | 10 | ||
11 | $assets-path: '../assets/'; | 11 | $assets-path: '../assets/'; |
12 | @import './player/player'; | 12 | @import './player/index'; |
13 | @import './loading-bar'; | 13 | @import './loading-bar'; |
14 | 14 | ||
15 | @import './primeng-custom'; | 15 | @import './primeng-custom'; |
diff --git a/client/src/sass/player/player.scss b/client/src/sass/player/index.scss index e4a315d1f..e4a315d1f 100644 --- a/client/src/sass/player/player.scss +++ b/client/src/sass/player/index.scss | |||
diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss index 185b00222..4e921e970 100644 --- a/client/src/sass/player/peertube-skin.scss +++ b/client/src/sass/player/peertube-skin.scss | |||
@@ -406,6 +406,7 @@ | |||
406 | 406 | ||
407 | width: 37px; | 407 | width: 37px; |
408 | margin-right: 1px; | 408 | margin-right: 1px; |
409 | cursor: pointer; | ||
409 | 410 | ||
410 | .vjs-icon-placeholder { | 411 | .vjs-icon-placeholder { |
411 | transition: transform 0.2s ease; | 412 | transition: transform 0.2s ease; |
@@ -504,10 +505,6 @@ | |||
504 | } | 505 | } |
505 | } | 506 | } |
506 | 507 | ||
507 | .vjs-playback-rate { | ||
508 | display: none; | ||
509 | } | ||
510 | |||
511 | .vjs-peertube { | 508 | .vjs-peertube { |
512 | padding: 0 !important; | 509 | padding: 0 !important; |
513 | 510 | ||
diff --git a/client/src/standalone/videos/embed.scss b/client/src/standalone/videos/embed.scss index 30650538f..c40ea1208 100644 --- a/client/src/standalone/videos/embed.scss +++ b/client/src/standalone/videos/embed.scss | |||
@@ -4,7 +4,7 @@ | |||
4 | @import '~videojs-dock/dist/videojs-dock.css'; | 4 | @import '~videojs-dock/dist/videojs-dock.css'; |
5 | 5 | ||
6 | $assets-path: '../../assets/'; | 6 | $assets-path: '../../assets/'; |
7 | @import '../../sass/player/player'; | 7 | @import '../../sass/player/index'; |
8 | 8 | ||
9 | [hidden] { | 9 | [hidden] { |
10 | display: none !important; | 10 | display: none !important; |
diff --git a/package.json b/package.json index 1cb5be181..53e07a72b 100644 --- a/package.json +++ b/package.json | |||
@@ -70,7 +70,7 @@ | |||
70 | }, | 70 | }, |
71 | "lint-staged": { | 71 | "lint-staged": { |
72 | "*.scss": [ | 72 | "*.scss": [ |
73 | "sass-lint -c .sass-lint.yml", | 73 | "sass-lint -c client/.sass-lint.yml", |
74 | "git add" | 74 | "git add" |
75 | ] | 75 | ] |
76 | }, | 76 | }, |