@gcorevideo/player 2.20.22 → 2.21.1

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 (76) hide show
  1. package/assets/audio-selector/style.scss +48 -82
  2. package/assets/audio-selector/track-selector.ejs +3 -3
  3. package/assets/bottom-gear/bottomgear.ejs +10 -12
  4. package/assets/bottom-gear/gear-sub-menu.scss +0 -15
  5. package/assets/bottom-gear/gear.scss +3 -32
  6. package/assets/media-control/media-control.ejs +5 -20
  7. package/assets/media-control/media-control.scss +124 -34
  8. package/assets/media-control/width370.scss +32 -104
  9. package/assets/picture-in-picture/button.ejs +1 -1
  10. package/assets/picture-in-picture/button.scss +5 -4
  11. package/dist/core.js +148 -23
  12. package/dist/index.css +530 -616
  13. package/dist/index.js +284 -282
  14. package/dist/player.d.ts +19 -16
  15. package/dist/plugins/index.css +1009 -1095
  16. package/dist/plugins/index.js +709 -23402
  17. package/docs/api/player.audioselector.md +4 -59
  18. package/docs/api/player.md +1 -1
  19. package/docs/api/player.mediacontrol.getelement.md +5 -0
  20. package/docs/api/player.mediacontrol.md +14 -0
  21. package/docs/api/{player.audioselector.updatecurrenttrack.md → player.mediacontrol.putelement.md} +7 -7
  22. package/docs/api/player.mediacontrolelement.md +1 -1
  23. package/docs/api/{player.audioselector.starttrackswitch.md → player.pictureinpicture.attributes.md} +5 -7
  24. package/docs/api/player.pictureinpicture.md +45 -0
  25. package/lib/playback/BasePlayback.d.ts +0 -1
  26. package/lib/playback/BasePlayback.d.ts.map +1 -1
  27. package/lib/playback/BasePlayback.js +0 -1
  28. package/lib/playback/HTML5Video.d.ts +4 -0
  29. package/lib/playback/HTML5Video.d.ts.map +1 -1
  30. package/lib/playback/HTML5Video.js +53 -4
  31. package/lib/playback/dash-playback/DashPlayback.d.ts +5 -0
  32. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  33. package/lib/playback/dash-playback/DashPlayback.js +48 -4
  34. package/lib/playback/hls-playback/HlsPlayback.d.ts +31 -25
  35. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  36. package/lib/playback/hls-playback/HlsPlayback.js +47 -14
  37. package/lib/plugins/audio-selector/AudioSelector.d.ts +12 -11
  38. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  39. package/lib/plugins/audio-selector/AudioSelector.js +65 -185
  40. package/lib/plugins/bottom-gear/BottomGear.d.ts +1 -1
  41. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  42. package/lib/plugins/bottom-gear/BottomGear.js +10 -9
  43. package/lib/plugins/level-selector/LevelSelector.js +1 -1
  44. package/lib/plugins/media-control/MediaControl.d.ts +3 -3
  45. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  46. package/lib/plugins/media-control/MediaControl.js +17 -9
  47. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +3 -0
  48. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  49. package/lib/plugins/picture-in-picture/PictureInPicture.js +6 -1
  50. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  51. package/lib/plugins/source-controller/SourceController.js +0 -1
  52. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +0 -2
  53. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
  54. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +1 -18
  55. package/lib/testUtils.d.ts.map +1 -1
  56. package/lib/testUtils.js +2 -0
  57. package/package.json +1 -1
  58. package/src/playback/BasePlayback.ts +0 -1
  59. package/src/playback/HTML5Video.ts +57 -4
  60. package/src/playback/dash-playback/DashPlayback.ts +64 -6
  61. package/src/playback/hls-playback/HlsPlayback.ts +82 -40
  62. package/src/plugins/audio-selector/AudioSelector.ts +84 -278
  63. package/src/plugins/bottom-gear/BottomGear.ts +11 -10
  64. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -3
  65. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +14 -37
  66. package/src/plugins/level-selector/LevelSelector.ts +1 -1
  67. package/src/plugins/media-control/MediaControl.ts +18 -13
  68. package/src/plugins/picture-in-picture/PictureInPicture.ts +7 -1
  69. package/src/plugins/source-controller/SourceController.ts +0 -1
  70. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +1 -20
  71. package/src/testUtils.ts +2 -0
  72. package/src/typings/globals.d.ts +19 -0
  73. package/temp/player.api.json +102 -143
  74. package/tsconfig.tsbuildinfo +1 -1
  75. package/assets/media-control/plugins.scss +0 -94
  76. package/docs/api/player.audioselector.highlightcurrenttrack.md +0 -18
@@ -4,7 +4,7 @@
4
4
  import { Events, Log, Playback, PlayerError, Utils, $ } from '@clappr/core';
5
5
  import { trace } from '@gcorevideo/utils';
6
6
  import assert from 'assert';
7
- import HLSJS from 'hls.js';
7
+ import HLSJS, { Events as HlsEvents, } from 'hls.js';
8
8
  import { PlaybackErrorCode, } from '../../playback.types.js';
9
9
  import { isHlsSource } from '../../utils/mediaSources.js';
10
10
  import { BasePlayback } from '../BasePlayback.js';
@@ -15,7 +15,6 @@ const DEFAULT_RECOVER_ATTEMPTS = 16;
15
15
  Events.register('PLAYBACK_FRAGMENT_CHANGED');
16
16
  Events.register('PLAYBACK_FRAGMENT_PARSING_METADATA');
17
17
  const T = 'playback.hls';
18
- // @ts-expect-error
19
18
  export default class HlsPlayback extends BasePlayback {
20
19
  _ccIsSetup = false;
21
20
  _ccTracksUpdated = false;
@@ -60,7 +59,7 @@ export default class HlsPlayback extends BasePlayback {
60
59
  set currentLevel(id) {
61
60
  this._currentLevel = id;
62
61
  this.trigger(Events.PLAYBACK_LEVEL_SWITCH_START);
63
- assert.ok(this._hls, 'Hls.js instance is not available');
62
+ assert.ok(this._hls, 'HLS.js is not initialized');
64
63
  if (this.options.playback.hlsUseNextLevel) {
65
64
  this._hls.nextLevel = this._currentLevel;
66
65
  }
@@ -69,11 +68,11 @@ export default class HlsPlayback extends BasePlayback {
69
68
  }
70
69
  }
71
70
  get latency() {
72
- assert.ok(this._hls, 'Hls.js instance is not available');
71
+ assert.ok(this._hls, 'HLS.js is not initialized');
73
72
  return this._hls.latency;
74
73
  }
75
74
  get currentProgramDateTime() {
76
- assert.ok(this._hls, 'Hls.js instance is not available');
75
+ assert.ok(this._hls, 'HLS.js is not initialized');
77
76
  assert.ok(this._hls.playingDate, 'Hls.js playingDate is not defined');
78
77
  return this._hls.playingDate;
79
78
  }
@@ -258,7 +257,7 @@ export default class HlsPlayback extends BasePlayback {
258
257
  return;
259
258
  }
260
259
  this._hls.once(HLSJS.Events.MEDIA_ATTACHED, () => {
261
- assert.ok(this._hls, 'Hls.js instance is not available');
260
+ assert.ok(this._hls, 'HLS.js is not initialized');
262
261
  this.options.hlsPlayback.preload && this._hls.loadSource(this.options.src);
263
262
  });
264
263
  const onPlaying = () => {
@@ -283,13 +282,15 @@ export default class HlsPlayback extends BasePlayback {
283
282
  // this._hls.on(HLSJS.Events.SUBTITLE_TRACK_LOADED, (evt, data) => this._onSubtitleLoaded(evt, data));
284
283
  this._hls.on(HLSJS.Events.SUBTITLE_TRACK_LOADED, () => this._onSubtitleLoaded());
285
284
  this._hls.on(HLSJS.Events.SUBTITLE_TRACKS_UPDATED, () => (this._ccTracksUpdated = true));
285
+ this._hls.on(HlsEvents.AUDIO_TRACKS_UPDATED, (evt, data) => this._onAudioTracksUpdated(evt, data));
286
+ this._hls.on(HlsEvents.AUDIO_TRACK_SWITCHED, (evt, data) => this._onAudioTrackSwitched(evt, data));
286
287
  this.bindCustomListeners();
287
288
  }
288
289
  bindCustomListeners() {
289
290
  this.customListeners.forEach((item) => {
290
291
  const requestedEventName = item.eventName;
291
292
  const typeOfListener = item.once ? 'once' : 'on';
292
- assert.ok(this._hls, 'Hls.js instance is not available');
293
+ assert.ok(this._hls, 'HLS.js is not initialized');
293
294
  requestedEventName &&
294
295
  this._hls[`${typeOfListener}`](requestedEventName, item.callback);
295
296
  });
@@ -297,7 +298,7 @@ export default class HlsPlayback extends BasePlayback {
297
298
  unbindCustomListeners() {
298
299
  this.customListeners.forEach((item) => {
299
300
  const requestedEventName = item.eventName;
300
- assert.ok(this._hls, 'Hls.js instance is not available');
301
+ assert.ok(this._hls, 'HLS.js is not initialized');
301
302
  requestedEventName && this._hls.off(requestedEventName, item.callback);
302
303
  });
303
304
  }
@@ -321,7 +322,7 @@ export default class HlsPlayback extends BasePlayback {
321
322
  this.trigger(Events.PLAYBACK_READY, this.name);
322
323
  }
323
324
  _recover(evt, data, error) {
324
- assert(this._hls, 'Hls.js instance is not available');
325
+ assert(this._hls, 'HLS.js is not initialized');
325
326
  if (!this._recoveredDecodingError) {
326
327
  this._recoveredDecodingError = true;
327
328
  this._hls.recoverMediaError();
@@ -484,10 +485,6 @@ export default class HlsPlayback extends BasePlayback {
484
485
  details: data.details,
485
486
  });
486
487
  error.level = PlayerError.Levels.WARN;
487
- // TODO check
488
- // if (data.error instanceof MediaError && data.error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) {
489
- // error.code = PlaybackErrorCode.MediaSourceUnavailable
490
- // }
491
488
  this._recover(evt, data, error);
492
489
  break;
493
490
  default:
@@ -630,7 +627,7 @@ export default class HlsPlayback extends BasePlayback {
630
627
  }
631
628
  }
632
629
  _fillLevels() {
633
- assert.ok(this._hls, 'Hls.js instance is not available');
630
+ assert.ok(this._hls, 'HLS.js is not initialized');
634
631
  this._levels = this._hls.levels.map((level, index) => {
635
632
  return {
636
633
  level: index, // or level.id?
@@ -831,6 +828,34 @@ export default class HlsPlayback extends BasePlayback {
831
828
  }));
832
829
  this.stop();
833
830
  }
831
+ get audioTracks() {
832
+ assert.ok(this._hls, 'HLS.js is not initialized');
833
+ return this._hls.audioTracks.map(toClapprTrack);
834
+ }
835
+ // @ts-expect-error
836
+ get currentAudioTrack() {
837
+ assert.ok(this._hls, 'HLS.js is not initialized');
838
+ const idx = this._hls.audioTrack;
839
+ const track = this._hls.audioTracks[idx]; // TODO or find by .id == idx?
840
+ if (track) {
841
+ return toClapprTrack(track);
842
+ }
843
+ return null;
844
+ }
845
+ switchAudioTrack(id) {
846
+ assert.ok(this._hls, 'HLS.js is not initialized');
847
+ this._hls.audioTrack = Number(id); // TODO or find index by .id == id?
848
+ }
849
+ _onAudioTracksUpdated(_, data) {
850
+ trace(`${T} onAudioTracksUpdated`);
851
+ this.trigger(Events.PLAYBACK_AUDIO_AVAILABLE, data.audioTracks.map(toClapprTrack));
852
+ }
853
+ _onAudioTrackSwitched(_, data) {
854
+ trace(`${T} onAudioTrackSwitched`);
855
+ // @ts-ignore
856
+ const track = this._hls.audioTracks[data.id];
857
+ this.trigger(Events.PLAYBACK_AUDIO_CHANGED, toClapprTrack(track));
858
+ }
834
859
  }
835
860
  HlsPlayback.canPlay = function (resource, mimeType) {
836
861
  if (!isHlsSource(resource, mimeType)) {
@@ -838,3 +863,11 @@ HlsPlayback.canPlay = function (resource, mimeType) {
838
863
  }
839
864
  return HLSJS.isSupported();
840
865
  };
866
+ function toClapprTrack(t) {
867
+ return {
868
+ id: String(t.id),
869
+ language: t.lang ?? '',
870
+ kind: t.type === 'main' ? 'main' : 'description', // TODO check
871
+ label: t.name,
872
+ };
873
+ }
@@ -1,12 +1,16 @@
1
1
  import { UICorePlugin } from '@clappr/core';
2
- import { AudioTrackLoadedData, AudioTrackSwitchedData, Events as HlsEvents } from 'hls.js';
3
2
  import '../../../assets/audio-selector/style.scss';
4
3
  /**
5
- * `PLUGIN` that adds an audio track selector to the media control UI.
4
+ * `PLUGIN` that makes possible to switch audio tracks via the media control UI.
6
5
  * @beta
6
+ * @remarks
7
+ * The plugin is activated when there are multiple audio tracks available.
8
+ * The plugin adds a button showing the current audio track and a dropdown to switch to another audio track.
9
+ * Depends on:
10
+ *
11
+ * - {@link MediaControl}
7
12
  */
8
13
  export declare class AudioSelector extends UICorePlugin {
9
- private selectedTrackId;
10
14
  private currentTrack;
11
15
  private tracks;
12
16
  /**
@@ -42,11 +46,11 @@ export declare class AudioSelector extends UICorePlugin {
42
46
  * @internal
43
47
  */
44
48
  bindEvents(): void;
45
- private unBindEvents;
49
+ private onCoreReady;
46
50
  private bindPlaybackEvents;
47
51
  private setupAudioTrackListeners;
48
52
  private onStop;
49
- private reload;
53
+ private onActiveContainerChanged;
50
54
  private shouldRender;
51
55
  /**
52
56
  * @internal
@@ -55,7 +59,7 @@ export declare class AudioSelector extends UICorePlugin {
55
59
  private fillTracks;
56
60
  private findTrackBy;
57
61
  private onTrackSelect;
58
- private setIndexTrack;
62
+ private selectAudioTrack;
59
63
  private onShowLevelSelectMenu;
60
64
  private hideSelectTrackMenu;
61
65
  private toggleContextMenu;
@@ -63,11 +67,8 @@ export declare class AudioSelector extends UICorePlugin {
63
67
  private buttonElementText;
64
68
  private trackElement;
65
69
  private getTitle;
66
- startTrackSwitch(): void;
70
+ private startTrackSwitch;
67
71
  private updateText;
68
- updateCurrentTrack(e: HlsEvents.AUDIO_TRACK_SWITCHED, info: AudioTrackSwitchedData | AudioTrackLoadedData): void;
69
- private updateCurrentTrackW3C;
70
- private setCurrentTrack;
71
- highlightCurrentTrack(): void;
72
+ private highlightCurrentTrack;
72
73
  }
73
74
  //# sourceMappingURL=AudioSelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AudioSelector.d.ts","sourceRoot":"","sources":["../../../src/plugins/audio-selector/AudioSelector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAY,MAAM,cAAc,CAAA;AAC7D,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EAEtB,MAAM,IAAI,SAAS,EACpB,MAAM,QAAQ,CAAA;AAKf,OAAO,2CAA2C,CAAA;AAiClD;;;GAGG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAG7C,OAAO,CAAC,eAAe,CAAoB;IAE3C,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO,CAAC,MAAM,CAAuB;IAErC;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,MAAM,KAAK,OAAO,WAEjB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAEvD;;OAEG;IACH,IAAa,UAAU;;;MAKtB;IAED;;OAEG;IACH,IAAa,MAAM;;;MAKlB;IAED;;OAEG;IACM,UAAU;IAoBnB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,wBAAwB;IA4EhC,OAAO,CAAC,MAAM;IAEd,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,YAAY;IAoBpB;;OAEG;IACM,MAAM;IA2Bf,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,QAAQ;IAYhB,gBAAgB;IAIhB,OAAO,CAAC,UAAU;IAYlB,kBAAkB,CAChB,CAAC,EAAE,SAAS,CAAC,oBAAoB,EACjC,IAAI,EAAE,sBAAsB,GAAG,oBAAoB;IAwBrD,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,eAAe;IASvB,qBAAqB;CAatB"}
1
+ {"version":3,"file":"AudioSelector.d.ts","sourceRoot":"","sources":["../../../src/plugins/audio-selector/AudioSelector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,YAAY,EAAY,MAAM,cAAc,CAAA;AAQ7D,OAAO,2CAA2C,CAAA;AAQlD;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,YAAY,CAA0B;IAE9C,OAAO,CAAC,MAAM,CAAmB;IAEjC;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,MAAM,KAAK,OAAO,WAEjB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAEvD;;OAEG;IACH,IAAa,UAAU;;;MAKtB;IAED;;OAEG;IACH,IAAa,MAAM;;;MAKlB;IAED;;OAEG;IACM,UAAU;IASnB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACM,MAAM;IAkBf,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,qBAAqB;CAW9B"}
@@ -1,20 +1,23 @@
1
1
  import { Events, UICorePlugin, template } from '@clappr/core';
2
- import { Events as HlsEvents, } from 'hls.js';
2
+ import { trace } from '@gcorevideo/utils';
3
+ import assert from 'assert';
3
4
  import { CLAPPR_VERSION } from '../../build.js';
4
5
  import pluginHtml from '../../../assets/audio-selector/track-selector.ejs';
5
6
  import '../../../assets/audio-selector/style.scss';
6
7
  import audioArrow from '../../../assets/icons/old/quality-arrow.svg';
7
- import assert from 'assert';
8
8
  const VERSION = '0.0.1';
9
- // const T = 'plugins.audio_selector';
10
- const AUTO = 0;
9
+ const T = 'plugins.audio_selector';
11
10
  /**
12
- * `PLUGIN` that adds an audio track selector to the media control UI.
11
+ * `PLUGIN` that makes possible to switch audio tracks via the media control UI.
13
12
  * @beta
13
+ * @remarks
14
+ * The plugin is activated when there are multiple audio tracks available.
15
+ * The plugin adds a button showing the current audio track and a dropdown to switch to another audio track.
16
+ * Depends on:
17
+ *
18
+ * - {@link MediaControl}
14
19
  */
15
20
  export class AudioSelector extends UICorePlugin {
16
- // TODO
17
- selectedTrackId;
18
21
  currentTrack = null;
19
22
  tracks = [];
20
23
  /**
@@ -41,7 +44,7 @@ export class AudioSelector extends UICorePlugin {
41
44
  */
42
45
  get attributes() {
43
46
  return {
44
- class: this.name,
47
+ class: 'media-control-audio-tracks',
45
48
  'data-track-selector': '',
46
49
  };
47
50
  }
@@ -58,101 +61,49 @@ export class AudioSelector extends UICorePlugin {
58
61
  * @internal
59
62
  */
60
63
  bindEvents() {
61
- this.listenTo(this.core, Events.CORE_READY, this.bindPlaybackEvents);
62
- // TODO CORE_ACTIVE_CONTAINER_CHANGED
63
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, this.reload);
64
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
65
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectTrackMenu);
64
+ this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
65
+ this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChanged);
66
66
  }
67
- unBindEvents() {
68
- // @ts-ignore
69
- this.stopListening(this.core, Events.CORE_READY);
70
- // @ts-ignore
71
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED);
72
- // @ts-ignore
73
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED);
74
- // @ts-ignore
75
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE);
67
+ onCoreReady() {
68
+ trace(`${T} onCoreReady`);
69
+ const mediaControl = this.core.getPlugin('media_control');
70
+ assert(mediaControl, 'media_control plugin is required');
71
+ this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
72
+ this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectTrackMenu);
76
73
  }
77
74
  bindPlaybackEvents() {
78
- // this.currentTrack = {};
79
- // this.removeAuto = false;
80
- this.selectedTrackId = undefined;
81
- const currentPlayback = this.core.activePlayback;
82
- this.listenTo(currentPlayback, Events.PLAYBACK_STOP, this.onStop);
75
+ trace(`${T} bindPlaybackEvents`);
76
+ this.currentTrack = null;
77
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_STOP, this.onStop);
83
78
  this.setupAudioTrackListeners();
84
79
  }
85
80
  setupAudioTrackListeners() {
86
- const currentPlayback = this.core.activePlayback;
87
- // TODO no-crutch:currentPlayback._hls
88
- if (currentPlayback._hls) {
89
- // TODO AUDIO_TRACKS_UPDATED
90
- // currentPlayback._hls.on('hlsAudioTracksUpdated', (e, data) => {
91
- currentPlayback._hls.on(HlsEvents.AUDIO_TRACKS_UPDATED, (e, data) => {
92
- // let id = -1;
93
- // for (const audioTrack of data.audioTracks) {
94
- // if (audioTrack.default) {
95
- // id = audioTrack.id;
96
- // this.currentTrack = audioTrack;
97
- // }
98
- // }
99
- const defaultTrack = data.audioTracks.find((track) => track.default);
100
- if (defaultTrack) {
101
- this.currentTrack = {
102
- id: defaultTrack.id,
103
- label: defaultTrack.name,
104
- };
105
- }
106
- this.fillTracks(data.audioTracks.map((p) => ({
107
- id: p.id,
108
- label: p.name,
109
- })), defaultTrack?.id);
110
- });
111
- currentPlayback._hls.on(HlsEvents.AUDIO_TRACK_SWITCHING, this.startTrackSwitch.bind(this));
112
- currentPlayback._hls.on(HlsEvents.AUDIO_TRACK_SWITCHED, this.updateCurrentTrack.bind(this));
113
- currentPlayback._hls.on(HlsEvents.AUDIO_TRACK_LOADED, this.updateCurrentTrack.bind(this));
114
- }
115
- else {
116
- this.listenToOnce(currentPlayback, Events.PLAYBACK_PLAY, () => {
117
- const mediaElement = currentPlayback.$el.get(0);
118
- // const { audioTracks } = currentPlayback.$el.get(0);
119
- const audioTracks = mediaElement.audioTracks;
120
- if (audioTracks && audioTracks.length) {
121
- let index = 0;
122
- const trackItems = [];
123
- for (const audioTrack of audioTracks) {
124
- if (audioTrack.enabled) {
125
- const t = {
126
- id: index,
127
- label: audioTrack.label,
128
- };
129
- this.currentTrack = t;
130
- trackItems.push(t);
131
- index++;
132
- }
133
- }
134
- audioTracks.addEventListener('change', () => this.updateCurrentTrackW3C());
135
- this.fillTracks(trackItems, trackItems[0].id);
136
- }
137
- });
138
- }
139
- }
140
- onStop() { }
141
- reload() {
142
- this.unBindEvents();
143
- this.bindEvents();
81
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_AUDIO_AVAILABLE, (tracks) => {
82
+ trace(`${T} on PLAYBACK_AUDIO_AVAILABLE`, { audioTracks: tracks });
83
+ this.currentTrack =
84
+ tracks.find((track) => track.kind === 'main') ?? null;
85
+ this.fillTracks(tracks);
86
+ });
87
+ this.listenTo(this.core.activePlayback, Events.PLAYBACK_AUDIO_CHANGED, (track) => {
88
+ trace(`${T} PLAYBACK_AUDIO_CHANGED`, { audioTrack: track });
89
+ this.currentTrack = track;
90
+ this.highlightCurrentTrack();
91
+ this.buttonElement().removeClass('changing');
92
+ this.updateText();
93
+ });
94
+ }
95
+ onStop() {
96
+ trace(`${T} onStop`);
97
+ }
98
+ onActiveContainerChanged() {
99
+ trace(`${T} onActiveContainerChanged`);
144
100
  this.bindPlaybackEvents();
145
101
  }
146
102
  shouldRender() {
147
- if (!this.core.activeContainer) {
148
- return false;
149
- }
150
- const currentPlayback = this.core.activePlayback;
151
- if (!currentPlayback) {
103
+ if (!this.core.activePlayback) {
152
104
  return false;
153
105
  }
154
- const { audioTracks } = currentPlayback.activePlayback._hls || currentPlayback.$el.get(0);
155
- this.tracks = audioTracks;
106
+ this.tracks = this.core.activePlayback.audioTracks;
156
107
  // Only care if we have at least 2 to choose from
157
108
  return this.tracks && this.tracks.length > 1;
158
109
  }
@@ -164,146 +115,75 @@ export class AudioSelector extends UICorePlugin {
164
115
  return this;
165
116
  }
166
117
  const mediaControl = this.core.getPlugin('media_control');
167
- assert(mediaControl, 'media_control plugin is required');
168
118
  this.$el.html(AudioSelector.template({ tracks: this.tracks, title: this.getTitle() }));
169
- const ats = mediaControl.getElement('audioTracksSelector');
170
- if (!(ats && ats.length > 0)) {
171
- return this;
172
- }
173
- ats.append(this.el);
119
+ this.$('.audio-arrow').append(audioArrow);
120
+ mediaControl.putElement('audioTracksSelector', this.$el);
121
+ this.updateText();
174
122
  this.highlightCurrentTrack();
175
- const aa = ats.find('audioArrow');
176
- if (aa.length > 0) {
177
- aa.append(audioArrow);
178
- }
179
123
  return this;
180
124
  }
181
- fillTracks(tracks, selected = AUTO) {
182
- if (this.selectedTrackId === undefined) {
183
- this.selectedTrackId = selected;
184
- }
185
- // this.tracks = levels.audioTracks;
186
- // for (let i = 0; i < this.tracks.length; i++) {
187
- // if (this.tracks[i].name && !this.tracks[i].label) {
188
- // this.tracks[i].label = this.tracks[i].name;
189
- // }
190
- // }
125
+ fillTracks(tracks) {
191
126
  this.tracks = tracks;
192
- // Player.player.trigger('tracks', this.tracks);
193
- // this.core.trigger('tracks', this.tracks);
194
127
  this.render();
195
128
  }
196
129
  findTrackBy(id) {
197
130
  return this.tracks.find((track) => track.id === id);
198
131
  }
199
132
  onTrackSelect(event) {
200
- // this.selectedTrackId = parseInt(event.target.dataset.levelSelectorSelect, 10)
201
133
  const id = event.target?.dataset?.trackSelectorSelect;
202
134
  if (id) {
203
- this.setIndexTrack(Number(id));
135
+ this.selectAudioTrack(id);
204
136
  }
205
137
  this.toggleContextMenu();
206
138
  event.stopPropagation();
207
139
  return false;
208
140
  }
209
- setIndexTrack(index) {
210
- this.selectedTrackId = index;
211
- if (this.core.activePlayback._hls) {
212
- if (this.core.activePlayback._hls.audioTrack.id === this.selectedTrackId) {
213
- return;
214
- }
215
- this.core.activePlayback._hls.audioTrack = this.selectedTrackId;
216
- }
217
- else {
218
- const { audioTracks } = this.core.activePlayback.$el.get(0);
219
- for (const track of audioTracks) {
220
- track.enabled = track.id === this.selectedTrackId;
221
- }
222
- }
223
- this.updateText(this.selectedTrackId);
141
+ selectAudioTrack(id) {
142
+ this.startTrackSwitch();
143
+ this.core.activePlayback.switchAudioTrack(id);
144
+ this.updateText();
224
145
  }
225
146
  onShowLevelSelectMenu() {
226
147
  this.toggleContextMenu();
227
148
  }
228
149
  hideSelectTrackMenu() {
229
150
  ;
230
- this.$('.audio_selector ul').hide();
151
+ this.$('ul').hide();
231
152
  }
232
153
  toggleContextMenu() {
233
154
  ;
234
- this.$('.audio_selector ul').toggle();
155
+ this.$('ul').toggle();
235
156
  }
236
157
  buttonElement() {
237
- return this.$('.audio_selector button');
158
+ return this.$('button');
238
159
  }
239
160
  buttonElementText() {
240
- return this.$('.audio_selector button .audio-text');
161
+ return this.$('button .audio-text');
241
162
  }
242
163
  trackElement(id) {
243
- return this.$('.audio_selector ul a' +
164
+ return this.$('ul a' +
244
165
  (id !== undefined ? '[data-track-selector-select="' + id + '"]' : '')).parent();
245
166
  }
246
167
  getTitle() {
247
- if (!this.tracks) {
248
- return '';
249
- }
250
- const selectedTrackId = this.selectedTrackId || 0;
251
- const selectedTrack = this.tracks[selectedTrackId];
252
- return selectedTrack?.label || '';
168
+ return this.currentTrack?.label || '';
253
169
  }
254
170
  startTrackSwitch() {
255
171
  this.buttonElement().addClass('changing');
256
172
  }
257
- updateText(trackId) {
258
- if (trackId === undefined) {
173
+ updateText() {
174
+ if (!this.currentTrack) {
259
175
  return;
260
176
  }
261
- const track = this.findTrackBy(trackId);
262
- if (track) {
263
- this.buttonElementText().text(track.label);
264
- }
265
- }
266
- updateCurrentTrack(e, info) {
267
- // if (!info) {
268
- // const { audioTracks } = this.core.activePlayback.$el.get(0);
269
- // for (const track of audioTracks) {
270
- // if (track.enabled) {
271
- // info = track;
272
- // }
273
- // }
274
- // }
275
- // if (!info) {
276
- // return;
277
- // }
278
- // const track = this.findTrackBy(info.id);
279
- // this.currentTrack = track ? track : null;
280
- // this.selectedTrackId = track?.id;
281
- // this.highlightCurrentTrack();
282
- // this.buttonElement().removeClass('changing');
283
- this.setCurrentTrack(info.id);
284
- }
285
- updateCurrentTrackW3C() {
286
- const { audioTracks } = this.core.activePlayback.$el.get(0);
287
- const index = audioTracks.findIndex((track) => track.enabled);
288
- if (index >= 0) {
289
- this.setCurrentTrack(index);
290
- }
291
- }
292
- setCurrentTrack(index) {
293
- const track = this.findTrackBy(index);
294
- this.currentTrack = track ?? null;
295
- this.selectedTrackId = index;
296
- this.highlightCurrentTrack();
297
- this.buttonElement().removeClass('changing');
177
+ this.buttonElementText().text(this.currentTrack.label);
298
178
  }
299
179
  highlightCurrentTrack() {
300
180
  this.trackElement().removeClass('current');
301
181
  this.trackElement().find('a').removeClass('gcore-skin-active');
302
182
  if (this.currentTrack) {
303
- const currentTrackElement = this.trackElement(this.currentTrack.id);
304
- currentTrackElement.addClass('current');
305
- currentTrackElement.find('a').addClass('gcore-skin-active');
183
+ this.trackElement(this.currentTrack.id)
184
+ .addClass('current')
185
+ .find('a')
186
+ .addClass('gcore-skin-active');
306
187
  }
307
- this.updateText(this.selectedTrackId);
308
188
  }
309
189
  }
@@ -39,7 +39,6 @@ export declare class BottomGear extends UICorePlugin {
39
39
  */
40
40
  get attributes(): {
41
41
  class: string;
42
- 'data-track-selector': string;
43
42
  };
44
43
  /**
45
44
  * @internal
@@ -76,5 +75,6 @@ export declare class BottomGear extends UICorePlugin {
76
75
  refresh(): void;
77
76
  private toggleGearMenu;
78
77
  private hide;
78
+ private onCoreReady;
79
79
  }
80
80
  //# sourceMappingURL=BottomGear.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BottomGear.d.ts","sourceRoot":"","sources":["../../../src/plugins/bottom-gear/BottomGear.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAoC,MAAM,cAAc,CAAC;AAO9E,OAAO,uCAAuC,CAAC;AAC/C,OAAO,gDAAgD,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO7C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D;;;;;;;;;GASG;AACH,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,IAAI,CAAS;IAErB;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,MAAM,KAAK,OAAO,WAEjB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAEvD;;OAEG;IACH,IAAa,UAAU;;;MAKtB;IAED;;OAEG;IACH,IAAa,MAAM;;MAIlB;IAED;;OAEG;IACM,UAAU;IASnB;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,GAAG,IAAI;IAIrD;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW;IAI/B,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACM,MAAM;IAkBf;;;;OAIG;IACH,OAAO;IAKP,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,IAAI;CAGb"}
1
+ {"version":3,"file":"BottomGear.d.ts","sourceRoot":"","sources":["../../../src/plugins/bottom-gear/BottomGear.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAoC,MAAM,cAAc,CAAC;AAO9E,OAAO,uCAAuC,CAAC;AAC/C,OAAO,gDAAgD,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO7C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D;;;;;;;;;GASG;AACH,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,IAAI,CAAS;IAErB;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED;;OAEG;IACH,MAAM,KAAK,OAAO,WAEjB;IAED,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAEvD;;OAEG;IACH,IAAa,UAAU;;MAItB;IAED;;OAEG;IACH,IAAa,MAAM;;MAIlB;IAED;;OAEG;IACM,UAAU;IAKnB;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,GAAG,IAAI;IAIrD;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,WAAW;IAI/B,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACM,MAAM;IAiBf;;;;OAIG;IACH,OAAO;IAKP,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,WAAW;CAMpB"}
@@ -46,8 +46,7 @@ export class BottomGear extends UICorePlugin {
46
46
  */
47
47
  get attributes() {
48
48
  return {
49
- 'class': this.name,
50
- 'data-track-selector': ''
49
+ 'class': 'media-control-gear',
51
50
  };
52
51
  }
53
52
  /**
@@ -62,18 +61,15 @@ export class BottomGear extends UICorePlugin {
62
61
  * @internal
63
62
  */
64
63
  bindEvents() {
65
- const mediaControl = this.core.getPlugin('media_control');
66
- assert(mediaControl, 'media_control plugin is required');
64
+ this.listenTo(this.core, ClapprEvents.CORE_READY, this.onCoreReady);
67
65
  this.listenTo(this.core, ClapprEvents.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChanged);
68
- this.listenTo(mediaControl, ClapprEvents.MEDIACONTROL_RENDERED, this.render);
69
- this.listenTo(mediaControl, ClapprEvents.MEDIACONTROL_HIDE, this.hide); // TODO mediacontrol show as well
70
66
  }
71
67
  /**
72
68
  * @param name - Name of a gear menu placeholder item to attach custom UI
73
69
  * @returns Zepto result of the element
74
70
  */
75
71
  getElement(name) {
76
- return this.core.getPlugin('media_control')?.getElement('gear')?.find(`.gear-options-list [data-${name}]`);
72
+ return this.$el.find(`.gear-options-list [data-${name}]`);
77
73
  }
78
74
  /**
79
75
  * Replaces the content of the gear menu
@@ -100,7 +96,6 @@ export class BottomGear extends UICorePlugin {
100
96
  */
101
97
  render() {
102
98
  const mediaControl = this.core.getPlugin('media_control');
103
- assert(mediaControl, 'media_control plugin is required');
104
99
  // TODO use options.mediaControl.gear.items
105
100
  const items = [
106
101
  'quality',
@@ -109,7 +104,7 @@ export class BottomGear extends UICorePlugin {
109
104
  ];
110
105
  const icon = this.isHd ? gearHdIcon : gearIcon;
111
106
  this.$el.html(BottomGear.template({ icon, items }));
112
- mediaControl.getElement('gear')?.html(this.el);
107
+ mediaControl.putElement('gear', this.$el);
113
108
  mediaControl.trigger(MediaControlEvents.MEDIACONTROL_GEAR_RENDERED);
114
109
  return this;
115
110
  }
@@ -128,4 +123,10 @@ export class BottomGear extends UICorePlugin {
128
123
  hide() {
129
124
  this.$el.find('.gear-wrapper').hide();
130
125
  }
126
+ onCoreReady() {
127
+ const mediaControl = this.core.getPlugin('media_control');
128
+ assert(mediaControl, 'media_control plugin is required');
129
+ this.listenTo(mediaControl, ClapprEvents.MEDIACONTROL_RENDERED, this.render);
130
+ this.listenTo(mediaControl, ClapprEvents.MEDIACONTROL_HIDE, this.hide); // TODO mediacontrol show as well
131
+ }
131
132
  }
@@ -68,7 +68,7 @@ export class LevelSelector extends UICorePlugin {
68
68
  */
69
69
  get attributes() {
70
70
  return {
71
- class: this.name,
71
+ class: 'level-selector',
72
72
  'data-level-selector': '',
73
73
  };
74
74
  }