@gcorevideo/player 2.20.22 → 2.21.3
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.
- package/assets/audio-selector/style.scss +48 -82
- package/assets/audio-selector/track-selector.ejs +3 -3
- package/assets/bottom-gear/bottomgear.ejs +10 -12
- package/assets/bottom-gear/gear-sub-menu.scss +0 -15
- package/assets/bottom-gear/gear.scss +3 -32
- package/assets/media-control/media-control.ejs +5 -25
- package/assets/media-control/media-control.scss +114 -34
- package/assets/media-control/width370.scss +35 -109
- package/assets/picture-in-picture/button.ejs +1 -1
- package/assets/picture-in-picture/button.scss +5 -4
- package/assets/subtitles/combobox.ejs +7 -9
- package/assets/subtitles/style.scss +8 -15
- package/dist/core.js +151 -23
- package/dist/index.css +897 -1000
- package/dist/index.js +416 -438
- package/dist/player.d.ts +19 -16
- package/dist/plugins/index.css +1454 -1557
- package/dist/plugins/index.js +826 -23550
- package/docs/api/player.audioselector.md +4 -59
- package/docs/api/player.md +1 -1
- package/docs/api/player.mediacontrol.getelement.md +5 -0
- package/docs/api/player.mediacontrol.md +14 -0
- package/docs/api/{player.audioselector.updatecurrenttrack.md → player.mediacontrol.putelement.md} +7 -7
- package/docs/api/player.mediacontrolelement.md +1 -1
- package/docs/api/{player.audioselector.starttrackswitch.md → player.pictureinpicture.attributes.md} +5 -7
- package/docs/api/player.pictureinpicture.md +45 -0
- package/lib/playback/BasePlayback.d.ts +1 -1
- package/lib/playback/BasePlayback.d.ts.map +1 -1
- package/lib/playback/BasePlayback.js +3 -1
- package/lib/playback/HTML5Video.d.ts +4 -0
- package/lib/playback/HTML5Video.d.ts.map +1 -1
- package/lib/playback/HTML5Video.js +53 -4
- package/lib/playback/dash-playback/DashPlayback.d.ts +5 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +48 -4
- package/lib/playback/hls-playback/HlsPlayback.d.ts +31 -25
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +47 -14
- package/lib/playback.types.d.ts +5 -0
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/plugins/audio-selector/AudioSelector.d.ts +12 -11
- package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
- package/lib/plugins/audio-selector/AudioSelector.js +65 -185
- package/lib/plugins/bottom-gear/BottomGear.d.ts +2 -2
- package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
- package/lib/plugins/bottom-gear/BottomGear.js +12 -10
- package/lib/plugins/level-selector/LevelSelector.js +1 -1
- package/lib/plugins/media-control/MediaControl.d.ts +3 -4
- package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
- package/lib/plugins/media-control/MediaControl.js +23 -13
- package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +3 -0
- package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
- package/lib/plugins/picture-in-picture/PictureInPicture.js +6 -1
- package/lib/plugins/playback-rate/PlaybackRate.d.ts +1 -0
- package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
- package/lib/plugins/playback-rate/PlaybackRate.js +1 -0
- package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
- package/lib/plugins/source-controller/SourceController.js +0 -1
- package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +0 -2
- package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
- package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +1 -18
- package/lib/plugins/subtitles/Subtitles.d.ts +21 -19
- package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
- package/lib/plugins/subtitles/Subtitles.js +121 -151
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +2 -0
- package/package.json +1 -1
- package/src/playback/BasePlayback.ts +4 -1
- package/src/playback/HTML5Video.ts +57 -4
- package/src/playback/dash-playback/DashPlayback.ts +64 -6
- package/src/playback/hls-playback/HlsPlayback.ts +82 -40
- package/src/playback.types.ts +6 -0
- package/src/plugins/audio-selector/AudioSelector.ts +84 -278
- package/src/plugins/bottom-gear/BottomGear.ts +14 -11
- package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -3
- package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +14 -37
- package/src/plugins/level-selector/LevelSelector.ts +1 -1
- package/src/plugins/media-control/MediaControl.ts +54 -32
- package/src/plugins/picture-in-picture/PictureInPicture.ts +7 -1
- package/src/plugins/playback-rate/PlaybackRate.ts +1 -0
- package/src/plugins/source-controller/SourceController.ts +0 -1
- package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +1 -20
- package/src/plugins/subtitles/Subtitles.ts +144 -179
- package/src/testUtils.ts +2 -0
- package/src/typings/globals.d.ts +19 -0
- package/temp/player.api.json +102 -143
- package/tsconfig.tsbuildinfo +1 -1
- package/assets/media-control/plugins.scss +0 -94
- 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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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
|
+
}
|
package/lib/playback.types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playback.types.d.ts","sourceRoot":"","sources":["../src/playback.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAE9B;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,EAAE,SAAS,CAAA;IAClB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAA;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3D;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG;IACtC,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,oBAAY,iBAAiB;IAC3B;;OAEG;IACH,OAAO,kBAAkB;IACzB;;OAEG;IACH,sBAAsB,6BAA6B;IACnD;;OAEG;IACH,uBAAuB,+BAA+B;CACvD;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;AAElD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,CAAA;AAEnE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,iBAAiB,CAAA;IACvB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,KAAK,EAAE,UAAU,CAAA;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,KAAK,EAAE,mBAAmB,CAAA;IAE1B;;OAEG;IACH,EAAE,CAAC,EAAE;QACH,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACF"}
|
|
1
|
+
{"version":3,"file":"playback.types.d.ts","sourceRoot":"","sources":["../src/playback.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAA;AAE9B;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,OAAO,EAAE,SAAS,CAAA;IAClB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAA;CACjB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3D;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG;IACtC,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,oBAAY,iBAAiB;IAC3B;;OAEG;IACH,OAAO,kBAAkB;IACzB;;OAEG;IACH,sBAAsB,6BAA6B;IACnD;;OAEG;IACH,uBAAuB,+BAA+B;CACvD;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;AAElD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,CAAA;AAEnE;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,EAAE,iBAAiB,CAAA;IACvB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;OAEG;IACH,KAAK,EAAE,UAAU,CAAA;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,KAAK,EAAE,mBAAmB,CAAA;IAE1B;;OAEG;IACH,EAAE,CAAC,EAAE;QACH,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,CAAA;CACjB"}
|
|
@@ -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
|
|
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
|
|
49
|
+
private onCoreReady;
|
|
46
50
|
private bindPlaybackEvents;
|
|
47
51
|
private setupAudioTrackListeners;
|
|
48
52
|
private onStop;
|
|
49
|
-
private
|
|
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
|
|
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
|
|
70
|
+
private startTrackSwitch;
|
|
67
71
|
private updateText;
|
|
68
|
-
|
|
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;
|
|
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 {
|
|
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
|
-
|
|
10
|
-
const AUTO = 0;
|
|
9
|
+
const T = 'plugins.audio_selector';
|
|
11
10
|
/**
|
|
12
|
-
* `PLUGIN` that
|
|
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:
|
|
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.
|
|
62
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
this.
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
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.
|
|
135
|
+
this.selectAudioTrack(id);
|
|
204
136
|
}
|
|
205
137
|
this.toggleContextMenu();
|
|
206
138
|
event.stopPropagation();
|
|
207
139
|
return false;
|
|
208
140
|
}
|
|
209
|
-
|
|
210
|
-
this.
|
|
211
|
-
|
|
212
|
-
|
|
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.$('
|
|
151
|
+
this.$('ul').hide();
|
|
231
152
|
}
|
|
232
153
|
toggleContextMenu() {
|
|
233
154
|
;
|
|
234
|
-
this.$('
|
|
155
|
+
this.$('ul').toggle();
|
|
235
156
|
}
|
|
236
157
|
buttonElement() {
|
|
237
|
-
return this.$('
|
|
158
|
+
return this.$('button');
|
|
238
159
|
}
|
|
239
160
|
buttonElementText() {
|
|
240
|
-
return this.$('
|
|
161
|
+
return this.$('button .audio-text');
|
|
241
162
|
}
|
|
242
163
|
trackElement(id) {
|
|
243
|
-
return this.$('
|
|
164
|
+
return this.$('ul a' +
|
|
244
165
|
(id !== undefined ? '[data-track-selector-select="' + id + '"]' : '')).parent();
|
|
245
166
|
}
|
|
246
167
|
getTitle() {
|
|
247
|
-
|
|
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(
|
|
258
|
-
if (
|
|
173
|
+
updateText() {
|
|
174
|
+
if (!this.currentTrack) {
|
|
259
175
|
return;
|
|
260
176
|
}
|
|
261
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
}
|
|
@@ -15,7 +15,7 @@ export type GearItemElement = 'quality' | 'rate' | 'nerd';
|
|
|
15
15
|
*
|
|
16
16
|
* Depends on:
|
|
17
17
|
*
|
|
18
|
-
* - {@link MediaControl
|
|
18
|
+
* - {@link MediaControl}
|
|
19
19
|
*/
|
|
20
20
|
export declare class BottomGear extends UICorePlugin {
|
|
21
21
|
private isHd;
|
|
@@ -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;
|
|
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;AAI1D;;;;;;;;;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"}
|