diff options
Diffstat (limited to 'client/src/assets/player')
-rw-r--r-- | client/src/assets/player/peertube-player-manager.ts | 260 |
1 files changed, 149 insertions, 111 deletions
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 4ddbaed82..4681c130c 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -208,10 +208,8 @@ export class PeertubePlayerManager { | |||
208 | 208 | ||
209 | private static getVideojsOptions (mode: PlayerMode, options: PeertubePlayerManagerOptions, p2pMediaLoaderModule?: any) { | 209 | private static getVideojsOptions (mode: PlayerMode, options: PeertubePlayerManagerOptions, p2pMediaLoaderModule?: any) { |
210 | const commonOptions = options.common | 210 | const commonOptions = options.common |
211 | const webtorrentOptions = options.webtorrent | ||
212 | const p2pMediaLoaderOptions = options.p2pMediaLoader | ||
213 | 211 | ||
214 | let autoplay = options.common.autoplay | 212 | let autoplay = commonOptions.autoplay |
215 | let html5 = {} | 213 | let html5 = {} |
216 | 214 | ||
217 | const plugins: VideoJSPluginOptions = { | 215 | const plugins: VideoJSPluginOptions = { |
@@ -227,64 +225,18 @@ export class PeertubePlayerManager { | |||
227 | } | 225 | } |
228 | } | 226 | } |
229 | 227 | ||
230 | if (mode === 'p2p-media-loader') { | 228 | if (commonOptions.enableHotkeys === true) { |
231 | const redundancyUrlManager = new RedundancyUrlManager(options.p2pMediaLoader.redundancyBaseUrls) | 229 | PeertubePlayerManager.addHotkeysOptions(plugins) |
232 | 230 | } | |
233 | const p2pMediaLoader: P2PMediaLoaderPluginOptions = { | ||
234 | redundancyUrlManager, | ||
235 | type: 'application/x-mpegURL', | ||
236 | startTime: commonOptions.startTime, | ||
237 | src: p2pMediaLoaderOptions.playlistUrl | ||
238 | } | ||
239 | |||
240 | const trackerAnnounce = p2pMediaLoaderOptions.trackerAnnounce | ||
241 | .filter(t => t.startsWith('ws')) | ||
242 | |||
243 | const p2pMediaLoaderConfig = { | ||
244 | loader: { | ||
245 | trackerAnnounce, | ||
246 | segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url), | ||
247 | rtcConfig: getRtcConfig(), | ||
248 | requiredSegmentsPriority: 5, | ||
249 | segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager), | ||
250 | useP2P: getStoredP2PEnabled() | ||
251 | }, | ||
252 | segments: { | ||
253 | swarmId: p2pMediaLoaderOptions.playlistUrl | ||
254 | } | ||
255 | } | ||
256 | const streamrootHls = { | ||
257 | levelLabelHandler: (level: { height: number, width: number }) => { | ||
258 | const file = p2pMediaLoaderOptions.videoFiles.find(f => f.resolution.id === level.height) | ||
259 | |||
260 | let label = file.resolution.label | ||
261 | if (file.fps >= 50) label += file.fps | ||
262 | 231 | ||
263 | return label | 232 | if (mode === 'p2p-media-loader') { |
264 | }, | 233 | const { streamrootHls } = PeertubePlayerManager.addP2PMediaLoaderOptions(plugins, options, p2pMediaLoaderModule) |
265 | html5: { | ||
266 | hlsjsConfig: { | ||
267 | capLevelToPlayerSize: true, | ||
268 | autoStartLoad: false, | ||
269 | liveSyncDurationCount: 7, | ||
270 | loader: new p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass() | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | 234 | ||
275 | Object.assign(plugins, { p2pMediaLoader, streamrootHls }) | ||
276 | html5 = streamrootHls.html5 | 235 | html5 = streamrootHls.html5 |
277 | } | 236 | } |
278 | 237 | ||
279 | if (mode === 'webtorrent') { | 238 | if (mode === 'webtorrent') { |
280 | const webtorrent = { | 239 | PeertubePlayerManager.addWebTorrentOptions(plugins, options) |
281 | autoplay, | ||
282 | videoDuration: commonOptions.videoDuration, | ||
283 | playerElement: commonOptions.playerElement, | ||
284 | videoFiles: webtorrentOptions.videoFiles, | ||
285 | startTime: commonOptions.startTime | ||
286 | } | ||
287 | Object.assign(plugins, { webtorrent }) | ||
288 | 240 | ||
289 | // WebTorrent plugin handles autoplay, because we do some hackish stuff in there | 241 | // WebTorrent plugin handles autoplay, because we do some hackish stuff in there |
290 | autoplay = false | 242 | autoplay = false |
@@ -302,11 +254,16 @@ export class PeertubePlayerManager { | |||
302 | ? commonOptions.muted | 254 | ? commonOptions.muted |
303 | : undefined, // Undefined so the player knows it has to check the local storage | 255 | : undefined, // Undefined so the player knows it has to check the local storage |
304 | 256 | ||
257 | autoplay: autoplay === true | ||
258 | ? 'any' // Use 'any' instead of true to get notifier by videojs if autoplay fails | ||
259 | : autoplay, | ||
260 | |||
305 | poster: commonOptions.poster, | 261 | poster: commonOptions.poster, |
306 | autoplay: autoplay === true ? 'any' : autoplay, // Use 'any' instead of true to get notifier by videojs if autoplay fails | ||
307 | inactivityTimeout: commonOptions.inactivityTimeout, | 262 | inactivityTimeout: commonOptions.inactivityTimeout, |
308 | playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ], | 263 | playbackRates: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ], |
264 | |||
309 | plugins, | 265 | plugins, |
266 | |||
310 | controlBar: { | 267 | controlBar: { |
311 | children: this.getControlBarChildren(mode, { | 268 | children: this.getControlBarChildren(mode, { |
312 | captions: commonOptions.captions, | 269 | captions: commonOptions.captions, |
@@ -316,68 +273,92 @@ export class PeertubePlayerManager { | |||
316 | } | 273 | } |
317 | } | 274 | } |
318 | 275 | ||
319 | if (commonOptions.enableHotkeys === true) { | 276 | if (commonOptions.language && !isDefaultLocale(commonOptions.language)) { |
320 | Object.assign(videojsOptions.plugins, { | 277 | Object.assign(videojsOptions, { language: commonOptions.language }) |
321 | hotkeys: { | 278 | } |
322 | enableVolumeScroll: false, | ||
323 | enableModifiersForNumbers: false, | ||
324 | |||
325 | fullscreenKey: function (event: KeyboardEvent) { | ||
326 | // fullscreen with the f key or Ctrl+Enter | ||
327 | return event.key === 'f' || (event.ctrlKey && event.key === 'Enter') | ||
328 | }, | ||
329 | 279 | ||
330 | seekStep: function (event: KeyboardEvent) { | 280 | return videojsOptions |
331 | // mimic VLC seek behavior, and default to 5 (original value is 5). | 281 | } |
332 | if (event.ctrlKey && event.altKey) { | ||
333 | return 5 * 60 | ||
334 | } else if (event.ctrlKey) { | ||
335 | return 60 | ||
336 | } else if (event.altKey) { | ||
337 | return 10 | ||
338 | } else { | ||
339 | return 5 | ||
340 | } | ||
341 | }, | ||
342 | 282 | ||
343 | customKeys: { | 283 | private static addP2PMediaLoaderOptions ( |
344 | increasePlaybackRateKey: { | 284 | plugins: VideoJSPluginOptions, |
345 | key: function (event: KeyboardEvent) { | 285 | options: PeertubePlayerManagerOptions, |
346 | return event.key === '>' | 286 | p2pMediaLoaderModule: any |
347 | }, | 287 | ) { |
348 | handler: function (player: videojs.Player) { | 288 | const p2pMediaLoaderOptions = options.p2pMediaLoader |
349 | player.playbackRate((player.playbackRate() + 0.1).toFixed(2)) | 289 | const commonOptions = options.common |
350 | } | 290 | |
351 | }, | 291 | const trackerAnnounce = p2pMediaLoaderOptions.trackerAnnounce |
352 | decreasePlaybackRateKey: { | 292 | .filter(t => t.startsWith('ws')) |
353 | key: function (event: KeyboardEvent) { | 293 | |
354 | return event.key === '<' | 294 | const redundancyUrlManager = new RedundancyUrlManager(options.p2pMediaLoader.redundancyBaseUrls) |
355 | }, | 295 | |
356 | handler: function (player: videojs.Player) { | 296 | const p2pMediaLoader: P2PMediaLoaderPluginOptions = { |
357 | player.playbackRate((player.playbackRate() - 0.1).toFixed(2)) | 297 | redundancyUrlManager, |
358 | } | 298 | type: 'application/x-mpegURL', |
359 | }, | 299 | startTime: commonOptions.startTime, |
360 | frameByFrame: { | 300 | src: p2pMediaLoaderOptions.playlistUrl |
361 | key: function (event: KeyboardEvent) { | 301 | } |
362 | return event.key === '.' | 302 | |
363 | }, | 303 | let consumeOnly = false |
364 | handler: function (player: videojs.Player) { | 304 | // FIXME: typings |
365 | player.pause() | 305 | if (navigator && (navigator as any).connection && (navigator as any).connection.effectiveType === 'cellular') { |
366 | // Calculate movement distance (assuming 30 fps) | 306 | console.log('We are on a cellular connection: disabling seeding.') |
367 | const dist = 1 / 30 | 307 | consumeOnly = true |
368 | player.currentTime(player.currentTime() + dist) | 308 | } |
369 | } | 309 | |
370 | } | 310 | const p2pMediaLoaderConfig = { |
371 | } | 311 | loader: { |
312 | trackerAnnounce, | ||
313 | segmentValidator: segmentValidatorFactory(options.p2pMediaLoader.segmentsSha256Url), | ||
314 | rtcConfig: getRtcConfig(), | ||
315 | requiredSegmentsPriority: 5, | ||
316 | segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager), | ||
317 | useP2P: getStoredP2PEnabled(), | ||
318 | consumeOnly | ||
319 | }, | ||
320 | segments: { | ||
321 | swarmId: p2pMediaLoaderOptions.playlistUrl | ||
322 | } | ||
323 | } | ||
324 | const streamrootHls = { | ||
325 | levelLabelHandler: (level: { height: number, width: number }) => { | ||
326 | const file = p2pMediaLoaderOptions.videoFiles.find(f => f.resolution.id === level.height) | ||
327 | |||
328 | let label = file.resolution.label | ||
329 | if (file.fps >= 50) label += file.fps | ||
330 | |||
331 | return label | ||
332 | }, | ||
333 | html5: { | ||
334 | hlsjsConfig: { | ||
335 | capLevelToPlayerSize: true, | ||
336 | autoStartLoad: false, | ||
337 | liveSyncDurationCount: 7, | ||
338 | loader: new p2pMediaLoaderModule.Engine(p2pMediaLoaderConfig).createLoaderClass() | ||
372 | } | 339 | } |
373 | }) | 340 | } |
374 | } | 341 | } |
375 | 342 | ||
376 | if (commonOptions.language && !isDefaultLocale(commonOptions.language)) { | 343 | const toAssign = { p2pMediaLoader, streamrootHls } |
377 | Object.assign(videojsOptions, { language: commonOptions.language }) | 344 | Object.assign(plugins, toAssign) |
345 | |||
346 | return toAssign | ||
347 | } | ||
348 | |||
349 | private static addWebTorrentOptions (plugins: VideoJSPluginOptions, options: PeertubePlayerManagerOptions) { | ||
350 | const commonOptions = options.common | ||
351 | const webtorrentOptions = options.webtorrent | ||
352 | |||
353 | const webtorrent = { | ||
354 | autoplay: commonOptions.autoplay, | ||
355 | videoDuration: commonOptions.videoDuration, | ||
356 | playerElement: commonOptions.playerElement, | ||
357 | videoFiles: webtorrentOptions.videoFiles, | ||
358 | startTime: commonOptions.startTime | ||
378 | } | 359 | } |
379 | 360 | ||
380 | return videojsOptions | 361 | Object.assign(plugins, { webtorrent }) |
381 | } | 362 | } |
382 | 363 | ||
383 | private static getControlBarChildren (mode: PlayerMode, options: { | 364 | private static getControlBarChildren (mode: PlayerMode, options: { |
@@ -481,6 +462,63 @@ export class PeertubePlayerManager { | |||
481 | player.contextmenuUI({ content }) | 462 | player.contextmenuUI({ content }) |
482 | } | 463 | } |
483 | 464 | ||
465 | private static addHotkeysOptions (plugins: VideoJSPluginOptions) { | ||
466 | Object.assign(plugins, { | ||
467 | hotkeys: { | ||
468 | enableVolumeScroll: false, | ||
469 | enableModifiersForNumbers: false, | ||
470 | |||
471 | fullscreenKey: function (event: KeyboardEvent) { | ||
472 | // fullscreen with the f key or Ctrl+Enter | ||
473 | return event.key === 'f' || (event.ctrlKey && event.key === 'Enter') | ||
474 | }, | ||
475 | |||
476 | seekStep: function (event: KeyboardEvent) { | ||
477 | // mimic VLC seek behavior, and default to 5 (original value is 5). | ||
478 | if (event.ctrlKey && event.altKey) { | ||
479 | return 5 * 60 | ||
480 | } else if (event.ctrlKey) { | ||
481 | return 60 | ||
482 | } else if (event.altKey) { | ||
483 | return 10 | ||
484 | } else { | ||
485 | return 5 | ||
486 | } | ||
487 | }, | ||
488 | |||
489 | customKeys: { | ||
490 | increasePlaybackRateKey: { | ||
491 | key: function (event: KeyboardEvent) { | ||
492 | return event.key === '>' | ||
493 | }, | ||
494 | handler: function (player: videojs.Player) { | ||
495 | player.playbackRate((player.playbackRate() + 0.1).toFixed(2)) | ||
496 | } | ||
497 | }, | ||
498 | decreasePlaybackRateKey: { | ||
499 | key: function (event: KeyboardEvent) { | ||
500 | return event.key === '<' | ||
501 | }, | ||
502 | handler: function (player: videojs.Player) { | ||
503 | player.playbackRate((player.playbackRate() - 0.1).toFixed(2)) | ||
504 | } | ||
505 | }, | ||
506 | frameByFrame: { | ||
507 | key: function (event: KeyboardEvent) { | ||
508 | return event.key === '.' | ||
509 | }, | ||
510 | handler: function (player: videojs.Player) { | ||
511 | player.pause() | ||
512 | // Calculate movement distance (assuming 30 fps) | ||
513 | const dist = 1 / 30 | ||
514 | player.currentTime(player.currentTime() + dist) | ||
515 | } | ||
516 | } | ||
517 | } | ||
518 | } | ||
519 | }) | ||
520 | } | ||
521 | |||
484 | private static getLocalePath (serverUrl: string, locale: string) { | 522 | private static getLocalePath (serverUrl: string, locale: string) { |
485 | const completeLocale = getCompleteLocale(locale) | 523 | const completeLocale = getCompleteLocale(locale) |
486 | 524 | ||