@gcorevideo/player 2.28.28 → 2.28.30

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 (39) hide show
  1. package/dist/core.js +128 -62
  2. package/dist/index.css +211 -211
  3. package/dist/index.embed.js +270 -190
  4. package/dist/index.js +241 -168
  5. package/lib/playback/BasePlayback.d.ts.map +1 -1
  6. package/lib/playback/dash-playback/DashPlayback.d.ts +22 -3
  7. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  8. package/lib/playback/dash-playback/DashPlayback.js +50 -5
  9. package/lib/playback/hls-playback/HlsPlayback.d.ts +32 -6
  10. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  11. package/lib/playback/hls-playback/HlsPlayback.js +78 -57
  12. package/lib/playback.types.d.ts +6 -0
  13. package/lib/playback.types.d.ts.map +1 -1
  14. package/lib/plugins/media-control/MediaControl.d.ts +5 -6
  15. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  16. package/lib/plugins/media-control/MediaControl.js +40 -38
  17. package/lib/plugins/subtitles/ClosedCaptions.d.ts +5 -0
  18. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
  19. package/lib/plugins/subtitles/ClosedCaptions.js +56 -54
  20. package/lib/plugins/utils/mobile.d.ts +2 -0
  21. package/lib/plugins/utils/mobile.d.ts.map +1 -0
  22. package/lib/plugins/utils/mobile.js +4 -0
  23. package/lib/testUtils.d.ts +3 -0
  24. package/lib/testUtils.d.ts.map +1 -1
  25. package/lib/testUtils.js +3 -0
  26. package/package.json +1 -1
  27. package/src/playback/BasePlayback.ts +2 -1
  28. package/src/playback/dash-playback/DashPlayback.ts +68 -13
  29. package/src/playback/hls-playback/HlsPlayback.ts +89 -80
  30. package/src/playback.types.ts +7 -0
  31. package/src/plugins/media-control/MediaControl.ts +45 -43
  32. package/src/plugins/media-control/__tests__/MediaControl.test.ts +63 -35
  33. package/src/plugins/multi-camera/MultiCamera.ts +5 -5
  34. package/src/plugins/subtitles/ClosedCaptions.ts +64 -57
  35. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +195 -196
  36. package/src/plugins/subtitles/__tests__/__snapshots__/ClosedCaptions.test.ts.snap +13 -1
  37. package/src/plugins/utils/mobile.ts +5 -0
  38. package/src/testUtils.ts +3 -0
  39. package/tsconfig.tsbuildinfo +1 -1
@@ -26,6 +26,8 @@ import { CLAPPR_VERSION } from '../../build.js'
26
26
  import { ZeptoResult } from '../../types.js'
27
27
  import { getPageX } from '../utils.js'
28
28
  import { fullscreenEnabled, isFullscreen } from '../utils/fullscreen.js'
29
+ import { isMobile } from '../utils/mobile.js'
30
+ import { mediaControlClickaway } from '../../utils/clickaway.js'
29
31
 
30
32
  import '../../../assets/media-control/media-control.scss'
31
33
 
@@ -37,7 +39,6 @@ import volumeMaxIcon from '../../../assets/icons/new/volume-max.svg'
37
39
  import volumeOffIcon from '../../../assets/icons/new/volume-off.svg'
38
40
  import fullscreenOffIcon from '../../../assets/icons/new/fullscreen-off.svg'
39
41
  import fullscreenOnIcon from '../../../assets/icons/new/fullscreen-on.svg'
40
- import { mediaControlClickaway } from '../../utils/clickaway.js'
41
42
 
42
43
  const STANDARD_MEDIA_CONTROL_ELEMENTS: string[] = [
43
44
  'duration',
@@ -352,8 +353,6 @@ export class MediaControl extends UICorePlugin {
352
353
  'touchmove .bar-container[data-seekbar]': 'mousemoveOnSeekBar',
353
354
  'mouseleave .bar-container[data-seekbar]': 'mouseleaveOnSeekBar',
354
355
  'touchend .bar-container[data-seekbar]': 'mouseleaveOnSeekBar',
355
- 'mouseenter .media-control-layer[data-controls]': 'setUserKeepVisible',
356
- 'mouseleave .media-control-layer[data-controls]': 'resetUserKeepVisible',
357
356
  }
358
357
  }
359
358
 
@@ -470,9 +469,6 @@ export class MediaControl extends UICorePlugin {
470
469
  Events.CONTAINER_DBLCLICK,
471
470
  this.toggleFullscreen,
472
471
  )
473
- this.listenTo(this.core.activeContainer, Events.CONTAINER_CLICK, () =>
474
- this.clickaway(this.core.activeContainer.$el[0]),
475
- )
476
472
  this.listenTo(
477
473
  this.core.activeContainer,
478
474
  Events.CONTAINER_TIMEUPDATE,
@@ -526,6 +522,7 @@ export class MediaControl extends UICorePlugin {
526
522
  )
527
523
  this.listenTo(this.core, Events.CONTAINER_DESTROYED, () => {
528
524
  this.cancelRenderTimer()
525
+ this.setKeepVisible(false)
529
526
  })
530
527
  this.listenTo(
531
528
  this.core.activeContainer,
@@ -537,10 +534,6 @@ export class MediaControl extends UICorePlugin {
537
534
  Events.CONTAINER_MOUSE_LEAVE,
538
535
  this.delayHide,
539
536
  )
540
-
541
- this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
542
- this.clickaway(null)
543
- })
544
537
  }
545
538
 
546
539
  /**
@@ -692,7 +685,7 @@ export class MediaControl extends UICorePlugin {
692
685
  this.$playPauseToggle?.append(playIcon)
693
686
  this.$playStopToggle?.append(playIcon)
694
687
  this.trigger(Events.MEDIACONTROL_NOTPLAYING)
695
- if (Browser.isMobile) {
688
+ if (isMobile()) {
696
689
  this.show()
697
690
  }
698
691
  }
@@ -745,7 +738,7 @@ export class MediaControl extends UICorePlugin {
745
738
  width: this.container.$el.width(),
746
739
  height: this.container.$el.height(),
747
740
  hideVolumeBar: this.options.hideVolumeBar,
748
- isMobile: Browser.isMobile,
741
+ isMobile: isMobile(),
749
742
  })
750
743
 
751
744
  try {
@@ -754,7 +747,7 @@ export class MediaControl extends UICorePlugin {
754
747
  this.$el.addClass('w370')
755
748
  }
756
749
 
757
- if (skinWidth <= 270 && !Browser.isMobile) {
750
+ if (skinWidth <= 270 && !isMobile()) {
758
751
  this.verticalVolume = true
759
752
  this.$el.addClass('w270')
760
753
  }
@@ -769,6 +762,18 @@ export class MediaControl extends UICorePlugin {
769
762
  return false
770
763
  }
771
764
 
765
+ private play() {
766
+ this.container && this.container.play()
767
+ }
768
+
769
+ private pause() {
770
+ this.container && this.container.pause()
771
+ }
772
+
773
+ private stop() {
774
+ this.container && this.container.stop()
775
+ }
776
+
772
777
  private togglePlayStop() {
773
778
  this.container.isPlaying()
774
779
  ? this.container.stop({ ui: true })
@@ -893,11 +898,11 @@ export class MediaControl extends UICorePlugin {
893
898
  }
894
899
 
895
900
  private toggleFullscreen() {
896
- if (!Browser.isMobile) {
901
+ if (!isMobile()) {
897
902
  this.trigger(Events.MEDIACONTROL_FULLSCREEN, this.name)
898
903
  this.core.activeContainer.fullscreen()
899
904
  this.core.toggleFullscreen()
900
- this.resetUserKeepVisible()
905
+ // this.resetUserKeepVisible()
901
906
  }
902
907
  }
903
908
 
@@ -1048,15 +1053,6 @@ export class MediaControl extends UICorePlugin {
1048
1053
  this.setSeekPercentage(pos)
1049
1054
  }
1050
1055
 
1051
- private setUserKeepVisible(e?: MouseEvent) {
1052
- this.userKeepVisible = true
1053
- this.clickaway(this.core.activeContainer.$el[0])
1054
- }
1055
-
1056
- private resetUserKeepVisible = (e?: MouseEvent) => {
1057
- this.userKeepVisible = false
1058
- }
1059
-
1060
1056
  private isVisible() {
1061
1057
  return !this.$el.hasClass('media-control-hide')
1062
1058
  }
@@ -1066,7 +1062,6 @@ export class MediaControl extends UICorePlugin {
1066
1062
  return
1067
1063
  }
1068
1064
 
1069
- const timeout = DEFAULT_HIDE_DELAY
1070
1065
  const mousePointerMoved =
1071
1066
  event &&
1072
1067
  event.clientX !== this.lastMouseX &&
@@ -1077,6 +1072,8 @@ export class MediaControl extends UICorePlugin {
1077
1072
  clearTimeout(this.hideId)
1078
1073
  this.hideId = null
1079
1074
  }
1075
+ this.hideId = setTimeout(() => this.hide(), DEFAULT_HIDE_DELAY)
1076
+
1080
1077
  this.$el.show()
1081
1078
  this.trigger(Events.MEDIACONTROL_SHOW, this.name)
1082
1079
  this.core.activeContainer?.trigger(
@@ -1084,15 +1081,13 @@ export class MediaControl extends UICorePlugin {
1084
1081
  this.name,
1085
1082
  )
1086
1083
  this.$el.removeClass('media-control-hide')
1087
- this.hideId = setTimeout(() => this.hide(), timeout)
1088
1084
  if (event) {
1089
1085
  this.lastMouseX = event.clientX
1090
1086
  this.lastMouseY = event.clientY
1091
1087
  }
1092
1088
  }
1093
- const showing = true
1094
1089
 
1095
- this.updateCursorStyle(showing)
1090
+ this.updateCursorStyle(true)
1096
1091
  }
1097
1092
 
1098
1093
  private hide(delay = 0) {
@@ -1100,10 +1095,9 @@ export class MediaControl extends UICorePlugin {
1100
1095
  return
1101
1096
  }
1102
1097
 
1103
- const timeout = delay || 2000
1104
-
1105
1098
  if (this.hideId !== null) {
1106
1099
  clearTimeout(this.hideId)
1100
+ this.hideId = null
1107
1101
  }
1108
1102
 
1109
1103
  if (!this.disabled && this.options.hideMediaControl === false) {
@@ -1117,7 +1111,7 @@ export class MediaControl extends UICorePlugin {
1117
1111
  !this.disabled &&
1118
1112
  (delay || hasKeepVisibleRequested || hasDraggingAction)
1119
1113
  ) {
1120
- this.hideId = setTimeout(() => this.hide(), timeout)
1114
+ this.hideId = setTimeout(() => this.hide(), delay || 2000)
1121
1115
  } else {
1122
1116
  if (!this.options.controlsDontHide || isFullscreen(this.container.el)) {
1123
1117
  this.trigger(Events.MEDIACONTROL_HIDE, this.name)
@@ -1278,18 +1272,13 @@ export class MediaControl extends UICorePlugin {
1278
1272
  /**
1279
1273
  * Set or reset the keep visibility state
1280
1274
  *
1281
- * Keep visibility state controls whether the media control is hidden automatically after a delay.
1282
- * Keep visibility prevents the the auto-hide behaviour
1275
+ * Keep visibility state controls whether the media control is hidden automatically after a delay, which is a default behaviour.
1283
1276
  *
1284
1277
  * @param keepVisible - The state
1285
1278
  */
1286
1279
  setKeepVisible(keepVisible: boolean) {
1287
1280
  this.keepVisible = keepVisible
1288
- if (keepVisible) {
1289
- this.clickaway(this.core.activeContainer.$el[0])
1290
- } else {
1291
- this.clickaway(null)
1292
- }
1281
+ this.clickaway(keepVisible ? this.core.activeContainer.$el[0] : null)
1293
1282
  }
1294
1283
 
1295
1284
  private getMountParent(name: MediaControlSlotMountPoint): ZeptoResult {
@@ -1390,7 +1379,7 @@ export class MediaControl extends UICorePlugin {
1390
1379
  }
1391
1380
 
1392
1381
  private bindKeyEvents() {
1393
- if (Browser.isMobile || this.options.disableKeyboardShortcuts) {
1382
+ if (isMobile() || this.options.disableKeyboardShortcuts) {
1394
1383
  return
1395
1384
  }
1396
1385
 
@@ -1491,6 +1480,7 @@ export class MediaControl extends UICorePlugin {
1491
1480
  * @internal
1492
1481
  */
1493
1482
  override destroy() {
1483
+ this.cancelTimers()
1494
1484
  this.cancelRenderTimer()
1495
1485
  $(document).unbind('mouseup', this.stopDrag)
1496
1486
  $(document).unbind('mousemove', this.updateDrag)
@@ -1500,6 +1490,18 @@ export class MediaControl extends UICorePlugin {
1500
1490
  return super.destroy()
1501
1491
  }
1502
1492
 
1493
+ private cancelTimers() {
1494
+ if (this.hideId !== null) {
1495
+ clearTimeout(this.hideId)
1496
+ this.hideId = null
1497
+ }
1498
+ if (this.hideVolumeId !== null) {
1499
+ clearTimeout(this.hideVolumeId)
1500
+ this.hideVolumeId = null
1501
+ }
1502
+ this.cancelRenderTimer()
1503
+ }
1504
+
1503
1505
  private cancelRenderTimer() {
1504
1506
  if (this.renderTimerId) {
1505
1507
  clearTimeout(this.renderTimerId)
@@ -1537,7 +1539,7 @@ export class MediaControl extends UICorePlugin {
1537
1539
 
1538
1540
  // Video volume cannot be changed with Safari on mobile devices
1539
1541
  // Display mute/unmute icon only if Safari version >= 10
1540
- if (Browser.isSafari && Browser.isMobile) {
1542
+ if (Browser.isSafari && isMobile()) {
1541
1543
  if (Browser.version < 10) {
1542
1544
  this.$volumeContainer?.css({ display: 'none' })
1543
1545
  } else {
@@ -1557,7 +1559,7 @@ export class MediaControl extends UICorePlugin {
1557
1559
  setTimeout(() => {
1558
1560
  !this.settings.seekEnabled &&
1559
1561
  this.$seekBarContainer?.addClass('seek-disabled')
1560
- !Browser.isMobile &&
1562
+ !isMobile() &&
1561
1563
  !this.options.disableKeyboardShortcuts &&
1562
1564
  this.bindKeyEvents()
1563
1565
  this.playerResize({
@@ -1587,6 +1589,7 @@ export class MediaControl extends UICorePlugin {
1587
1589
  this.container.fullscreen()
1588
1590
  // TODO: fix after it full screen will be fixed on iOS
1589
1591
  if (Browser.isiOS) {
1592
+ // TODO use isFullscreen utility function
1590
1593
  if (this.core.isFullscreen()) {
1591
1594
  Fullscreen.cancelFullscreen(this.core.el)
1592
1595
  } else {
@@ -1595,7 +1598,6 @@ export class MediaControl extends UICorePlugin {
1595
1598
  } else {
1596
1599
  this.core.toggleFullscreen()
1597
1600
  }
1598
- this.resetUserKeepVisible()
1599
1601
  }
1600
1602
 
1601
1603
  private static getPageX(event: MouseEvent | TouchEvent): number {
@@ -1653,7 +1655,7 @@ export class MediaControl extends UICorePlugin {
1653
1655
  this.hide(this.options.hideMediaControlDelay || DEFAULT_HIDE_DELAY)
1654
1656
  }
1655
1657
 
1656
- private clickaway = mediaControlClickaway(() => this.resetUserKeepVisible())
1658
+ private clickaway = mediaControlClickaway(() => this.setKeepVisible(false))
1657
1659
  }
1658
1660
 
1659
1661
  MediaControl.extend = function (properties) {
@@ -237,41 +237,38 @@ describe('MediaControl', () => {
237
237
  )
238
238
  expect(el.length).toEqual(0)
239
239
  })
240
- it(`should ${
241
- settings.seekEnabled ? '' : 'not '
242
- }render the seek bar`, () => {
243
- const seekbar = mediaControl.$el.find(
244
- '.media-control-center-panel [data-seekbar]',
245
- )
246
- expect(seekbar.length).toBeGreaterThan(1)
247
- if (settings.seekEnabled) {
248
- expect(seekbar.hasClass('seek-disabled')).toBe(false)
249
- } else {
250
- expect(seekbar.hasClass('seek-disabled')).toBe(true)
251
- }
252
- })
253
- it(`should ${
254
- settings.left.includes('volume') ? '' : 'not '
255
- }render the volume control`, () => {
256
- const volume = mediaControl.$el.find('.drawer-container[data-volume]')
257
- if (settings.left.includes('volume')) {
258
- expect(volume.length).toEqual(1)
259
- } else {
260
- expect(volume.length).toEqual(0)
261
- }
262
- })
263
- it(`should ${
264
- settings.right.includes('fullscreen') ? '' : 'not '
265
- }render the fullscreen control`, () => {
266
- const fullscreen = mediaControl.$el.find(
267
- '.media-control-right-panel [data-fullscreen]',
268
- )
269
- if (settings.right.includes('fullscreen')) {
270
- expect(fullscreen.length).toEqual(1)
271
- } else {
272
- expect(fullscreen.length).toEqual(0)
273
- }
274
- })
240
+ it(`should ${settings.seekEnabled ? '' : 'not '
241
+ }render the seek bar`, () => {
242
+ const seekbar = mediaControl.$el.find(
243
+ '.media-control-center-panel [data-seekbar]',
244
+ )
245
+ expect(seekbar.length).toBeGreaterThan(1)
246
+ if (settings.seekEnabled) {
247
+ expect(seekbar.hasClass('seek-disabled')).toBe(false)
248
+ } else {
249
+ expect(seekbar.hasClass('seek-disabled')).toBe(true)
250
+ }
251
+ })
252
+ it(`should ${settings.left.includes('volume') ? '' : 'not '
253
+ }render the volume control`, () => {
254
+ const volume = mediaControl.$el.find('.drawer-container[data-volume]')
255
+ if (settings.left.includes('volume')) {
256
+ expect(volume.length).toEqual(1)
257
+ } else {
258
+ expect(volume.length).toEqual(0)
259
+ }
260
+ })
261
+ it(`should ${settings.right.includes('fullscreen') ? '' : 'not '
262
+ }render the fullscreen control`, () => {
263
+ const fullscreen = mediaControl.$el.find(
264
+ '.media-control-right-panel [data-fullscreen]',
265
+ )
266
+ if (settings.right.includes('fullscreen')) {
267
+ expect(fullscreen.length).toEqual(1)
268
+ } else {
269
+ expect(fullscreen.length).toEqual(0)
270
+ }
271
+ })
275
272
  })
276
273
  describe('basically', () => {
277
274
  let handler: MockedFunction<() => void>
@@ -338,6 +335,37 @@ describe('MediaControl', () => {
338
335
  expect(mediaControl.el.innerHTML).toMatchSnapshot()
339
336
  })
340
337
  })
338
+ describe('auto-hide', () => {
339
+ beforeEach(async () => {
340
+ mediaControl = new MediaControl(core)
341
+ core.emit(Events.CORE_READY)
342
+ core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
343
+ await runMetadataLoaded(core)
344
+ })
345
+ describe('when rendered', () => {
346
+ it('should auto-hide', async () => {
347
+ expect(mediaControl.$el.hasClass('media-control-hide')).toBe(false)
348
+ await new Promise((resolve) => setTimeout(resolve, 2000))
349
+ expect(mediaControl.$el.hasClass('media-control-hide')).toBe(true)
350
+ })
351
+ })
352
+ describe('after rendered', () => {
353
+ beforeEach(async () => {
354
+ await new Promise((resolve) => setTimeout(resolve, 2000))
355
+ })
356
+ describe('after user interaction', () => {
357
+ beforeEach(async () => {
358
+ core.activeContainer.trigger(Events.CONTAINER_MOUSE_ENTER)
359
+ await new Promise((resolve) => setTimeout(resolve, 0))
360
+ })
361
+ it('should auto-hide', async () => {
362
+ expect(mediaControl.$el.hasClass('media-control-hide')).toBe(false)
363
+ await new Promise((resolve) => setTimeout(resolve, 2000))
364
+ expect(mediaControl.$el.hasClass('media-control-hide')).toBe(true)
365
+ })
366
+ })
367
+ })
368
+ })
341
369
  })
342
370
 
343
371
  function arraySubtract<T extends string>(arr1: T[], arr2: T[]) {
@@ -457,11 +457,11 @@ export class MultiCamera extends UICorePlugin {
457
457
  }
458
458
 
459
459
  private hideSelectLevelMenu() {
460
- ;(this.$('.multicamera ul') as ZeptoResult).hide()
460
+ ; (this.$('.multicamera ul') as ZeptoResult).hide()
461
461
  }
462
462
 
463
463
  private toggleContextMenu() {
464
- ;(this.$('.multicamera ul') as ZeptoResult).toggle()
464
+ ; (this.$('.multicamera ul') as ZeptoResult).toggle()
465
465
  }
466
466
 
467
467
  // private buttonElement(): ZeptoResult {
@@ -475,9 +475,9 @@ export class MultiCamera extends UICorePlugin {
475
475
  private levelElement(id?: number): ZeptoResult {
476
476
  return this.$(
477
477
  '.multicamera ul li > div' +
478
- (id !== undefined
479
- ? '[data-multicamera-selector-select="' + id + '"]'
480
- : ''),
478
+ (id !== undefined
479
+ ? '[data-multicamera-selector-select="' + id + '"]'
480
+ : ''),
481
481
  )
482
482
  }
483
483
 
@@ -15,6 +15,7 @@ import { isFullscreen } from '../utils/fullscreen.js'
15
15
  import type { ZeptoResult } from '../../types.js'
16
16
  import { ExtendedEvents } from '../media-control/MediaControl.js'
17
17
  import { mediaControlClickaway } from '../../utils/clickaway.js'
18
+ import { VTTCueInfo } from '../../playback.types.js'
18
19
 
19
20
  const VERSION: string = '2.19.14'
20
21
 
@@ -153,6 +154,8 @@ export class ClosedCaptions extends UICorePlugin {
153
154
  Events.CORE_ACTIVE_CONTAINER_CHANGED,
154
155
  this.onContainerChanged,
155
156
  )
157
+ this.listenTo(this.core, Events.PLAYBACK_READY, this.onPlaybackReady)
158
+
156
159
  }
157
160
 
158
161
  private onCoreReady() {
@@ -171,9 +174,11 @@ export class ClosedCaptions extends UICorePlugin {
171
174
  }
172
175
  },
173
176
  )
177
+ this.onPlaybackReady()
174
178
  }
175
179
 
176
180
  private onContainerChanged() {
181
+ this.tracks = []
177
182
  this.listenTo(
178
183
  this.core.activeContainer,
179
184
  Events.CONTAINER_FULLSCREEN,
@@ -187,11 +192,6 @@ export class ClosedCaptions extends UICorePlugin {
187
192
  this.listenTo(this.core.activeContainer, Events.CONTAINER_DESTROYED, () => {
188
193
  this.clickaway(null)
189
194
  })
190
- // this.listenTo(
191
- // this.core.activeContainer,
192
- // 'container:advertisement:start',
193
- // this.onStartAd,
194
- // )
195
195
  this.listenTo(
196
196
  this.core.activePlayback,
197
197
  Events.PLAYBACK_SUBTITLE_AVAILABLE,
@@ -226,21 +226,24 @@ export class ClosedCaptions extends UICorePlugin {
226
226
  this.isPreselectedApplied = false
227
227
  }
228
228
 
229
+ private onPlaybackReady() {
230
+ trace(`${T} onPlaybackReady`)
231
+ this.core.activePlayback.oncueenter = (e: VTTCueInfo) => {
232
+ this.setSubtitleText(e.text)
233
+ }
234
+ this.core.activePlayback.oncueexit = () => {
235
+ this.clearSubtitleText()
236
+ }
237
+ }
238
+
229
239
  private onSubtitleAvailable() {
230
- trace(`${T} onSubtitleAvailable`, {
231
- tracks: this.core.activePlayback.closedCaptionsTracks.length,
232
- })
233
240
  this.applyTracks()
234
241
  this.mount()
235
242
  }
236
243
 
237
244
  private onSubtitleChanged({ id: changedId }: { id: number }) {
238
245
  // ignoring the subtitle selected by the playback engine or user agent
239
- const id = this.track?.id ?? -1
240
- trace(`${T} onSubtitleChanged`, {
241
- changedId,
242
- id,
243
- })
246
+ const id = this.currentTrack?.id ?? -1
244
247
  if (id === -1) {
245
248
  this.clearSubtitleText()
246
249
  }
@@ -248,7 +251,11 @@ export class ClosedCaptions extends UICorePlugin {
248
251
  }
249
252
 
250
253
  private activateTrack(id: number) {
251
- for (const track of this.tracks) {
254
+ if (['dash', 'hls'].includes(this.core.activePlayback?.name)) {
255
+ this.core.activePlayback.setTextTrack(id)
256
+ return
257
+ }
258
+ for (const track of this.currentTracks) {
252
259
  if (track.id === id) {
253
260
  if (this.useNativeSubtitles) {
254
261
  track.track.mode = 'showing'
@@ -272,7 +279,7 @@ export class ClosedCaptions extends UICorePlugin {
272
279
  }
273
280
  }
274
281
  } else {
275
- track.track.oncuechange = null
282
+ track.track.oncuechange = () => { }
276
283
  track.track.mode = 'disabled'
277
284
  }
278
285
  }
@@ -280,7 +287,8 @@ export class ClosedCaptions extends UICorePlugin {
280
287
 
281
288
  private applyTracks() {
282
289
  try {
283
- this.tracks = this.core.activePlayback.closedCaptionsTracks
290
+ // TODO ensure to apply only once
291
+ this.currentTracks = this.core.activePlayback.closedCaptionsTracks
284
292
  this.applyPreselectedSubtitles()
285
293
  this.render()
286
294
  } catch (error) {
@@ -288,32 +296,12 @@ export class ClosedCaptions extends UICorePlugin {
288
296
  }
289
297
  }
290
298
 
291
- // private onStartAd() {
292
- // if (this.active && this.core.activeContainer) {
293
- // this.hide()
294
- // this.listenTo(
295
- // this.core.activeContainer,
296
- // 'container:advertisement:finish',
297
- // this.onFinishAd,
298
- // )
299
- // }
300
- // }
301
-
302
- // private onFinishAd() {
303
- // this.show()
304
- // this.stopListening(
305
- // this.core.activeContainer,
306
- // 'container:advertisement:finish',
307
- // this.onFinishAd,
308
- // )
309
- // }
310
-
311
299
  private onContainerResize() {
312
300
  const shouldShow =
313
301
  this.core.activeContainer &&
314
302
  isFullscreen(this.core.activeContainer.el) &&
315
- this.track &&
316
- this.track.track.mode &&
303
+ this.currentTrack &&
304
+ this.currentTrack.track.mode &&
317
305
  Browser.isiOS &&
318
306
  this.active
319
307
 
@@ -339,10 +327,8 @@ export class ClosedCaptions extends UICorePlugin {
339
327
  this.$el.find('#gplayer-cc-menu').hide()
340
328
  this.$el.find('#gplayer-cc-button').attr('aria-expanded', 'false')
341
329
  this.$line.hide()
342
- if (this.tracks) {
343
- for (const t of this.tracks) {
344
- t.track.mode = 'hidden'
345
- }
330
+ for (const track of this.currentTracks) {
331
+ track.track.mode = 'hidden'
346
332
  }
347
333
  }
348
334
 
@@ -355,19 +341,20 @@ export class ClosedCaptions extends UICorePlugin {
355
341
  if (
356
342
  this.core.activeContainer &&
357
343
  isFullscreen(this.core.activeContainer.el) &&
358
- this.track &&
359
- this.track.track.mode &&
344
+ this.currentTrack &&
345
+ // this.currentTrack.track.mode &&
360
346
  (Browser.isiOS || this.useNativeSubtitles)
361
347
  ) {
362
348
  this.$line.hide()
363
- this.track.track.mode = 'showing'
349
+ this.currentTrack.track.mode = 'showing'
364
350
  } else {
365
351
  this.$line.show()
366
352
  }
367
353
  }
368
354
 
369
355
  private shouldRender() {
370
- return this.tracks?.length > 0
356
+ // this might not have been fully initialized yet since `render` is called from the parent class constructor
357
+ return this.currentTracks?.length > 0
371
358
  }
372
359
 
373
360
  private resizeFont() {
@@ -384,15 +371,18 @@ export class ClosedCaptions extends UICorePlugin {
384
371
  * @internal
385
372
  */
386
373
  override render() {
374
+ if (!this.shouldRender()) {
375
+ return this
376
+ }
387
377
  if (!this.core.activeContainer) {
388
378
  return this
389
379
  }
390
380
 
391
381
  this.$el.html(
392
382
  ClosedCaptions.templateControl({
393
- tracks: this.tracks ?? [],
383
+ tracks: this.currentTracks,
394
384
  i18n: this.core.i18n,
395
- current: this.track?.id ?? -1,
385
+ current: this.currentTrack?.id ?? -1,
396
386
  }),
397
387
  )
398
388
  this.$el.find('#gplayer-cc-menu').hide()
@@ -411,13 +401,13 @@ export class ClosedCaptions extends UICorePlugin {
411
401
  return this
412
402
  }
413
403
 
414
- private findById(id: number) {
415
- return this.tracks.find((track) => track.id === id) ?? null
404
+ private findById(id: number): TextTrackItem | null {
405
+ return this.currentTracks.find((track) => track.id === id) || null
416
406
  }
417
407
 
418
408
  private selectItem(item: TextTrackItem | null) {
419
409
  this.clearSubtitleText()
420
- this.track = item
410
+ this.currentTrack = item
421
411
  const trackId = item?.id ?? -1
422
412
  this.core.activePlayback.closedCaptionsTrackId = trackId
423
413
 
@@ -489,7 +479,7 @@ export class ClosedCaptions extends UICorePlugin {
489
479
  }
490
480
 
491
481
  private selectSubtitles() {
492
- const trackId = this.track ? this.track.id : -1
482
+ const trackId = this.currentTrack?.id ?? -1
493
483
 
494
484
  // TODO find out if this is needed
495
485
  this.core.activePlayback.closedCaptionsTrackId = trackId
@@ -522,7 +512,7 @@ export class ClosedCaptions extends UICorePlugin {
522
512
  }
523
513
 
524
514
  private updateSelection() {
525
- if (!this.track) {
515
+ if (!this.currentTrack) {
526
516
  this.hide()
527
517
  } else {
528
518
  this.show()
@@ -539,7 +529,7 @@ export class ClosedCaptions extends UICorePlugin {
539
529
  .attr('aria-checked', 'false')
540
530
 
541
531
  const currentLevelElement = this.itemElement(
542
- this.track ? this.track.id : -1,
532
+ this.currentTrack?.id ?? -1,
543
533
  )
544
534
  currentLevelElement
545
535
  .addClass('current')
@@ -569,9 +559,6 @@ export class ClosedCaptions extends UICorePlugin {
569
559
  }
570
560
 
571
561
  private get useNativeSubtitles() {
572
- if (this.core.activePlayback?.name === 'dash') {
573
- return true
574
- }
575
562
  const mode =
576
563
  this.core.options.cc?.mode ??
577
564
  this.core.options.subtitles?.mode ??
@@ -580,5 +567,25 @@ export class ClosedCaptions extends UICorePlugin {
580
567
  return mode === 'native'
581
568
  }
582
569
 
570
+ private get currentTracks(): TextTrackItem[] {
571
+ return this.tracks
572
+ }
573
+
574
+ private get currentTrack(): TextTrackItem | null {
575
+ return this.track
576
+ }
577
+
578
+ private set currentTrack(track: TextTrackItem | null) {
579
+ this.track = track
580
+ }
581
+
582
+ private set currentTracks(tracks: TextTrackItem[]) {
583
+ this.tracks = tracks.map(track => ({
584
+ id: track.id,
585
+ name: !track.name || track.name === "null" ? track.track.language : track.name,
586
+ track: track.track,
587
+ }))
588
+ }
589
+
583
590
  private clickaway = mediaControlClickaway(() => this.hideMenu())
584
591
  }