@gcorevideo/player 2.28.35 → 2.28.36

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 (31) hide show
  1. package/README.md +22 -1
  2. package/assets/{subtitles → cc}/style.scss +5 -0
  3. package/dist/core.js +17 -23
  4. package/dist/index.css +346 -342
  5. package/dist/index.embed.js +46 -39
  6. package/dist/index.js +46 -39
  7. package/lib/playback/BasePlayback.d.ts +1 -0
  8. package/lib/playback/BasePlayback.d.ts.map +1 -1
  9. package/lib/playback/BasePlayback.js +3 -0
  10. package/lib/playback/dash-playback/DashPlayback.d.ts +1 -0
  11. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  12. package/lib/playback/dash-playback/DashPlayback.js +9 -22
  13. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  14. package/lib/playback/hls-playback/HlsPlayback.js +4 -0
  15. package/lib/plugins/subtitles/ClosedCaptions.d.ts +1 -1
  16. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
  17. package/lib/plugins/subtitles/ClosedCaptions.js +32 -22
  18. package/lib/testUtils.d.ts +1 -0
  19. package/lib/testUtils.d.ts.map +1 -1
  20. package/lib/testUtils.js +3 -0
  21. package/package.json +1 -1
  22. package/src/playback/BasePlayback.ts +4 -0
  23. package/src/playback/dash-playback/DashPlayback.ts +10 -27
  24. package/src/playback/hls-playback/HlsPlayback.ts +4 -0
  25. package/src/plugins/subtitles/ClosedCaptions.ts +34 -20
  26. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +73 -112
  27. package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +3 -3
  28. package/src/testUtils.ts +3 -0
  29. package/tsconfig.tsbuildinfo +1 -1
  30. /package/assets/{subtitles → cc}/combobox.ejs +0 -0
  31. /package/assets/{subtitles → cc}/string.ejs +0 -0
@@ -2,18 +2,15 @@ import { Events, UICorePlugin, Browser, template, $ } from '@clappr/core';
2
2
  import { reportError } from '@gcorevideo/utils';
3
3
  import assert from 'assert';
4
4
  import { CLAPPR_VERSION } from '../../build.js';
5
- import '../../../assets/subtitles/style.scss';
5
+ import '../../../assets/cc/style.scss';
6
6
  import subtitlesOffIcon from '../../../assets/icons/new/subtitles-off.svg';
7
7
  import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg';
8
- import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
9
- import stringHTML from '../../../assets/subtitles/string.ejs';
8
+ import comboboxHTML from '../../../assets/cc/combobox.ejs';
9
+ import stringHTML from '../../../assets/cc/string.ejs';
10
10
  import { isFullscreen } from '../utils/fullscreen.js';
11
11
  import { ExtendedEvents } from '../media-control/MediaControl.js';
12
12
  import { mediaControlClickaway } from '../../utils/clickaway.js';
13
13
  const VERSION = '2.19.14';
14
- // TODO review
15
- // const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected'
16
- const T = 'plugins.cc';
17
14
  /**
18
15
  * `PLUGIN` that provides a UI to select the subtitles when available.
19
16
  * @public
@@ -115,8 +112,12 @@ export class ClosedCaptions extends UICorePlugin {
115
112
  const mediaControl = this.core.getPlugin('media_control');
116
113
  assert(mediaControl, 'media_control plugin is required');
117
114
  this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.mount);
115
+ this.listenTo(mediaControl, Events.MEDIACONTROL_SHOW, () => {
116
+ this.$line?.removeClass('media-control-cc-pulled');
117
+ });
118
118
  this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, () => {
119
119
  this.hideMenu();
120
+ this.$line?.addClass('media-control-cc-pulled');
120
121
  });
121
122
  this.listenTo(mediaControl, ExtendedEvents.MEDIACONTROL_MENU_COLLAPSE, (from) => {
122
123
  if (from !== this.name) {
@@ -174,23 +175,27 @@ export class ClosedCaptions extends UICorePlugin {
174
175
  this.activateTrack(id);
175
176
  }
176
177
  activateTrack(id) {
177
- if (this.core.activePlayback && ['dash', 'hls'].includes(this.core.activePlayback.name)) {
178
- this.core.activePlayback.setTextTrack(id);
178
+ const isManaged = this.core.activePlayback?.name === 'hls';
179
+ this.core.activePlayback.setTextTrack(id);
180
+ if (isManaged) {
181
+ return;
182
+ }
183
+ if (!this.core.activePlayback?.el.textTracks) {
179
184
  return;
180
185
  }
181
- for (const track of this.currentTracks) {
182
- if (track.id === id) {
186
+ for (const [index, track] of Array.from(this.core.activePlayback?.el.textTracks ?? []).entries()) {
187
+ if (index === id) {
183
188
  if (this.useNativeSubtitles) {
184
- track.track.mode = 'showing';
189
+ track.mode = 'showing';
185
190
  }
186
191
  else {
187
- track.track.mode = 'hidden';
192
+ track.mode = 'hidden';
188
193
  }
189
- this.setSubtitleText(this.getSubtitleText(track.track));
190
- track.track.oncuechange = (e) => {
194
+ this.setSubtitleText(this.getSubtitleText(track));
195
+ track.oncuechange = () => {
191
196
  try {
192
- if (track.track.activeCues?.length) {
193
- const html = track.track.activeCues[0].getCueAsHTML();
197
+ if (track.activeCues?.length) {
198
+ const html = track.activeCues[0].getCueAsHTML();
194
199
  this.setSubtitleText(html);
195
200
  }
196
201
  else {
@@ -203,8 +208,8 @@ export class ClosedCaptions extends UICorePlugin {
203
208
  };
204
209
  }
205
210
  else {
206
- track.track.oncuechange = () => { };
207
- track.track.mode = 'disabled';
211
+ track.oncuechange = () => { };
212
+ track.mode = 'disabled';
208
213
  }
209
214
  }
210
215
  }
@@ -247,8 +252,10 @@ export class ClosedCaptions extends UICorePlugin {
247
252
  this.$el.find('#gplayer-cc-menu').hide();
248
253
  this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false');
249
254
  this.$line.hide();
250
- for (const track of this.currentTracks) {
251
- track.track.mode = 'hidden';
255
+ for (const track of this.core.activePlayback.el.textTracks) {
256
+ if (track.mode === 'showing') {
257
+ track.mode = 'hidden';
258
+ }
252
259
  }
253
260
  }
254
261
  /**
@@ -260,7 +267,6 @@ export class ClosedCaptions extends UICorePlugin {
260
267
  if (this.core.activeContainer &&
261
268
  isFullscreen(this.core.activeContainer.el) &&
262
269
  this.currentTrack &&
263
- // this.currentTrack.track.mode &&
264
270
  (Browser.isiOS || this.useNativeSubtitles)) {
265
271
  this.$line.hide();
266
272
  this.currentTrack.track.mode = 'showing';
@@ -302,6 +308,10 @@ export class ClosedCaptions extends UICorePlugin {
302
308
  this.resizeFont();
303
309
  this.clampPopup();
304
310
  this.core.activeContainer.$el.append(this.$line);
311
+ const mc = this.core.getPlugin('media_control');
312
+ if (!mc?.isVisible()) {
313
+ this.$line?.addClass('media-control-cc-pulled');
314
+ }
305
315
  this.updateSelection();
306
316
  this.renderIcon();
307
317
  return this;
@@ -407,7 +417,7 @@ export class ClosedCaptions extends UICorePlugin {
407
417
  this.setSubtitleText('');
408
418
  }
409
419
  updateSelection() {
410
- if (!this.currentTrack) {
420
+ if (this.core.activePlayback.closedCaptionsTrackId === -1) {
411
421
  this.hide();
412
422
  }
413
423
  else {
@@ -65,6 +65,7 @@ export declare function createMockPlayback(name?: string, options?: Record<strin
65
65
  canAutoPlay: import("vitest").Mock<(...args: any[]) => any>;
66
66
  onResize: import("vitest").Mock<(...args: any[]) => any>;
67
67
  setPlaybackRate: import("vitest").Mock<(...args: any[]) => any>;
68
+ setTextTrack: import("vitest").Mock<(...args: any[]) => any>;
68
69
  switchAudioTrack: import("vitest").Mock<(...args: any[]) => any>;
69
70
  trigger: <T extends string | symbol>(event: T, ...args: any[]) => boolean;
70
71
  };
@@ -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;AAGlC,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAAkC;;;;;;;;;;;;;;;;;;EAuB9C;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,SAAS,EACb,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuCtC;AAED,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,QAAQ,GAAE,GAAgD;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBAyB/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,OAe7C"}
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;AAGlC,wBAAgB,cAAc,CAC5B,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,SAAS,GAAE,GAAkC;;;;;;;;;;;;;;;;;;EAuB9C;AAED,wBAAgB,gBAAgB;;;EAK/B;AAED,wBAAgB,mBAAmB;;;;;;EAKlC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,SAAS,EACb,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCtC;AAED,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACrC,QAAQ,GAAE,GAAgD;;;;;;;;;;;;;;;;;;;;;;;;;EA8B3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,gBA2B/C;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,GAAG,OAe7C"}
package/lib/testUtils.js CHANGED
@@ -71,6 +71,7 @@ export function createMockPlayback(name = 'mock', options = {}) {
71
71
  canAutoPlay: vi.fn().mockImplementation(() => true),
72
72
  onResize: vi.fn().mockImplementation(() => true),
73
73
  setPlaybackRate: vi.fn(),
74
+ setTextTrack: vi.fn(),
74
75
  switchAudioTrack: vi.fn(),
75
76
  trigger: emitter.emit,
76
77
  });
@@ -122,6 +123,8 @@ export function createMockMediaControl(core) {
122
123
  // @ts-ignore
123
124
  mediaControl.getAvailablePopupHeight = vi.fn().mockReturnValue(286);
124
125
  // @ts-ignore
126
+ mediaControl.isVisible = vi.fn().mockReturnValue(true);
127
+ // @ts-ignore
125
128
  mediaControl.toggleElement = vi.fn();
126
129
  // @ts-ignore
127
130
  mediaControl.setKeepVisible = vi.fn();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.28.35",
3
+ "version": "2.28.36",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -50,4 +50,8 @@ export class BasePlayback extends HTML5Video {
50
50
  super._onPlaying()
51
51
  this.trigger(Events.PLAYBACK_MEDIACONTROL_ENABLE)
52
52
  }
53
+
54
+ setTextTrack(id: number) {
55
+ // noop
56
+ }
53
57
  }
@@ -61,6 +61,8 @@ export default class DashPlayback extends BasePlayback {
61
61
 
62
62
  _currentLevel: number = AUTO
63
63
 
64
+ _currentTextTrackId: number = -1
65
+
64
66
  // true when the actual duration is longer than hlsjs's live sync point
65
67
  // when this is false playableRegionDuration will be the actual duration
66
68
  // when this is true playableRegionDuration will exclude the time after the sync point
@@ -248,7 +250,11 @@ export default class DashPlayback extends BasePlayback {
248
250
  streaming: {
249
251
  text: {
250
252
  defaultEnabled: false,
251
- dispatchForManualRendering: true,
253
+ // NOTE: dispatchForManualRendering is not correctly implemented in DASH.js;
254
+ // it does not work when there are multiple text tracks.
255
+ // CUE_ENTER and CUE_EXIT events might be dispatched additionally
256
+ // for a track, other than the currently active one.
257
+ // dispatchForManualRendering: true, // TODO only when useNativeSubtitles is not true?
252
258
  },
253
259
  },
254
260
  },
@@ -319,29 +325,6 @@ export default class DashPlayback extends BasePlayback {
319
325
  this.trigger(PlaybackEvents.PLAYBACK_RATE_CHANGED, e.playbackRate)
320
326
  },
321
327
  )
322
-
323
- this._dash.on(MediaPlayer.events.TRACK_CHANGE_RENDERED, (e: any) => {
324
- if ((e as TrackChangeRenderedEvent).mediaType === 'audio') {
325
- this.trigger(
326
- Events.PLAYBACK_AUDIO_CHANGED,
327
- toClapprTrack(e.newMediaInfo),
328
- )
329
- }
330
- })
331
-
332
- this._dash.on(MediaPlayer.events.CUE_ENTER, (e: CueEnterEvent) => {
333
- this.oncueenter?.({
334
- end: e.end,
335
- id: e.id,
336
- start: e.start,
337
- text: e.text,
338
- })
339
- })
340
- this._dash.on(MediaPlayer.events.CUE_EXIT, (e: CueExitEvent) => {
341
- this.oncueexit?.({
342
- id: e.id,
343
- })
344
- })
345
328
  }
346
329
 
347
330
  render() {
@@ -725,6 +708,7 @@ export default class DashPlayback extends BasePlayback {
725
708
  }
726
709
 
727
710
  setTextTrack(id: number) {
711
+ this._currentTextTrackId = id
728
712
  this._dash?.setTextTrack(id)
729
713
  }
730
714
 
@@ -732,8 +716,7 @@ export default class DashPlayback extends BasePlayback {
732
716
  * @override
733
717
  */
734
718
  get closedCaptionsTracks() {
735
- const tt = this.getTextTracks()
736
- return tt;
719
+ return this.getTextTracks()
737
720
  }
738
721
 
739
722
  private getTextTracks() {
@@ -744,7 +727,7 @@ export default class DashPlayback extends BasePlayback {
744
727
  id: index,
745
728
  label: getTextTrackLabel(t) || "",
746
729
  language: t.lang,
747
- mode: "hidden",
730
+ mode: this._currentTextTrackId === index ? "showing" : "hidden",
748
731
  },
749
732
  })) || []
750
733
  }
@@ -1135,7 +1135,11 @@ export default class HlsPlayback extends BasePlayback {
1135
1135
  }
1136
1136
 
1137
1137
  setTextTrack(id: number) {
1138
+ if (id === this._hls!.subtitleTrack) {
1139
+ return
1140
+ }
1138
1141
  this._hls!.subtitleTrack = id
1142
+ this.cues = []
1139
1143
  }
1140
1144
 
1141
1145
  /**
@@ -5,11 +5,11 @@ import assert from 'assert'
5
5
  import { CLAPPR_VERSION } from '../../build.js'
6
6
  import type { TextTrackItem } from '../../internal.types.js'
7
7
 
8
- import '../../../assets/subtitles/style.scss'
8
+ import '../../../assets/cc/style.scss'
9
9
  import subtitlesOffIcon from '../../../assets/icons/new/subtitles-off.svg'
10
10
  import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg'
11
- import comboboxHTML from '../../../assets/subtitles/combobox.ejs'
12
- import stringHTML from '../../../assets/subtitles/string.ejs'
11
+ import comboboxHTML from '../../../assets/cc/combobox.ejs'
12
+ import stringHTML from '../../../assets/cc/string.ejs'
13
13
 
14
14
  import { isFullscreen } from '../utils/fullscreen.js'
15
15
  import type { ZeptoResult } from '../../types.js'
@@ -22,7 +22,7 @@ const VERSION: string = '2.19.14'
22
22
  // TODO review
23
23
  // const LOCAL_STORAGE_CC_ID = 'gplayer.plugins.cc.selected'
24
24
 
25
- const T = 'plugins.cc'
25
+ // const T = 'plugins.cc'
26
26
 
27
27
  /**
28
28
  * Configuration options for the {@link ClosedCaptions} plugin.
@@ -168,8 +168,12 @@ export class ClosedCaptions extends UICorePlugin {
168
168
  const mediaControl = this.core.getPlugin('media_control')
169
169
  assert(mediaControl, 'media_control plugin is required')
170
170
  this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.mount)
171
+ this.listenTo(mediaControl, Events.MEDIACONTROL_SHOW, () => {
172
+ this.$line?.removeClass('media-control-cc-pulled')
173
+ })
171
174
  this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, () => {
172
175
  this.hideMenu()
176
+ this.$line?.addClass('media-control-cc-pulled')
173
177
  })
174
178
  this.listenTo(
175
179
  mediaControl,
@@ -256,24 +260,28 @@ export class ClosedCaptions extends UICorePlugin {
256
260
  }
257
261
 
258
262
  private activateTrack(id: number) {
259
- if (this.core.activePlayback && ['dash', 'hls'].includes(this.core.activePlayback.name)) {
260
- this.core.activePlayback.setTextTrack(id)
263
+ const isManaged = this.core.activePlayback?.name === 'hls'
264
+ this.core.activePlayback.setTextTrack(id)
265
+ if (isManaged) {
266
+ return
267
+ }
268
+ if (!this.core.activePlayback?.el.textTracks) {
261
269
  return
262
270
  }
263
- for (const track of this.currentTracks) {
264
- if (track.id === id) {
271
+ for (const [index, track] of (Array.from(this.core.activePlayback?.el.textTracks ?? []) as TextTrack[]).entries()) {
272
+ if (index === id) {
265
273
  if (this.useNativeSubtitles) {
266
- track.track.mode = 'showing'
274
+ track.mode = 'showing'
267
275
  } else {
268
- track.track.mode = 'hidden'
276
+ track.mode = 'hidden'
269
277
  }
270
278
 
271
- this.setSubtitleText(this.getSubtitleText(track.track))
279
+ this.setSubtitleText(this.getSubtitleText(track))
272
280
 
273
- track.track.oncuechange = (e) => {
281
+ track.oncuechange = () => {
274
282
  try {
275
- if (track.track.activeCues?.length) {
276
- const html = (track.track.activeCues[0] as VTTCue).getCueAsHTML()
283
+ if (track.activeCues?.length) {
284
+ const html = (track.activeCues[0] as VTTCue).getCueAsHTML()
277
285
 
278
286
  this.setSubtitleText(html)
279
287
  } else {
@@ -284,8 +292,8 @@ export class ClosedCaptions extends UICorePlugin {
284
292
  }
285
293
  }
286
294
  } else {
287
- track.track.oncuechange = () => { }
288
- track.track.mode = 'disabled'
295
+ track.oncuechange = () => { }
296
+ track.mode = 'disabled'
289
297
  }
290
298
  }
291
299
  }
@@ -332,8 +340,10 @@ export class ClosedCaptions extends UICorePlugin {
332
340
  this.$el.find('#gplayer-cc-menu').hide()
333
341
  this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false')
334
342
  this.$line.hide()
335
- for (const track of this.currentTracks) {
336
- track.track.mode = 'hidden'
343
+ for (const track of this.core.activePlayback.el.textTracks) {
344
+ if (track.mode === 'showing') {
345
+ track.mode = 'hidden'
346
+ }
337
347
  }
338
348
  }
339
349
 
@@ -347,7 +357,6 @@ export class ClosedCaptions extends UICorePlugin {
347
357
  this.core.activeContainer &&
348
358
  isFullscreen(this.core.activeContainer.el) &&
349
359
  this.currentTrack &&
350
- // this.currentTrack.track.mode &&
351
360
  (Browser.isiOS || this.useNativeSubtitles)
352
361
  ) {
353
362
  this.$line.hide()
@@ -398,6 +407,10 @@ export class ClosedCaptions extends UICorePlugin {
398
407
  this.clampPopup()
399
408
 
400
409
  this.core.activeContainer.$el.append(this.$line)
410
+ const mc = this.core.getPlugin('media_control')
411
+ if (!mc?.isVisible()) {
412
+ this.$line?.addClass('media-control-cc-pulled')
413
+ }
401
414
 
402
415
  this.updateSelection()
403
416
 
@@ -452,6 +465,7 @@ export class ClosedCaptions extends UICorePlugin {
452
465
  } else {
453
466
  return;
454
467
  }
468
+
455
469
  setTimeout(() => {
456
470
  this.selectItem(
457
471
  this.tracks.find(matcher) ?? null,
@@ -526,7 +540,7 @@ export class ClosedCaptions extends UICorePlugin {
526
540
  }
527
541
 
528
542
  private updateSelection() {
529
- if (!this.currentTrack) {
543
+ if (this.core.activePlayback.closedCaptionsTrackId === -1) {
530
544
  this.hide()
531
545
  } else {
532
546
  this.show()