@gcorevideo/player 2.12.1 → 2.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Player.ts CHANGED
@@ -15,9 +15,7 @@ import type {
15
15
  CoreOptions,
16
16
  CorePluginOptions,
17
17
  } from './internal.types.js'
18
- import type {
19
- PlayerPlugin,
20
- } from './types.js'
18
+ import type { PlayerMediaSourceDesc, PlayerPlugin } from './types.js'
21
19
  import { PlayerConfig, PlayerEvent } from './types.js'
22
20
  import {
23
21
  buildMediaSourcesList,
@@ -74,12 +72,6 @@ export class Player {
74
72
 
75
73
  private tunedIn = false
76
74
 
77
- // private sourcesList: PlayerMediaSourceDesc[] = []
78
-
79
- // private currentSourceIndex = 0
80
-
81
- // private sourcesDelay: Record<string, number> = {}
82
-
83
75
  constructor(config: PlayerConfig) {
84
76
  this.setConfig(config)
85
77
  }
@@ -125,10 +117,6 @@ export class Player {
125
117
  Log.setLevel(0)
126
118
  }
127
119
 
128
- trace(`${T} init`, {
129
- // TODO selected options
130
- })
131
-
132
120
  this.configurePlaybacks()
133
121
  const coreOpts = this.buildCoreOptions(playerElement)
134
122
  const { core, container } = Loader.registeredPlugins
@@ -400,11 +388,11 @@ export class Player {
400
388
  }
401
389
 
402
390
  private buildCoreOptions(rootNode: HTMLElement): CoreOptions {
403
- this.buildMediaSourcesList()
404
- const source = this.config.sources[0]
405
- if (!source) {
406
- trace(`${T} buildCoreOptions no source selected`)
407
- }
391
+ const sources = this.buildMediaSourcesList()
392
+ const source = sources[0]
393
+ trace(`${T} buildCoreOptions`, {
394
+ source
395
+ })
408
396
 
409
397
  this.rootNode = rootNode
410
398
 
@@ -412,7 +400,7 @@ export class Player {
412
400
  ...this.config, // plugin settings
413
401
  allowUserInteraction: true,
414
402
  autoPlay: false,
415
- dash: this.config.dash,
403
+ dash: this.config.dash, // TODO move this to the playback section
416
404
  debug: this.config.debug || 'none',
417
405
  events: this.events,
418
406
  height: rootNode.clientHeight,
@@ -432,7 +420,7 @@ export class Player {
432
420
  playbackType: this.config.playbackType,
433
421
  width: rootNode.clientWidth,
434
422
  source: source ? unwrapSource(source) : undefined,
435
- sources: undefined,
423
+ sources, // prevent Clappr from loading all sources simultaneously
436
424
  strings: this.config.strings,
437
425
  }
438
426
  return coreOptions
@@ -442,9 +430,10 @@ export class Player {
442
430
  registerPlaybacks()
443
431
  }
444
432
 
445
- private buildMediaSourcesList() {
446
- return buildMediaSourcesList( // TODO ensure unsupported sources are filtered out
447
- this.config.sources.map(s => wrapSource(s)),
433
+ private buildMediaSourcesList(): PlayerMediaSourceDesc[] {
434
+ return buildMediaSourcesList(
435
+ // TODO ensure unsupported sources are filtered out
436
+ this.config.sources.map((s) => wrapSource(s)),
448
437
  this.config.priorityTransport,
449
438
  )
450
439
  }
package/src/index.ts CHANGED
@@ -10,7 +10,6 @@
10
10
 
11
11
  export { setTracer } from '@gcorevideo/utils'
12
12
  export * from './Player.js'
13
- export { PlaybackError } from './internal.types.js'
14
13
  export * from './playback.types.js'
15
14
  export * from './types.js'
16
15
  export * from './version.js'
@@ -5,6 +5,7 @@ import type {
5
5
  } from "@clappr/core";
6
6
 
7
7
  import { PlaybackType, PlayerDebugTag, PlayerMediaSource } from "./types";
8
+ import { PlaybackError } from "./playback.types";
8
9
 
9
10
  type ExternalTrack = {
10
11
  kind?: "subtitles" | "captions";
@@ -56,20 +57,6 @@ export interface CorePlaybackConfig {
56
57
  shakaConfiguration?: ShakaConfig;
57
58
  }
58
59
 
59
- type ErrorLevel = "FATAL" | "WARN" | "INFO";
60
-
61
- /**
62
- * @beta
63
- */
64
- export type PlaybackError = {
65
- code?: number | string;
66
- description: string;
67
- raw?: MediaError;
68
- level?: ErrorLevel;
69
- message: string;
70
- scope?: string;
71
- }
72
-
73
60
  /**
74
61
  * @internal
75
62
  */
@@ -1,33 +1,32 @@
1
-
2
1
  /**
3
2
  * Playback time in seconds since the beginning of the stream (or a segment for the live streams)
4
3
  * For the plugin development
5
4
  * @internal
6
5
  */
7
- export type TimeValue = number;
6
+ export type TimeValue = number
8
7
 
9
8
  /**
10
9
  * For the plugin development
11
10
  * @internal
12
11
  */
13
12
  export type TimePosition = {
14
- current: TimeValue;
15
- total: TimeValue;
13
+ current: TimeValue
14
+ total: TimeValue
16
15
  }
17
16
 
18
17
  /**
19
18
  * For the plugin development
20
19
  * @internal
21
20
  */
22
- export type TimeProgress = TimePosition & { start: number; };
21
+ export type TimeProgress = TimePosition & { start: number }
23
22
 
24
23
  /**
25
24
  * For the plugin development
26
25
  * @internal
27
26
  */
28
27
  export type TimeUpdate = TimePosition & {
29
- firstFragDateTime: number;
30
- };
28
+ firstFragDateTime: number
29
+ }
31
30
 
32
31
  /**
33
32
  * @beta
@@ -47,3 +46,15 @@ export enum PlaybackErrorCode {
47
46
  MediaSourceUnavailable = 1,
48
47
  QualityLevelUnavailable = 2,
49
48
  }
49
+
50
+ export type ErrorLevel = 'FATAL' | 'WARN' | 'INFO'
51
+
52
+ /**
53
+ * @beta
54
+ */
55
+ export interface PlaybackError {
56
+ code: PlaybackErrorCode
57
+ description: string
58
+ level: ErrorLevel
59
+ message: string
60
+ }
@@ -2,7 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style
3
3
  // license that can be found in the LICENSE file.
4
4
 
5
- import { Events, HTML5Video, Log, Playback, Utils } from '@clappr/core'
5
+ import { Events, HTML5Video, Log, Playback, PlayerError, Utils } from '@clappr/core'
6
6
  import { trace } from '@gcorevideo/utils'
7
7
  import assert from 'assert'
8
8
  import DASHJS, {
@@ -14,7 +14,7 @@ import DASHJS, {
14
14
  IManifestInfo,
15
15
  } from 'dashjs'
16
16
 
17
- import { PlaybackErrorCode, QualityLevel, TimePosition, TimeUpdate, TimeValue } from '../../playback.types.js'
17
+ import { PlaybackError, PlaybackErrorCode, QualityLevel, TimePosition, TimeUpdate, TimeValue } from '../../playback.types.js'
18
18
 
19
19
  const AUTO = -1
20
20
 
@@ -406,7 +406,6 @@ export default class DashPlayback extends HTML5Video {
406
406
 
407
407
  private _onDASHJSSError = (event: DashErrorEvent) => {
408
408
  trace(`${T} _onDASHJSSError`, { event })
409
- // TODO figure out what's for
410
409
  this._stopTimeUpdateTimer()
411
410
 
412
411
  const e = (event as MediaPlayerErrorEvent).error
@@ -414,31 +413,32 @@ export default class DashPlayback extends HTML5Video {
414
413
  case DASHJS.MediaPlayer.errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE:
415
414
  case DASHJS.MediaPlayer.errors.MANIFEST_LOADER_LOADING_FAILURE_ERROR_CODE:
416
415
  case DASHJS.MediaPlayer.errors.DOWNLOAD_ERROR_ID_MANIFEST_CODE:
417
- this.trigger(Events.PLAYBACK_ERROR, this.createError({
416
+ case DASHJS.MediaPlayer.errors.DOWNLOAD_ERROR_ID_CONTENT_CODE:
417
+ this.triggerError({
418
418
  code: PlaybackErrorCode.MediaSourceUnavailable,
419
419
  message: e.message,
420
- }, {
421
- useCodePrefix: false,
422
- }))
420
+ description: e.message,
421
+ level: PlayerError.Levels.FATAL,
422
+ })
423
423
  break;
424
424
  // TODO more cases
425
425
  default:
426
- this.trigger(Events.PLAYBACK_ERROR, this.createError({
426
+ this.triggerError({
427
427
  code: PlaybackErrorCode.Generic,
428
428
  message: e.message,
429
- }, {
430
- useCodePrefix: false,
431
- }))
429
+ description: e.message,
430
+ level: PlayerError.Levels.FATAL,
431
+ })
432
432
  }
433
+ }
433
434
 
435
+ private triggerError(error: PlaybackError) {
436
+ trace(`${T} triggerError`, { error })
437
+ this.trigger(Events.PLAYBACK_ERROR, error)
434
438
  // only reset the dash player in 10ms async, so that the rest of the
435
439
  // calling function finishes
436
440
  setTimeout(() => {
437
- assert.ok(
438
- this._dash,
439
- 'An instance of dashjs MediaPlayer is required to reset',
440
- )
441
- this._dash.reset()
441
+ this.stop()
442
442
  }, 10)
443
443
  }
444
444
 
@@ -25,7 +25,7 @@ import HLSJS, {
25
25
  type LevelSwitchingData,
26
26
  } from 'hls.js'
27
27
 
28
- import { PlaybackErrorCode, QualityLevel, TimePosition, TimeUpdate } from '../../playback.types.js'
28
+ import { PlaybackError, PlaybackErrorCode, QualityLevel, TimePosition, TimeUpdate } from '../../playback.types.js'
29
29
  import { PlaybackType } from '../../types'
30
30
  import { TimerId } from '../../utils/types'
31
31
 
@@ -493,7 +493,7 @@ export default class HlsPlayback extends HTML5Video {
493
493
  this.trigger(Events.PLAYBACK_READY, this.name)
494
494
  }
495
495
 
496
- _recover(evt: HlsEvents.ERROR, data: HlsErrorData, error: ErrorInfo) {
496
+ _recover(evt: HlsEvents.ERROR, data: HlsErrorData, error: PlaybackError) {
497
497
  assert(this._hls, 'Hls.js instance is not available')
498
498
  if (!this._recoveredDecodingError) {
499
499
  this._recoveredDecodingError = true
@@ -505,10 +505,8 @@ export default class HlsPlayback extends HTML5Video {
505
505
  } else {
506
506
  Log.error('hlsjs: failed to recover', { evt, data })
507
507
  error.level = PlayerError.Levels.FATAL
508
- const formattedError = this.createError(error)
509
508
 
510
- this.trigger(Events.PLAYBACK_ERROR, formattedError)
511
- this.stop()
509
+ this.triggerError(error)
512
510
  }
513
511
  }
514
512
 
@@ -608,13 +606,12 @@ export default class HlsPlayback extends HTML5Video {
608
606
  }
609
607
 
610
608
  _onHLSJSError(evt: HlsEvents.ERROR, data: HlsErrorData) {
611
- const error: Record<string, unknown> = {
612
- // code: `${data.type}_${data.details}`,
609
+ const error: PlaybackError = {
613
610
  code: PlaybackErrorCode.Generic,
614
- description: `${this.name} error: type: ${data.type}, details: ${data.details}`,
615
- raw: data,
611
+ description: `${this.name} error: type: ${data.type}, details: ${data.details} fatal: ${data.fatal}`,
612
+ level: data.fatal ? PlayerError.Levels.FATAL : PlayerError.Levels.WARN,
613
+ message: `${this.name} error: type: ${data.type}, details: ${data.details}`,
616
614
  }
617
- let formattedError
618
615
 
619
616
  if (data.response) {
620
617
  error.description += `, response: ${JSON.stringify(data.response)}`
@@ -639,18 +636,8 @@ export default class HlsPlayback extends HTML5Video {
639
636
  evt,
640
637
  data,
641
638
  })
642
- error.code = [
643
- HLSJS.ErrorDetails.MANIFEST_LOAD_ERROR,
644
- HLSJS.ErrorDetails.MANIFEST_LOAD_TIMEOUT,
645
- HLSJS.ErrorDetails.MANIFEST_PARSING_ERROR
646
- ].includes(data.details)
647
- ? PlaybackErrorCode.MediaSourceUnavailable
648
- : PlaybackErrorCode.QualityLevelUnavailable
649
- formattedError = this.createError(error, {
650
- useCodePrefix: false,
651
- })
652
- this.trigger(Events.PLAYBACK_ERROR, formattedError)
653
- this.stop()
639
+ error.code = PlaybackErrorCode.MediaSourceUnavailable;
640
+ this.triggerError(error)
654
641
  break
655
642
  default:
656
643
  Log.warn('hlsjs: trying to recover from network error.', {
@@ -658,8 +645,7 @@ export default class HlsPlayback extends HTML5Video {
658
645
  data,
659
646
  })
660
647
  error.level = PlayerError.Levels.WARN
661
- assert(this._hls, 'Hls.js instance is not available')
662
- this._hls.startLoad()
648
+ this._hls?.startLoad()
663
649
  break
664
650
  }
665
651
  break
@@ -673,11 +659,7 @@ export default class HlsPlayback extends HTML5Video {
673
659
  break
674
660
  default:
675
661
  Log.error('hlsjs: could not recover from error.', { evt, data })
676
- formattedError = this.createError(error, {
677
- useCodePrefix: false,
678
- })
679
- this.trigger(Events.PLAYBACK_ERROR, formattedError)
680
- this.stop()
662
+ this.triggerError(error)
681
663
  break
682
664
  }
683
665
  } else {
@@ -686,11 +668,7 @@ export default class HlsPlayback extends HTML5Video {
686
668
  { evt, data },
687
669
  )
688
670
  // TODO
689
- formattedError = this.createError(error, {
690
- useCodePrefix: false,
691
- })
692
- this.trigger(Events.PLAYBACK_ERROR, formattedError)
693
- this.stop()
671
+ this.triggerError(error)
694
672
  }
695
673
  } else {
696
674
  // Transforms HLSJS.ErrorDetails.KEY_LOAD_ERROR non-fatal error to
@@ -702,16 +680,11 @@ export default class HlsPlayback extends HTML5Video {
702
680
  this._keyIsDenied(data)
703
681
  ) {
704
682
  Log.error('hlsjs: could not load decrypt key.', { evt, data })
705
- formattedError = this.createError(error, {
706
- useCodePrefix: false,
707
- })
708
- this.trigger(Events.PLAYBACK_ERROR, formattedError)
709
- this.stop()
683
+ this.triggerError(error)
710
684
 
711
685
  return
712
686
  }
713
687
 
714
- error.level = PlayerError.Levels.WARN
715
688
  Log.warn('hlsjs: non-fatal error occurred', { evt, data })
716
689
  }
717
690
  }
@@ -1081,6 +1054,12 @@ export default class HlsPlayback extends HTML5Video {
1081
1054
  isSeekEnabled() {
1082
1055
  return this._playbackType === Playback.VOD || this.dvrEnabled
1083
1056
  }
1057
+
1058
+ private triggerError(error: PlaybackError) {
1059
+ trace(`${T} triggerError`, { error })
1060
+ this.trigger(Events.PLAYBACK_ERROR, error)
1061
+ this.stop()
1062
+ }
1084
1063
  }
1085
1064
 
1086
1065
  HlsPlayback.canPlay = function (resource: string, mimeType?: string): boolean {
@@ -1,4 +1,8 @@
1
- import type { PlayerMediaSource, PlayerMediaSourceDesc, TransportPreference } from '../types'
1
+ import type {
2
+ PlayerMediaSource,
3
+ PlayerMediaSourceDesc,
4
+ TransportPreference,
5
+ } from '../types'
2
6
  import { canPlayDash, canPlayHls } from '../playback/index.js'
3
7
 
4
8
  export type SourceVariants = {
@@ -8,10 +12,10 @@ export type SourceVariants = {
8
12
  }
9
13
 
10
14
  /**
11
- *
15
+ *
12
16
  * @param sources
13
17
  * @deprecated
14
- * @returns
18
+ * @returns
15
19
  */
16
20
  export function buildSourcesSet(sources: PlayerMediaSource[]): SourceVariants {
17
21
  const sv: SourceVariants = {
@@ -38,23 +42,29 @@ export function buildMediaSourcesList(
38
42
  ): PlayerMediaSourceDesc[] {
39
43
  const [preferred, rest] = sources.reduce(
40
44
  priorityTransport === 'dash' || priorityTransport === 'auto'
41
- ? (acc: [PlayerMediaSourceDesc[], PlayerMediaSourceDesc[]], item: PlayerMediaSourceDesc) => {
42
- if (canPlayDash(item.source, item.mimeType)) {
43
- acc[0].push(item)
44
- } else if (canPlayHls(item.source, item.mimeType)) {
45
- acc[1].push(item)
46
- }
47
- return acc
48
- }
49
- : (acc: [PlayerMediaSourceDesc[], PlayerMediaSourceDesc[]], item: PlayerMediaSourceDesc) => {
50
- if (canPlayHls(item.source, item.mimeType)) {
51
- acc[0].push(item)
52
- } else if (canPlayDash(item.source, item.mimeType)) {
53
- acc[1].push(item)
54
- }
55
- return acc
56
- },
57
- [[], []]
45
+ ? (
46
+ acc: [PlayerMediaSourceDesc[], PlayerMediaSourceDesc[]],
47
+ item: PlayerMediaSourceDesc,
48
+ ) => {
49
+ if (canPlayDash(item.source, item.mimeType)) {
50
+ acc[0].push(item)
51
+ } else if (canPlayHls(item.source, item.mimeType)) {
52
+ acc[1].push(item)
53
+ }
54
+ return acc
55
+ }
56
+ : (
57
+ acc: [PlayerMediaSourceDesc[], PlayerMediaSourceDesc[]],
58
+ item: PlayerMediaSourceDesc,
59
+ ) => {
60
+ if (canPlayHls(item.source, item.mimeType)) {
61
+ acc[0].push(item)
62
+ } else if (canPlayDash(item.source, item.mimeType)) {
63
+ acc[1].push(item)
64
+ }
65
+ return acc
66
+ },
67
+ [[], []],
58
68
  )
59
69
  return preferred.concat(rest)
60
70
  }