@gcorevideo/player 2.20.4 → 2.20.5

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 (64) hide show
  1. package/assets/error-screen/error_screen.ejs +3 -1
  2. package/dist/core.js +407 -205
  3. package/dist/index.css +1238 -1238
  4. package/dist/index.js +542 -383
  5. package/dist/plugins/index.css +993 -993
  6. package/dist/plugins/index.js +113 -159
  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 +70 -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 +7 -5
  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 +11 -15
  49. package/src/playback/hls-playback/HlsPlayback.ts +7 -5
  50. package/src/playback/utils.ts +2 -0
  51. package/src/playback.types.ts +11 -3
  52. package/src/plugins/context-menu/ContextMenu.ts +1 -2
  53. package/src/plugins/error-screen/ErrorScreen.ts +121 -195
  54. package/src/plugins/error-screen/__tests__/ErrorScreen.test.ts +113 -0
  55. package/src/plugins/error-screen/__tests__/__snapshots__/ErrorScreen.test.ts.snap +20 -0
  56. package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +32 -57
  57. package/src/plugins/media-control/MediaControl.ts +8 -5
  58. package/src/plugins/multi-camera/MultiCamera.ts +2 -3
  59. package/src/plugins/poster/Poster.ts +1 -1
  60. package/src/plugins/source-controller/SourceController.ts +20 -14
  61. package/src/plugins/source-controller/__tests__/SourceController.test.ts +29 -46
  62. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +20 -3
  63. package/src/testUtils.ts +100 -3
  64. package/tsconfig.tsbuildinfo +1 -1
@@ -1,246 +1,172 @@
1
- import { UICorePlugin, Events, template, PlayerError } from '@clappr/core';
2
- import { trace } from '@gcorevideo/utils';
3
-
4
- import { CLAPPR_VERSION } from '../../build.js';
5
- import type { TimerId, ZeptoResult } from '../../utils/types.js';
6
-
7
- import reloadIcon from '../../../assets/icons/old/reload.svg';
8
- import templateHtml from '../../../assets/error-screen/error_screen.ejs';
9
- import '../../../assets/error-screen/error_screen.scss';
10
-
11
- const TIME_FOR_UPDATE = 10000;
12
- const MAX_RETRY = 10;
1
+ import { UICorePlugin, Events, template } from '@clappr/core'
2
+ import { trace } from '@gcorevideo/utils'
3
+
4
+ import { CLAPPR_VERSION } from '../../build.js'
5
+
6
+ import reloadIcon from '../../../assets/icons/old/reload.svg'
7
+ import templateHtml from '../../../assets/error-screen/error_screen.ejs'
8
+ import '../../../assets/error-screen/error_screen.scss'
9
+ import { PlaybackErrorCode } from '../../playback.types.js'
10
+
11
+ export type ErrorDesc = {
12
+ description: string
13
+ level: string
14
+ code: string
15
+ origin: string
16
+ scope: string
17
+ raw?: string
18
+ UI?: {
19
+ icon?: string
20
+ title: string
21
+ message: string
22
+ }
23
+ }
13
24
 
14
- type ErrorObject = {
15
- description: string;
16
- details?: string;
17
- level: string;
18
- code: string;
19
- origin: string;
25
+ type ErrorScreenDesc = {
26
+ title: string
27
+ message: string
28
+ code: string
29
+ icon?: string
20
30
  }
21
31
 
22
- type PresentationalError = {
23
- title: string;
24
- message: string;
25
- code: string;
26
- icon: string;
27
- reloadIcon: string;
32
+ export type ErrorScreenPluginSettings = {
33
+ noReload?: boolean
28
34
  }
29
35
 
30
36
  const T = 'plugins.error_screen'
31
37
 
32
38
  /**
33
- * Displays a descriptive error in the overlay on top of the player.
39
+ * Displays an error nicely in the overlay on top of the player.
34
40
  * @beta
35
41
  */
36
42
  export class ErrorScreen extends UICorePlugin {
37
- private _retry = 0;
38
-
39
- private err: PresentationalError | null = null;
40
-
41
- private hideValue = false;
42
-
43
- private timeout: TimerId | null = null;
44
-
45
- private reloadButton: ZeptoResult | null = null;
43
+ private err: ErrorScreenDesc | null = null
46
44
 
45
+ /**
46
+ * @internal
47
+ */
47
48
  get name() {
48
- return 'error_gplayer';
49
+ return 'error_screen'
49
50
  }
50
51
 
52
+ /**
53
+ * @internal
54
+ */
51
55
  get supportedVersion() {
52
- return { min: CLAPPR_VERSION };
56
+ return { min: CLAPPR_VERSION }
53
57
  }
54
58
 
55
- get template() {
56
- return template(templateHtml);
57
- }
58
-
59
- get container() {
60
- return this.core.activeContainer;
61
- }
59
+ private static readonly template = template(templateHtml)
62
60
 
61
+ /**
62
+ * @internal
63
+ */
63
64
  override get attributes() {
64
65
  return {
65
- 'class': 'player-error-screen',
66
+ class: 'player-error-screen',
66
67
  'data-error-screen': '',
67
- };
68
+ }
68
69
  }
69
70
 
71
+ /**
72
+ * @internal
73
+ */
70
74
  override bindEvents() {
71
- this.listenTo(this.core, Events.ERROR, this.onError);
72
- this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
73
- this.listenTo(this.core, 'core:advertisement:start', this.onStartAd);
74
- this.listenTo(this.core, 'core:advertisement:finish', this.onFinishAd);
75
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, this.onContainerChanged);
76
- }
77
-
78
- private onCoreReady() {
79
- trace(`${T} onCoreReady`)
80
- if (this.core.activePlayback) {
81
- this.listenTo(this.core.activePlayback, Events.PLAYBACK_PLAY, this.onPlay);
82
- }
75
+ this.listenTo(this.core, Events.ERROR, this.onError)
76
+ this.listenTo(
77
+ this.core,
78
+ Events.CORE_ACTIVE_CONTAINER_CHANGED,
79
+ this.onActiveContainerChanged,
80
+ )
83
81
  }
84
82
 
85
83
  private onPlay() {
86
84
  trace(`${T} onPlay`)
87
- this.destroyError();
88
- }
89
-
90
- private destroyError() {
91
- trace(`${T} destroyError`)
92
- this._retry = 0;
93
- this.err = null;
94
- if (this.timeout !== null) {
95
- clearTimeout(this.timeout);
96
- this.timeout = null;
97
- }
98
- this.$el.hide();
99
- }
100
-
101
- unBindEvents() {
102
- // @ts-ignore
103
- this.stopListening(this.core, 'core:advertisement:start');
104
- // @ts-ignore
105
- this.stopListening(this.core, 'core:advertisement:finish');
106
- // @ts-ignore
107
- this.stopListening(this.core, Events.ERROR);
85
+ this.unmount()
108
86
  }
109
87
 
110
- private bindReload() {
111
- this.reloadButton = this.$el.find('.player-error-screen__reload');
112
- this.reloadButton && this.reloadButton.on('click', this.reload.bind(this));
88
+ private unmount() {
89
+ trace(`${T} unmount`)
90
+ this.err = null
91
+ this.$el.empty()
92
+ // this.$el.hide();
113
93
  }
114
94
 
115
- private reload() {
116
- this._retry++;
117
- this.core.configure({
118
- ...this.options,
119
- autoPlay: true
120
- });
121
- this.core.activeContainer.mediaControlDisabled = false;
122
- this.unbindReload();
123
- }
124
-
125
- private unbindReload() {
126
- this.reloadButton && this.reloadButton.off('click');
127
- }
128
-
129
- private onContainerChanged() {
130
- this.err = null;
131
- if (this.core.getPlugin('error_screen')) {
132
- this.core.getPlugin('error_screen').disable();
133
- }
134
- this.unbindReload();
135
- this.hide();
136
- }
137
-
138
- private onStartAd() {
139
- this.hideValue = true;
140
- if (this.err) {
141
- this.hide();
95
+ /**
96
+ * @internal
97
+ */
98
+ override get events() {
99
+ return {
100
+ 'click .player-error-screen__reload': 'reload',
142
101
  }
143
102
  }
144
103
 
145
- private onFinishAd() {
146
- this.hideValue = false;
147
- if (this.err) {
148
- this.container.disableMediaControl();
149
- this.container.stop();
150
- this.show();
104
+ private reload() {
105
+ setTimeout(() => {
106
+ this.core.configure({
107
+ reloading: true,
108
+ source: this.core.options.source,
109
+ sources: this.core.options.sources,
110
+ })
111
+ }, 0)
112
+ }
113
+
114
+ private onActiveContainerChanged() {
115
+ trace(`${T} onActiveContainerChanged`, {
116
+ reloading: this.core.options.reloading,
117
+ })
118
+ this.err = null
119
+ this.listenTo(
120
+ this.core.activeContainer.playback,
121
+ Events.PLAYBACK_PLAY,
122
+ this.onPlay,
123
+ )
124
+ if (this.core.options.reloading) {
125
+ setTimeout(() => {
126
+ this.core.options.reloading = false
127
+ this.unmount()
128
+ this.core.activeContainer.play({
129
+ reloading: true,
130
+ })
131
+ }, 0)
151
132
  }
152
133
  }
153
134
 
154
- private onError(err: ErrorObject) {
135
+ private onError(err: ErrorDesc) {
155
136
  trace(`${T} onError`, { err })
156
- if (
157
- err.level === PlayerError.Levels.FATAL ||
158
- err.details === 'bufferStalledError' ||
159
- err.details === 'manifestParsingError'
160
- ) {
161
- this.err = {
162
- title: this.core.i18n.t('no_broadcast'),
163
- message: '',
164
- code: '',
165
- // icon: (this.err.UI && this.err.UI.icon) || '',
166
- icon: '',
167
- reloadIcon,
168
- };
169
-
170
- if (this.options.errorScreen?.reloadOnError === false) {
171
- return;
172
- }
173
-
174
- if (this.options.errorScreen?.neverStopToRetry) {
175
- this._retry = 0;
176
- }
177
-
178
- if (this._retry >= MAX_RETRY) {
179
- this.drying();
180
-
181
- return;
137
+ if (err.UI) {
138
+ if (this.err) {
139
+ this.unmount()
182
140
  }
183
-
184
- const ctp = this.container.getPlugin('click_to_pause_custom');
185
-
186
- const toggleCTP = !!ctp?.enabled;
187
- if (toggleCTP) {
188
- // clickToPausePlugin.afterEnabled = true;
189
- ctp.disable();
190
- }
191
-
192
- this.timeout = setTimeout(() => {
193
- if (toggleCTP) {
194
- ctp.enable();
195
- }
196
- this.reload();
197
- }, TIME_FOR_UPDATE);
198
-
199
- const spinnerPlugin = this.container.getPlugin('spinner');
200
- if (spinnerPlugin) {
201
- spinnerPlugin.show(); // TODO remove?
202
- setTimeout(() => spinnerPlugin.show(), 0);
141
+ this.err = {
142
+ title: err.UI.title,
143
+ message: err.UI.message,
144
+ code: err.code,
145
+ icon: err.UI.icon,
203
146
  }
147
+ this.render()
204
148
  }
205
149
  }
206
150
 
207
- private drying() {
208
- const spinnerPlugin = this.container.getPlugin('spinner');
209
-
210
- spinnerPlugin?.hide();
211
-
212
- this._retry = 0;
213
- if (!this.hideValue) {
214
- this.container.disableMediaControl();
215
- this.container.stop();
216
- this.show();
217
- }
218
- }
219
-
220
- show(err?: PresentationalError) {
221
- if (err) {
222
- this.err = err;
223
- }
224
- // TODO use container.disableMediaControl() instead
225
- this.core.mediaControl.disable();
226
- this.render();
227
- this.$el.show();
228
- }
229
-
230
- hide() {
231
- this.$el.hide();
232
- }
233
-
151
+ /**
152
+ * @internal
153
+ */
234
154
  override render() {
235
155
  if (!this.err) {
236
- return this;
156
+ return this
157
+ }
158
+ this.$el.html(
159
+ ErrorScreen.template({
160
+ ...this.err,
161
+ reloadIcon: this.options.errorScreen?.noReload ? null : reloadIcon,
162
+ }),
163
+ )
164
+
165
+ // TODO append to container instead of core?
166
+ if (!this.el.parentElement) {
167
+ this.core.$el.append(this.el)
237
168
  }
238
- this.$el.html(this.template(this.err));
239
-
240
- this.core.$el.append(this.el);
241
-
242
- this.bindReload();
243
169
 
244
- return this;
170
+ return this
245
171
  }
246
172
  }
@@ -0,0 +1,113 @@
1
+ import { createMockContainer, createMockCore, createMockPlayback } from '../../../testUtils'
2
+ import { ErrorScreen } from '../ErrorScreen'
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ describe('ErrorScreen', () => {
6
+ let core: any
7
+ let container: any
8
+ let playback: any
9
+ let errorScreen: ErrorScreen
10
+ beforeEach(() => {
11
+ core = createMockCore()
12
+ container = createMockContainer()
13
+ playback = createMockPlayback()
14
+ container.playback = playback
15
+ core.activeContainer = container
16
+ })
17
+ describe('on error', () => {
18
+ describe.each([
19
+ [
20
+ 'targeted at UI',
21
+ {
22
+ code: "foo",
23
+ UI: {
24
+ title: 'My error',
25
+ message: 'Lorem ipsum dolor sit amet',
26
+ icon: '<svg>...</svg>'
27
+ },
28
+ },
29
+ true,
30
+ ],
31
+ [
32
+ 'other',
33
+ {
34
+ code: 'MEDIA_SOURCE_ACCESS_DENIED',
35
+ title: 'The server refused to serve the protected media',
36
+ message: 'Wrong credentials',
37
+ },
38
+ false,
39
+ ],
40
+ ])("%s", (_, err, shouldRender) => {
41
+ beforeEach(() => {
42
+ errorScreen = new ErrorScreen(core)
43
+ core.emit('core:ready')
44
+ core.emit('core:active:container:changed')
45
+ core.emit('error', err)
46
+ })
47
+ if (shouldRender) {
48
+ it('should render', () => {
49
+ expect(errorScreen.el.innerHTML).toBeTruthy()
50
+ expect(errorScreen.el.innerHTML).toMatchSnapshot()
51
+ })
52
+ } else {
53
+ it('should not render', () => {
54
+ expect(errorScreen.el.innerHTML).toBeFalsy()
55
+ })
56
+ }
57
+ })
58
+ describe('reload button', () => {
59
+ describe('basically', () => {
60
+ beforeEach(() => {
61
+ core.options.source = 'https://222/master.mpd'
62
+ errorScreen = new ErrorScreen(core)
63
+ core.emit('core:ready')
64
+ core.emit('core:active:container:changed')
65
+ core.emit('error', {
66
+ code: 'foo',
67
+ UI: {
68
+ title: 'My error',
69
+ message: 'Lorem ipsum dolor sit amet',
70
+ icon: '<svg>...</svg>'
71
+ },
72
+ })
73
+ core.configure.mockClear()
74
+ })
75
+ describe('when clicked', () => {
76
+ beforeEach(async () => {
77
+ (errorScreen.el.querySelector('.player-error-screen__reload') as HTMLElement)?.click()
78
+ return new Promise(resolve => setTimeout(resolve, 0))
79
+ })
80
+ it('should reload the player', () => {
81
+ expect(core.configure).toHaveBeenCalledWith(expect.objectContaining({
82
+ reloading: true,
83
+ source: 'https://222/master.mpd'
84
+ }))
85
+ })
86
+ })
87
+ })
88
+ describe('when disabled', () => {
89
+ beforeEach(() => {
90
+ core.options.errorScreen = {
91
+ noReload: true
92
+ }
93
+ errorScreen = new ErrorScreen(core)
94
+ core.emit('core:ready')
95
+ core.emit('core:active:container:changed')
96
+ core.emit('error', {
97
+ code: 'foo',
98
+ UI: {
99
+ title: 'My error',
100
+ message: 'Lorem ipsum dolor sit amet',
101
+ icon: '<svg>...</svg>'
102
+ },
103
+ })
104
+ })
105
+ it('should not render the reload button', () => {
106
+ expect(errorScreen.el.querySelector('.player-error-screen__reload')).toBeNull()
107
+ })
108
+ })
109
+ })
110
+ })
111
+ })
112
+
113
+
@@ -0,0 +1,20 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`ErrorScreen > on error > targeted at UI > should render 1`] = `
4
+ "<div class="player-error-screen__content" data-error-screen="">
5
+
6
+ <div class="player-error-screen__icon" data-error-screen=""><svg>...</svg></div>
7
+
8
+ <div class="player-error-screen__title" data-error-screen="">My error</div>
9
+
10
+ <div class="player-error-screen__message" data-error-screen="">Lorem ipsum dolor sit amet</div>
11
+
12
+
13
+ <div class="player-error-screen__code" data-error-screen="">Error code: foo</div>
14
+
15
+
16
+ <div class="player-error-screen__reload" data-error-screen="">/assets/icons/old/reload.svg</div>
17
+
18
+ </div>
19
+ "
20
+ `;
@@ -1,9 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
2
  import { UICorePlugin } from '@clappr/core'
3
3
  import FakeTimers from '@sinonjs/fake-timers'
4
- import EventLite from 'event-lite'
5
4
  import { Logger, LogTracer, setTracer } from '@gcorevideo/utils'
6
5
  import { LevelSelector } from '../LevelSelector.js'
6
+ import { createMockCore, createMockPlayback } from '../../../testUtils.js'
7
7
 
8
8
  setTracer(new LogTracer('LevelSelector.test'))
9
9
  Logger.enable('*')
@@ -41,28 +41,25 @@ describe('LevelSelector', () => {
41
41
  })
42
42
  describe('basically', () => {
43
43
  beforeEach(() => {
44
- const activeContainer = createContainer()
45
- activePlayback = createPlayback()
44
+ // const activeContainer = createMockContainer()
46
45
  let mediaControl: UICorePlugin | null = null
47
46
  let bottomGear: UICorePlugin | null = null
48
- core = Object.assign(new EventLite(), {
49
- activeContainer,
50
- activePlayback,
51
- options: {
52
- levelSelector: {
53
- // restrictResolution: 360,
54
- labels: { 360: '360p', 720: 'HD' },
55
- },
47
+ // TODO create mock core
48
+ core = createMockCore({
49
+ levelSelector: {
50
+ // restrictResolution: 360,
51
+ labels: { 360: '360p', 720: 'HD' },
56
52
  },
57
- getPlugin: vi.fn().mockImplementation((name: string) => {
58
- if (name === 'media_control') {
59
- return mediaControl
60
- }
61
- if (name === 'bottom_gear') {
62
- return bottomGear
63
- }
64
- return null
65
- }),
53
+ })
54
+ activePlayback = core.activePlayback
55
+ core.getPlugin.mockImplementation((name: string) => {
56
+ if (name === 'media_control') {
57
+ return mediaControl
58
+ }
59
+ if (name === 'bottom_gear') {
60
+ return bottomGear
61
+ }
62
+ return null
66
63
  })
67
64
  mediaControl = createMediaControl(core)
68
65
  bottomGear = createBottomGear(core)
@@ -75,7 +72,7 @@ describe('LevelSelector', () => {
75
72
  activePlayback.emit('playback:levels:available', LEVELS)
76
73
  await clock.tickAsync(1)
77
74
  })
78
- it('should render the proper level label', () => {
75
+ it('should render proper level label', () => {
79
76
  // @ts-ignore
80
77
  expect(levelSelector.el.textContent).toMatchQualityLevelLabel('Auto')
81
78
  })
@@ -111,28 +108,23 @@ describe('LevelSelector', () => {
111
108
  })
112
109
  describe('options.restrictResolution', () => {
113
110
  beforeEach(() => {
114
- const activeContainer = createContainer()
115
- activePlayback = createPlayback()
116
111
  let mediaControl: UICorePlugin | null = null
117
112
  let bottomGear: UICorePlugin | null = null
118
- core = Object.assign(new EventLite(), {
119
- activeContainer,
120
- activePlayback,
121
- options: {
122
- levelSelector: {
123
- restrictResolution: 360,
124
- labels: { 360: '360p', 720: '720p' },
125
- },
113
+ core = createMockCore({
114
+ levelSelector: {
115
+ restrictResolution: 360,
116
+ labels: { 360: '360p', 720: '720p' },
126
117
  },
127
- getPlugin: vi.fn().mockImplementation((name: string) => {
128
- if (name === 'media_control') {
129
- return mediaControl
130
- }
131
- if (name === 'bottom_gear') {
132
- return bottomGear
133
- }
134
- return null
135
- }),
118
+ })
119
+ activePlayback = core.activePlayback
120
+ core.getPlugin.mockImplementation((name: string) => {
121
+ if (name === 'media_control') {
122
+ return mediaControl
123
+ }
124
+ if (name === 'bottom_gear') {
125
+ return bottomGear
126
+ }
127
+ return null
136
128
  })
137
129
  mediaControl = createMediaControl(core)
138
130
  bottomGear = createBottomGear(core)
@@ -196,23 +188,6 @@ describe('LevelSelector', () => {
196
188
  })
197
189
  })
198
190
 
199
- function createContainer() {
200
- const container = Object.assign(new EventLite(), {
201
- $el: {
202
- html: vi.fn(),
203
- },
204
- })
205
- return container
206
- }
207
-
208
- function createPlayback() {
209
- const playback = Object.assign(new EventLite(), {
210
- currentLevel: -1,
211
- levels: [],
212
- })
213
- return playback
214
- }
215
-
216
191
  expect.extend({
217
192
  toMatchQualityLevelLabel(received, expected) {
218
193
  const { isNot } = this
@@ -13,7 +13,8 @@ import {
13
13
  $,
14
14
  Core,
15
15
  } from '@clappr/core'
16
- import { reportError } from '@gcorevideo/utils'
16
+ import { reportError, trace } from '@gcorevideo/utils'
17
+
17
18
  import { type TimeProgress } from '../../playback.types.js'
18
19
 
19
20
  import { Kibo } from '../kibo/index.js'
@@ -417,9 +418,10 @@ export class MediaControl extends UICorePlugin {
417
418
  }
418
419
 
419
420
  /**
420
- * Disables the plugin and unmounts its UI
421
+ * Hides the media control UI
421
422
  */
422
423
  override disable() {
424
+ trace(`${T} disable`)
423
425
  this.userDisabled = true
424
426
  this.hide()
425
427
  this.unbindKeyEvents()
@@ -430,6 +432,7 @@ export class MediaControl extends UICorePlugin {
430
432
  * Reenables the plugin disabled earlier with the {@link MediaControl.disable} method
431
433
  */
432
434
  override enable() {
435
+ trace(`${T} enable`)
433
436
  if (this.options.chromeless) {
434
437
  return
435
438
  }
@@ -1273,14 +1276,14 @@ export class MediaControl extends UICorePlugin {
1273
1276
  override destroy() {
1274
1277
  $(document).unbind('mouseup', this.stopDrag)
1275
1278
  $(document).unbind('mousemove', this.updateDrag)
1279
+ $(document).unbind('touchend', this.stopDrag)
1280
+ $(document).unbind('touchmove', this.updateDrag)
1276
1281
  this.unbindKeyEvents()
1277
- // @ts-ignore
1278
- this.stopListening()
1279
1282
  return super.destroy()
1280
1283
  }
1281
1284
 
1282
1285
  private configure() {
1283
- this.advertisementPlaying ? this.disable() : this.enable()
1286
+ // this.advertisementPlaying ? this.disable() : this.enable()
1284
1287
  this.trigger(Events.MEDIACONTROL_OPTIONS_CHANGE)
1285
1288
  }
1286
1289
 
@@ -320,13 +320,12 @@ export class MultiCamera extends UICorePlugin {
320
320
  } catch (error) {
321
321
  reportError(error);
322
322
  }
323
- // TODO figure out
324
- this.core.getPlugin('error_gplayer')?.show({
323
+ // TODO trigger error instead
324
+ this.core.getPlugin('error_screen')?.show({
325
325
  title: this.core.i18n.t('source_offline'),
326
326
  message: '',
327
327
  code: '',
328
328
  icon: '',
329
- reloadIcon: '',
330
329
  });
331
330
  }
332
331