@gcorevideo/player 2.24.11 → 2.24.14

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 (47) hide show
  1. package/README.md +1 -0
  2. package/assets/dvr-controls/dvr_controls.scss +35 -73
  3. package/assets/dvr-controls/index.ejs +8 -2
  4. package/assets/media-control/width370.scss +1 -1
  5. package/dist/core.js +18 -17
  6. package/dist/index.css +867 -896
  7. package/dist/index.embed.js +91 -65
  8. package/dist/index.js +156 -128
  9. package/dist/player.d.ts +3264 -0
  10. package/lib/playback/dash-playback/DashPlayback.d.ts +2 -0
  11. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  12. package/lib/playback/dash-playback/DashPlayback.js +17 -11
  13. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  14. package/lib/playback/hls-playback/HlsPlayback.js +0 -5
  15. package/lib/plugins/audio-selector/AudioTracks.js +1 -1
  16. package/lib/plugins/bottom-gear/BottomGear.js +1 -1
  17. package/lib/plugins/clips/Clips.js +1 -1
  18. package/lib/plugins/dvr-controls/DvrControls.d.ts +3 -3
  19. package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
  20. package/lib/plugins/dvr-controls/DvrControls.js +14 -12
  21. package/lib/plugins/media-control/MediaControl.d.ts +14 -5
  22. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  23. package/lib/plugins/media-control/MediaControl.js +55 -29
  24. package/lib/plugins/picture-in-picture/PictureInPicture.js +1 -1
  25. package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
  26. package/lib/testUtils.d.ts.map +1 -1
  27. package/lib/testUtils.js +2 -0
  28. package/package.json +1 -1
  29. package/src/playback/dash-playback/DashPlayback.ts +20 -13
  30. package/src/playback/hls-playback/HlsPlayback.ts +3 -8
  31. package/src/plugins/audio-selector/AudioTracks.ts +1 -1
  32. package/src/plugins/audio-selector/__tests__/AudioTracks.test.ts +2 -2
  33. package/src/plugins/bottom-gear/BottomGear.ts +1 -1
  34. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +2 -2
  35. package/src/plugins/clips/Clips.ts +1 -1
  36. package/src/plugins/clips/__tests__/Clips.test.ts +1 -1
  37. package/src/plugins/dvr-controls/DvrControls.ts +14 -14
  38. package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +20 -17
  39. package/src/plugins/dvr-controls/__tests__/__snapshots__/DvrControls.test.ts.snap +4 -2
  40. package/src/plugins/media-control/MediaControl.ts +69 -35
  41. package/src/plugins/media-control/__tests__/MediaControl.test.ts +128 -112
  42. package/src/plugins/media-control/__tests__/__snapshots__/MediaControl.test.ts.snap +227 -24
  43. package/src/plugins/picture-in-picture/PictureInPicture.ts +1 -1
  44. package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
  45. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -1
  46. package/src/testUtils.ts +2 -0
  47. package/tsconfig.tsbuildinfo +1 -1
@@ -504,12 +504,7 @@ export default class HlsPlayback extends BasePlayback {
504
504
  }
505
505
 
506
506
  protected override _ready() {
507
- trace(`${T} _ready`, {
508
- isReadyState: this._isReadyState, // is defined in HTML5Video
509
- })
510
507
  !this._hls && this._setup()
511
- // this._isReadyState = true
512
- // this.trigger(Events.PLAYBACK_READY, this.name)
513
508
  super._ready()
514
509
  }
515
510
 
@@ -818,13 +813,13 @@ export default class HlsPlayback extends BasePlayback {
818
813
  this.trigger(Events.PLAYBACK_PROGRESS, progress, buffered)
819
814
  }
820
815
 
821
- load(url: string) {
816
+ override load(url: string) {
822
817
  this._stopTimeUpdateTimer()
823
818
  this.options.src = url
824
819
  this._setup()
825
820
  }
826
821
 
827
- play() {
822
+ override play() {
828
823
  !this._hls && this._setup()
829
824
  !this._manifestParsed &&
830
825
  !this.options.hlsPlayback.preload &&
@@ -834,7 +829,7 @@ export default class HlsPlayback extends BasePlayback {
834
829
  this._startTimeUpdateTimer()
835
830
  }
836
831
 
837
- pause() {
832
+ override pause() {
838
833
  if (!this._hls) {
839
834
  return
840
835
  }
@@ -88,7 +88,7 @@ export class AudioTracks extends UICorePlugin {
88
88
  const mediaControl = this.core.getPlugin('media_control')
89
89
  assert(mediaControl, 'media_control plugin is required')
90
90
  this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, () => {
91
- mediaControl.mount('audiotracks', this.$el)
91
+ mediaControl.slot('audiotracks', this.$el)
92
92
  })
93
93
  this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideMenu)
94
94
  this.listenTo(mediaControl, ExtendedEvents.MEDIACONTROL_MENU_COLLAPSE, (from: string) => {
@@ -34,7 +34,7 @@ describe('AudioTracks', () => {
34
34
  emitTracksAvailable(core, TRACKS)
35
35
  })
36
36
  it('should not attach to the media control', () => {
37
- expect(mediaControl.mount).not.toHaveBeenCalledWith(
37
+ expect(mediaControl.slot).not.toHaveBeenCalledWith(
38
38
  'audiotracks',
39
39
  expect.anything(),
40
40
  )
@@ -45,7 +45,7 @@ describe('AudioTracks', () => {
45
45
  mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
46
46
  })
47
47
  it('should attach to the media control', () => {
48
- expect(mediaControl.mount).toHaveBeenCalledWith(
48
+ expect(mediaControl.slot).toHaveBeenCalledWith(
49
49
  'audiotracks',
50
50
  audioTracks.$el,
51
51
  )
@@ -320,7 +320,7 @@ export class BottomGear extends UICorePlugin {
320
320
 
321
321
  private mount() {
322
322
  const mediaControl = this.core.getPlugin('media_control')
323
- mediaControl.mount('gear', this.$el)
323
+ mediaControl.slot('gear', this.$el)
324
324
  }
325
325
 
326
326
  private alignSubmenu($subMenu: ZeptoResult) {
@@ -61,7 +61,7 @@ describe('BottomGear', () => {
61
61
  })
62
62
  describe('until media control is rendered', () => {
63
63
  it('should not attach to media control', () => {
64
- expect(mediaControl.mount).not.toHaveBeenCalledWith(
64
+ expect(mediaControl.slot).not.toHaveBeenCalledWith(
65
65
  'gear',
66
66
  expect.anything(),
67
67
  )
@@ -72,7 +72,7 @@ describe('BottomGear', () => {
72
72
  mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
73
73
  })
74
74
  it('should attach to media control', () => {
75
- expect(mediaControl.mount).toHaveBeenCalledWith('gear', bottomGear.$el)
75
+ expect(mediaControl.slot).toHaveBeenCalledWith('gear', bottomGear.$el)
76
76
  })
77
77
  })
78
78
  describe('when clicked', () => {
@@ -139,7 +139,7 @@ export class Clips extends UICorePlugin {
139
139
  private onMcRender() {
140
140
  trace(`${T} onMcRender`)
141
141
  const mediaControl = this.core.getPlugin('media_control')
142
- mediaControl.mount('clips', this.$el)
142
+ mediaControl.slot('clips', this.$el)
143
143
  }
144
144
 
145
145
  private onContainerChanged() {
@@ -66,7 +66,7 @@ describe('Clips', () => {
66
66
  mediaControl.trigger(Events.MEDIACONTROL_RENDERED)
67
67
  })
68
68
  it('should mount the indicator', () => {
69
- expect(mediaControl.mount).toHaveBeenCalledWith('clips', clips.$el)
69
+ expect(mediaControl.slot).toHaveBeenCalledWith('clips', clips.$el)
70
70
  })
71
71
  })
72
72
  })
@@ -1,14 +1,14 @@
1
1
  import { Events, Playback, UICorePlugin, template } from '@clappr/core'
2
2
  import assert from 'assert'
3
+ import { trace } from '@gcorevideo/utils'
3
4
 
4
5
  import { CLAPPR_VERSION } from '../../build.js'
5
6
 
6
7
  import dvrHTML from '../../../assets/dvr-controls/index.ejs'
7
8
  import '../../../assets/dvr-controls/dvr_controls.scss'
8
- // import { trace } from '@gcorevideo/utils'
9
9
  import { MediaControl } from '../media-control/MediaControl.js'
10
10
 
11
- // const T = 'plugins.dvr_controls'
11
+ const T = 'plugins.dvr_controls'
12
12
 
13
13
  /**
14
14
  * `PLUGIN` that adds the DVR controls to the media control UI
@@ -46,7 +46,7 @@ export class DvrControls extends UICorePlugin {
46
46
  */
47
47
  override get events() {
48
48
  return {
49
- 'click .live-button': 'click',
49
+ 'click #gplayer-mc-back-to-live': 'clicked',
50
50
  }
51
51
  }
52
52
 
@@ -56,6 +56,7 @@ export class DvrControls extends UICorePlugin {
56
56
  override get attributes() {
57
57
  return {
58
58
  class: 'dvr-controls',
59
+ 'data-dvr': '',
59
60
  }
60
61
  }
61
62
 
@@ -91,7 +92,7 @@ export class DvrControls extends UICorePlugin {
91
92
  )
92
93
  }
93
94
 
94
- private click() {
95
+ private clicked() {
95
96
  const container = this.core.activeContainer
96
97
  if (!container.isPlaying()) {
97
98
  container.play()
@@ -103,19 +104,18 @@ export class DvrControls extends UICorePlugin {
103
104
  * @internal
104
105
  */
105
106
  override render() {
107
+ trace(`${T} render`)
106
108
  this.$el.html(
107
109
  DvrControls.template({
108
110
  i18n: this.core.i18n,
109
111
  }),
110
112
  )
113
+ this.$el.find('#gplayer-mc-back-to-live').hide()
114
+ this.$el.find('#gplayer-mc-live').hide()
111
115
 
112
116
  return this
113
117
  }
114
118
 
115
- private onMediacontrolRendered() {
116
- this.render()
117
- }
118
-
119
119
  private onMetadataLoaded() {
120
120
  this.mount()
121
121
  this.toggleState(this.core.activeContainer.isDvrInUse())
@@ -128,10 +128,10 @@ export class DvrControls extends UICorePlugin {
128
128
  }
129
129
  const mediaControl = this.core.getPlugin('media_control') as MediaControl
130
130
  assert(mediaControl, 'media_control plugin is required')
131
- // TODO -> to MediaControl
131
+ // TODO -> to MediaControl (auto hide)
132
132
  mediaControl.toggleElement('duration', false)
133
133
  mediaControl.toggleElement('position', false)
134
- mediaControl.mount('dvr', this.$el)
134
+ mediaControl.mount('left', this.$el) // TODO use independent mount point
135
135
  }
136
136
 
137
137
  private onDvrStateChanged(dvrInUse: boolean) {
@@ -140,11 +140,11 @@ export class DvrControls extends UICorePlugin {
140
140
 
141
141
  private toggleState(dvrInUse: boolean) {
142
142
  if (dvrInUse) {
143
- this.$el.find('#media-control-back-to-live').show()
144
- this.$el.find('#media-control-live').hide()
143
+ this.$el.find('#gplayer-mc-back-to-live').show()
144
+ this.$el.find('#gplayer-mc-live').hide()
145
145
  } else {
146
- this.$el.find('#media-control-back-to-live').hide()
147
- this.$el.find('#media-control-live').show()
146
+ this.$el.find('#gplayer-mc-back-to-live').hide()
147
+ this.$el.find('#gplayer-mc-live').show()
148
148
  }
149
149
  }
150
150
  }
@@ -33,10 +33,7 @@ describe('DvrControls', () => {
33
33
  })
34
34
  describe('while playback type is unknown', () => {
35
35
  it('should not mount', () => {
36
- expect(mediaControl.mount).not.toHaveBeenCalledWith(
37
- 'dvr',
38
- dvrControls.$el,
39
- )
36
+ expect(mediaControl.slot).not.toHaveBeenCalledWith('dvr', dvrControls.$el)
40
37
  })
41
38
  })
42
39
  describe('live stream', () => {
@@ -75,33 +72,29 @@ describe('DvrControls', () => {
75
72
  )
76
73
  })
77
74
  it('should mount to the media control', () => {
78
- expect(mediaControl.mount).toHaveBeenCalledWith('dvr', dvrControls.$el)
75
+ expect(mediaControl.mount).toHaveBeenCalledWith('left', dvrControls.$el)
79
76
  })
80
77
  if (dvrEnabled) {
81
78
  if (dvrInUse) {
82
79
  it('should show back_to_live button', () => {
83
80
  expect(
84
- dvrControls.$el
85
- .find('#media-control-back-to-live')
86
- .css('display'),
81
+ dvrControls.$el.find('#gplayer-mc-back-to-live').css('display'),
87
82
  ).not.toBe('none')
88
83
  })
89
- it('should hide live inficator', () => {
84
+ it('should hide live indicator', () => {
90
85
  expect(
91
- dvrControls.$el.find('#media-control-live').css('display'),
86
+ dvrControls.$el.find('#gplayer-mc-live').css('display'),
92
87
  ).toBe('none')
93
88
  })
94
89
  } else {
95
- it('should show live inficator', () => {
90
+ it('should show live indicator', () => {
96
91
  expect(
97
- dvrControls.$el.find('#media-control-live').css('display'),
92
+ dvrControls.$el.find('#gplayer-mc-live').css('display'),
98
93
  ).not.toBe('none')
99
94
  })
100
95
  it('should hide back_to_live button', () => {
101
96
  expect(
102
- dvrControls.$el
103
- .find('#media-control-back-to-live')
104
- .css('display'),
97
+ dvrControls.$el.find('#gplayer-mc-back-to-live').css('display'),
105
98
  ).toBe('none')
106
99
  })
107
100
  }
@@ -118,7 +111,7 @@ describe('DvrControls', () => {
118
111
  Events.CONTAINER_PLAYBACKDVRSTATECHANGED,
119
112
  true,
120
113
  )
121
- dvrControls.$el.find('#media-control-back-to-live').click()
114
+ dvrControls.$el.find('#gplayer-mc-back-to-live').click()
122
115
  })
123
116
  it('should play stream', () => {
124
117
  expect(core.activeContainer.play).toHaveBeenCalled()
@@ -139,9 +132,19 @@ describe('DvrControls', () => {
139
132
  // TODO handle mount points in MediaControl
140
133
  it('should not mount', () => {
141
134
  expect(mediaControl.mount).not.toHaveBeenCalledWith(
142
- 'dvr',
135
+ 'left',
143
136
  expect.anything(),
144
137
  )
145
138
  })
139
+ it('should not alter position and duration indicators', () => {
140
+ expect(mediaControl.toggleElement).not.toHaveBeenCalledWith(
141
+ 'duration',
142
+ false,
143
+ )
144
+ expect(mediaControl.toggleElement).not.toHaveBeenCalledWith(
145
+ 'position',
146
+ false,
147
+ )
148
+ })
146
149
  })
147
150
  })
@@ -1,7 +1,9 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`DvrControls > basically > should render 1`] = `
4
- "<div class="live-info" id="media-control-live">live</div>
5
- <button type="button" class="live-button" aria-label="back_to_live" id="media-control-back-to-live">back_to_live</button>
4
+ "<div class="live-info" id="gplayer-mc-live" style="display: none;">live</div>
5
+ <button type="button" class="live-button" aria-label="back_to_live" id="gplayer-mc-back-to-live" style="display: none;">
6
+ back_to_live
7
+ </button>
6
8
  "
7
9
  `;
@@ -56,7 +56,7 @@ export type MediaControlLeftElement =
56
56
  * Media control elements that appear in main layer, spanning the entire width of the player.
57
57
  * @beta
58
58
  */
59
- export type MediaControlLayerElement = 'seekbar' | 'seekBarContainer' // TODO rename seekbar
59
+ export type MediaControlLayerElement = 'seekbar' | 'seekBarContainer' | '_' // TODO remove seekbar
60
60
 
61
61
  /**
62
62
  * Media control elements that appear in the right area.
@@ -91,6 +91,14 @@ const MANAGED_ELEMENTS: MediaControlElement[] = [
91
91
  'volume',
92
92
  ]
93
93
 
94
+ export type MediaControlSlotSelector =
95
+ | 'root'
96
+ | 'base'
97
+ | 'left'
98
+ | 'right'
99
+ | 'center'
100
+ | 'seekbar'
101
+
94
102
  /**
95
103
  * Specifies the allowed media control elements in each area.
96
104
  * Can be used to restrict rendered media control elements.
@@ -723,7 +731,9 @@ export class MediaControl extends UICorePlugin {
723
731
  }
724
732
 
725
733
  private togglePlayStop() {
726
- this.container.isPlaying() ? this.container.stop({ ui: true }) : this.container.play()
734
+ this.container.isPlaying()
735
+ ? this.container.stop({ ui: true })
736
+ : this.container.play()
727
737
  }
728
738
 
729
739
  private startSeekDrag(event: MouseEvent) {
@@ -897,19 +907,19 @@ export class MediaControl extends UICorePlugin {
897
907
  }
898
908
 
899
909
  private hideVolumeBar(timeout = 400) {
900
- if (!this.$volumeBarContainer) {
901
- return
902
- }
903
910
  if (this.hideVolumeId) {
904
911
  clearTimeout(this.hideVolumeId)
905
912
  }
906
913
  if (this.draggingVolumeBar) {
907
- this.hideVolumeId = setTimeout(() => this.hideVolumeBar(), timeout)
914
+ this.hideVolumeId = setTimeout(() => {
915
+ this.hideVolumeId = null
916
+ this.hideVolumeBar()
917
+ }, timeout)
908
918
  } else {
909
- this.hideVolumeId = setTimeout(
910
- () => this.$volumeBarContainer?.addClass('volume-bar-hide'),
911
- timeout,
912
- )
919
+ this.hideVolumeId = setTimeout(() => {
920
+ this.hideVolumeId = null
921
+ this.$volumeBarContainer.addClass('volume-bar-hide')
922
+ }, timeout)
913
923
  }
914
924
  }
915
925
 
@@ -1133,8 +1143,7 @@ export class MediaControl extends UICorePlugin {
1133
1143
  newSettings.seekEnabled = this.isSeekEnabledForHtml5Playback()
1134
1144
  }
1135
1145
 
1136
- const settingsChanged =
1137
- serializeSettings(this.settings) !== serializeSettings(newSettings)
1146
+ const settingsChanged = !isEqualSettings(this.settings, newSettings)
1138
1147
 
1139
1148
  if (settingsChanged) {
1140
1149
  this.settings = newSettings
@@ -1182,34 +1191,52 @@ export class MediaControl extends UICorePlugin {
1182
1191
  /**
1183
1192
  * Get a media control element DOM node
1184
1193
  * @param name - The name of the media control element
1185
- * @returns The DOM node to render to or extend
1194
+ * @param element - The DOM node/fragment to mount
1195
+ * @deprecated Use {@link MediaControl.mount} instead
1196
+ */
1197
+ slot(name: MediaControlElement, element: ZeptoResult): void {
1198
+ const panel = this.getElementLocation(name)
1199
+ if (panel) {
1200
+ element.attr(`data-${name}`, '')
1201
+ mountTo(panel, element)
1202
+ }
1203
+ }
1204
+
1205
+ /**
1206
+ * Mounts a media control element at a specific location
1207
+ * @param name - The location to mount the element
1208
+ * @param element - The element to mount
1186
1209
  * @remarks
1187
1210
  * Use this method to render custom media control UI in a plugin
1188
1211
  * @example
1189
1212
  * ```ts
1190
1213
  * class MyPlugin extends UICorePlugin {
1191
1214
  * override render() {
1192
- * this.$el.html('<div data-clips>Here we go</div>')
1193
- * this.core.getPlugin('media_control').mount('clips', this.$el)
1215
+ * this.$el.html('<div id="my-element" class="my-class">Here we go</div>')
1216
+ * this.core.getPlugin('media_control').mount('left', this.$el)
1194
1217
  * return this
1195
1218
  * }
1196
1219
  * }
1197
1220
  * ```
1198
1221
  */
1199
- mount(name: MediaControlElement, element: ZeptoResult) {
1200
- const panel = this.getElementLocation(name)
1201
- if (panel) {
1202
- const current = panel.find(`[data-${name}]`)
1203
- element.attr(`data-${name}`, '')
1204
- // TODO test
1205
- if (current.length) {
1206
- if (current[0] === element[0]) {
1207
- return
1208
- }
1209
- current.replaceWith(element)
1210
- } else {
1211
- panel.append(element)
1212
- }
1222
+ mount(name: MediaControlSlotSelector, element: ZeptoResult) {
1223
+ mountTo(this.getMountParent(name), element)
1224
+ }
1225
+
1226
+ private getMountParent(name: MediaControlSlotSelector): ZeptoResult {
1227
+ switch (name) {
1228
+ case 'root':
1229
+ return this.$el
1230
+ case 'base':
1231
+ return this.$el.find('.media-control-layer')
1232
+ case 'center':
1233
+ return this.getCenterPanel()
1234
+ case 'left':
1235
+ return this.getLeftPanel()
1236
+ case 'right':
1237
+ return this.getRightPanel()
1238
+ default:
1239
+ assert.fail(`Invalid mount parent name: ${name}`)
1213
1240
  }
1214
1241
  }
1215
1242
 
@@ -1452,17 +1479,14 @@ export class MediaControl extends UICorePlugin {
1452
1479
  }
1453
1480
  }
1454
1481
 
1482
+ // TODO check if needed
1455
1483
  this.$seekBarPosition?.addClass('media-control-notransition')
1456
1484
  this.$seekBarScrubber?.addClass('media-control-notransition')
1457
1485
 
1458
- let previousSeekPercentage = 0
1459
-
1460
- if (this.displayedSeekBarPercentage) {
1461
- previousSeekPercentage = this.displayedSeekBarPercentage
1462
- }
1486
+ const prevSeekPercentage = this.displayedSeekBarPercentage || 0
1463
1487
 
1464
1488
  this.displayedSeekBarPercentage = null
1465
- this.setSeekPercentage(previousSeekPercentage)
1489
+ this.setSeekPercentage(prevSeekPercentage)
1466
1490
 
1467
1491
  setTimeout(() => {
1468
1492
  !this.settings.seekEnabled &&
@@ -1609,3 +1633,13 @@ function serializeSettings(s: MediaControlSettings) {
1609
1633
  .concat([s.seekEnabled as any])
1610
1634
  .join(',')
1611
1635
  }
1636
+
1637
+ function isEqualSettings(a: MediaControlSettings, b: MediaControlSettings) {
1638
+ return serializeSettings(a) === serializeSettings(b)
1639
+ }
1640
+
1641
+ function mountTo(parent: ZeptoResult, element: ZeptoResult) {
1642
+ if (!parent.find(element).length) {
1643
+ parent.append(element)
1644
+ }
1645
+ }