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/p2p-media-loader/hls-plugin.ts94
-rw-r--r--client/src/assets/player/p2p-media-loader/segment-validator.ts5
-rw-r--r--client/src/assets/player/peertube-player-manager.ts42
-rw-r--r--client/src/assets/player/peertube-plugin.ts2
-rw-r--r--client/src/assets/player/peertube-videojs-typings.ts9
-rw-r--r--client/src/assets/player/stats/stats-card.ts9
-rw-r--r--client/src/assets/player/translations-manager.ts6
-rw-r--r--client/src/assets/player/upnext/end-card.ts14
-rw-r--r--client/src/assets/player/utils.ts2
-rw-r--r--client/src/assets/player/videojs-components/p2p-info-button.ts8
-rw-r--r--client/src/assets/player/videojs-components/resolution-menu-item.ts4
-rw-r--r--client/src/assets/player/videojs-components/settings-dialog.ts4
-rw-r--r--client/src/assets/player/videojs-components/settings-menu-item.ts21
-rw-r--r--client/src/assets/player/videojs-components/settings-panel-child.ts4
-rw-r--r--client/src/assets/player/videojs-components/settings-panel.ts4
-rw-r--r--client/src/assets/player/webtorrent/peertube-chunk-store.ts8
-rw-r--r--client/src/assets/player/webtorrent/video-renderer.ts8
-rw-r--r--client/src/assets/player/webtorrent/webtorrent-plugin.ts24
18 files changed, 132 insertions, 136 deletions
diff --git a/client/src/assets/player/p2p-media-loader/hls-plugin.ts b/client/src/assets/player/p2p-media-loader/hls-plugin.ts
index 78f0944ef..a1b07aea6 100644
--- a/client/src/assets/player/p2p-media-loader/hls-plugin.ts
+++ b/client/src/assets/player/p2p-media-loader/hls-plugin.ts
@@ -13,6 +13,8 @@ type Metadata = {
13 levels: Level[] 13 levels: Level[]
14} 14}
15 15
16type HookFn = (player: videojs.Player, hljs: Hlsjs) => void
17
16const registerSourceHandler = function (vjs: typeof videojs) { 18const registerSourceHandler = function (vjs: typeof videojs) {
17 if (!Hlsjs.isSupported()) { 19 if (!Hlsjs.isSupported()) {
18 console.warn('Hls.js is not supported in this browser!') 20 console.warn('Hls.js is not supported in this browser!')
@@ -82,7 +84,7 @@ const registerConfigPlugin = function (vjs: typeof videojs) {
82} 84}
83 85
84class Html5Hlsjs { 86class Html5Hlsjs {
85 private static readonly hooks: { [id: string]: Function[] } = {} 87 private static readonly hooks: { [id: string]: HookFn[] } = {}
86 88
87 private readonly videoElement: HTMLVideoElement 89 private readonly videoElement: HTMLVideoElement
88 private readonly errorCounts: ErrorCounts = {} 90 private readonly errorCounts: ErrorCounts = {}
@@ -131,7 +133,8 @@ class Html5Hlsjs {
131 errorTxt = 'You aborted the video playback' 133 errorTxt = 'You aborted the video playback'
132 break 134 break
133 case mediaError.MEDIA_ERR_DECODE: 135 case mediaError.MEDIA_ERR_DECODE:
134 errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features your browser did not support' 136 errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features ' +
137 'your browser did not support'
135 this._handleMediaError(mediaError) 138 this._handleMediaError(mediaError)
136 break 139 break
137 case mediaError.MEDIA_ERR_NETWORK: 140 case mediaError.MEDIA_ERR_NETWORK:
@@ -182,58 +185,57 @@ class Html5Hlsjs {
182 this.hls.destroy() 185 this.hls.destroy()
183 } 186 }
184 187
185 static addHook (type: string, callback: Function) { 188 static addHook (type: string, callback: HookFn) {
186 Html5Hlsjs.hooks[ type ] = this.hooks[ type ] || [] 189 Html5Hlsjs.hooks[type] = this.hooks[type] || []
187 Html5Hlsjs.hooks[ type ].push(callback) 190 Html5Hlsjs.hooks[type].push(callback)
188 } 191 }
189 192
190 static removeHook (type: string, callback: Function) { 193 static removeHook (type: string, callback: HookFn) {
191 if (Html5Hlsjs.hooks[ type ] === undefined) return false 194 if (Html5Hlsjs.hooks[type] === undefined) return false
192 195
193 const index = Html5Hlsjs.hooks[ type ].indexOf(callback) 196 const index = Html5Hlsjs.hooks[type].indexOf(callback)
194 if (index === -1) return false 197 if (index === -1) return false
195 198
196 Html5Hlsjs.hooks[ type ].splice(index, 1) 199 Html5Hlsjs.hooks[type].splice(index, 1)
197 200
198 return true 201 return true
199 } 202 }
200 203
201 private _executeHooksFor (type: string) { 204 private _executeHooksFor (type: string) {
202 if (Html5Hlsjs.hooks[ type ] === undefined) { 205 if (Html5Hlsjs.hooks[type] === undefined) {
203 return 206 return
204 } 207 }
205 208
206 // ES3 and IE < 9 209 // ES3 and IE < 9
207 for (let i = 0; i < Html5Hlsjs.hooks[ type ].length; i++) { 210 for (let i = 0; i < Html5Hlsjs.hooks[type].length; i++) {
208 Html5Hlsjs.hooks[ type ][ i ](this.player, this.hls) 211 Html5Hlsjs.hooks[type][i](this.player, this.hls)
209 } 212 }
210 } 213 }
211 214
212 private _handleMediaError (error: any) { 215 private _handleMediaError (error: any) {
213 if (this.errorCounts[ Hlsjs.ErrorTypes.MEDIA_ERROR ] === 1) { 216 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 1) {
214 console.info('trying to recover media error') 217 console.info('trying to recover media error')
215 this.hls.recoverMediaError() 218 this.hls.recoverMediaError()
216 return 219 return
217 } 220 }
218 221
219 if (this.errorCounts[ Hlsjs.ErrorTypes.MEDIA_ERROR ] === 2) { 222 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 2) {
220 console.info('2nd try to recover media error (by swapping audio codec') 223 console.info('2nd try to recover media error (by swapping audio codec')
221 this.hls.swapAudioCodec() 224 this.hls.swapAudioCodec()
222 this.hls.recoverMediaError() 225 this.hls.recoverMediaError()
223 return 226 return
224 } 227 }
225 228
226 if (this.errorCounts[ Hlsjs.ErrorTypes.MEDIA_ERROR ] > 2) { 229 if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) {
227 console.info('bubbling media error up to VIDEOJS') 230 console.info('bubbling media error up to VIDEOJS')
228 this.hls.destroy() 231 this.hls.destroy()
229 this.tech.error = () => error 232 this.tech.error = () => error
230 this.tech.trigger('error') 233 this.tech.trigger('error')
231 return
232 } 234 }
233 } 235 }
234 236
235 private _handleNetworkError (error: any) { 237 private _handleNetworkError (error: any) {
236 if (this.errorCounts[ Hlsjs.ErrorTypes.NETWORK_ERROR] <= 5) { 238 if (this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] <= 5) {
237 console.info('trying to recover network error') 239 console.info('trying to recover network error')
238 240
239 // Wait 1 second and retry 241 // Wait 1 second and retry
@@ -241,7 +243,7 @@ class Html5Hlsjs {
241 243
242 // Reset error count on success 244 // Reset error count on success
243 this.hls.once(Hlsjs.Events.FRAG_LOADED, () => { 245 this.hls.once(Hlsjs.Events.FRAG_LOADED, () => {
244 this.errorCounts[ Hlsjs.ErrorTypes.NETWORK_ERROR] = 0 246 this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] = 0
245 }) 247 })
246 248
247 return 249 return
@@ -259,8 +261,8 @@ class Html5Hlsjs {
259 } 261 }
260 262
261 // increment/set error count 263 // increment/set error count
262 if (this.errorCounts[ data.type ]) this.errorCounts[ data.type ] += 1 264 if (this.errorCounts[data.type]) this.errorCounts[data.type] += 1
263 else this.errorCounts[ data.type ] = 1 265 else this.errorCounts[data.type] = 1
264 266
265 if (data.fatal) console.warn(error.message) 267 if (data.fatal) console.warn(error.message)
266 else console.error(error.message, data) 268 else console.error(error.message, data)
@@ -300,7 +302,7 @@ class Html5Hlsjs {
300 let isAuto = true 302 let isAuto = true
301 303
302 for (let i = 0; i < qualityLevels.length; i++) { 304 for (let i = 0; i < qualityLevels.length; i++) {
303 if (!qualityLevels[ i ]._enabled) { 305 if (!qualityLevels[i]._enabled) {
304 isAuto = false 306 isAuto = false
305 break 307 break
306 } 308 }
@@ -316,7 +318,7 @@ class Html5Hlsjs {
316 let selectedTrack: number 318 let selectedTrack: number
317 319
318 for (selectedTrack = qualityLevels.length - 1; selectedTrack >= 0; selectedTrack--) { 320 for (selectedTrack = qualityLevels.length - 1; selectedTrack >= 0; selectedTrack--) {
319 if (qualityLevels[ selectedTrack ]._enabled) { 321 if (qualityLevels[selectedTrack]._enabled) {
320 break 322 break
321 } 323 }
322 } 324 }
@@ -327,11 +329,11 @@ class Html5Hlsjs {
327 private _handleQualityLevels () { 329 private _handleQualityLevels () {
328 if (!this.metadata) return 330 if (!this.metadata) return
329 331
330 const qualityLevels = this.player.qualityLevels && this.player.qualityLevels() 332 const qualityLevels = this.player.qualityLevels?.()
331 if (!qualityLevels) return 333 if (!qualityLevels) return
332 334
333 for (let i = 0; i < this.metadata.levels.length; i++) { 335 for (let i = 0; i < this.metadata.levels.length; i++) {
334 const details = this.metadata.levels[ i ] 336 const details = this.metadata.levels[i]
335 const representation: QualityLevelRepresentation = { 337 const representation: QualityLevelRepresentation = {
336 id: i, 338 id: i,
337 width: details.width, 339 width: details.width,
@@ -345,11 +347,11 @@ class Html5Hlsjs {
345 representation.enabled = function (this: QualityLevels, level: number, toggle?: boolean) { 347 representation.enabled = function (this: QualityLevels, level: number, toggle?: boolean) {
346 // Brightcove switcher works TextTracks-style (enable tracks that it wants to ABR on) 348 // Brightcove switcher works TextTracks-style (enable tracks that it wants to ABR on)
347 if (typeof toggle === 'boolean') { 349 if (typeof toggle === 'boolean') {
348 this[ level ]._enabled = toggle 350 this[level]._enabled = toggle
349 self._relayQualityChange(this) 351 self._relayQualityChange(this)
350 } 352 }
351 353
352 return this[ level ]._enabled 354 return this[level]._enabled
353 } 355 }
354 356
355 qualityLevels.addQualityLevel(representation) 357 qualityLevels.addQualityLevel(representation)
@@ -395,7 +397,7 @@ class Html5Hlsjs {
395 const playerAudioTracks = this.tech.audioTracks() 397 const playerAudioTracks = this.tech.audioTracks()
396 for (let j = 0; j < playerAudioTracks.length; j++) { 398 for (let j = 0; j < playerAudioTracks.length; j++) {
397 // FIXME: typings 399 // FIXME: typings
398 if ((playerAudioTracks[ j ] as any).enabled) { 400 if ((playerAudioTracks[j] as any).enabled) {
399 this.hls.audioTrack = j 401 this.hls.audioTrack = j
400 break 402 break
401 } 403 }
@@ -412,8 +414,8 @@ class Html5Hlsjs {
412 playerAudioTracks.addTrack(new this.vjs.AudioTrack({ 414 playerAudioTracks.addTrack(new this.vjs.AudioTrack({
413 id: i.toString(), 415 id: i.toString(),
414 kind: 'alternative', 416 kind: 'alternative',
415 label: hlsAudioTracks[ i ].name || hlsAudioTracks[ i ].lang, 417 label: hlsAudioTracks[i].name || hlsAudioTracks[i].lang,
416 language: hlsAudioTracks[ i ].lang, 418 language: hlsAudioTracks[i].lang,
417 enabled: i === this.hls.audioTrack 419 enabled: i === this.hls.audioTrack
418 })) 420 }))
419 } 421 }
@@ -430,8 +432,8 @@ class Html5Hlsjs {
430 } 432 }
431 433
432 private _isSameTextTrack (track1: TextTrack, track2: TextTrack) { 434 private _isSameTextTrack (track1: TextTrack, track2: TextTrack) {
433 return this._getTextTrackLabel(track1) === this._getTextTrackLabel(track2) 435 return this._getTextTrackLabel(track1) === this._getTextTrackLabel(track2) &&
434 && track1.kind === track2.kind 436 track1.kind === track2.kind
435 } 437 }
436 438
437 private _updateSelectedTextTrack () { 439 private _updateSelectedTextTrack () {
@@ -439,16 +441,16 @@ class Html5Hlsjs {
439 let activeTrack: TextTrack = null 441 let activeTrack: TextTrack = null
440 442
441 for (let j = 0; j < playerTextTracks.length; j++) { 443 for (let j = 0; j < playerTextTracks.length; j++) {
442 if (playerTextTracks[ j ].mode === 'showing') { 444 if (playerTextTracks[j].mode === 'showing') {
443 activeTrack = playerTextTracks[ j ] 445 activeTrack = playerTextTracks[j]
444 break 446 break
445 } 447 }
446 } 448 }
447 449
448 const hlsjsTracks = this.videoElement.textTracks 450 const hlsjsTracks = this.videoElement.textTracks
449 for (let k = 0; k < hlsjsTracks.length; k++) { 451 for (let k = 0; k < hlsjsTracks.length; k++) {
450 if (hlsjsTracks[ k ].kind === 'subtitles' || hlsjsTracks[ k ].kind === 'captions') { 452 if (hlsjsTracks[k].kind === 'subtitles' || hlsjsTracks[k].kind === 'captions') {
451 hlsjsTracks[ k ].mode = activeTrack && this._isSameTextTrack(hlsjsTracks[ k ], activeTrack) 453 hlsjsTracks[k].mode = activeTrack && this._isSameTextTrack(hlsjsTracks[k], activeTrack)
452 ? 'showing' 454 ? 'showing'
453 : 'disabled' 455 : 'disabled'
454 } 456 }
@@ -460,11 +462,11 @@ class Html5Hlsjs {
460 this.videoElement.removeEventListener('play', this.handlers.play) 462 this.videoElement.removeEventListener('play', this.handlers.play)
461 } 463 }
462 464
463 private _oneLevelObjClone (obj: object) { 465 private _oneLevelObjClone (obj: { [ id: string ]: any }) {
464 const result = {} 466 const result = {}
465 const objKeys = Object.keys(obj) 467 const objKeys = Object.keys(obj)
466 for (let i = 0; i < objKeys.length; i++) { 468 for (let i = 0; i < objKeys.length; i++) {
467 result[ objKeys[ i ] ] = obj[ objKeys[ i ] ] 469 result[objKeys[i]] = obj[objKeys[i]]
468 } 470 }
469 471
470 return result 472 return result
@@ -475,8 +477,8 @@ class Html5Hlsjs {
475 477
476 // Filter out tracks that is displayable (captions or subtitles) 478 // Filter out tracks that is displayable (captions or subtitles)
477 for (let idx = 0; idx < textTracks.length; idx++) { 479 for (let idx = 0; idx < textTracks.length; idx++) {
478 if (textTracks[ idx ].kind === 'subtitles' || textTracks[ idx ].kind === 'captions') { 480 if (textTracks[idx].kind === 'subtitles' || textTracks[idx].kind === 'captions') {
479 displayableTracks.push(textTracks[ idx ]) 481 displayableTracks.push(textTracks[idx])
480 } 482 }
481 } 483 }
482 484
@@ -493,14 +495,14 @@ class Html5Hlsjs {
493 let isAdded = false 495 let isAdded = false
494 496
495 for (let jdx = 0; jdx < playerTextTracks.length; jdx++) { 497 for (let jdx = 0; jdx < playerTextTracks.length; jdx++) {
496 if (this._isSameTextTrack(displayableTracks[ idx ], playerTextTracks[ jdx ])) { 498 if (this._isSameTextTrack(displayableTracks[idx], playerTextTracks[jdx])) {
497 isAdded = true 499 isAdded = true
498 break 500 break
499 } 501 }
500 } 502 }
501 503
502 if (!isAdded) { 504 if (!isAdded) {
503 const hlsjsTextTrack = displayableTracks[ idx ] 505 const hlsjsTextTrack = displayableTracks[idx]
504 this.player.addRemoteTextTrack({ 506 this.player.addRemoteTextTrack({
505 kind: hlsjsTextTrack.kind as videojs.TextTrack.Kind, 507 kind: hlsjsTextTrack.kind as videojs.TextTrack.Kind,
506 label: this._getTextTrackLabel(hlsjsTextTrack), 508 label: this._getTextTrackLabel(hlsjsTextTrack),
@@ -536,12 +538,12 @@ class Html5Hlsjs {
536 const VTTCue = (window as any).VTTCue || (window as any).TextTrackCue 538 const VTTCue = (window as any).VTTCue || (window as any).TextTrackCue
537 539
538 for (let r = 0; r < captionScreen.rows.length; r++) { 540 for (let r = 0; r < captionScreen.rows.length; r++) {
539 row = captionScreen.rows[ r ] 541 row = captionScreen.rows[r]
540 text = '' 542 text = ''
541 543
542 if (!row.isEmpty()) { 544 if (!row.isEmpty()) {
543 for (let c = 0; c < row.chars.length; c++) { 545 for (let c = 0; c < row.chars.length; c++) {
544 text += row.chars[ c ].ucharj 546 text += row.chars[c].ucharj
545 } 547 }
546 548
547 cue = new VTTCue(startTime, endTime, text.trim()) 549 cue = new VTTCue(startTime, endTime, text.trim())
@@ -552,7 +554,7 @@ class Html5Hlsjs {
552 const configKeys = Object.keys(captionConfig) 554 const configKeys = Object.keys(captionConfig)
553 555
554 for (let k = 0; k < configKeys.length; k++) { 556 for (let k = 0; k < configKeys.length; k++) {
555 cue[ configKeys[ k ] ] = captionConfig[ configKeys[ k ] ] 557 cue[configKeys[k]] = captionConfig[configKeys[k]]
556 } 558 }
557 } 559 }
558 track.addCue(cue) 560 track.addCue(cue)
@@ -567,7 +569,7 @@ class Html5Hlsjs {
567 const techOptions = this.tech.options_ as HlsjsConfigHandlerOptions 569 const techOptions = this.tech.options_ as HlsjsConfigHandlerOptions
568 const srOptions_ = this.player.srOptions_ 570 const srOptions_ = this.player.srOptions_
569 571
570 const hlsjsConfigRef = srOptions_ && srOptions_.hlsjsConfig || techOptions.hlsjsConfig 572 const hlsjsConfigRef = srOptions_?.hlsjsConfig || techOptions.hlsjsConfig
571 // Hls.js will write to the reference thus change the object for later streams 573 // Hls.js will write to the reference thus change the object for later streams
572 this.hlsjsConfig = hlsjsConfigRef ? this._oneLevelObjClone(hlsjsConfigRef) : {} 574 this.hlsjsConfig = hlsjsConfigRef ? this._oneLevelObjClone(hlsjsConfigRef) : {}
573 575
@@ -575,7 +577,7 @@ class Html5Hlsjs {
575 this.hlsjsConfig.autoStartLoad = false 577 this.hlsjsConfig.autoStartLoad = false
576 } 578 }
577 579
578 const captionConfig = srOptions_ && srOptions_.captionConfig || techOptions.captionConfig 580 const captionConfig = srOptions_?.captionConfig || techOptions.captionConfig
579 if (captionConfig) { 581 if (captionConfig) {
580 this.hlsjsConfig.cueHandler = this._createCueHandler(captionConfig) 582 this.hlsjsConfig.cueHandler = this._createCueHandler(captionConfig)
581 } 583 }
diff --git a/client/src/assets/player/p2p-media-loader/segment-validator.ts b/client/src/assets/player/p2p-media-loader/segment-validator.ts
index d0a4c4a3f..f7f83a8a4 100644
--- a/client/src/assets/player/p2p-media-loader/segment-validator.ts
+++ b/client/src/assets/player/p2p-media-loader/segment-validator.ts
@@ -86,6 +86,7 @@ async function sha256Hex (data?: ArrayBuffer) {
86 86
87 // Fallback for non HTTPS context 87 // Fallback for non HTTPS context
88 const shaModule = (await import('sha.js') as any).default 88 const shaModule = (await import('sha.js') as any).default
89 // eslint-disable-next-line new-cap
89 return new shaModule.sha256().update(Buffer.from(data)).digest('hex') 90 return new shaModule.sha256().update(Buffer.from(data)).digest('hex')
90} 91}
91 92
@@ -97,7 +98,9 @@ function bufferToHex (buffer?: ArrayBuffer) {
97 const h = '0123456789abcdef' 98 const h = '0123456789abcdef'
98 const o = new Uint8Array(buffer) 99 const o = new Uint8Array(buffer)
99 100
100 o.forEach((v: any) => s += h[ v >> 4 ] + h[ v & 15 ]) 101 o.forEach((v: any) => {
102 s += h[v >> 4] + h[v & 15]
103 })
101 104
102 return s 105 return s
103} 106}
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index c45e8f53e..f3c21fc4c 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -435,8 +435,6 @@ export class PeertubePlayerManager {
435 const p2pMediaLoaderOptions = options.p2pMediaLoader 435 const p2pMediaLoaderOptions = options.p2pMediaLoader
436 436
437 const autoplay = this.getAutoPlayValue(commonOptions.autoplay) === 'play' 437 const autoplay = this.getAutoPlayValue(commonOptions.autoplay) === 'play'
438 ? true
439 : false
440 438
441 const webtorrent = { 439 const webtorrent = {
442 autoplay, 440 autoplay,
@@ -459,10 +457,10 @@ export class PeertubePlayerManager {
459 theaterButton: boolean 457 theaterButton: boolean
460 captions: boolean 458 captions: boolean
461 459
462 nextVideo?: Function 460 nextVideo?: () => void
463 hasNextVideo?: () => boolean 461 hasNextVideo?: () => boolean
464 462
465 previousVideo?: Function 463 previousVideo?: () => void
466 hasPreviousVideo?: () => boolean 464 hasPreviousVideo?: () => boolean
467 }) { 465 }) {
468 const settingEntries = [] 466 const settingEntries = []
@@ -487,7 +485,7 @@ export class PeertubePlayerManager {
487 } 485 }
488 486
489 Object.assign(children, { 487 Object.assign(children, {
490 'previousVideoButton': buttonOptions 488 previousVideoButton: buttonOptions
491 }) 489 })
492 } 490 }
493 491
@@ -505,35 +503,35 @@ export class PeertubePlayerManager {
505 } 503 }
506 504
507 Object.assign(children, { 505 Object.assign(children, {
508 'nextVideoButton': buttonOptions 506 nextVideoButton: buttonOptions
509 }) 507 })
510 } 508 }
511 509
512 Object.assign(children, { 510 Object.assign(children, {
513 'currentTimeDisplay': {}, 511 currentTimeDisplay: {},
514 'timeDivider': {}, 512 timeDivider: {},
515 'durationDisplay': {}, 513 durationDisplay: {},
516 'liveDisplay': {}, 514 liveDisplay: {},
517 515
518 'flexibleWidthSpacer': {}, 516 flexibleWidthSpacer: {},
519 'progressControl': { 517 progressControl: {
520 children: { 518 children: {
521 'seekBar': { 519 seekBar: {
522 children: { 520 children: {
523 [loadProgressBar]: {}, 521 [loadProgressBar]: {},
524 'mouseTimeDisplay': {}, 522 mouseTimeDisplay: {},
525 'playProgressBar': {} 523 playProgressBar: {}
526 } 524 }
527 } 525 }
528 } 526 }
529 }, 527 },
530 528
531 'p2PInfoButton': {}, 529 p2PInfoButton: {},
532 530
533 'muteToggle': {}, 531 muteToggle: {},
534 'volumeControl': {}, 532 volumeControl: {},
535 533
536 'settingsButton': { 534 settingsButton: {
537 setup: { 535 setup: {
538 maxHeightOffset: 40 536 maxHeightOffset: 40
539 }, 537 },
@@ -543,18 +541,18 @@ export class PeertubePlayerManager {
543 541
544 if (options.peertubeLink === true) { 542 if (options.peertubeLink === true) {
545 Object.assign(children, { 543 Object.assign(children, {
546 'peerTubeLinkButton': { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions 544 peerTubeLinkButton: { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions
547 }) 545 })
548 } 546 }
549 547
550 if (options.theaterButton === true) { 548 if (options.theaterButton === true) {
551 Object.assign(children, { 549 Object.assign(children, {
552 'theaterButton': {} 550 theaterButton: {}
553 }) 551 })
554 } 552 }
555 553
556 Object.assign(children, { 554 Object.assign(children, {
557 'fullscreenToggle': {} 555 fullscreenToggle: {}
558 }) 556 })
559 557
560 return children 558 return children
diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts
index ade8e2ee4..b4841b235 100644
--- a/client/src/assets/player/peertube-plugin.ts
+++ b/client/src/assets/player/peertube-plugin.ts
@@ -211,7 +211,7 @@ class PeerTubePlugin extends Plugin {
211 const body = new URLSearchParams() 211 const body = new URLSearchParams()
212 body.append('currentTime', currentTime.toString()) 212 body.append('currentTime', currentTime.toString())
213 213
214 const headers = new Headers({ 'Authorization': authorizationHeader }) 214 const headers = new Headers({ Authorization: authorizationHeader })
215 215
216 return fetch(url, { method: 'PUT', body, headers }) 216 return fetch(url, { method: 'PUT', body, headers })
217 } 217 }
diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts
index f0eb129d4..97828c802 100644
--- a/client/src/assets/player/peertube-videojs-typings.ts
+++ b/client/src/assets/player/peertube-videojs-typings.ts
@@ -1,3 +1,6 @@
1// FIXME: lint
2/* eslint-disable @typescript-eslint/ban-types */
3
1import { HlsConfig, Level } from 'hls.js' 4import { HlsConfig, Level } from 'hls.js'
2import videojs from 'video.js' 5import videojs from 'video.js'
3import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@shared/models' 6import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@shared/models'
@@ -93,7 +96,7 @@ type VideoJSCaption = {
93} 96}
94 97
95type UserWatching = { 98type UserWatching = {
96 url: string, 99 url: string
97 authorizationHeader: string 100 authorizationHeader: string
98} 101}
99 102
@@ -166,7 +169,7 @@ type VideoJSPluginOptions = {
166} 169}
167 170
168type LoadedQualityData = { 171type LoadedQualityData = {
169 qualitySwitchCallback: Function, 172 qualitySwitchCallback: (resolutionId: number, type: 'video') => void
170 qualityData: { 173 qualityData: {
171 video: { 174 video: {
172 id: number 175 id: number
@@ -177,7 +180,7 @@ type LoadedQualityData = {
177} 180}
178 181
179type ResolutionUpdateData = { 182type ResolutionUpdateData = {
180 auto: boolean, 183 auto: boolean
181 resolutionId: number 184 resolutionId: number
182 id?: number 185 id?: number
183} 186}
diff --git a/client/src/assets/player/stats/stats-card.ts b/client/src/assets/player/stats/stats-card.ts
index b271d0526..a32f6fb97 100644
--- a/client/src/assets/player/stats/stats-card.ts
+++ b/client/src/assets/player/stats/stats-card.ts
@@ -39,10 +39,6 @@ class StatsCard extends Component {
39 intervalMs = 300 39 intervalMs = 300
40 playerNetworkInfo: PlayerNetworkInfo = {} 40 playerNetworkInfo: PlayerNetworkInfo = {}
41 41
42 constructor (player: videojs.Player, options: StatsCardOptions) {
43 super(player, options)
44 }
45
46 createEl () { 42 createEl () {
47 const container = super.createEl('div', { 43 const container = super.createEl('div', {
48 className: 'vjs-stats-content', 44 className: 'vjs-stats-content',
@@ -81,9 +77,8 @@ class StatsCard extends Component {
81 } 77 }
82 78
83 toggle () { 79 toggle () {
84 this.updateInterval 80 if (this.updateInterval) this.hide()
85 ? this.hide() 81 else this.show()
86 : this.show()
87 } 82 }
88 83
89 show () { 84 show () {
diff --git a/client/src/assets/player/translations-manager.ts b/client/src/assets/player/translations-manager.ts
index d5a09a31a..8a6e67dda 100644
--- a/client/src/assets/player/translations-manager.ts
+++ b/client/src/assets/player/translations-manager.ts
@@ -23,13 +23,13 @@ export class TranslationsManager {
23 23
24 let p: Promise<any> 24 let p: Promise<any>
25 25
26 if (TranslationsManager.videojsLocaleCache[ path ]) { 26 if (TranslationsManager.videojsLocaleCache[path]) {
27 p = Promise.resolve(TranslationsManager.videojsLocaleCache[ path ]) 27 p = Promise.resolve(TranslationsManager.videojsLocaleCache[path])
28 } else { 28 } else {
29 p = fetch(path + '/player.json') 29 p = fetch(path + '/player.json')
30 .then(res => res.json()) 30 .then(res => res.json())
31 .then(json => { 31 .then(json => {
32 TranslationsManager.videojsLocaleCache[ path ] = json 32 TranslationsManager.videojsLocaleCache[path] = json
33 return json 33 return json
34 }) 34 })
35 .catch(err => { 35 .catch(err => {
diff --git a/client/src/assets/player/upnext/end-card.ts b/client/src/assets/player/upnext/end-card.ts
index 8fabfc3fd..61668e407 100644
--- a/client/src/assets/player/upnext/end-card.ts
+++ b/client/src/assets/player/upnext/end-card.ts
@@ -9,7 +9,9 @@ function getMainTemplate (options: any) {
9 <div class="vjs-upnext-autoplay-icon"> 9 <div class="vjs-upnext-autoplay-icon">
10 <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%"> 10 <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%">
11 <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle> 11 <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle>
12 <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5" stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"></circle> 12 <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5"
13 stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"
14 ></circle>
13 <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg> 15 <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg>
14 </div> 16 </div>
15 <span class="vjs-upnext-bottom"> 17 <span class="vjs-upnext-bottom">
@@ -22,7 +24,7 @@ function getMainTemplate (options: any) {
22} 24}
23 25
24export interface EndCardOptions extends videojs.ComponentOptions { 26export interface EndCardOptions extends videojs.ComponentOptions {
25 next: Function, 27 next: () => void
26 getTitle: () => string 28 getTitle: () => string
27 timeout: number 29 timeout: number
28 cancelText: string 30 cancelText: string
@@ -99,11 +101,11 @@ class EndCard extends Component {
99 return container 101 return container
100 } 102 }
101 103
102 showCard (cb: Function) { 104 showCard (cb: (value: boolean) => void) {
103 let timeout: any 105 let timeout: any
104 106
105 this.autoplayRing.setAttribute('stroke-dasharray', '' + this.dashOffsetStart) 107 this.autoplayRing.setAttribute('stroke-dasharray', `${this.dashOffsetStart}`)
106 this.autoplayRing.setAttribute('stroke-dashoffset', '' + -this.dashOffsetStart) 108 this.autoplayRing.setAttribute('stroke-dashoffset', `${-this.dashOffsetStart}`)
107 109
108 this.title.innerHTML = this.options_.getTitle() 110 this.title.innerHTML = this.options_.getTitle()
109 111
@@ -123,7 +125,7 @@ class EndCard extends Component {
123 }) 125 })
124 126
125 const goToPercent = (percent: number) => { 127 const goToPercent = (percent: number) => {
126 const newOffset = Math.max(-this.dashOffsetTotal, - this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100) 128 const newOffset = Math.max(-this.dashOffsetTotal, -this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
127 this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset) 129 this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)
128 } 130 }
129 131
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts
index f0a1b1aee..f2e9adb14 100644
--- a/client/src/assets/player/utils.ts
+++ b/client/src/assets/player/utils.ts
@@ -17,7 +17,7 @@ function isIOS () {
17 // Detect iPad Desktop mode 17 // Detect iPad Desktop mode
18 return !!(navigator.maxTouchPoints && 18 return !!(navigator.maxTouchPoints &&
19 navigator.maxTouchPoints > 2 && 19 navigator.maxTouchPoints > 2 &&
20 /MacIntel/.test(navigator.platform)) 20 navigator.platform.includes('MacIntel'))
21} 21}
22 22
23function isSafari () { 23function isSafari () {
diff --git a/client/src/assets/player/videojs-components/p2p-info-button.ts b/client/src/assets/player/videojs-components/p2p-info-button.ts
index 81f9544f4..07ed18989 100644
--- a/client/src/assets/player/videojs-components/p2p-info-button.ts
+++ b/client/src/assets/player/videojs-components/p2p-info-button.ts
@@ -96,11 +96,11 @@ class P2pInfoButton extends Button {
96 } 96 }
97 subDivWebtorrent.title += this.player().localize('Total uploaded: ') + totalUploaded.join(' ') 97 subDivWebtorrent.title += this.player().localize('Total uploaded: ') + totalUploaded.join(' ')
98 98
99 downloadSpeedNumber.textContent = downloadSpeed[ 0 ] 99 downloadSpeedNumber.textContent = downloadSpeed[0]
100 downloadSpeedUnit.textContent = ' ' + downloadSpeed[ 1 ] 100 downloadSpeedUnit.textContent = ' ' + downloadSpeed[1]
101 101
102 uploadSpeedNumber.textContent = uploadSpeed[ 0 ] 102 uploadSpeedNumber.textContent = uploadSpeed[0]
103 uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ] 103 uploadSpeedUnit.textContent = ' ' + uploadSpeed[1]
104 104
105 peersNumber.textContent = numPeers.toString() 105 peersNumber.textContent = numPeers.toString()
106 peersText.textContent = ' ' + (numPeers > 1 ? this.player().localize('peers') : this.player_.localize('peer')) 106 peersText.textContent = ' ' + (numPeers > 1 ? this.player().localize('peers') : this.player_.localize('peer'))
diff --git a/client/src/assets/player/videojs-components/resolution-menu-item.ts b/client/src/assets/player/videojs-components/resolution-menu-item.ts
index 73ad47d2b..c1f502600 100644
--- a/client/src/assets/player/videojs-components/resolution-menu-item.ts
+++ b/client/src/assets/player/videojs-components/resolution-menu-item.ts
@@ -6,7 +6,7 @@ const MenuItem = videojs.getComponent('MenuItem')
6export interface ResolutionMenuItemOptions extends videojs.MenuItemOptions { 6export interface ResolutionMenuItemOptions extends videojs.MenuItemOptions {
7 labels?: { [id: number]: string } 7 labels?: { [id: number]: string }
8 id: number 8 id: number
9 callback: Function 9 callback: (resolutionId: number, type: 'video') => void
10} 10}
11 11
12class ResolutionMenuItem extends MenuItem { 12class ResolutionMenuItem extends MenuItem {
@@ -14,7 +14,7 @@ class ResolutionMenuItem extends MenuItem {
14 private readonly label: string 14 private readonly label: string
15 // Only used for the automatic item 15 // Only used for the automatic item
16 private readonly labels: { [id: number]: string } 16 private readonly labels: { [id: number]: string }
17 private readonly callback: Function 17 private readonly callback: (resolutionId: number, type: 'video') => void
18 18
19 private autoResolutionPossible: boolean 19 private autoResolutionPossible: boolean
20 private currentResolutionLabel: string 20 private currentResolutionLabel: string
diff --git a/client/src/assets/player/videojs-components/settings-dialog.ts b/client/src/assets/player/videojs-components/settings-dialog.ts
index 41911e7e8..8cd98967f 100644
--- a/client/src/assets/player/videojs-components/settings-dialog.ts
+++ b/client/src/assets/player/videojs-components/settings-dialog.ts
@@ -12,8 +12,6 @@ class SettingsDialog extends Component {
12 /** 12 /**
13 * Create the component's DOM element 13 * Create the component's DOM element
14 * 14 *
15 * @return {Element}
16 * @method createEl
17 */ 15 */
18 createEl () { 16 createEl () {
19 const uniqueId = this.id() 17 const uniqueId = this.id()
@@ -25,7 +23,7 @@ class SettingsDialog extends Component {
25 innerHTML: '', 23 innerHTML: '',
26 tabIndex: -1 24 tabIndex: -1
27 }, { 25 }, {
28 'role': 'dialog', 26 role: 'dialog',
29 'aria-labelledby': dialogLabelId, 27 'aria-labelledby': dialogLabelId,
30 'aria-describedby': dialogDescriptionId 28 'aria-describedby': dialogDescriptionId
31 }) 29 })
diff --git a/client/src/assets/player/videojs-components/settings-menu-item.ts b/client/src/assets/player/videojs-components/settings-menu-item.ts
index f1342f179..1871d41f8 100644
--- a/client/src/assets/player/videojs-components/settings-menu-item.ts
+++ b/client/src/assets/player/videojs-components/settings-menu-item.ts
@@ -1,8 +1,8 @@
1import videojs from 'video.js'
1// Thanks to Yanko Shterev: https://github.com/yshterev/videojs-settings-menu 2// Thanks to Yanko Shterev: https://github.com/yshterev/videojs-settings-menu
2import { toTitleCase } from '../utils' 3import { toTitleCase } from '../utils'
3import videojs from 'video.js'
4import { SettingsButton } from './settings-menu-button'
5import { SettingsDialog } from './settings-dialog' 4import { SettingsDialog } from './settings-dialog'
5import { SettingsButton } from './settings-menu-button'
6import { SettingsPanel } from './settings-panel' 6import { SettingsPanel } from './settings-panel'
7import { SettingsPanelChild } from './settings-panel-child' 7import { SettingsPanelChild } from './settings-panel-child'
8 8
@@ -57,7 +57,7 @@ class SettingsMenuItem extends MenuItem {
57 const newOptions = Object.assign({}, options, { entry: options.menuButton, menuButton: this }) 57 const newOptions = Object.assign({}, options, { entry: options.menuButton, menuButton: this })
58 58
59 this.subMenu = new SubMenuComponent(this.player(), newOptions) as any // FIXME: typings 59 this.subMenu = new SubMenuComponent(this.player(), newOptions) as any // FIXME: typings
60 const subMenuClass = this.subMenu.buildCSSClass().split(' ')[ 0 ] 60 const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0]
61 this.settingsSubMenuEl_.className += ' ' + subMenuClass 61 this.settingsSubMenuEl_.className += ' ' + subMenuClass
62 62
63 this.eventHandlers() 63 this.eventHandlers()
@@ -104,7 +104,7 @@ class SettingsMenuItem extends MenuItem {
104 target = event.currentTarget 104 target = event.currentTarget
105 } 105 }
106 106
107 if (target && target.classList.contains('vjs-back-button')) { 107 if (target?.classList.contains('vjs-back-button')) {
108 this.loadMainMenu() 108 this.loadMainMenu()
109 return 109 return
110 } 110 }
@@ -121,8 +121,6 @@ class SettingsMenuItem extends MenuItem {
121 /** 121 /**
122 * Create the component's DOM element 122 * Create the component's DOM element
123 * 123 *
124 * @return {Element}
125 * @method createEl
126 */ 124 */
127 createEl () { 125 createEl () {
128 const el = videojs.dom.createEl('li', { 126 const el = videojs.dom.createEl('li', {
@@ -198,14 +196,14 @@ class SettingsMenuItem extends MenuItem {
198 const prefix = [ 'webkit', 'moz', 'MS', 'o', '' ] 196 const prefix = [ 'webkit', 'moz', 'MS', 'o', '' ]
199 197
200 for (let p = 0; p < prefix.length; p++) { 198 for (let p = 0; p < prefix.length; p++) {
201 if (!prefix[ p ]) { 199 if (!prefix[p]) {
202 type = type.toLowerCase() 200 type = type.toLowerCase()
203 } 201 }
204 202
205 if (action === 'addEvent') { 203 if (action === 'addEvent') {
206 element.addEventListener(prefix[ p ] + type, callback, false) 204 element.addEventListener(prefix[p] + type, callback, false)
207 } else if (action === 'removeEvent') { 205 } else if (action === 'removeEvent') {
208 element.removeEventListener(prefix[ p ] + type, callback, false) 206 element.removeEventListener(prefix[p] + type, callback, false)
209 } 207 }
210 } 208 }
211 } 209 }
@@ -292,7 +290,10 @@ class SettingsMenuItem extends MenuItem {
292 // Thus we get the submenu value based on the labelEl of playbackRateMenuButton 290 // Thus we get the submenu value based on the labelEl of playbackRateMenuButton
293 if (subMenu === 'PlaybackRateMenuButton') { 291 if (subMenu === 'PlaybackRateMenuButton') {
294 const html = (this.subMenu as any).labelEl_.innerHTML 292 const html = (this.subMenu as any).labelEl_.innerHTML
295 setTimeout(() => this.settingsSubMenuValueEl_.innerHTML = html, 250) 293
294 setTimeout(() => {
295 this.settingsSubMenuValueEl_.innerHTML = html
296 }, 250)
296 } else { 297 } else {
297 // Loop trough the submenu items to find the selected child 298 // Loop trough the submenu items to find the selected child
298 for (const subMenuItem of this.subMenu.menu.children_) { 299 for (const subMenuItem of this.subMenu.menu.children_) {
diff --git a/client/src/assets/player/videojs-components/settings-panel-child.ts b/client/src/assets/player/videojs-components/settings-panel-child.ts
index d1582412c..161420c38 100644
--- a/client/src/assets/player/videojs-components/settings-panel-child.ts
+++ b/client/src/assets/player/videojs-components/settings-panel-child.ts
@@ -4,10 +4,6 @@ const Component = videojs.getComponent('Component')
4 4
5class SettingsPanelChild extends Component { 5class SettingsPanelChild extends Component {
6 6
7 constructor (player: videojs.Player, options?: videojs.ComponentOptions) {
8 super(player, options)
9 }
10
11 createEl () { 7 createEl () {
12 return super.createEl('div', { 8 return super.createEl('div', {
13 className: 'vjs-settings-panel-child', 9 className: 'vjs-settings-panel-child',
diff --git a/client/src/assets/player/videojs-components/settings-panel.ts b/client/src/assets/player/videojs-components/settings-panel.ts
index 1ad8bb1fc..28b579bdd 100644
--- a/client/src/assets/player/videojs-components/settings-panel.ts
+++ b/client/src/assets/player/videojs-components/settings-panel.ts
@@ -4,10 +4,6 @@ const Component = videojs.getComponent('Component')
4 4
5class SettingsPanel extends Component { 5class SettingsPanel extends Component {
6 6
7 constructor (player: videojs.Player, options?: videojs.ComponentOptions) {
8 super(player, options)
9 }
10
11 createEl () { 7 createEl () {
12 return super.createEl('div', { 8 return super.createEl('div', {
13 className: 'vjs-settings-panel', 9 className: 'vjs-settings-panel',
diff --git a/client/src/assets/player/webtorrent/peertube-chunk-store.ts b/client/src/assets/player/webtorrent/peertube-chunk-store.ts
index 66762bef8..93ca8e1d8 100644
--- a/client/src/assets/player/webtorrent/peertube-chunk-store.ts
+++ b/client/src/assets/player/webtorrent/peertube-chunk-store.ts
@@ -36,7 +36,7 @@ export class PeertubeChunkStore extends EventEmitter {
36 36
37 chunkLength: number 37 chunkLength: number
38 38
39 private pendingPut: { id: number, buf: Buffer, cb: Function }[] = [] 39 private pendingPut: { id: number, buf: Buffer, cb: (err?: Error) => void }[] = []
40 // If the store is full 40 // If the store is full
41 private memoryChunks: { [ id: number ]: Buffer | true } = {} 41 private memoryChunks: { [ id: number ]: Buffer | true } = {}
42 private databaseName: string 42 private databaseName: string
@@ -54,7 +54,7 @@ export class PeertubeChunkStore extends EventEmitter {
54 this.databaseName = 'webtorrent-chunks-' 54 this.databaseName = 'webtorrent-chunks-'
55 55
56 if (!opts) opts = {} 56 if (!opts) opts = {}
57 if (opts.torrent && opts.torrent.infoHash) this.databaseName += opts.torrent.infoHash 57 if (opts.torrent?.infoHash) this.databaseName += opts.torrent.infoHash
58 else this.databaseName += '-default' 58 else this.databaseName += '-default'
59 59
60 this.setMaxListeners(100) 60 this.setMaxListeners(100)
@@ -106,7 +106,9 @@ export class PeertubeChunkStore extends EventEmitter {
106 } catch (err) { 106 } catch (err) {
107 console.log('Cannot bulk insert chunks. Store them in memory.', { err }) 107 console.log('Cannot bulk insert chunks. Store them in memory.', { err })
108 108
109 processing.forEach(p => this.memoryChunks[ p.id ] = p.buf) 109 processing.forEach(p => {
110 this.memoryChunks[p.id] = p.buf
111 })
110 } finally { 112 } finally {
111 processing.forEach(p => p.cb()) 113 processing.forEach(p => p.cb())
112 } 114 }
diff --git a/client/src/assets/player/webtorrent/video-renderer.ts b/client/src/assets/player/webtorrent/video-renderer.ts
index c3cbea725..8fb3e9672 100644
--- a/client/src/assets/player/webtorrent/video-renderer.ts
+++ b/client/src/assets/player/webtorrent/video-renderer.ts
@@ -3,7 +3,7 @@
3 3
4const MediaElementWrapper = require('mediasource') 4const MediaElementWrapper = require('mediasource')
5import { extname } from 'path' 5import { extname } from 'path'
6const videostream = require('videostream') 6const Videostream = require('videostream')
7 7
8const VIDEOSTREAM_EXTS = [ 8const VIDEOSTREAM_EXTS = [
9 '.m4a', 9 '.m4a',
@@ -34,7 +34,7 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
34 let renderer: any 34 let renderer: any
35 35
36 try { 36 try {
37 if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) { 37 if (VIDEOSTREAM_EXTS.includes(extension)) {
38 renderer = useVideostream() 38 renderer = useVideostream()
39 } else { 39 } else {
40 renderer = useMediaSource() 40 renderer = useMediaSource()
@@ -51,7 +51,7 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
51 return callback(err) 51 return callback(err)
52 }) 52 })
53 preparedElem.addEventListener('loadstart', onLoadStart) 53 preparedElem.addEventListener('loadstart', onLoadStart)
54 return new videostream(file, preparedElem) 54 return new Videostream(file, preparedElem)
55 } 55 }
56 56
57 function useMediaSource (useVP9 = false) { 57 function useMediaSource (useVP9 = false) {
@@ -62,7 +62,7 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
62 preparedElem.removeEventListener('error', onError) 62 preparedElem.removeEventListener('error', onError)
63 63
64 // Try with vp9 before returning an error 64 // Try with vp9 before returning an error
65 if (codecs.indexOf('vp8') !== -1) return fallbackToMediaSource(true) 65 if (codecs.includes('vp8')) return fallbackToMediaSource(true)
66 66
67 return callback(err) 67 return callback(err)
68 }) 68 })
diff --git a/client/src/assets/player/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
index 17d369c10..0587ddee6 100644
--- a/client/src/assets/player/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
@@ -17,8 +17,8 @@ import { renderVideo } from './video-renderer'
17const CacheChunkStore = require('cache-chunk-store') 17const CacheChunkStore = require('cache-chunk-store')
18 18
19type PlayOptions = { 19type PlayOptions = {
20 forcePlay?: boolean, 20 forcePlay?: boolean
21 seek?: number, 21 seek?: number
22 delay?: number 22 delay?: number
23} 23}
24 24
@@ -126,8 +126,8 @@ class WebTorrentPlugin extends Plugin {
126 updateVideoFile ( 126 updateVideoFile (
127 videoFile?: VideoFile, 127 videoFile?: VideoFile,
128 options: { 128 options: {
129 forcePlay?: boolean, 129 forcePlay?: boolean
130 seek?: number, 130 seek?: number
131 delay?: number 131 delay?: number
132 } = {}, 132 } = {},
133 done: () => void = () => { /* empty */ } 133 done: () => void = () => { /* empty */ }
@@ -248,7 +248,7 @@ class WebTorrentPlugin extends Plugin {
248 magnetOrTorrentUrl: string, 248 magnetOrTorrentUrl: string,
249 previousVideoFile: VideoFile, 249 previousVideoFile: VideoFile,
250 options: PlayOptions, 250 options: PlayOptions,
251 done: Function 251 done: (err?: Error) => void
252 ) { 252 ) {
253 if (!magnetOrTorrentUrl) return this.fallbackToHttp(options, done) 253 if (!magnetOrTorrentUrl) return this.fallbackToHttp(options, done)
254 254
@@ -272,7 +272,7 @@ class WebTorrentPlugin extends Plugin {
272 this.stopTorrent(oldTorrent) 272 this.stopTorrent(oldTorrent)
273 273
274 // We use a fake renderer so we download correct pieces of the next file 274 // We use a fake renderer so we download correct pieces of the next file
275 if (options.delay) this.renderFileInFakeElement(torrent.files[ 0 ], options.delay) 275 if (options.delay) this.renderFileInFakeElement(torrent.files[0], options.delay)
276 } 276 }
277 277
278 // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution) 278 // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution)
@@ -288,7 +288,7 @@ class WebTorrentPlugin extends Plugin {
288 if (options.seek) this.player.currentTime(options.seek) 288 if (options.seek) this.player.currentTime(options.seek)
289 289
290 const renderVideoOptions = { autoplay: false, controls: true } 290 const renderVideoOptions = { autoplay: false, controls: true }
291 renderVideo(torrent.files[ 0 ], this.playerElement, renderVideoOptions, (err, renderer) => { 291 renderVideo(torrent.files[0], this.playerElement, renderVideoOptions, (err, renderer) => {
292 this.renderer = renderer 292 this.renderer = renderer
293 293
294 if (err) return this.fallbackToHttp(options, done) 294 if (err) return this.fallbackToHttp(options, done)
@@ -321,7 +321,7 @@ class WebTorrentPlugin extends Plugin {
321 if (err.message.indexOf('incorrect info hash') !== -1) { 321 if (err.message.indexOf('incorrect info hash') !== -1) {
322 console.error('Incorrect info hash detected, falling back to torrent file.') 322 console.error('Incorrect info hash detected, falling back to torrent file.')
323 const newOptions = { forcePlay: true, seek: options.seek } 323 const newOptions = { forcePlay: true, seek: options.seek }
324 return this.addTorrent(this.torrent[ 'xs' ], previousVideoFile, newOptions, done) 324 return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done)
325 } 325 }
326 326
327 // Remote instance is down 327 // Remote instance is down
@@ -340,7 +340,7 @@ class WebTorrentPlugin extends Plugin {
340 if (playPromise !== undefined) { 340 if (playPromise !== undefined) {
341 return playPromise.then(() => done()) 341 return playPromise.then(() => done())
342 .catch((err: Error) => { 342 .catch((err: Error) => {
343 if (err.message.indexOf('The play() request was interrupted by a call to pause()') !== -1) { 343 if (err.message.includes('The play() request was interrupted by a call to pause()')) {
344 return 344 return
345 } 345 }
346 346
@@ -479,7 +479,7 @@ class WebTorrentPlugin extends Plugin {
479 } 479 }
480 480
481 private isPlayerWaiting () { 481 private isPlayerWaiting () {
482 return this.player && this.player.hasClass('vjs-waiting') 482 return this.player?.hasClass('vjs-waiting')
483 } 483 }
484 484
485 private runTorrentInfoScheduler () { 485 private runTorrentInfoScheduler () {
@@ -513,7 +513,7 @@ class WebTorrentPlugin extends Plugin {
513 }, this.CONSTANTS.INFO_SCHEDULER) 513 }, this.CONSTANTS.INFO_SCHEDULER)
514 } 514 }
515 515
516 private fallbackToHttp (options: PlayOptions, done?: Function) { 516 private fallbackToHttp (options: PlayOptions, done?: (err?: Error) => void) {
517 const paused = this.player.paused() 517 const paused = this.player.paused()
518 518
519 this.disableAutoResolution(true) 519 this.disableAutoResolution(true)
@@ -565,7 +565,7 @@ class WebTorrentPlugin extends Plugin {
565 private stopTorrent (torrent: WebTorrent.Torrent) { 565 private stopTorrent (torrent: WebTorrent.Torrent) {
566 torrent.pause() 566 torrent.pause()
567 // Pause does not remove actual peers (in particular the webseed peer) 567 // Pause does not remove actual peers (in particular the webseed peer)
568 torrent.removePeer(torrent[ 'ws' ]) 568 torrent.removePeer(torrent['ws'])
569 } 569 }
570 570
571 private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) { 571 private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {