@gcorevideo/player 2.20.4 → 2.20.6

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.
Files changed (63) hide show
  1. package/assets/error-screen/error_screen.ejs +3 -1
  2. package/dist/core.js +407 -205
  3. package/dist/index.css +1285 -1285
  4. package/dist/index.js +550 -386
  5. package/dist/plugins/index.css +966 -966
  6. package/dist/plugins/index.js +121 -162
  7. package/lib/Player.d.ts.map +1 -1
  8. package/lib/Player.js +2 -2
  9. package/lib/playback/BasePlayback.d.ts +11 -0
  10. package/lib/playback/BasePlayback.d.ts.map +1 -0
  11. package/lib/playback/BasePlayback.js +33 -0
  12. package/lib/playback/dash-playback/DashPlayback.d.ts +3 -2
  13. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  14. package/lib/playback/dash-playback/DashPlayback.js +7 -7
  15. package/lib/playback/hls-playback/HlsPlayback.d.ts +2 -2
  16. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  17. package/lib/playback/hls-playback/HlsPlayback.js +8 -5
  18. package/lib/playback/utils.d.ts +2 -0
  19. package/lib/playback/utils.d.ts.map +1 -0
  20. package/lib/playback/utils.js +1 -0
  21. package/lib/playback.types.d.ts +10 -3
  22. package/lib/playback.types.d.ts.map +1 -1
  23. package/lib/playback.types.js +3 -3
  24. package/lib/plugins/context-menu/ContextMenu.d.ts.map +1 -1
  25. package/lib/plugins/context-menu/ContextMenu.js +1 -2
  26. package/lib/plugins/error-screen/ErrorScreen.d.ts +39 -24
  27. package/lib/plugins/error-screen/ErrorScreen.d.ts.map +1 -1
  28. package/lib/plugins/error-screen/ErrorScreen.js +69 -136
  29. package/lib/plugins/media-control/MediaControl.d.ts +1 -1
  30. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  31. package/lib/plugins/media-control/MediaControl.js +16 -8
  32. package/lib/plugins/multi-camera/MultiCamera.d.ts.map +1 -1
  33. package/lib/plugins/multi-camera/MultiCamera.js +2 -3
  34. package/lib/plugins/poster/Poster.js +1 -1
  35. package/lib/plugins/source-controller/SourceController.d.ts +2 -1
  36. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  37. package/lib/plugins/source-controller/SourceController.js +12 -6
  38. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +2 -1
  39. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
  40. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +19 -3
  41. package/lib/testUtils.d.ts +66 -2
  42. package/lib/testUtils.d.ts.map +1 -1
  43. package/lib/testUtils.js +95 -2
  44. package/package.json +2 -2
  45. package/src/Player.ts +2 -2
  46. package/src/__tests__/Player.test.ts +2 -3
  47. package/src/playback/BasePlayback.ts +41 -0
  48. package/src/playback/dash-playback/DashPlayback.ts +12 -17
  49. package/src/playback/hls-playback/HlsPlayback.ts +9 -7
  50. package/src/playback.types.ts +11 -3
  51. package/src/plugins/context-menu/ContextMenu.ts +1 -2
  52. package/src/plugins/error-screen/ErrorScreen.ts +120 -195
  53. package/src/plugins/error-screen/__tests__/ErrorScreen.test.ts +113 -0
  54. package/src/plugins/error-screen/__tests__/__snapshots__/ErrorScreen.test.ts.snap +20 -0
  55. package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +32 -57
  56. package/src/plugins/media-control/MediaControl.ts +16 -8
  57. package/src/plugins/multi-camera/MultiCamera.ts +2 -3
  58. package/src/plugins/poster/Poster.ts +1 -1
  59. package/src/plugins/source-controller/SourceController.ts +20 -14
  60. package/src/plugins/source-controller/__tests__/SourceController.test.ts +29 -46
  61. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +20 -3
  62. package/src/testUtils.ts +100 -3
  63. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Events as ClapprEvents, CorePlugin, } from '@clappr/core';
2
- import { PlaybackErrorCode, } from '../../playback.types.js';
2
+ import { PlaybackErrorCode } from '../../playback.types.js';
3
3
  import { trace } from '@gcorevideo/utils';
4
4
  import { SpinnerEvents } from '../spinner-three-bounce/SpinnerThreeBounce.js';
5
5
  import { CLAPPR_VERSION } from '../../build.js';
@@ -100,10 +100,15 @@ export class SourceController extends CorePlugin {
100
100
  */
101
101
  bindEvents() {
102
102
  super.bindEvents();
103
- this.listenTo(this.core, ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED, () => this.onReady());
103
+ this.listenTo(this.core, ClapprEvents.CORE_READY, this.onCoreReady);
104
+ this.listenTo(this.core, ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChanged);
104
105
  }
105
- onReady() {
106
- trace(`${T} onReady`, {
106
+ onCoreReady() {
107
+ trace(`${T} onCoreReady`);
108
+ this.core.getPlugin('error_screen')?.disable(); // TODO test
109
+ }
110
+ onActiveContainerChanged() {
111
+ trace(`${T} onActiveContainerChanged`, {
107
112
  retrying: this.active,
108
113
  currentSource: this.sourcesList[this.currentSourceIndex],
109
114
  });
@@ -119,7 +124,7 @@ export class SourceController extends CorePlugin {
119
124
  this.bindContainerEventListeners();
120
125
  if (this.active) {
121
126
  this.core.activeContainer?.getPlugin('poster_custom')?.disable();
122
- spinner?.show();
127
+ spinner?.show(0);
123
128
  }
124
129
  }
125
130
  bindContainerEventListeners() {
@@ -136,7 +141,7 @@ export class SourceController extends CorePlugin {
136
141
  switch (error.code) {
137
142
  case PlaybackErrorCode.MediaSourceUnavailable:
138
143
  this.core.activeContainer?.getPlugin('poster_custom')?.disable();
139
- this.retryPlayback();
144
+ setTimeout(() => this.retryPlayback(), 0);
140
145
  break;
141
146
  // TODO handle other errors
142
147
  default:
@@ -166,6 +171,7 @@ export class SourceController extends CorePlugin {
166
171
  currentSource: this.sourcesList[this.currentSourceIndex],
167
172
  });
168
173
  this.active = true;
174
+ this.core.activeContainer?.getPlugin('spinner')?.show(0);
169
175
  this.getNextMediaSource().then((nextSource) => {
170
176
  trace(`${T} retryPlayback syncing...`, {
171
177
  nextSource,
@@ -34,6 +34,7 @@ export declare class SpinnerThreeBounce extends UIContainerPlugin {
34
34
  'data-spinner': string;
35
35
  class: string;
36
36
  };
37
+ private hideTimeout;
37
38
  private showTimeout;
38
39
  private template;
39
40
  private hasFatalError;
@@ -47,7 +48,7 @@ export declare class SpinnerThreeBounce extends UIContainerPlugin {
47
48
  /**
48
49
  * Shows the spinner
49
50
  */
50
- show(): void;
51
+ show(delay?: number): void;
51
52
  /**
52
53
  * Hides the spinner
53
54
  */
@@ -1 +1 @@
1
- {"version":3,"file":"SpinnerThreeBounce.d.ts","sourceRoot":"","sources":["../../../src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAA0B,iBAAiB,EAAY,MAAM,cAAc,CAAC;AAK9F,OAAO,mDAAmD,CAAC;AAM3D;;GAEG;AACH,oBAAY,aAAa;IACvB;;;OAGG;IACH,IAAI,yBAAyB;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,iBAAiB;IACvD;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,IAAa,UAAU;;;MAKtB;IAED,OAAO,CAAC,WAAW,CAAwB;IAE3C,OAAO,CAAC,QAAQ,CAAyB;IAEzC,OAAO,CAAC,aAAa,CAAQ;IAE7B,OAAO,CAAC,YAAY,CAAQ;gBAEhB,SAAS,EAAE,SAAS;IAWhC,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,OAAO;IAef;;OAEG;IACH,IAAI;IAIJ;;OAEG;IACH,IAAI;IAQJ;;OAEG;IACM,MAAM;CAmBhB"}
1
+ {"version":3,"file":"SpinnerThreeBounce.d.ts","sourceRoot":"","sources":["../../../src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAA0B,iBAAiB,EAAY,MAAM,cAAc,CAAC;AAK9F,OAAO,mDAAmD,CAAC;AAM3D;;GAEG;AACH,oBAAY,aAAa;IACvB;;;OAGG;IACH,IAAI,yBAAyB;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,iBAAiB;IACvD;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,IAAa,UAAU;;;MAKtB;IAED,OAAO,CAAC,WAAW,CAAwB;IAE3C,OAAO,CAAC,WAAW,CAAwB;IAE3C,OAAO,CAAC,QAAQ,CAAyB;IAEzC,OAAO,CAAC,aAAa,CAAQ;IAE7B,OAAO,CAAC,YAAY,CAAQ;gBAEhB,SAAS,EAAE,SAAS;IAWhC,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,OAAO;IAef;;OAEG;IACH,IAAI,CAAC,KAAK,SAAM;IAchB;;OAEG;IACH,IAAI;IAaJ;;OAEG;IACM,MAAM;CAmBhB"}
@@ -47,6 +47,7 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
47
47
  'class': 'spinner-three-bounce'
48
48
  };
49
49
  }
50
+ hideTimeout = null;
50
51
  showTimeout = null;
51
52
  template = template(spinnerHTML);
52
53
  hasFatalError = false;
@@ -102,8 +103,18 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
102
103
  /**
103
104
  * Shows the spinner
104
105
  */
105
- show() {
106
- this.showTimeout = setTimeout(() => this.$el.show(), 300);
106
+ show(delay = 300) {
107
+ trace(`${T} show`);
108
+ if (this.showTimeout === null) {
109
+ if (this.hideTimeout !== null) {
110
+ clearTimeout(this.hideTimeout);
111
+ this.hideTimeout = null;
112
+ }
113
+ this.showTimeout = setTimeout(() => {
114
+ this.showTimeout = null;
115
+ this.$el.show();
116
+ }, delay);
117
+ }
107
118
  }
108
119
  /**
109
120
  * Hides the spinner
@@ -113,7 +124,12 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
113
124
  clearTimeout(this.showTimeout);
114
125
  this.showTimeout = null;
115
126
  }
116
- this.$el.hide();
127
+ this.hideTimeout = setTimeout(() => {
128
+ this.hideTimeout = null;
129
+ if (this.showTimeout === null) {
130
+ this.$el.hide();
131
+ }
132
+ }, 0);
117
133
  }
118
134
  /**
119
135
  * @internal
@@ -1,8 +1,10 @@
1
- import EventLite from 'event-lite';
1
+ import Events from 'eventemitter3';
2
2
  /**
3
3
  * @internal
4
+ * @deprecated
5
+ * TODO use createMockPlayback() instead
4
6
  */
5
- export declare class _MockPlayback extends EventLite {
7
+ export declare class _MockPlayback extends Events {
6
8
  protected options: any;
7
9
  readonly i18n: any;
8
10
  protected playerError?: any | undefined;
@@ -31,4 +33,66 @@ export declare class _MockPlayback extends EventLite {
31
33
  onResize(): boolean;
32
34
  trigger(event: string, ...args: any[]): void;
33
35
  }
36
+ export declare function createMockCore(options?: Record<string, unknown>, container?: any): Events<string | symbol, any> & {
37
+ el: HTMLDivElement;
38
+ $el: {
39
+ 0: HTMLDivElement;
40
+ append: import("vitest").Mock<(...args: any[]) => any>;
41
+ };
42
+ activePlayback: any;
43
+ activeContainer: any;
44
+ options: {
45
+ [x: string]: unknown;
46
+ };
47
+ configure: import("vitest").Mock<(...args: any[]) => any>;
48
+ getPlugin: import("vitest").Mock<(...args: any[]) => any>;
49
+ load: import("vitest").Mock<(...args: any[]) => any>;
50
+ };
51
+ export declare function createMockPlugin(): Events<string | symbol, any> & {
52
+ enable: import("vitest").Mock<(...args: any[]) => any>;
53
+ disable: import("vitest").Mock<(...args: any[]) => any>;
54
+ };
55
+ export declare function createSpinnerPlugin(): Events<string | symbol, any> & {
56
+ enable: import("vitest").Mock<(...args: any[]) => any>;
57
+ disable: import("vitest").Mock<(...args: any[]) => any>;
58
+ } & {
59
+ show: import("vitest").Mock<(...args: any[]) => any>;
60
+ hide: import("vitest").Mock<(...args: any[]) => any>;
61
+ };
62
+ export declare function createMockPlayback(name?: string): Events<string | symbol, any> & {
63
+ name: string;
64
+ currentLevel: number;
65
+ levels: never[];
66
+ consent(): void;
67
+ play(): void;
68
+ pause(): void;
69
+ stop(): void;
70
+ destroy(): void;
71
+ seek(): void;
72
+ seekPercentage(): void;
73
+ getDuration(): 100;
74
+ enterPiP(): void;
75
+ exitPiP(): void;
76
+ getPlaybackType(): "live";
77
+ getStartTimeOffset(): 0;
78
+ getCurrentTime(): 0;
79
+ isHighDefinitionInUse(): false;
80
+ mute(): void;
81
+ unmute(): void;
82
+ volume(): void;
83
+ configure(): void;
84
+ attemptAutoPlay(): true;
85
+ canAutoPlay(): true;
86
+ onResize(): true;
87
+ trigger(event: string, ...args: any[]): void;
88
+ };
89
+ export declare function createMockContainer(playback?: any): Events<string | symbol, any> & {
90
+ $el: {
91
+ html: import("vitest").Mock<(...args: any[]) => any>;
92
+ 0: HTMLDivElement;
93
+ };
94
+ el: HTMLDivElement;
95
+ getPlugin: import("vitest").Mock<(...args: any[]) => any>;
96
+ playback: any;
97
+ };
34
98
  //# sourceMappingURL=testUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAA;AAElC;;GAEG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAExC,SAAS,CAAC,OAAO,EAAE,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG;IAClB,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,GAAG,EACb,IAAI,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,GAAG,YAAA;IAK7B,IAAI,IAAI,WAEP;IAED,OAAO;IAEP,IAAI;IAEJ,KAAK;IAEL,IAAI;IAEJ,OAAO;IAEP,IAAI;IAEJ,cAAc;IAEd,WAAW;IAIX,QAAQ;IAER,OAAO;IAEP,eAAe;IAIf,kBAAkB;IAIlB,cAAc;IAId,qBAAqB;IAIrB,IAAI;IAEJ,MAAM;IAEN,MAAM;IAEN,SAAS;IAET,eAAe;IAIf,WAAW;IAIX,QAAQ;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC"}
1
+ {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,eAAe,CAAA;AAElC;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,MAAM;IAErC,SAAS,CAAC,OAAO,EAAE,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG;IAClB,SAAS,CAAC,WAAW,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,GAAG,EACb,IAAI,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,GAAG,YAAA;IAK7B,IAAI,IAAI,WAEP;IAED,OAAO;IAEP,IAAI;IAEJ,KAAK;IAEL,IAAI;IAEJ,OAAO;IAEP,IAAI;IAEJ,cAAc;IAEd,WAAW;IAIX,QAAQ;IAER,OAAO;IAEP,eAAe;IAIf,kBAAkB;IAIlB,cAAc;IAId,qBAAqB;IAIrB,IAAI;IAEJ,MAAM;IAEN,MAAM;IAEN,SAAS;IAET,eAAe;IAIf,WAAW;IAIX,QAAQ;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;CAGtC;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAAE,SAAS,GAAE,GAA2B;;;;;;;;;;;;;;EAiB3G;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;mBA2C7B,MAAM,WAAW,GAAG,EAAE;EAIxC;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,GAA0B;;;;;;;;EAWvE"}
package/lib/testUtils.js CHANGED
@@ -1,8 +1,11 @@
1
- import EventLite from 'event-lite';
1
+ import Events from 'eventemitter3';
2
+ import { vi } from 'vitest';
2
3
  /**
3
4
  * @internal
5
+ * @deprecated
6
+ * TODO use createMockPlayback() instead
4
7
  */
5
- export class _MockPlayback extends EventLite {
8
+ export class _MockPlayback extends Events {
6
9
  options;
7
10
  i18n;
8
11
  playerError;
@@ -56,3 +59,93 @@ export class _MockPlayback extends EventLite {
56
59
  this.emit(event, ...args);
57
60
  }
58
61
  }
62
+ export function createMockCore(options = {}, container = createMockContainer()) {
63
+ const el = document.createElement('div');
64
+ return Object.assign(new Events(), {
65
+ el,
66
+ $el: {
67
+ [0]: el,
68
+ append: vi.fn(),
69
+ },
70
+ activePlayback: container.playback,
71
+ activeContainer: container,
72
+ options: {
73
+ ...options,
74
+ },
75
+ configure: vi.fn(),
76
+ getPlugin: vi.fn(),
77
+ load: vi.fn(),
78
+ });
79
+ }
80
+ export function createMockPlugin() {
81
+ return Object.assign(new Events(), {
82
+ enable: vi.fn(),
83
+ disable: vi.fn(),
84
+ });
85
+ }
86
+ export function createSpinnerPlugin() {
87
+ return Object.assign(createMockPlugin(), {
88
+ show: vi.fn(),
89
+ hide: vi.fn(),
90
+ });
91
+ }
92
+ export function createMockPlayback(name = 'mock') {
93
+ const emitter = new Events();
94
+ return Object.assign(emitter, {
95
+ name,
96
+ currentLevel: -1,
97
+ levels: [],
98
+ consent() { },
99
+ play() { },
100
+ pause() { },
101
+ stop() { },
102
+ destroy() { },
103
+ seek() { },
104
+ seekPercentage() { },
105
+ getDuration() {
106
+ return 100;
107
+ },
108
+ enterPiP() { },
109
+ exitPiP() { },
110
+ getPlaybackType() {
111
+ return 'live';
112
+ },
113
+ getStartTimeOffset() {
114
+ return 0;
115
+ },
116
+ getCurrentTime() {
117
+ return 0;
118
+ },
119
+ isHighDefinitionInUse() {
120
+ return false;
121
+ },
122
+ mute() { },
123
+ unmute() { },
124
+ volume() { },
125
+ configure() { },
126
+ attemptAutoPlay() {
127
+ return true;
128
+ },
129
+ canAutoPlay() {
130
+ return true;
131
+ },
132
+ onResize() {
133
+ return true;
134
+ },
135
+ trigger(event, ...args) {
136
+ emitter.emit(event, ...args);
137
+ },
138
+ });
139
+ }
140
+ export function createMockContainer(playback = createMockPlayback()) {
141
+ const el = document.createElement('div');
142
+ return Object.assign(new Events(), {
143
+ $el: {
144
+ html: vi.fn(),
145
+ [0]: el,
146
+ },
147
+ el,
148
+ getPlugin: vi.fn(),
149
+ playback,
150
+ });
151
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.20.4",
3
+ "version": "2.20.6",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -42,6 +42,7 @@
42
42
  "@types/node": "^22.10.1",
43
43
  "@types/sinonjs__fake-timers": "^8.1.5",
44
44
  "assert": "^2.1.0",
45
+ "eventemitter3": "^5.0.1",
45
46
  "jsdom": "^26.0.0",
46
47
  "nodemon": "^3.1.9",
47
48
  "rollup": "^4.27.4",
@@ -57,7 +58,6 @@
57
58
  "@gcorevideo/utils": "^0.0.1",
58
59
  "@sentry/types": "^8.47.0",
59
60
  "dashjs": "^4.7.4",
60
- "event-lite": "^1.0.0",
61
61
  "hls.js": "^1.5.17",
62
62
  "human-format": "^1.2.1",
63
63
  "mousetrap": "^1.6.5",
package/src/Player.ts CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from '@clappr/core'
9
9
  import { reportError, trace } from '@gcorevideo/utils'
10
10
  import assert from 'assert'
11
- import EventLite from 'event-lite'
11
+ import EventEmitter from 'eventemitter3'
12
12
 
13
13
  import type {
14
14
  CorePlayerEvents,
@@ -84,7 +84,7 @@ type PluginOptions = Record<string, unknown>
84
84
  export class Player {
85
85
  private config: PlayerConfig = DEFAULT_OPTIONS
86
86
 
87
- private emitter = new EventLite()
87
+ private emitter = new EventEmitter()
88
88
 
89
89
  private player: PlayerClappr | null = null
90
90
 
@@ -1,3 +1,4 @@
1
+ import Events from 'eventemitter3'
1
2
  import {
2
3
  afterEach,
3
4
  beforeEach,
@@ -9,8 +10,6 @@ import {
9
10
  } from 'vitest'
10
11
  import { LogTracer, setTracer } from '@gcorevideo/utils'
11
12
  import { Loader, Player as PlayerClappr } from '@clappr/core'
12
- import EventLite from 'event-lite'
13
-
14
13
  import { Player } from '../Player'
15
14
  import { CorePluginConstructor, TransportPreference } from '../types'
16
15
  import { canPlayDash, canPlayHls } from '../playback'
@@ -18,7 +17,7 @@ import { isDashSource, isHlsSource } from '../utils/mediaSources'
18
17
 
19
18
  function createMockClapprPlayer(): MockedObject<typeof PlayerClappr> {
20
19
  return {
21
- core: Object.assign(new EventLite(), {
20
+ core: Object.assign(new Events(), {
22
21
  activeContainer: null,
23
22
  activePlayback: null,
24
23
  load: vi.fn(),
@@ -0,0 +1,41 @@
1
+ import { ErrorOptions, Events, HTML5Video, PlayerError } from '@clappr/core'
2
+
3
+ import { PlaybackErrorCode } from '../playback.types.js'
4
+
5
+ /**
6
+ * This class adds common behaviors to all playback modules.
7
+ * @internal
8
+ * TODO use custom HTML5Video playback with this layer applied
9
+ */
10
+ export class BasePlayback extends HTML5Video {
11
+ createError(errorData: any, options?: ErrorOptions) {
12
+ const i18n =
13
+ this.i18n ||
14
+ // @ts-ignore
15
+ (this.core && this.core.i18n) ||
16
+ // @ts-ignore
17
+ (this.container && this.container.i18n)
18
+
19
+ if (
20
+ i18n &&
21
+ !errorData.UI &&
22
+ errorData.code === PlaybackErrorCode.MediaSourceUnavailable
23
+ ) {
24
+ const defaultUI = {
25
+ title: i18n.t('no_broadcast'),
26
+ message: '',
27
+ }
28
+ errorData.UI = defaultUI
29
+ }
30
+
31
+ if (errorData.level === PlayerError.Levels.FATAL) {
32
+ this.trigger(Events.PLAYBACK_MEDIACONTROL_DISABLE)
33
+ }
34
+ return super.createError(errorData, options)
35
+ }
36
+
37
+ override _onPlaying() {
38
+ super._onPlaying()
39
+ this.trigger(Events.PLAYBACK_MEDIACONTROL_ENABLE)
40
+ }
41
+ }
@@ -2,15 +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 {
6
- Events,
7
- HTML5Video,
8
- Log,
9
- Playback,
10
- PlayerError,
11
- Utils,
12
- $,
13
- } from '@clappr/core'
5
+ import { Events, Log, Playback, PlayerError, Utils, $ } from '@clappr/core'
14
6
  import { trace } from '@gcorevideo/utils'
15
7
  import assert from 'assert'
16
8
  import DASHJS, {
@@ -32,6 +24,7 @@ import {
32
24
  TimeValue,
33
25
  } from '../../playback.types.js'
34
26
  import { isDashSource } from '../../utils/mediaSources.js'
27
+ import { BasePlayback } from '../BasePlayback.js'
35
28
 
36
29
  const AUTO = -1
37
30
 
@@ -53,7 +46,7 @@ type LocalTimeCorrelation = {
53
46
  const T = 'playback.dash'
54
47
 
55
48
  // @ts-expect-error
56
- export default class DashPlayback extends HTML5Video {
49
+ export default class DashPlayback extends BasePlayback {
57
50
  _levels: QualityLevel[] | null = null
58
51
 
59
52
  _currentLevel: number | null = null
@@ -470,11 +463,14 @@ export default class DashPlayback extends HTML5Video {
470
463
  error: Pick<PlaybackError, 'code' | 'message' | 'description' | 'level'>,
471
464
  ) {
472
465
  trace(`${T} triggerError`, { error })
473
- this.trigger(Events.PLAYBACK_ERROR, {
474
- ...error,
475
- origin: this.name,
476
- scope: DashPlayback.type as PlayerComponentType,
477
- })
466
+
467
+ // this triggers Events.ERROR to be handled by the UI
468
+ this.trigger(
469
+ Events.PLAYBACK_ERROR,
470
+ this.createError(error, {
471
+ useCodePrefix: false,
472
+ }),
473
+ )
478
474
  // only reset the dash player in 10ms async, so that the rest of the
479
475
  // calling function finishes
480
476
  setTimeout(() => {
@@ -525,7 +521,7 @@ export default class DashPlayback extends HTML5Video {
525
521
  )
526
522
  }
527
523
 
528
- _onProgress() {
524
+ override _onProgress() {
529
525
  if (!this._dash) {
530
526
  return
531
527
  }
@@ -558,7 +554,6 @@ export default class DashPlayback extends HTML5Video {
558
554
  if (!this._dash) {
559
555
  return
560
556
  }
561
-
562
557
  super.pause()
563
558
  if (this.dvrEnabled) {
564
559
  this._updateDvr(true)
@@ -1,10 +1,9 @@
1
1
  // Copyright 2014 Globo.com Player authors. All rights reserved.
2
2
  // Use of this source code is governed by a BSD-style
3
- // license that can be found in the LICENSE file.
3
+ // license that can be found on https://github.com/clappr/hlsjs-playback/blob/main/LICENSE
4
4
 
5
5
  import {
6
6
  Events,
7
- HTML5Video,
8
7
  Log,
9
8
  Playback,
10
9
  PlayerError,
@@ -37,10 +36,11 @@ import {
37
36
  import { PlaybackType } from '../../types.js'
38
37
  import { isHlsSource } from '../../utils/mediaSources.js'
39
38
  import { TimerId } from '../../utils/types.js'
39
+ import { BasePlayback } from '../BasePlayback.js'
40
40
 
41
41
  import { CLAPPR_VERSION } from '../../build.js'
42
42
 
43
- const { now, listContainsIgnoreCase } = Utils
43
+ const { now } = Utils
44
44
 
45
45
  const AUTO = -1
46
46
  const DEFAULT_RECOVER_ATTEMPTS = 16
@@ -78,7 +78,7 @@ type CustomListener = {
78
78
  type ErrorInfo = Record<string, unknown>
79
79
 
80
80
  // @ts-expect-error
81
- export default class HlsPlayback extends HTML5Video {
81
+ export default class HlsPlayback extends BasePlayback {
82
82
  private _ccIsSetup = false
83
83
 
84
84
  private _ccTracksUpdated = false
@@ -763,7 +763,7 @@ export default class HlsPlayback extends HTML5Video {
763
763
  this.trigger(Events.PLAYBACK_TIMEUPDATE, update, this.name)
764
764
  }
765
765
 
766
- _onDurationChange() {
766
+ override _onDurationChange() {
767
767
  const duration = this.getDuration()
768
768
 
769
769
  if (this._lastDuration === duration) {
@@ -773,7 +773,7 @@ export default class HlsPlayback extends HTML5Video {
773
773
  super._onDurationChange() // will call _onTimeUpdate
774
774
  }
775
775
 
776
- _onProgress() {
776
+ override _onProgress() {
777
777
  if (!(this.el as HTMLMediaElement).buffered.length) {
778
778
  return
779
779
  }
@@ -1104,7 +1104,9 @@ export default class HlsPlayback extends HTML5Video {
1104
1104
  }
1105
1105
 
1106
1106
  private triggerError(error: PlaybackError) {
1107
- this.trigger(Events.PLAYBACK_ERROR, error)
1107
+ this.trigger(Events.PLAYBACK_ERROR, this.createError(error, {
1108
+ useCodePrefix: false,
1109
+ }))
1108
1110
  this.stop()
1109
1111
  }
1110
1112
  }
@@ -64,15 +64,15 @@ export enum PlaybackErrorCode {
64
64
  /**
65
65
  * An unknown or uncategorised error.
66
66
  */
67
- Generic = 0,
67
+ Generic = 'GENERIC_ERROR',
68
68
  /**
69
69
  * The media source is not available. Typically a network error.
70
70
  */
71
- MediaSourceUnavailable = 1,
71
+ MediaSourceUnavailable = 'MEDIA_SOURCE_UNAVAILABLE',
72
72
  /**
73
73
  * The media source is not accessible due to some protection policy.
74
74
  */
75
- MediaSourceAccessDenied = 3,
75
+ MediaSourceAccessDenied = 'MEDIA_SOURCE_ACCESS_DENIED',
76
76
  }
77
77
 
78
78
  /**
@@ -120,4 +120,12 @@ export interface PlaybackError {
120
120
  * Component subsystem of the error origin
121
121
  */
122
122
  scope: PlayerComponentType
123
+
124
+ /**
125
+ * UI description of the error.
126
+ */
127
+ UI?: {
128
+ title: string
129
+ message: string
130
+ }
123
131
  }
@@ -98,7 +98,7 @@ export class ContextMenu extends UIContainerPlugin {
98
98
  this._url = this.options.contextMenu.url
99
99
  }
100
100
  this.render()
101
- this.bindEvents()
101
+ $('body').on('click', this.hideOnBodyClick)
102
102
  }
103
103
 
104
104
  /**
@@ -111,7 +111,6 @@ export class ContextMenu extends UIContainerPlugin {
111
111
  this.toggleContextMenu,
112
112
  )
113
113
  this.listenTo(this.container, Events.CONTAINER_CLICK, this.hide)
114
- $('body').on('click', this.hideOnBodyClick)
115
114
  }
116
115
 
117
116
  /**