@gcorevideo/player 2.21.1 → 2.21.4

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 (97) hide show
  1. package/assets/audio-selector/style.scss +1 -1
  2. package/assets/audio-selector/track-selector.ejs +3 -3
  3. package/assets/bottom-gear/bottomgear.ejs +2 -2
  4. package/assets/media-control/container.scss +1 -1
  5. package/assets/media-control/media-control.ejs +1 -11
  6. package/assets/media-control/media-control.scss +49 -57
  7. package/assets/media-control/width270.scss +1 -1
  8. package/assets/media-control/width370.scss +7 -9
  9. package/assets/playback-rate/button.ejs +2 -2
  10. package/assets/playback-rate/list.ejs +4 -4
  11. package/assets/subtitles/combobox.ejs +10 -12
  12. package/assets/subtitles/string.ejs +1 -1
  13. package/assets/subtitles/style.scss +9 -16
  14. package/dist/core.js +5 -1
  15. package/dist/index.css +782 -794
  16. package/dist/index.js +240 -244
  17. package/dist/player.d.ts +141 -119
  18. package/dist/plugins/index.css +862 -874
  19. package/dist/plugins/index.js +222 -238
  20. package/docs/api/player.bottomgear.getelement.md +2 -2
  21. package/docs/api/player.bottomgear.md +1 -1
  22. package/docs/api/{player.subtitles.hide.md → player.closedcaptions.hide.md} +2 -2
  23. package/docs/api/{player.subtitles.md → player.closedcaptions.md} +11 -11
  24. package/docs/api/{player.subtitles.show.md → player.closedcaptions.show.md} +2 -2
  25. package/docs/api/player.closedcaptionspluginsettings.md +13 -0
  26. package/docs/api/player.gearitemelement.md +6 -4
  27. package/docs/api/player.gearoptionsitem.md +16 -0
  28. package/docs/api/player.md +48 -12
  29. package/docs/api/player.mediacontrol.putelement.md +2 -2
  30. package/docs/api/player.mediacontrolelement.md +1 -1
  31. package/docs/api/player.playbackrate.md +1 -1
  32. package/docs/api/player.subtitlespluginsettings.md +18 -0
  33. package/docs/api/player.texttrackitem.id.md +11 -0
  34. package/docs/api/player.texttrackitem.md +87 -0
  35. package/docs/api/player.texttrackitem.name.md +11 -0
  36. package/docs/api/player.texttrackitem.track.md +11 -0
  37. package/lib/index.d.ts +1 -1
  38. package/lib/index.js +1 -1
  39. package/lib/index.plugins.d.ts +2 -1
  40. package/lib/index.plugins.d.ts.map +1 -1
  41. package/lib/index.plugins.js +2 -1
  42. package/lib/playback/BasePlayback.d.ts +1 -0
  43. package/lib/playback/BasePlayback.d.ts.map +1 -1
  44. package/lib/playback/BasePlayback.js +3 -0
  45. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  46. package/lib/playback/dash-playback/DashPlayback.js +1 -0
  47. package/lib/playback.types.d.ts +5 -0
  48. package/lib/playback.types.d.ts.map +1 -1
  49. package/lib/plugins/audio-selector/AudioSelector.d.ts +2 -3
  50. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  51. package/lib/plugins/audio-selector/AudioSelector.js +6 -7
  52. package/lib/plugins/bottom-gear/BottomGear.d.ts +7 -3
  53. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  54. package/lib/plugins/bottom-gear/BottomGear.js +4 -2
  55. package/lib/plugins/media-control/MediaControl.d.ts +5 -6
  56. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  57. package/lib/plugins/media-control/MediaControl.js +48 -39
  58. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +1 -0
  59. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  60. package/lib/plugins/picture-in-picture/PictureInPicture.js +4 -4
  61. package/lib/plugins/playback-rate/PlaybackRate.d.ts +1 -1
  62. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  63. package/lib/plugins/playback-rate/PlaybackRate.js +24 -14
  64. package/lib/plugins/subtitles/ClosedCaptions.d.ts +118 -0
  65. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -0
  66. package/lib/plugins/subtitles/ClosedCaptions.js +348 -0
  67. package/lib/plugins/subtitles/Subtitles.d.ts +31 -26
  68. package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
  69. package/lib/plugins/subtitles/Subtitles.js +138 -169
  70. package/lib/testUtils.d.ts +22 -18
  71. package/lib/testUtils.d.ts.map +1 -1
  72. package/lib/testUtils.js +22 -36
  73. package/package.json +1 -1
  74. package/src/index.plugins.ts +2 -1
  75. package/src/index.ts +1 -1
  76. package/src/playback/BasePlayback.ts +4 -0
  77. package/src/playback/dash-playback/DashPlayback.ts +1 -0
  78. package/src/playback.types.ts +6 -0
  79. package/src/plugins/audio-selector/AudioSelector.ts +9 -8
  80. package/src/plugins/bottom-gear/BottomGear.ts +14 -5
  81. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -1
  82. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +2 -2
  83. package/src/plugins/media-control/MediaControl.ts +84 -60
  84. package/src/plugins/media-control/__tests__/MediaControl.test.ts +43 -0
  85. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +175 -0
  86. package/src/plugins/picture-in-picture/PictureInPicture.ts +5 -5
  87. package/src/plugins/playback-rate/PlaybackRate.ts +143 -100
  88. package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +65 -0
  89. package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +11 -0
  90. package/src/plugins/subtitles/ClosedCaptions.ts +469 -0
  91. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +58 -0
  92. package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +25 -0
  93. package/src/testUtils.ts +22 -36
  94. package/temp/player.api.json +269 -89
  95. package/tsconfig.tsbuildinfo +1 -1
  96. package/src/plugins/index.ts +0 -39
  97. package/src/plugins/subtitles/Subtitles.ts +0 -496
@@ -1,4 +1,4 @@
1
- import { Events, UICorePlugin, Browser, template, $, } from '@clappr/core';
1
+ import { Events, UICorePlugin, Browser, template, $ } from '@clappr/core';
2
2
  import { reportError, trace } from '@gcorevideo/utils';
3
3
  import assert from 'assert';
4
4
  import { CLAPPR_VERSION } from '../../build.js';
@@ -9,9 +9,8 @@ import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
9
9
  import stringHTML from '../../../assets/subtitles/string.ejs';
10
10
  import { isFullscreen } from '../utils.js';
11
11
  const VERSION = '2.19.14';
12
- const LOCAL_STORAGE_SUBTITLES_ID = 'gplayer.plugins.subtitles.selected';
13
- const T = 'plugins.subtitles';
14
- const NO_TRACK = { language: 'off' };
12
+ const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected';
13
+ const T = 'plugins.cc';
15
14
  /**
16
15
  * `PLUGIN` that provides a UI to select the subtitles when available.
17
16
  * @beta
@@ -21,36 +20,32 @@ const NO_TRACK = { language: 'off' };
21
20
  *
22
21
  * - {@link MediaControl}
23
22
  *
24
- * Configuration options:
25
- *
26
- * - subtitles.language - The language of the subtitles to select by default.
27
- *
23
+ * Configuration options - {@link ClosedCaptionsPluginSettings}
28
24
  * @example
29
25
  * ```ts
30
- * import { Subtitles } from '@gcorevideo/player'
26
+ * import { ClosedCaptions } from '@gcorevideo/player'
31
27
  *
32
- * Player.registerPlugin(Subtitles)
28
+ * Player.registerPlugin(ClosedCaptions)
33
29
  *
34
30
  * new Player({
35
31
  * ...
36
- * subtitles: {
32
+ * cc: {
37
33
  * language: 'en',
38
34
  * },
39
35
  * })
40
36
  * ```
41
37
  */
42
38
  export class Subtitles extends UICorePlugin {
43
- currentLevel = null;
44
39
  isPreselectedApplied = false;
45
40
  isShowing = false;
46
- track = { ...NO_TRACK };
47
- tracks = null;
48
- $string = null;
41
+ track = null;
42
+ tracks = [];
43
+ $line = null;
49
44
  /**
50
45
  * @internal
51
46
  */
52
47
  get name() {
53
- return 'subtitles';
48
+ return 'subtitles'; // TODO rename to 'cc'
54
49
  }
55
50
  /**
56
51
  * @internal
@@ -71,8 +66,7 @@ export class Subtitles extends UICorePlugin {
71
66
  */
72
67
  get attributes() {
73
68
  return {
74
- class: this.name,
75
- 'data-subtitles': '',
69
+ class: 'media-control-cc',
76
70
  };
77
71
  }
78
72
  /**
@@ -80,28 +74,34 @@ export class Subtitles extends UICorePlugin {
80
74
  */
81
75
  get events() {
82
76
  return {
83
- 'click [data-subtitles-select]': 'onLevelSelect',
84
- 'click [data-subtitles-button]': 'onShowLevelSelectMenu',
77
+ 'click [data-cc-select]': 'onItemSelect',
78
+ 'click [data-cc-button]': 'toggleMenu',
85
79
  };
86
80
  }
87
81
  get preselectedLanguage() {
88
- return this.core.options.subtitles?.language ?? 'off';
82
+ return this.core.options.cc?.language ?? this.core.options.subtitles?.language ?? '';
89
83
  }
90
84
  /**
91
85
  * @internal
92
86
  */
93
87
  bindEvents() {
88
+ this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
89
+ this.listenTo(this.core, Events.CORE_RESIZE, this.playerResize);
90
+ this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onContainerChanged);
91
+ }
92
+ onCoreReady() {
93
+ trace(`${T} onCoreReady`);
94
94
  const mediaControl = this.core.getPlugin('media_control');
95
95
  assert(mediaControl, 'media_control plugin is required');
96
- this.listenTo(this.core, Events.CORE_RESIZE, this.playerResize);
97
- this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.bindPlaybackEvents);
98
96
  this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
99
- this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu);
97
+ this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideMenu);
100
98
  }
101
- bindPlaybackEvents() {
99
+ onContainerChanged() {
100
+ trace(`${T} onContainerChanged`);
102
101
  this.listenTo(this.core.activeContainer, Events.CONTAINER_FULLSCREEN, this.playerResize);
103
- this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, this.getTracks);
104
102
  this.listenTo(this.core.activeContainer, 'container:advertisement:start', this.onStartAd);
103
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_AVAILABLE, this.onSubtitleAvailable);
104
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_SUBTITLE_CHANGED, this.onSubtitleChanged);
105
105
  // fix for iOS
106
106
  const video = this.core.activePlayback.el;
107
107
  assert(video, 'video element is required');
@@ -116,20 +116,50 @@ export class Subtitles extends UICorePlugin {
116
116
  }
117
117
  });
118
118
  }
119
- getTracks() {
120
- if (this.core.activePlayback) {
121
- try {
122
- const tracks = this.core.activePlayback.el
123
- .textTracks;
124
- if (tracks.length > 0) {
125
- this.setTracks(tracks);
126
- }
119
+ onSubtitleAvailable() {
120
+ trace(`${T} onSubtitleAvailable`);
121
+ this.applyTracks();
122
+ }
123
+ onSubtitleChanged({ id }) {
124
+ trace(`${T} onSubtitleChanged`, { id });
125
+ if (id === -1) {
126
+ this.clearSubtitleText();
127
+ }
128
+ for (const track of this.tracks) {
129
+ if (track.id === id) {
130
+ track.track.mode = 'showing';
131
+ this.setSubtitleText(this.getSubtitleText(track.track));
132
+ track.track.oncuechange = (e) => {
133
+ try {
134
+ if (track.track.activeCues?.length) {
135
+ const html = track.track.activeCues[0].getCueAsHTML();
136
+ this.setSubtitleText(html);
137
+ }
138
+ else {
139
+ this.clearSubtitleText();
140
+ }
141
+ }
142
+ catch (error) {
143
+ reportError(error);
144
+ }
145
+ };
127
146
  }
128
- catch (error) {
129
- reportError(error);
147
+ else {
148
+ track.track.oncuechange = null;
149
+ track.track.mode = 'hidden';
130
150
  }
131
151
  }
132
152
  }
153
+ applyTracks() {
154
+ try {
155
+ this.tracks = this.core.activePlayback.closedCaptionsTracks;
156
+ this.applyPreselectedSubtitles();
157
+ this.render();
158
+ }
159
+ catch (error) {
160
+ reportError(error);
161
+ }
162
+ }
133
163
  onStartAd() {
134
164
  if (this.isShowing && this.core.activeContainer) {
135
165
  this.hide();
@@ -141,10 +171,11 @@ export class Subtitles extends UICorePlugin {
141
171
  this.stopListening(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
142
172
  }
143
173
  playerResize() {
174
+ trace(`${T} playerResize`);
144
175
  const shouldShow = this.core.activeContainer &&
145
176
  isFullscreen(this.core.activeContainer.el) &&
146
- this.currentLevel &&
147
- this.currentLevel.mode &&
177
+ this.track &&
178
+ this.track.track.mode &&
148
179
  Browser.isiOS &&
149
180
  this.isShowing;
150
181
  if (shouldShow) {
@@ -163,10 +194,10 @@ export class Subtitles extends UICorePlugin {
163
194
  hide() {
164
195
  this.isShowing = false;
165
196
  this.renderIcon();
166
- this.$string.hide();
197
+ this.$line.hide();
167
198
  if (this.tracks) {
168
199
  for (const t of this.tracks) {
169
- t.mode = 'hidden';
200
+ t.track.mode = 'hidden';
170
201
  }
171
202
  }
172
203
  }
@@ -178,28 +209,25 @@ export class Subtitles extends UICorePlugin {
178
209
  this.renderIcon();
179
210
  if (this.core.activeContainer &&
180
211
  isFullscreen(this.core.activeContainer.el) &&
181
- this.currentLevel &&
182
- this.currentLevel.mode &&
212
+ this.track &&
213
+ this.track.track.mode &&
183
214
  Browser.isiOS) {
184
- this.$string.hide();
185
- this.currentLevel.mode = 'showing';
215
+ this.$line.hide();
216
+ this.track.track.mode = 'showing';
186
217
  }
187
218
  else {
188
- this.$string.show();
219
+ this.$line.show();
189
220
  }
190
221
  }
191
222
  shouldRender() {
192
- return !!(this.tracks && this.tracks.length > 0);
223
+ return this.tracks?.length > 0;
193
224
  }
194
225
  resizeFont() {
195
- if (!this.core.activeContainer) {
196
- return;
197
- }
198
- if (!this.$string) {
226
+ if (!this.$line) {
199
227
  return;
200
228
  }
201
229
  const skinWidth = this.core.activeContainer.$el.width();
202
- this.$string.find('p').css('font-size', skinWidth * 0.03);
230
+ this.$line.find('p').css('font-size', skinWidth * 0.03);
203
231
  }
204
232
  /**
205
233
  * @internal
@@ -211,143 +239,83 @@ export class Subtitles extends UICorePlugin {
211
239
  if (!this.shouldRender()) {
212
240
  return this;
213
241
  }
214
- trace(`${T} render`, {
215
- tracks: this.tracks?.length,
216
- track: this.track?.language,
217
- });
218
242
  const mediaControl = this.core.getPlugin('media_control');
219
- assert(mediaControl, 'media_control plugin is required');
220
243
  this.$el.html(Subtitles.template({ tracks: this.tracks }));
221
- this.core.activeContainer.$el.find('.subtitle-string').remove();
222
- this.$string = $(Subtitles.templateString());
244
+ this.core.activeContainer.$el.find('#cc-line').remove();
245
+ this.$line = $(Subtitles.templateString());
223
246
  this.resizeFont();
224
- this.core.activeContainer.$el.append(this.$string[0]);
225
- const ss = mediaControl.getElement('subtitlesSelector');
226
- if (ss && ss.length > 0) {
227
- ss.append(this.el);
228
- }
229
- else {
230
- mediaControl.getRightPanel().append(this.el);
231
- }
232
- this.updateCurrentLevel(this.track);
233
- this.highlightCurrentSubtitles();
234
- this.applyPreselectedSubtitles();
247
+ this.core.activeContainer.$el.append(this.$line);
248
+ mediaControl.putElement('cc', this.el);
249
+ this.updateSelection();
235
250
  this.renderIcon();
236
251
  return this;
237
252
  }
238
- setTracks(tracks) {
239
- this.tracks = tracks;
240
- this.render();
241
- }
242
- findLevelBy(id) {
243
- if (this.tracks) {
244
- for (const track of this.tracks) {
245
- if (track.language === id) {
246
- return track; // TODO TrackInfo?
247
- }
248
- }
249
- }
253
+ findById(id) {
254
+ return this.tracks.find((track) => track.id === id) ?? null;
250
255
  }
251
- selectLevel(id) {
256
+ selectItem(item) {
252
257
  this.clearSubtitleText();
253
- this.track = this.findLevelBy(id) || { ...NO_TRACK };
254
- this.hideSelectLevelMenu();
255
- if (!this.track) {
256
- this.track = { language: 'off' };
257
- }
258
- this.updateCurrentLevel(this.track);
259
- }
260
- onLevelSelect(event) {
261
- const id = event.target.dataset.subtitlesSelect;
262
- if (id) {
263
- localStorage.setItem(LOCAL_STORAGE_SUBTITLES_ID, id);
264
- this.selectLevel(id);
265
- }
258
+ this.track = item;
259
+ this.hideMenu();
260
+ this.updateSelection();
261
+ }
262
+ onItemSelect(event) {
263
+ const id = event.target.dataset.ccSelect ?? '-1';
264
+ trace(`${T} onItemSelect`, { id });
265
+ localStorage.setItem(LOCAL_STORAGE_CC_ID, id);
266
+ this.selectItem(this.findById(Number(id)));
266
267
  return false;
267
268
  }
268
269
  applyPreselectedSubtitles() {
269
270
  if (!this.isPreselectedApplied) {
270
271
  this.isPreselectedApplied = true;
272
+ if (!this.preselectedLanguage) {
273
+ return;
274
+ }
271
275
  setTimeout(() => {
272
- this.selectLevel(this.preselectedLanguage);
273
- }, 300);
276
+ this.selectItem(this.tracks.find((t) => t.track.language === this.preselectedLanguage) ?? null);
277
+ }, 300); // TODO why delay?
274
278
  }
275
279
  }
276
- onShowLevelSelectMenu() {
277
- trace(`${T} onShowLevelSelectMenu`);
278
- this.toggleContextMenu();
279
- }
280
- hideSelectLevelMenu() {
280
+ hideMenu() {
281
281
  ;
282
- this.$('[data-subtitles] ul').hide();
282
+ this.$('[data-cc] ul').hide();
283
283
  }
284
- toggleContextMenu() {
285
- this.$('[data-subtitles] ul').toggle();
284
+ toggleMenu() {
285
+ trace(`${T} toggleMenu`);
286
+ this.$('[data-cc] ul').toggle();
286
287
  }
287
- buttonElement() {
288
- return this.$('[data-subtitles] button');
288
+ itemElement(id) {
289
+ return this.$(`ul li a[data-cc-select="${id}"]`).parent();
289
290
  }
290
- levelElement(id) {
291
- return this.$('[data-subtitles] ul a' + (id ? '[data-subtitles-select="' + id + '"]' : '')).parent();
292
- }
293
- startLevelSwitch() {
294
- this.buttonElement().addClass('changing');
295
- }
296
- stopLevelSwitch() {
297
- this.buttonElement().removeClass('changing');
291
+ allItemElements() {
292
+ return this.$('[data-cc] li');
298
293
  }
299
294
  selectSubtitles() {
300
- if (!this.currentLevel) {
301
- return;
302
- }
303
- if (this.tracks) {
304
- for (let i = 0; i < this.tracks.length; i++) {
305
- const track = this.tracks[i];
306
- if (track.language === this.currentLevel.language) {
307
- track.mode = 'showing';
308
- const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0;
309
- const cues = track.cues;
310
- let subtitleText = '';
311
- if (cues && cues.length) {
312
- for (const cue of cues) {
313
- if (currentTime >= cue.startTime && currentTime <= cue.endTime) {
314
- subtitleText +=
315
- cue.getCueAsHTML().textContent + '\n';
316
- }
317
- }
318
- }
319
- this.setSubtitleText(subtitleText);
320
- track.oncuechange = (e) => {
321
- try {
322
- if (track.activeCues?.length) {
323
- const html = track.activeCues[0].getCueAsHTML();
324
- this.setSubtitleText(html);
325
- }
326
- else {
327
- this.clearSubtitleText();
328
- }
329
- }
330
- catch (error) {
331
- // console.error(error);
332
- reportError(error);
333
- }
334
- };
335
- continue;
295
+ const trackId = this.track ? this.track.id : -1;
296
+ this.core.activePlayback.closedCaptionsTrackId = trackId;
297
+ }
298
+ getSubtitleText(track) {
299
+ const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0;
300
+ const cues = track.cues;
301
+ const lines = [];
302
+ if (cues && cues.length) {
303
+ for (const cue of cues) {
304
+ if (currentTime >= cue.startTime && currentTime <= cue.endTime) {
305
+ lines.push(cue.getCueAsHTML().textContent);
336
306
  }
337
- this.tracks[i].oncuechange = null;
338
- this.tracks[i].mode = 'hidden';
339
307
  }
340
308
  }
309
+ return lines.join('\n');
341
310
  }
342
311
  setSubtitleText(text) {
343
- this.$string.find('p').html(text);
312
+ this.$line.find('p').html(text);
344
313
  }
345
314
  clearSubtitleText() {
346
315
  this.setSubtitleText('');
347
316
  }
348
- updateCurrentLevel(track) {
349
- this.currentLevel = track;
350
- if (track.language === 'off') {
317
+ updateSelection() {
318
+ if (!this.track) {
351
319
  this.hide();
352
320
  }
353
321
  else {
@@ -357,20 +325,21 @@ export class Subtitles extends UICorePlugin {
357
325
  this.highlightCurrentSubtitles();
358
326
  }
359
327
  highlightCurrentSubtitles() {
360
- this.levelElement().removeClass('current');
361
- this.levelElement().find('a').removeClass('gcore-skin-active');
362
- if (this.currentLevel) {
363
- const currentLevelElement = this.levelElement(this.currentLevel.language);
364
- currentLevelElement.addClass('current');
365
- currentLevelElement.find('a').addClass('gcore-skin-active');
366
- }
328
+ this.allItemElements()
329
+ .removeClass('current')
330
+ .find('a')
331
+ .removeClass('gcore-skin-active');
332
+ trace(`${T} highlightCurrentSubtitles`, {
333
+ track: this.track?.id,
334
+ });
335
+ const currentLevelElement = this.itemElement(this.track ? this.track.id : -1);
336
+ currentLevelElement
337
+ .addClass('current')
338
+ .find('a')
339
+ .addClass('gcore-skin-active');
367
340
  }
368
341
  renderIcon() {
369
342
  const icon = this.isShowing ? subtitlesOnIcon : subtitlesOffIcon;
370
- this.core
371
- .getPlugin('media_control')
372
- .getElement('subtitlesSelector')
373
- ?.find('span.subtitle-text')
374
- .html(icon);
343
+ this.$el.find('span.cc-text').html(icon);
375
344
  }
376
345
  }
@@ -65,29 +65,31 @@ export declare function createSpinnerPlugin(): Events<string | symbol, any> & {
65
65
  export declare function createMockPlayback(name?: string): Events<string | symbol, any> & {
66
66
  name: string;
67
67
  currentLevel: number;
68
+ dvrEnabled: boolean;
68
69
  levels: never[];
69
70
  consent(): void;
70
71
  play(): void;
71
72
  pause(): void;
72
73
  stop(): void;
73
- destroy(): void;
74
- seek(): void;
75
- seekPercentage(): void;
76
- getDuration(): 100;
77
- enterPiP(): void;
78
- exitPiP(): void;
79
- getPlaybackType(): "live";
80
- getStartTimeOffset(): 0;
81
- getCurrentTime(): 0;
82
- isHighDefinitionInUse(): false;
83
- mute(): void;
84
- unmute(): void;
85
- volume(): void;
86
- configure(): void;
87
- attemptAutoPlay(): true;
88
- canAutoPlay(): true;
89
- onResize(): true;
90
- trigger(event: string, ...args: any[]): void;
74
+ destroy: import("vitest").Mock<(...args: any[]) => any>;
75
+ seek: import("vitest").Mock<(...args: any[]) => any>;
76
+ seekPercentage: import("vitest").Mock<(...args: any[]) => any>;
77
+ getDuration: import("vitest").Mock<(...args: any[]) => any>;
78
+ enterPiP: import("vitest").Mock<(...args: any[]) => any>;
79
+ exitPiP: import("vitest").Mock<(...args: any[]) => any>;
80
+ getPlaybackType: import("vitest").Mock<(...args: any[]) => any>;
81
+ getStartTimeOffset: import("vitest").Mock<(...args: any[]) => any>;
82
+ getCurrentTime: import("vitest").Mock<(...args: any[]) => any>;
83
+ isHighDefinitionInUse: import("vitest").Mock<(...args: any[]) => any>;
84
+ mute: import("vitest").Mock<(...args: any[]) => any>;
85
+ unmute: import("vitest").Mock<(...args: any[]) => any>;
86
+ volume: import("vitest").Mock<(...args: any[]) => any>;
87
+ configure: import("vitest").Mock<(...args: any[]) => any>;
88
+ attemptAutoPlay: import("vitest").Mock<(...args: any[]) => any>;
89
+ canAutoPlay: import("vitest").Mock<(...args: any[]) => any>;
90
+ onResize: import("vitest").Mock<(...args: any[]) => any>;
91
+ setPlaybackRate: import("vitest").Mock<(...args: any[]) => any>;
92
+ trigger: <T extends string | symbol>(event: T, ...args: any[]) => boolean;
91
93
  };
92
94
  export declare function createMockContainer(playback?: any): Events<string | symbol, any> & {
93
95
  el: HTMLDivElement;
@@ -95,6 +97,8 @@ export declare function createMockContainer(playback?: any): Events<string | sym
95
97
  $el: any;
96
98
  getDuration: import("vitest").Mock<(...args: any[]) => any>;
97
99
  getPlugin: import("vitest").Mock<(...args: any[]) => any>;
100
+ getPlaybackType: import("vitest").Mock<(...args: any[]) => any>;
101
+ isDvrInUse: import("vitest").Mock<(...args: any[]) => any>;
98
102
  isPlaying: import("vitest").Mock<(...args: any[]) => any>;
99
103
  play: import("vitest").Mock<(...args: any[]) => any>;
100
104
  seek: import("vitest").Mock<(...args: any[]) => any>;
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9C,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,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAA2B;;;;;;;;;;;;;;;;EAqBvC;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;;;;;;;;;;EAcvE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAqB/C"}
1
+ {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../src/testUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,YAAY,EAAE,MAAM,cAAc,CAAA;AAC9C,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,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAA2B;;;;;;;;;;;;;;;;EAqBvC;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B/C;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,GAA0B;;;;;;;;;;;;EAgBvE;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAqB/C"}
package/lib/testUtils.js CHANGED
@@ -98,47 +98,31 @@ export function createMockPlayback(name = 'mock') {
98
98
  return Object.assign(emitter, {
99
99
  name,
100
100
  currentLevel: -1,
101
+ dvrEnabled: false,
101
102
  levels: [],
102
103
  consent() { },
103
104
  play() { },
104
105
  pause() { },
105
106
  stop() { },
106
- destroy() { },
107
- seek() { },
108
- seekPercentage() { },
109
- getDuration() {
110
- return 100;
111
- },
112
- enterPiP() { },
113
- exitPiP() { },
114
- getPlaybackType() {
115
- return 'live';
116
- },
117
- getStartTimeOffset() {
118
- return 0;
119
- },
120
- getCurrentTime() {
121
- return 0;
122
- },
123
- isHighDefinitionInUse() {
124
- return false;
125
- },
126
- mute() { },
127
- unmute() { },
128
- volume() { },
129
- configure() { },
130
- attemptAutoPlay() {
131
- return true;
132
- },
133
- canAutoPlay() {
134
- return true;
135
- },
136
- onResize() {
137
- return true;
138
- },
139
- trigger(event, ...args) {
140
- emitter.emit(event, ...args);
141
- },
107
+ destroy: vi.fn(),
108
+ seek: vi.fn(),
109
+ seekPercentage: vi.fn(),
110
+ getDuration: vi.fn().mockImplementation(() => 100),
111
+ enterPiP: vi.fn(),
112
+ exitPiP: vi.fn(),
113
+ getPlaybackType: vi.fn().mockImplementation(() => 'live'),
114
+ getStartTimeOffset: vi.fn().mockImplementation(() => 0),
115
+ getCurrentTime: vi.fn().mockImplementation(() => 0),
116
+ isHighDefinitionInUse: vi.fn().mockImplementation(() => false),
117
+ mute: vi.fn(),
118
+ unmute: vi.fn(),
119
+ volume: vi.fn(),
120
+ configure: vi.fn(),
121
+ attemptAutoPlay: vi.fn().mockImplementation(() => true),
122
+ canAutoPlay: vi.fn().mockImplementation(() => true),
123
+ onResize: vi.fn().mockImplementation(() => true),
124
+ setPlaybackRate: vi.fn(),
125
+ trigger: emitter.emit,
142
126
  });
143
127
  }
144
128
  export function createMockContainer(playback = createMockPlayback()) {
@@ -150,6 +134,8 @@ export function createMockContainer(playback = createMockPlayback()) {
150
134
  $el: $(el),
151
135
  getDuration: vi.fn().mockReturnValue(0),
152
136
  getPlugin: vi.fn(),
137
+ getPlaybackType: vi.fn().mockReturnValue('live'),
138
+ isDvrInUse: vi.fn().mockReturnValue(false),
153
139
  isPlaying: vi.fn().mockReturnValue(false),
154
140
  play: vi.fn(),
155
141
  seek: vi.fn(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.21.1",
3
+ "version": "2.21.4",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -25,7 +25,8 @@ export * from "./plugins/share/Share.js";
25
25
  export * from "./plugins/skip-time/SkipTime.js";
26
26
  export * from "./plugins/spinner-three-bounce/SpinnerThreeBounce.js";
27
27
  export * from "./plugins/source-controller/SourceController.js";
28
- export * from "./plugins/subtitles/Subtitles.js";
28
+ export * from "./plugins/subtitles/ClosedCaptions.js";
29
+ export { ClosedCaptions as Subtitles } from "./plugins/subtitles/ClosedCaptions.js"; // TODO remove in future versions
29
30
  export * from "./plugins/telemetry/Telemetry.js";
30
31
  export * from "./plugins/thumbnails/Thumbnails.js";
31
32
  // _ vast-ads
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * It is built on top of the {@link https://github.com/clappr/clappr | Clappr} library and provides a framework for building custom integrations.
7
7
  * Start with {@link Player} for more information.
8
8
  *
9
- * Various plugins (marked with `PLUGIN` keyword) are available to extend the player with additional features.
9
+ * Various plugins (marked with `PLUGIN` keyword) are available to extend the core functionality with additional features.
10
10
  * @example
11
11
  * ```ts
12
12
  * import { Player, MediaControl, ErrorScreen } from '@gcorevideo/player'
@@ -7,6 +7,10 @@ import { PlaybackErrorCode } from '../playback.types.js'
7
7
  * @internal
8
8
  */
9
9
  export class BasePlayback extends HTML5Video {
10
+ get isHTML5Video() {
11
+ return true
12
+ }
13
+
10
14
  createError(errorData: any, options?: ErrorOptions) {
11
15
  const i18n =
12
16
  this.i18n ||
@@ -416,6 +416,7 @@ export default class DashPlayback extends BasePlayback {
416
416
  this.trigger(Events.PLAYBACK_STATS_ADD, { dvr: status })
417
417
  }
418
418
 
419
+ // TODO move to the base class
419
420
  override _updateSettings() {
420
421
  if (this._playbackType === Playback.VOD) {
421
422
  // @ts-expect-error