aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/assets/player
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/assets/player')
-rw-r--r--client/src/assets/player/peertube-player-manager.ts260
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