@gcorevideo/player 2.22.14 → 2.22.16

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 (80) hide show
  1. package/assets/clips/clips.ejs +1 -0
  2. package/assets/clips/clips.scss +23 -3
  3. package/assets/context-menu/context_menu.ejs +14 -6
  4. package/assets/context-menu/context_menu.scss +18 -4
  5. package/assets/level-selector/list.ejs +9 -3
  6. package/assets/media-control/media-control.ejs +1 -9
  7. package/assets/media-control/media-control.scss +0 -25
  8. package/assets/media-control/width370.scss +4 -4
  9. package/dist/core.js +5 -23
  10. package/dist/index.css +424 -412
  11. package/dist/index.js +294 -286
  12. package/dist/player.d.ts +211 -144
  13. package/dist/plugins/index.css +1513 -1501
  14. package/dist/plugins/index.js +224 -227
  15. package/docs/api/{player.audioselector.md → player.audiotracks.md} +3 -3
  16. package/docs/api/player.contextmenu.md +2 -0
  17. package/docs/api/player.contextmenupluginsettings.md +2 -40
  18. package/docs/api/{player.contextmenupluginsettings.label.md → player.contextmenupluginsettings.options.md} +3 -3
  19. package/docs/api/player.md +78 -23
  20. package/docs/api/player.mediacontrol.md +8 -14
  21. package/docs/api/player.mediacontrolelement.md +4 -2
  22. package/docs/api/{player.contextmenupluginsettings.preventshowcontextmenu.md → player.mediacontrollayerelement.md} +5 -3
  23. package/docs/api/player.mediacontrolleftelement.md +16 -0
  24. package/docs/api/player.mediacontrolrightelement.md +16 -0
  25. package/docs/api/player.mediacontrolsettings.md +23 -0
  26. package/docs/api/{player.contextmenupluginsettings.url.md → player.menuoption.md} +10 -3
  27. package/docs/api/player.playbackrate.md +1 -1
  28. package/docs/api/player.playerconfig.md +1 -1
  29. package/docs/api/player.playerconfig.playbacktype.md +1 -1
  30. package/docs/api/{player.levelselector.events.md → player.qualitylevels.events.md} +2 -2
  31. package/docs/api/{player.levelselector.md → player.qualitylevels.md} +6 -6
  32. package/docs/api/{player.levelselectorpluginsettings.labels.md → player.qualitylevelspluginsettings.labels.md} +2 -2
  33. package/docs/api/{player.levelselectorpluginsettings.md → player.qualitylevelspluginsettings.md} +6 -6
  34. package/docs/api/{player.levelselectorpluginsettings.restrictresolution.md → player.qualitylevelspluginsettings.restrictresolution.md} +2 -2
  35. package/lib/Player.d.ts.map +1 -1
  36. package/lib/Player.js +4 -1
  37. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  38. package/lib/playback/hls-playback/HlsPlayback.js +0 -21
  39. package/lib/plugins/click-to-pause/ClickToPause.js +1 -1
  40. package/lib/plugins/clips/Clips.d.ts +21 -16
  41. package/lib/plugins/clips/Clips.d.ts.map +1 -1
  42. package/lib/plugins/clips/Clips.js +96 -98
  43. package/lib/plugins/clips/types.d.ts +19 -0
  44. package/lib/plugins/clips/types.d.ts.map +1 -0
  45. package/lib/plugins/clips/types.js +1 -0
  46. package/lib/plugins/clips/utils.d.ts +4 -0
  47. package/lib/plugins/clips/utils.d.ts.map +1 -0
  48. package/lib/plugins/clips/utils.js +36 -0
  49. package/lib/plugins/context-menu/ContextMenu.d.ts +33 -12
  50. package/lib/plugins/context-menu/ContextMenu.d.ts.map +1 -1
  51. package/lib/plugins/context-menu/ContextMenu.js +40 -37
  52. package/lib/plugins/media-control/MediaControl.d.ts +4 -7
  53. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  54. package/lib/plugins/media-control/MediaControl.js +19 -31
  55. package/lib/plugins/utils.d.ts +9 -1
  56. package/lib/plugins/utils.d.ts.map +1 -1
  57. package/lib/plugins/utils.js +9 -10
  58. package/lib/plugins/vast-ads/loaderxml.js +2 -2
  59. package/lib/testUtils.d.ts.map +1 -1
  60. package/lib/testUtils.js +2 -5
  61. package/package.json +1 -1
  62. package/src/Player.ts +4 -3
  63. package/src/playback/hls-playback/HlsPlayback.ts +0 -22
  64. package/src/plugins/click-to-pause/ClickToPause.ts +1 -1
  65. package/src/plugins/clips/Clips.ts +116 -135
  66. package/src/plugins/clips/__tests__/Clips.test.ts +72 -0
  67. package/src/plugins/clips/__tests__/__snapshots__/Clips.test.ts.snap +14 -0
  68. package/src/plugins/clips/types.ts +22 -0
  69. package/src/plugins/clips/utils.ts +54 -0
  70. package/src/plugins/context-menu/ContextMenu.ts +72 -56
  71. package/src/plugins/level-selector/__tests__/__snapshots__/QualityLevels.test.ts.snap +18 -18
  72. package/src/plugins/media-control/MediaControl.ts +31 -58
  73. package/src/plugins/media-control/__tests__/MediaControl.test.ts +66 -30
  74. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +7 -35
  75. package/src/plugins/utils.ts +9 -7
  76. package/src/plugins/vast-ads/loaderxml.ts +2 -2
  77. package/src/testUtils.ts +2 -5
  78. package/temp/player.api.json +332 -262
  79. package/tsconfig.tsbuildinfo +1 -1
  80. package/docs/api/player.mediacontrol.handlecustomarea.md +0 -52
@@ -56,14 +56,26 @@ describe('MediaControl', () => {
56
56
  core.activePlayback.emit(Events.PLAYBACK_LOADEDMETADATA)
57
57
  core.activeContainer.emit(Events.CONTAINER_LOADEDMETADATA)
58
58
  })
59
- it('should wait a delay before rendering anything', async () => {
59
+ it('should wait a delay before rendering anything', async () => {
60
60
  expect(mediaControl.el.innerHTML).toBe('')
61
61
  await new Promise((resolve) => setTimeout(resolve, 35))
62
62
  expect(mediaControl.el.innerHTML).toMatchSnapshot()
63
- expect(mediaControl.$el.find('.media-control-left-panel [data-playpause]').length).toBeGreaterThan(0)
64
- expect(mediaControl.$el.find('.media-control-left-panel [data-volume]').length).toBeGreaterThan(0)
65
- expect(mediaControl.$el.find('.media-control-center-panel [data-seekbar]').length).toBeGreaterThan(0)
66
- expect(mediaControl.$el.find('.media-control-right-panel [data-fullscreen]').length).toBeGreaterThan(0)
63
+ expect(
64
+ mediaControl.$el.find('.media-control-left-panel [data-playpause]')
65
+ .length,
66
+ ).toBeGreaterThan(0)
67
+ expect(
68
+ mediaControl.$el.find('.media-control-left-panel [data-volume]')
69
+ .length,
70
+ ).toBeGreaterThan(0)
71
+ expect(
72
+ mediaControl.$el.find('.media-control-center-panel [data-seekbar]')
73
+ .length,
74
+ ).toBeGreaterThan(0)
75
+ expect(
76
+ mediaControl.$el.find('.media-control-right-panel [data-fullscreen]')
77
+ .length,
78
+ ).toBeGreaterThan(0)
67
79
  })
68
80
  })
69
81
  })
@@ -72,9 +84,7 @@ describe('MediaControl', () => {
72
84
  mediaControl = new MediaControl(core)
73
85
  core.emit(Events.CORE_READY)
74
86
  core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
75
- core.activePlayback.emit(Events.PLAYBACK_LOADEDMETADATA)
76
- core.activeContainer.emit(Events.CONTAINER_LOADEDMETADATA)
77
- await new Promise((resolve) => setTimeout(resolve, 25))
87
+ await runMetadataLoaded(core)
78
88
  })
79
89
  describe.each([
80
90
  [
@@ -92,20 +102,29 @@ describe('MediaControl', () => {
92
102
  core.activePlayback.emit(Events.PLAYBACK_SETTINGSUPDATE)
93
103
  core.activeContainer.emit(Events.CONTAINER_SETTINGSUPDATE)
94
104
  })
95
- it.each(
96
- settings.left
97
- )("should render %s control", (element) => {
98
- const el = mediaControl.$el.find(`.media-control-left-panel [data-${element}]`)
105
+ it.each(settings.left)('should render %s control', (element) => {
106
+ const el = mediaControl.$el.find(
107
+ `.media-control-left-panel [data-${element}]`,
108
+ )
99
109
  expect(el.length).toBeGreaterThan(0)
100
110
  })
101
111
  it.each(
102
- arraySubtract(['playpause', 'playstop', 'position', 'duration', 'volume'], settings.left)
103
- )("should not render %s control", (element) => {
104
- const el = mediaControl.$el.find(`.media-control-left-panel [data-${element}]`)
112
+ arraySubtract(
113
+ ['playpause', 'playstop', 'position', 'duration', 'volume'],
114
+ settings.left,
115
+ ),
116
+ )('should not render %s control', (element) => {
117
+ const el = mediaControl.$el.find(
118
+ `.media-control-left-panel [data-${element}]`,
119
+ )
105
120
  expect(el.length).toEqual(0)
106
121
  })
107
- it(`should ${settings.seekEnabled ? '' : 'not '}render the seek bar`, () => {
108
- const seekbar = mediaControl.$el.find('.media-control-center-panel [data-seekbar]')
122
+ it(`should ${
123
+ settings.seekEnabled ? '' : 'not '
124
+ }render the seek bar`, () => {
125
+ const seekbar = mediaControl.$el.find(
126
+ '.media-control-center-panel [data-seekbar]',
127
+ )
109
128
  expect(seekbar.length).toBeGreaterThan(1)
110
129
  if (settings.seekEnabled) {
111
130
  expect(seekbar.hasClass('seek-disabled')).toBe(false)
@@ -113,7 +132,9 @@ describe('MediaControl', () => {
113
132
  expect(seekbar.hasClass('seek-disabled')).toBe(true)
114
133
  }
115
134
  })
116
- it(`should ${settings.left.includes('volume') ? '' : 'not '}render the volume control`, () => {
135
+ it(`should ${
136
+ settings.left.includes('volume') ? '' : 'not '
137
+ }render the volume control`, () => {
117
138
  const volume = mediaControl.$el.find('.drawer-container[data-volume]')
118
139
  if (settings.left.includes('volume')) {
119
140
  expect(volume.length).toEqual(1)
@@ -121,8 +142,12 @@ describe('MediaControl', () => {
121
142
  expect(volume.length).toEqual(0)
122
143
  }
123
144
  })
124
- it(`should ${settings.right.includes('fullscreen') ? '' : 'not '}render the fullscreen control`, () => {
125
- const fullscreen = mediaControl.$el.find('.media-control-right-panel [data-fullscreen]')
145
+ it(`should ${
146
+ settings.right.includes('fullscreen') ? '' : 'not '
147
+ }render the fullscreen control`, () => {
148
+ const fullscreen = mediaControl.$el.find(
149
+ '.media-control-right-panel [data-fullscreen]',
150
+ )
126
151
  if (settings.right.includes('fullscreen')) {
127
152
  expect(fullscreen.length).toEqual(1)
128
153
  } else {
@@ -138,12 +163,13 @@ describe('MediaControl', () => {
138
163
  core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
139
164
  })
140
165
  describe('when live', () => {
141
- beforeEach(() => {
166
+ beforeEach(async () => {
142
167
  core.activeContainer.getPlaybackType.mockReturnValue(Playback.LIVE)
143
168
  core.activePlayback.getPlaybackType.mockReturnValue(Playback.LIVE)
144
169
  core.getPlaybackType.mockReturnValue(Playback.LIVE)
145
- core.activeContainer.emit(Events.CONTAINER_LOADEDMETADATA)
146
- core.activePlayback.emit(Events.PLAYBACK_LOADEDMETADATA)
170
+ // This is not strictly necessary as the CSS class is applied on the root element and does not require rendering.
171
+ // However, it makes the scenario more realistic
172
+ await runMetadataLoaded(core)
147
173
  // TODO playback.settings
148
174
  core.activePlayback.emit(Events.PLAYBACK_SETTINGSUPDATE)
149
175
  })
@@ -152,11 +178,11 @@ describe('MediaControl', () => {
152
178
  })
153
179
  })
154
180
  describe('when vod', () => {
155
- beforeEach(() => {
181
+ beforeEach(async () => {
156
182
  core.activeContainer.getPlaybackType.mockReturnValue(Playback.VOD)
157
183
  core.activePlayback.getPlaybackType.mockReturnValue(Playback.VOD)
158
184
  core.getPlaybackType.mockReturnValue(Playback.VOD)
159
- core.activeContainer.emit(Events.CONTAINER_LOADEDMETADATA)
185
+ await runMetadataLoaded(core)
160
186
  })
161
187
  it('should not apply live style class', () => {
162
188
  expect(mediaControl.$el.hasClass('live')).toBe(false)
@@ -164,11 +190,13 @@ describe('MediaControl', () => {
164
190
  })
165
191
  })
166
192
  describe('putElement', () => {
167
- beforeEach(() => {
193
+ beforeEach(async () => {
168
194
  mediaControl = new MediaControl(core)
169
- core.emit('core:ready')
195
+ core.emit(Events.CORE_READY)
196
+ core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
170
197
  core.activeContainer.settings = {}
171
- core.emit('core:active:container:changed', core.activeContainer)
198
+ core.emit(Events.CONTAINER_SETTINGSUPDATE)
199
+ await runMetadataLoaded(core)
172
200
  })
173
201
  describe.each([
174
202
  ['pip' as MediaControlElement],
@@ -200,12 +228,13 @@ describe('MediaControl', () => {
200
228
  core.emit(Events.CORE_READY)
201
229
  })
202
230
  describe('dvr', () => {
203
- beforeEach(() => {
231
+ beforeEach(async () => {
204
232
  core.activeContainer.settings = {
205
233
  left: ['playpause', 'position', 'duration'],
206
234
  seekEnabled: true,
207
235
  }
208
236
  core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
237
+ await runMetadataLoaded(core)
209
238
  })
210
239
  describe('when enabled', () => {
211
240
  beforeEach(() => {
@@ -241,10 +270,11 @@ describe('MediaControl', () => {
241
270
  })
242
271
  })
243
272
  describe('dvr mode', () => {
244
- beforeEach(() => {
273
+ beforeEach(async () => {
245
274
  mediaControl = new MediaControl(core)
246
275
  core.emit(Events.CORE_READY)
247
276
  core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
277
+ await runMetadataLoaded(core)
248
278
  })
249
279
  describe('by default', () => {
250
280
  it('should not apply DVR style class', () => {
@@ -270,3 +300,9 @@ describe('MediaControl', () => {
270
300
  function arraySubtract<T extends string>(arr1: T[], arr2: T[]) {
271
301
  return arr1.filter((item) => !arr2.includes(item))
272
302
  }
303
+
304
+ function runMetadataLoaded(core: any) {
305
+ core.activePlayback.emit(Events.PLAYBACK_LOADEDMETADATA)
306
+ core.activeContainer.emit(Events.CONTAINER_LOADEDMETADATA)
307
+ return new Promise((resolve) => setTimeout(resolve, 25))
308
+ }
@@ -13,7 +13,6 @@ exports[`MediaControl > putElement > audiotracks > should put the element in the
13
13
 
14
14
 
15
15
 
16
-
17
16
 
18
17
 
19
18
 
@@ -36,10 +35,7 @@ exports[`MediaControl > putElement > audiotracks > should put the element in the
36
35
 
37
36
  </div>
38
37
 
39
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
40
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
41
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
42
- </div>
38
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
43
39
 
44
40
  </div>
45
41
 
@@ -72,7 +68,6 @@ exports[`MediaControl > putElement > cc > should put the element in the right pa
72
68
 
73
69
 
74
70
 
75
-
76
71
 
77
72
 
78
73
 
@@ -95,10 +90,7 @@ exports[`MediaControl > putElement > cc > should put the element in the right pa
95
90
 
96
91
  </div>
97
92
 
98
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
99
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
100
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
101
- </div>
93
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
102
94
 
103
95
  </div>
104
96
 
@@ -131,7 +123,6 @@ exports[`MediaControl > putElement > gear > should put the element in the right
131
123
 
132
124
 
133
125
 
134
-
135
126
 
136
127
 
137
128
 
@@ -154,10 +145,7 @@ exports[`MediaControl > putElement > gear > should put the element in the right
154
145
 
155
146
  </div>
156
147
 
157
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
158
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
159
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
160
- </div>
148
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
161
149
 
162
150
  </div>
163
151
 
@@ -190,7 +178,6 @@ exports[`MediaControl > putElement > pip > should put the element in the right p
190
178
 
191
179
 
192
180
 
193
-
194
181
 
195
182
 
196
183
 
@@ -213,10 +200,7 @@ exports[`MediaControl > putElement > pip > should put the element in the right p
213
200
 
214
201
  </div>
215
202
 
216
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
217
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
218
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
219
- </div>
203
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
220
204
 
221
205
  </div>
222
206
 
@@ -249,7 +233,6 @@ exports[`MediaControl > rendering timing > once metadata is loaded > should wait
249
233
 
250
234
 
251
235
 
252
-
253
236
 
254
237
 
255
238
 
@@ -278,10 +261,7 @@ exports[`MediaControl > rendering timing > once metadata is loaded > should wait
278
261
 
279
262
  <div class="media-control-indicator gcore-skin-text-color" data-duration="">00:00</div>
280
263
 
281
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
282
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
283
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
284
- </div>
264
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
285
265
 
286
266
  </div>
287
267
 
@@ -329,7 +309,6 @@ exports[`MediaControl > updateSettings > dvr > when disabled > should disable DV
329
309
 
330
310
 
331
311
 
332
-
333
312
 
334
313
 
335
314
 
@@ -358,10 +337,7 @@ exports[`MediaControl > updateSettings > dvr > when disabled > should disable DV
358
337
 
359
338
  <div class="media-control-indicator gcore-skin-text-color" data-duration="">00:00</div>
360
339
 
361
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
362
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
363
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
364
- </div>
340
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
365
341
 
366
342
  </div>
367
343
 
@@ -394,7 +370,6 @@ exports[`MediaControl > updateSettings > dvr > when enabled > should enable DVR
394
370
 
395
371
 
396
372
 
397
-
398
373
 
399
374
 
400
375
 
@@ -423,10 +398,7 @@ exports[`MediaControl > updateSettings > dvr > when enabled > should enable DVR
423
398
 
424
399
  <div class="media-control-indicator gcore-skin-text-color" data-duration="">00:00</div>
425
400
 
426
- <div class="media-clip-container gcore-skin-text-color" data-clipstext="">
427
- <div class="media-clip-point gcore-skin-text-color" data-clipstext="">•</div>
428
- <div class="media-clip-text gcore-skin-text-color" data-clipstext=""></div>
429
- </div>
401
+ <div class="media-control-indicator gcore-skin-text-color" data-clips=""></div>
430
402
 
431
403
  <div class="my-dvr-controls" data-dvr="">live</div></div>
432
404
 
@@ -6,7 +6,15 @@ export function getLocation(href: string) {
6
6
  return l;
7
7
  }
8
8
 
9
- export function strtimeToMiliseconds(str: string): number {
9
+ /**
10
+ * Parse a time string in the format "hh:mm:ss" or "mm:ss" or "ss" to seconds.
11
+ * @param str - The time string to parse.
12
+ * @returns The time in seconds.
13
+ * @example "01:01:00" -> 3660
14
+ * @example "01:00" -> 60
15
+ * @example "33" -> 33
16
+ */
17
+ export function parseClipTime(str: string): number {
10
18
  if (!str) {
11
19
  return 0;
12
20
  }
@@ -15,19 +23,13 @@ export function strtimeToMiliseconds(str: string): number {
15
23
 
16
24
  if (arr.length >= 3) {
17
25
  h = parseInt(arr[arr.length - 3]) * 60 * 60;
18
- } else {
19
- h = 0;
20
26
  }
21
27
  if (arr.length >= 2) {
22
28
  m = parseInt(arr[arr.length - 2]) * 60;
23
- } else {
24
- m = 0;
25
29
  }
26
30
 
27
31
  if (arr.length >= 1) {
28
32
  s = parseInt(arr[arr.length - 1]);
29
- } else {
30
- s = 0;
31
33
  }
32
34
 
33
35
  return (h + m + s);
@@ -2,7 +2,7 @@ import { reportError } from '@gcorevideo/utils';
2
2
  import assert from 'assert';
3
3
  import URLHandler from './urlhandler.js';
4
4
  import MergeVast from './xmlmerge.js';
5
- import { strtimeToMiliseconds } from '../utils.js';
5
+ import { parseClipTime } from '../utils.js';
6
6
 
7
7
  export default class LoaderXML {
8
8
  private config: string = '';
@@ -104,7 +104,7 @@ export default class LoaderXML {
104
104
  }
105
105
 
106
106
  if (linearSkipOffset && linearSkipOffset.length > 1) {
107
- timeOffset = strtimeToMiliseconds(linearSkipOffset[1]);
107
+ timeOffset = parseClipTime(linearSkipOffset[1]);
108
108
  this.config = this.config.replace(/<Linear skipoffset="(\d+:\d+:\d+)".*?>/g, '<Linear>');
109
109
  }
110
110
 
package/src/testUtils.ts CHANGED
@@ -184,14 +184,11 @@ export function createMockMediaControl(core: any) {
184
184
  <div class="media-control-right-panel" data-media-control></div>
185
185
  <div class="media-control-center-panel" data-media-control></div>`,
186
186
  )
187
- const elements = {
188
- gear: $(document.createElement('div')),
189
- }
190
- // @ts-ignore
191
- mediaControl.getElement = vi.fn().mockImplementation((name) => elements[name])
192
187
  // @ts-ignore
193
188
  mediaControl.putElement = vi.fn()
194
189
  // @ts-ignore
190
+ mediaControl.mount = vi.fn()
191
+ // @ts-ignore
195
192
  mediaControl.toggleElement = vi.fn()
196
193
  return mediaControl
197
194
  }