@gcorevideo/player 2.29.0 → 2.30.0
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/README.md +108 -0
- package/dist/core.js +17 -2
- package/dist/index.css +179 -179
- package/dist/index.embed.js +16 -1
- package/dist/index.js +395 -65
- package/lib/Player.d.ts +9 -0
- package/lib/Player.d.ts.map +1 -1
- package/lib/Player.js +11 -0
- package/lib/index.plugins.d.ts +1 -0
- package/lib/index.plugins.d.ts.map +1 -1
- package/lib/index.plugins.js +1 -0
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +5 -1
- package/lib/plugins/token-refresh/TokenRefreshPlugin.d.ts +119 -0
- package/lib/plugins/token-refresh/TokenRefreshPlugin.d.ts.map +1 -0
- package/lib/plugins/token-refresh/TokenRefreshPlugin.js +318 -0
- package/lib/plugins/token-refresh/index.d.ts +2 -0
- package/lib/plugins/token-refresh/index.d.ts.map +1 -0
- package/lib/plugins/token-refresh/index.js +1 -0
- package/package.json +1 -1
- package/src/Player.ts +12 -0
- package/src/index.plugins.ts +1 -0
- package/src/playback/dash-playback/DashPlayback.ts +6 -1
- package/src/plugins/token-refresh/TokenRefreshPlugin.ts +425 -0
- package/src/plugins/token-refresh/index.ts +5 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/index.js
CHANGED
|
@@ -12922,7 +12922,7 @@ var PlaybackEvents;
|
|
|
12922
12922
|
// https://github.com/clappr/clappr/blob/8752995ea439321ac7ca3cd35e8c64de7a3c3d17/LICENSE
|
|
12923
12923
|
const AUTO$1 = -1;
|
|
12924
12924
|
const { now: now$2 } = Utils;
|
|
12925
|
-
const T$
|
|
12925
|
+
const T$f = 'playback.dash';
|
|
12926
12926
|
class DashPlayback extends BasePlayback {
|
|
12927
12927
|
_levels = [];
|
|
12928
12928
|
_currentLevel = AUTO$1;
|
|
@@ -13055,6 +13055,7 @@ class DashPlayback extends BasePlayback {
|
|
|
13055
13055
|
this._dash = dash;
|
|
13056
13056
|
this._dash.initialize();
|
|
13057
13057
|
if (this.options.dash) {
|
|
13058
|
+
const { requestInterceptor, ...dashSettings } = this.options.dash;
|
|
13058
13059
|
const settings = $.extend(true, {
|
|
13059
13060
|
streaming: {
|
|
13060
13061
|
text: {
|
|
@@ -13066,8 +13067,11 @@ class DashPlayback extends BasePlayback {
|
|
|
13066
13067
|
// dispatchForManualRendering: true, // TODO only when useNativeSubtitles is not true?
|
|
13067
13068
|
},
|
|
13068
13069
|
},
|
|
13069
|
-
},
|
|
13070
|
+
}, dashSettings);
|
|
13070
13071
|
this._dash.updateSettings(settings);
|
|
13072
|
+
if (typeof requestInterceptor === 'function') {
|
|
13073
|
+
this._dash.addRequestInterceptor(requestInterceptor);
|
|
13074
|
+
}
|
|
13071
13075
|
}
|
|
13072
13076
|
this._dash.attachView(this.el);
|
|
13073
13077
|
this._dash.setAutoPlay(false);
|
|
@@ -13195,7 +13199,7 @@ class DashPlayback extends BasePlayback {
|
|
|
13195
13199
|
this.trigger(Events$1.PLAYBACK_SETTINGSUPDATE);
|
|
13196
13200
|
}
|
|
13197
13201
|
_onPlaybackError = (event) => {
|
|
13198
|
-
trace(`${T$
|
|
13202
|
+
trace(`${T$f} _onPlaybackError`, { type: event.type, code: event.error.code, message: event.error.message });
|
|
13199
13203
|
};
|
|
13200
13204
|
_onDASHJSSError = (event) => {
|
|
13201
13205
|
this._stopTimeUpdateTimer();
|
|
@@ -50095,7 +50099,7 @@ const { now } = Utils;
|
|
|
50095
50099
|
const AUTO = -1;
|
|
50096
50100
|
const DEFAULT_RECOVER_ATTEMPTS = 16;
|
|
50097
50101
|
Events$1.register('PLAYBACK_FRAGMENT_PARSING_METADATA');
|
|
50098
|
-
const T$
|
|
50102
|
+
const T$e = 'playback.hls';
|
|
50099
50103
|
class HlsPlayback extends BasePlayback {
|
|
50100
50104
|
_ccTracksUpdated = false;
|
|
50101
50105
|
_currentFragment = null;
|
|
@@ -50420,7 +50424,7 @@ class HlsPlayback extends BasePlayback {
|
|
|
50420
50424
|
}
|
|
50421
50425
|
else {
|
|
50422
50426
|
Log.error('hlsjs: failed to recover', { evt, data });
|
|
50423
|
-
trace(`${T$
|
|
50427
|
+
trace(`${T$e} _recover failed to recover`, {
|
|
50424
50428
|
type: data.type,
|
|
50425
50429
|
details: data.details,
|
|
50426
50430
|
});
|
|
@@ -50507,7 +50511,7 @@ class HlsPlayback extends BasePlayback {
|
|
|
50507
50511
|
this.trigger(Events$1.PLAYBACK_SETTINGSUPDATE);
|
|
50508
50512
|
}
|
|
50509
50513
|
_onHLSJSError(evt, data) {
|
|
50510
|
-
trace(`${T$
|
|
50514
|
+
trace(`${T$e} _onHLSJSError`, {
|
|
50511
50515
|
fatal: data.fatal,
|
|
50512
50516
|
type: data.type,
|
|
50513
50517
|
details: data.details,
|
|
@@ -50555,7 +50559,7 @@ class HlsPlayback extends BasePlayback {
|
|
|
50555
50559
|
evt,
|
|
50556
50560
|
data,
|
|
50557
50561
|
});
|
|
50558
|
-
trace(`${T$
|
|
50562
|
+
trace(`${T$e} _onHLSJSError trying to recover from network error`, {
|
|
50559
50563
|
details: data.details,
|
|
50560
50564
|
});
|
|
50561
50565
|
error.level = PlayerError.Levels.WARN;
|
|
@@ -50568,7 +50572,7 @@ class HlsPlayback extends BasePlayback {
|
|
|
50568
50572
|
evt,
|
|
50569
50573
|
data,
|
|
50570
50574
|
});
|
|
50571
|
-
trace(`${T$
|
|
50575
|
+
trace(`${T$e} _onHLSJSError trying to recover from media error`, {
|
|
50572
50576
|
details: data.details,
|
|
50573
50577
|
});
|
|
50574
50578
|
error.level = PlayerError.Levels.WARN;
|
|
@@ -50598,7 +50602,7 @@ class HlsPlayback extends BasePlayback {
|
|
|
50598
50602
|
return;
|
|
50599
50603
|
}
|
|
50600
50604
|
Log.warn('hlsjs: non-fatal error occurred', { evt, data });
|
|
50601
|
-
trace(`${T$
|
|
50605
|
+
trace(`${T$e} _onHLSJSError non-fatal error occurred`, {
|
|
50602
50606
|
type: data.type,
|
|
50603
50607
|
details: data.details,
|
|
50604
50608
|
});
|
|
@@ -50939,7 +50943,7 @@ class HlsPlayback extends BasePlayback {
|
|
|
50939
50943
|
this.trigger(Events$1.PLAYBACK_AUDIO_AVAILABLE, data.audioTracks.map(toClapprTrack));
|
|
50940
50944
|
}
|
|
50941
50945
|
_onAudioTrackSwitched(_, data) {
|
|
50942
|
-
trace(`${T$
|
|
50946
|
+
trace(`${T$e} onAudioTrackSwitched`);
|
|
50943
50947
|
// @ts-ignore
|
|
50944
50948
|
const track = this._hls.audioTracks[data.id];
|
|
50945
50949
|
this.trigger(Events$1.PLAYBACK_AUDIO_CHANGED, toClapprTrack(track));
|
|
@@ -50984,7 +50988,7 @@ function toClapprTrack(t) {
|
|
|
50984
50988
|
};
|
|
50985
50989
|
}
|
|
50986
50990
|
|
|
50987
|
-
const T$
|
|
50991
|
+
const T$d = 'playback.html5_video';
|
|
50988
50992
|
const STALL_TIMEOUT = 15000;
|
|
50989
50993
|
class HTML5Video extends BasePlayback {
|
|
50990
50994
|
stallTimerId = null;
|
|
@@ -51085,7 +51089,7 @@ class HTML5Video extends BasePlayback {
|
|
|
51085
51089
|
switchAudioTrack(id) {
|
|
51086
51090
|
const tracks = this.el.audioTracks;
|
|
51087
51091
|
const supported = !!tracks;
|
|
51088
|
-
trace(`${T$
|
|
51092
|
+
trace(`${T$d} switchAudioTrack`, {
|
|
51089
51093
|
supported,
|
|
51090
51094
|
});
|
|
51091
51095
|
if (supported) {
|
|
@@ -51104,7 +51108,7 @@ function registerPlaybacks() {
|
|
|
51104
51108
|
Loader.registerPlayback(DashPlayback);
|
|
51105
51109
|
}
|
|
51106
51110
|
|
|
51107
|
-
const T$
|
|
51111
|
+
const T$c = 'gplayer';
|
|
51108
51112
|
const DEFAULT_OPTIONS = {
|
|
51109
51113
|
autoPlay: false,
|
|
51110
51114
|
debug: 'none',
|
|
@@ -51298,6 +51302,17 @@ class Player {
|
|
|
51298
51302
|
}
|
|
51299
51303
|
this.player?.load(ms, ms[0].mimeType ?? '');
|
|
51300
51304
|
}
|
|
51305
|
+
/**
|
|
51306
|
+
* Returns a registered core plugin instance by name, or `null` if not found.
|
|
51307
|
+
*
|
|
51308
|
+
* @example
|
|
51309
|
+
* ```ts
|
|
51310
|
+
* const tokenRefresh = player.getPlugin('token_refresh') as TokenRefreshPlugin | null
|
|
51311
|
+
* ```
|
|
51312
|
+
*/
|
|
51313
|
+
getPlugin(name) {
|
|
51314
|
+
return this.player?.core.getPlugin(name) ?? null;
|
|
51315
|
+
}
|
|
51301
51316
|
/**
|
|
51302
51317
|
* Mutes the sound of the video.
|
|
51303
51318
|
*/
|
|
@@ -51440,7 +51455,7 @@ class Player {
|
|
|
51440
51455
|
}
|
|
51441
51456
|
}
|
|
51442
51457
|
triggerAutoPlay() {
|
|
51443
|
-
trace(`${T$
|
|
51458
|
+
trace(`${T$c} triggerAutoPlay`);
|
|
51444
51459
|
setTimeout(() => {
|
|
51445
51460
|
this.player?.play({
|
|
51446
51461
|
autoPlay: true,
|
|
@@ -51458,7 +51473,7 @@ class Player {
|
|
|
51458
51473
|
// TODO test
|
|
51459
51474
|
events = {
|
|
51460
51475
|
onReady: () => {
|
|
51461
|
-
trace(`${T$
|
|
51476
|
+
trace(`${T$c} onReady`, {
|
|
51462
51477
|
ready: this.ready,
|
|
51463
51478
|
});
|
|
51464
51479
|
if (this.ready) {
|
|
@@ -51492,7 +51507,7 @@ class Player {
|
|
|
51492
51507
|
buildCoreOptions(rootNode) {
|
|
51493
51508
|
const sources = this.buildMediaSourcesList();
|
|
51494
51509
|
const source = sources[0];
|
|
51495
|
-
trace(`${T$
|
|
51510
|
+
trace(`${T$c} buildCoreOptions`, {
|
|
51496
51511
|
source,
|
|
51497
51512
|
sources,
|
|
51498
51513
|
});
|
|
@@ -51569,7 +51584,7 @@ class Player {
|
|
|
51569
51584
|
}
|
|
51570
51585
|
}
|
|
51571
51586
|
|
|
51572
|
-
var version$1 = "2.
|
|
51587
|
+
var version$1 = "2.30.0";
|
|
51573
51588
|
|
|
51574
51589
|
var packages = {
|
|
51575
51590
|
"node_modules/@clappr/core": {
|
|
@@ -52002,7 +52017,7 @@ const INITIAL_SETTINGS = {
|
|
|
52002
52017
|
default: [],
|
|
52003
52018
|
seekEnabled: false,
|
|
52004
52019
|
};
|
|
52005
|
-
const T$
|
|
52020
|
+
const T$b = 'plugins.media_control';
|
|
52006
52021
|
/**
|
|
52007
52022
|
* Extended events for the {@link MediaControl} plugin
|
|
52008
52023
|
* @public
|
|
@@ -52295,7 +52310,7 @@ class MediaControl extends UICorePlugin {
|
|
|
52295
52310
|
* Reenables the plugin disabled earlier with the {@link MediaControl.disable} method
|
|
52296
52311
|
*/
|
|
52297
52312
|
enable() {
|
|
52298
|
-
trace(`${T$
|
|
52313
|
+
trace(`${T$b} enable`, {
|
|
52299
52314
|
chromeless: this.options.chromeless,
|
|
52300
52315
|
userDisabled: this.userDisabled,
|
|
52301
52316
|
});
|
|
@@ -52452,7 +52467,7 @@ class MediaControl extends UICorePlugin {
|
|
|
52452
52467
|
this.$el.removeClass('w370');
|
|
52453
52468
|
this.$el.removeClass('w270');
|
|
52454
52469
|
this.verticalVolume = false;
|
|
52455
|
-
trace(`${T$
|
|
52470
|
+
trace(`${T$b} playerResize`, {
|
|
52456
52471
|
size,
|
|
52457
52472
|
width: this.container.$el.width(),
|
|
52458
52473
|
height: this.container.$el.height(),
|
|
@@ -53454,7 +53469,7 @@ class AudioTracks extends UICorePlugin {
|
|
|
53454
53469
|
|
|
53455
53470
|
const templateHtml$2 = "<div class=\"big-mute-icon-wrapper\" data-big-mute id=\"gplayer-big-mute-button\">\n <div class=\"big-mute-icon gcore-skin-border-color\" data-big-mute-icon id=\"gplayer-big-mute-icon\"></div>\n</div>\n";
|
|
53456
53471
|
|
|
53457
|
-
const T$
|
|
53472
|
+
const T$a = 'plugins.big_mute_button';
|
|
53458
53473
|
// TODO rewrite as a container plugin
|
|
53459
53474
|
/**
|
|
53460
53475
|
* `PLUGIN` that displays a big mute button over the video when it's being played muted.
|
|
@@ -53517,7 +53532,7 @@ class BigMuteButton extends UICorePlugin {
|
|
|
53517
53532
|
if (autoPlay) {
|
|
53518
53533
|
this.autoPlay = true;
|
|
53519
53534
|
}
|
|
53520
|
-
trace(`${T$
|
|
53535
|
+
trace(`${T$a} onPlay`, {
|
|
53521
53536
|
autoPlay: this.autoPlay,
|
|
53522
53537
|
wasMuted,
|
|
53523
53538
|
volume,
|
|
@@ -53531,7 +53546,7 @@ class BigMuteButton extends UICorePlugin {
|
|
|
53531
53546
|
}
|
|
53532
53547
|
onStop(_, metadata) {
|
|
53533
53548
|
const ui = metadata?.ui;
|
|
53534
|
-
trace(`${T$
|
|
53549
|
+
trace(`${T$a} onStop`, { ui });
|
|
53535
53550
|
if (ui) {
|
|
53536
53551
|
this.destroy();
|
|
53537
53552
|
}
|
|
@@ -56495,7 +56510,7 @@ const PLAYBACK_NAMES = {
|
|
|
56495
56510
|
hls: 'HLS.js',
|
|
56496
56511
|
html5_video: 'Native',
|
|
56497
56512
|
};
|
|
56498
|
-
const T$
|
|
56513
|
+
const T$9 = 'plugins.nerd_stats';
|
|
56499
56514
|
/**
|
|
56500
56515
|
* `PLUGIN` that displays useful statistics regarding the playback as well as the network quality estimation.
|
|
56501
56516
|
* @public
|
|
@@ -56632,7 +56647,7 @@ class NerdStats extends UICorePlugin {
|
|
|
56632
56647
|
return super.destroy();
|
|
56633
56648
|
}
|
|
56634
56649
|
toggle = () => {
|
|
56635
|
-
trace(`${T$
|
|
56650
|
+
trace(`${T$9} toggle`, {
|
|
56636
56651
|
open: this.open,
|
|
56637
56652
|
});
|
|
56638
56653
|
if (this.open) {
|
|
@@ -56652,14 +56667,14 @@ class NerdStats extends UICorePlugin {
|
|
|
56652
56667
|
})
|
|
56653
56668
|
.catch((e) => {
|
|
56654
56669
|
reportError(e);
|
|
56655
|
-
trace(`${T$
|
|
56670
|
+
trace(`${T$9} speedtest error`, {
|
|
56656
56671
|
error: e,
|
|
56657
56672
|
});
|
|
56658
56673
|
this.disable();
|
|
56659
56674
|
});
|
|
56660
56675
|
}
|
|
56661
56676
|
hide() {
|
|
56662
|
-
trace(`${T$
|
|
56677
|
+
trace(`${T$9} hide`);
|
|
56663
56678
|
this.$el.hide();
|
|
56664
56679
|
this.open = false;
|
|
56665
56680
|
stopSpeedtest();
|
|
@@ -57392,7 +57407,7 @@ const reloadIcon = "<svg fill=\"#FFFFFF\" height=\"24\" viewBox=\"0 0 24 24\" wi
|
|
|
57392
57407
|
|
|
57393
57408
|
const templateHtml = "<div class=\"player-error-screen__content\" data-error-screen>\n <% if (icon) { %>\n <div class=\"player-error-screen__icon\" data-error-screen><%= icon %></div>\n <% } %>\n <div class=\"player-error-screen__title\" data-error-screen><%= title %></div>\n <% if (message) { %>\n <div class=\"player-error-screen__message\" data-error-screen><%= message %></div>\n <% } %>\n <% if (code) { %>\n <div class=\"player-error-screen__code\" data-error-screen><%= i18n.t('error_code') %>: <%= code %></div>\n <% } %>\n <% if (reloadIcon) { %>\n <div class=\"player-error-screen__reload\" data-error-screen><%= reloadIcon %></div>\n <% } %>\n</div>\n";
|
|
57394
57409
|
|
|
57395
|
-
const T$
|
|
57410
|
+
const T$8 = 'plugins.error_screen';
|
|
57396
57411
|
/**
|
|
57397
57412
|
* `PLUGIN` that displays fatal errors nicely in the overlay on top of the player.
|
|
57398
57413
|
* @public
|
|
@@ -57444,11 +57459,11 @@ class ErrorScreen extends UICorePlugin {
|
|
|
57444
57459
|
this.listenTo(this.core, Events$1.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChanged);
|
|
57445
57460
|
}
|
|
57446
57461
|
onPlay() {
|
|
57447
|
-
trace(`${T$
|
|
57462
|
+
trace(`${T$8} onPlay`);
|
|
57448
57463
|
this.unmount();
|
|
57449
57464
|
}
|
|
57450
57465
|
unmount() {
|
|
57451
|
-
trace(`${T$
|
|
57466
|
+
trace(`${T$8} unmount`);
|
|
57452
57467
|
this.err = null;
|
|
57453
57468
|
this.$el.remove();
|
|
57454
57469
|
}
|
|
@@ -57461,7 +57476,7 @@ class ErrorScreen extends UICorePlugin {
|
|
|
57461
57476
|
};
|
|
57462
57477
|
}
|
|
57463
57478
|
reload() {
|
|
57464
|
-
trace(`${T$
|
|
57479
|
+
trace(`${T$8} reload`);
|
|
57465
57480
|
setTimeout(() => {
|
|
57466
57481
|
this.core.configure({
|
|
57467
57482
|
reloading: true,
|
|
@@ -57484,7 +57499,7 @@ class ErrorScreen extends UICorePlugin {
|
|
|
57484
57499
|
}
|
|
57485
57500
|
}
|
|
57486
57501
|
onError(err) {
|
|
57487
|
-
trace(`${T$
|
|
57502
|
+
trace(`${T$8} onError`, { err });
|
|
57488
57503
|
if (err.UI) {
|
|
57489
57504
|
if (this.err) {
|
|
57490
57505
|
this.unmount();
|
|
@@ -57965,7 +57980,7 @@ const pluginHtml$3 = "<button data-multicamera-button class='gcore-skin-button-c
|
|
|
57965
57980
|
const streamsIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"20\" viewBox=\"0 0 24 20\">\n <path fill=\"#FFF\" fill-rule=\"nonzero\" d=\"M24 1.5v13a1.5 1.5 0 0 1-1.5 1.5h-1a.5.5 0 0 1-.5-.5v-10A2.5 2.5 0 0 0 18.5 3h-14a.5.5 0 0 1-.5-.5v-1A1.5 1.5 0 0 1 5.5 0h17A1.5 1.5 0 0 1 24 1.5M12.724 12.447l-5 2.5a.505.505 0 0 1-.487-.021A.503.503 0 0 1 7 14.5v-5c0-.173.09-.334.237-.426a.505.505 0 0 1 .487-.021l5 2.5a.5.5 0 0 1 0 .894M18.5 4h-17C.673 4 0 4.673 0 5.5v13c0 .827.673 1.5 1.5 1.5h17c.827 0 1.5-.673 1.5-1.5v-13c0-.827-.673-1.5-1.5-1.5\"/>\n</svg>\n";
|
|
57966
57981
|
|
|
57967
57982
|
const VERSION$4 = '0.0.1';
|
|
57968
|
-
const T$
|
|
57983
|
+
const T$7 = 'plugins.multicamera';
|
|
57969
57984
|
/**
|
|
57970
57985
|
* `PLUGIN` that adds support for loading multiple streams and switching between them using the media control UI.
|
|
57971
57986
|
* @beta
|
|
@@ -58092,7 +58107,7 @@ class MultiCamera extends UICorePlugin {
|
|
|
58092
58107
|
onCameraSelect(event) {
|
|
58093
58108
|
const value = event.currentTarget.dataset
|
|
58094
58109
|
.multicameraSelectorSelect;
|
|
58095
|
-
trace(`${T$
|
|
58110
|
+
trace(`${T$7} onCameraSelect`, { value });
|
|
58096
58111
|
if (value !== undefined) {
|
|
58097
58112
|
this.changeById(parseInt(value, 10));
|
|
58098
58113
|
}
|
|
@@ -58190,7 +58205,7 @@ class MultiCamera extends UICorePlugin {
|
|
|
58190
58205
|
}
|
|
58191
58206
|
}
|
|
58192
58207
|
changeById(id) {
|
|
58193
|
-
trace(`${T$
|
|
58208
|
+
trace(`${T$7} changeById`, { id });
|
|
58194
58209
|
queueMicrotask(() => {
|
|
58195
58210
|
const playbackOptions = this.core.options.playback || {};
|
|
58196
58211
|
// TODO figure out if it's needed
|
|
@@ -58212,7 +58227,7 @@ class MultiCamera extends UICorePlugin {
|
|
|
58212
58227
|
// TODO remove?
|
|
58213
58228
|
// for html5 playback:
|
|
58214
58229
|
this.options.dvrEnabled = this.currentCamera.dvr;
|
|
58215
|
-
trace(`${T$
|
|
58230
|
+
trace(`${T$7} changeById`, { currentCamera: this.currentCamera });
|
|
58216
58231
|
// TODO
|
|
58217
58232
|
this.core.configure({
|
|
58218
58233
|
playback: playbackOptions,
|
|
@@ -58275,7 +58290,7 @@ const pipIcon = "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"no
|
|
|
58275
58290
|
const buttonHtml$2 = "<button class=\"gplayer-lite-btn gcore-skin-button-color\">\n <%= pipIcon %>\n</button>\n";
|
|
58276
58291
|
|
|
58277
58292
|
const VERSION$3 = '0.0.1';
|
|
58278
|
-
const T$
|
|
58293
|
+
const T$6 = `plugins.pip`;
|
|
58279
58294
|
/**
|
|
58280
58295
|
* `PLUGIN` that enables picture-in-picture mode.
|
|
58281
58296
|
* @public
|
|
@@ -58333,7 +58348,7 @@ class PictureInPicture extends UICorePlugin {
|
|
|
58333
58348
|
});
|
|
58334
58349
|
}
|
|
58335
58350
|
isPiPSupported() {
|
|
58336
|
-
trace(`${T$
|
|
58351
|
+
trace(`${T$6} isPiPSupported`, {
|
|
58337
58352
|
pictureInPictureEnabled: !!document.pictureInPictureEnabled,
|
|
58338
58353
|
requestPictureInPicture: !!HTMLVideoElement.prototype.requestPictureInPicture,
|
|
58339
58354
|
});
|
|
@@ -58355,7 +58370,7 @@ class PictureInPicture extends UICorePlugin {
|
|
|
58355
58370
|
return this;
|
|
58356
58371
|
}
|
|
58357
58372
|
togglePictureInPicture() {
|
|
58358
|
-
trace(`${T$
|
|
58373
|
+
trace(`${T$6} togglePictureInPicture`);
|
|
58359
58374
|
if (this.videoElement !== document.pictureInPictureElement) {
|
|
58360
58375
|
this.requestPictureInPicture();
|
|
58361
58376
|
}
|
|
@@ -58364,13 +58379,13 @@ class PictureInPicture extends UICorePlugin {
|
|
|
58364
58379
|
}
|
|
58365
58380
|
}
|
|
58366
58381
|
requestPictureInPicture() {
|
|
58367
|
-
trace(`${T$
|
|
58382
|
+
trace(`${T$6} requestPictureInPicture`, {
|
|
58368
58383
|
videoElement: !!this.videoElement,
|
|
58369
58384
|
});
|
|
58370
58385
|
this.videoElement.requestPictureInPicture();
|
|
58371
58386
|
}
|
|
58372
58387
|
exitPictureInPicture() {
|
|
58373
|
-
trace(`${T$
|
|
58388
|
+
trace(`${T$6} exitPictureInPicture`);
|
|
58374
58389
|
document.exitPictureInPicture();
|
|
58375
58390
|
}
|
|
58376
58391
|
}
|
|
@@ -58397,7 +58412,7 @@ const DEFAULT_PLAYBACK_RATES = [
|
|
|
58397
58412
|
{ value: 2.0, label: '2x' },
|
|
58398
58413
|
];
|
|
58399
58414
|
const DEFAULT_PLAYBACK_RATE = 1;
|
|
58400
|
-
const T$
|
|
58415
|
+
const T$5 = 'plugins.playback_rate';
|
|
58401
58416
|
/**
|
|
58402
58417
|
* `PLUGIN` that allows changing the playback speed of the video.
|
|
58403
58418
|
* @public
|
|
@@ -58483,7 +58498,7 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58483
58498
|
this.listenTo(this.core, Events$1.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChange);
|
|
58484
58499
|
}
|
|
58485
58500
|
onCoreReady() {
|
|
58486
|
-
trace(`${T$
|
|
58501
|
+
trace(`${T$5} onCoreReady`);
|
|
58487
58502
|
const mediaControl = this.core.getPlugin('media_control');
|
|
58488
58503
|
assert(mediaControl, 'media_control plugin is required');
|
|
58489
58504
|
const gear = this.core.getPlugin('bottom_gear');
|
|
@@ -58492,7 +58507,7 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58492
58507
|
this.listenTo(gear, GearEvents.RENDERED, this.onGearRendered);
|
|
58493
58508
|
}
|
|
58494
58509
|
onActiveContainerChange() {
|
|
58495
|
-
trace(`${T$
|
|
58510
|
+
trace(`${T$5} onActiveContainerChange`);
|
|
58496
58511
|
this.metadataLoaded = false;
|
|
58497
58512
|
this.listenTo(this.core.activePlayback, Events$1.PLAYBACK_STOP, this.onStop);
|
|
58498
58513
|
this.listenTo(this.core.activePlayback, Events$1.PLAYBACK_PLAY, this.onPlay);
|
|
@@ -58500,15 +58515,15 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58500
58515
|
this.listenTo(this.core.activeContainer, Events$1.CONTAINER_LOADEDMETADATA, this.onMetaDataLoaded);
|
|
58501
58516
|
}
|
|
58502
58517
|
onMediaControlRendered() {
|
|
58503
|
-
trace(`${T$
|
|
58518
|
+
trace(`${T$5} onMediaControlRendered`);
|
|
58504
58519
|
this.render();
|
|
58505
58520
|
}
|
|
58506
58521
|
onGearRendered() {
|
|
58507
|
-
trace(`${T$
|
|
58522
|
+
trace(`${T$5} onGearRendered`);
|
|
58508
58523
|
this.mount();
|
|
58509
58524
|
}
|
|
58510
58525
|
mount() {
|
|
58511
|
-
trace(`${T$
|
|
58526
|
+
trace(`${T$5} mount`, {
|
|
58512
58527
|
shouldMount: this.shouldMount(),
|
|
58513
58528
|
});
|
|
58514
58529
|
if (!this.shouldMount()) {
|
|
@@ -58525,7 +58540,7 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58525
58540
|
})));
|
|
58526
58541
|
}
|
|
58527
58542
|
onMetaDataLoaded() {
|
|
58528
|
-
trace(`${T$
|
|
58543
|
+
trace(`${T$5} onMetaDataLoaded`, {
|
|
58529
58544
|
playbackType: this.core.activePlayback.getPlaybackType(),
|
|
58530
58545
|
dvrEnabled: this.core.activePlayback.dvrEnabled,
|
|
58531
58546
|
});
|
|
@@ -58547,7 +58562,7 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58547
58562
|
this.core.activePlayback?.setPlaybackRate(this.selectedRate);
|
|
58548
58563
|
}
|
|
58549
58564
|
else {
|
|
58550
|
-
trace(`${T$
|
|
58565
|
+
trace(`${T$5} onPlaybackRateChange not steering to the selected rate, it is seemingly a catchup algorithm working`, {
|
|
58551
58566
|
playbackRate,
|
|
58552
58567
|
selectedRate: this.selectedRate,
|
|
58553
58568
|
});
|
|
@@ -58610,13 +58625,13 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58610
58625
|
}
|
|
58611
58626
|
}
|
|
58612
58627
|
syncRate() {
|
|
58613
|
-
trace(`${T$
|
|
58628
|
+
trace(`${T$5} syncRate`, {
|
|
58614
58629
|
selectedRate: this.selectedRate,
|
|
58615
58630
|
});
|
|
58616
58631
|
this.core.activePlayback?.setPlaybackRate(this.selectedRate);
|
|
58617
58632
|
}
|
|
58618
58633
|
resetPlaybackRate() {
|
|
58619
|
-
trace(`${T$
|
|
58634
|
+
trace(`${T$5} resetPlaybackRate`, {
|
|
58620
58635
|
selectedRate: this.selectedRate,
|
|
58621
58636
|
});
|
|
58622
58637
|
this.core.activePlayback?.setPlaybackRate(DEFAULT_PLAYBACK_RATE);
|
|
@@ -58651,7 +58666,7 @@ class PlaybackRate extends UICorePlugin {
|
|
|
58651
58666
|
?.label || `x${rate}`);
|
|
58652
58667
|
}
|
|
58653
58668
|
highlightCurrentRate() {
|
|
58654
|
-
trace(`${T$
|
|
58669
|
+
trace(`${T$5} highlightCurrentRate`, {
|
|
58655
58670
|
selectedRate: this.selectedRate,
|
|
58656
58671
|
});
|
|
58657
58672
|
this.allRateElements().removeClass('current');
|
|
@@ -59687,7 +59702,7 @@ class SpinnerThreeBounce extends UIContainerPlugin {
|
|
|
59687
59702
|
}
|
|
59688
59703
|
}
|
|
59689
59704
|
|
|
59690
|
-
const T$
|
|
59705
|
+
const T$4 = 'plugins.source_controller';
|
|
59691
59706
|
const INITIAL_RETRY_DELAY = 1000;
|
|
59692
59707
|
const MAX_RETRY_DELAY = 5000;
|
|
59693
59708
|
const RETRY_DELAY_BLUR = 500;
|
|
@@ -59824,7 +59839,7 @@ class SourceController extends CorePlugin {
|
|
|
59824
59839
|
}
|
|
59825
59840
|
bindContainerEventListeners() {
|
|
59826
59841
|
this.core.activePlayback.on(Events$1.PLAYBACK_ERROR, (error) => {
|
|
59827
|
-
trace(`${T$
|
|
59842
|
+
trace(`${T$4} on PLAYBACK_ERROR`, {
|
|
59828
59843
|
error: {
|
|
59829
59844
|
code: error?.code,
|
|
59830
59845
|
description: error?.description,
|
|
@@ -59848,7 +59863,7 @@ class SourceController extends CorePlugin {
|
|
|
59848
59863
|
}
|
|
59849
59864
|
});
|
|
59850
59865
|
this.listenTo(this.core.activeContainer, Events$1.CONTAINER_PLAY, (_, { autoPlay }) => {
|
|
59851
|
-
trace(`${T$
|
|
59866
|
+
trace(`${T$4} onContainerPlay`, {
|
|
59852
59867
|
autoPlay,
|
|
59853
59868
|
currentSource: this.sourcesList[this.currentSourceIndex],
|
|
59854
59869
|
retrying: this.active,
|
|
@@ -59866,7 +59881,7 @@ class SourceController extends CorePlugin {
|
|
|
59866
59881
|
this.sourcesDelay = {};
|
|
59867
59882
|
}
|
|
59868
59883
|
retryPlayback() {
|
|
59869
|
-
trace(`${T$
|
|
59884
|
+
trace(`${T$4} retryPlayback enter`, {
|
|
59870
59885
|
currentSourceIndex: this.currentSourceIndex,
|
|
59871
59886
|
currentSource: this.sourcesList[this.currentSourceIndex],
|
|
59872
59887
|
});
|
|
@@ -59874,18 +59889,18 @@ class SourceController extends CorePlugin {
|
|
|
59874
59889
|
this.switching = true;
|
|
59875
59890
|
this.core.activeContainer?.getPlugin('spinner')?.show(0);
|
|
59876
59891
|
this.getNextMediaSource().then((nextSource) => {
|
|
59877
|
-
trace(`${T$
|
|
59892
|
+
trace(`${T$4} retryPlayback syncing...`, {
|
|
59878
59893
|
nextSource,
|
|
59879
59894
|
});
|
|
59880
59895
|
const rnd = Math.round(RETRY_DELAY_BLUR * Math.random());
|
|
59881
59896
|
this.sync(() => {
|
|
59882
59897
|
this.switching = false;
|
|
59883
59898
|
this.core.load(nextSource.source, nextSource.mimeType);
|
|
59884
|
-
trace(`${T$
|
|
59899
|
+
trace(`${T$4} retryPlayback loaded`, {
|
|
59885
59900
|
nextSource,
|
|
59886
59901
|
});
|
|
59887
59902
|
setTimeout(() => {
|
|
59888
|
-
trace(`${T$
|
|
59903
|
+
trace(`${T$4} retryPlayback playing`, {
|
|
59889
59904
|
autoPlay: this.autoPlay,
|
|
59890
59905
|
nextSource,
|
|
59891
59906
|
});
|
|
@@ -60397,7 +60412,7 @@ class ClosedCaptions extends UICorePlugin {
|
|
|
60397
60412
|
// An example implementation of client side performancestatistics
|
|
60398
60413
|
const WATCH_CUTOFF = 5;
|
|
60399
60414
|
const STALL_MEASURE_PERIOD = 10;
|
|
60400
|
-
const T$
|
|
60415
|
+
const T$3 = 'plugins.telemetry';
|
|
60401
60416
|
/**
|
|
60402
60417
|
* Telemetry event type
|
|
60403
60418
|
* @beta
|
|
@@ -60507,7 +60522,7 @@ class Telemetry extends ContainerPlugin {
|
|
|
60507
60522
|
}
|
|
60508
60523
|
onReady() {
|
|
60509
60524
|
this.sendInit();
|
|
60510
|
-
trace(`${T$
|
|
60525
|
+
trace(`${T$3} onReady`, {
|
|
60511
60526
|
autoPlay: this.options.autoPlay,
|
|
60512
60527
|
});
|
|
60513
60528
|
if (this.options.autoPlay) {
|
|
@@ -63208,7 +63223,7 @@ function loadImageDimensions(url) {
|
|
|
63208
63223
|
});
|
|
63209
63224
|
}
|
|
63210
63225
|
|
|
63211
|
-
const T$
|
|
63226
|
+
const T$2 = 'plugins.thumbnails';
|
|
63212
63227
|
/**
|
|
63213
63228
|
* `PLUGIN` that displays the thumbnails of the video when available.
|
|
63214
63229
|
* @public
|
|
@@ -63326,14 +63341,14 @@ class Thumbnails extends UICorePlugin {
|
|
|
63326
63341
|
if (!this.options.thumbnails ||
|
|
63327
63342
|
!this.options.thumbnails.sprite ||
|
|
63328
63343
|
!this.options.thumbnails.vtt) {
|
|
63329
|
-
trace(`${T$
|
|
63344
|
+
trace(`${T$2} misconfigured: options.thumbnails.sprite and options.thumbnails.vtt are required`);
|
|
63330
63345
|
this.destroy();
|
|
63331
63346
|
return;
|
|
63332
63347
|
}
|
|
63333
63348
|
const { sprite: spriteSheet, vtt } = this.options.thumbnails;
|
|
63334
63349
|
this.thumbs = this.buildSpriteConfig(parseVTT(vtt), spriteSheet);
|
|
63335
63350
|
if (!this.thumbs.length) {
|
|
63336
|
-
trace(`${T$
|
|
63351
|
+
trace(`${T$2} failed to parse the sprite sheet`);
|
|
63337
63352
|
this.destroy();
|
|
63338
63353
|
return;
|
|
63339
63354
|
}
|
|
@@ -63582,6 +63597,321 @@ function parseVTT(vtt) {
|
|
|
63582
63597
|
return cues;
|
|
63583
63598
|
}
|
|
63584
63599
|
|
|
63600
|
+
const T$1 = 'plugins.token_refresh';
|
|
63601
|
+
/**
|
|
63602
|
+
* Matches the `/{token}/{expires}/` segment in a Gcore protected-content URL.
|
|
63603
|
+
* Token is base64url (letters, digits, `-`, `_`); expires is a ≥10-digit Unix timestamp.
|
|
63604
|
+
*/
|
|
63605
|
+
const TOKEN_SEGMENT_RE = /\/([A-Za-z0-9_-]{6,})\/(1\d{9,})\//;
|
|
63606
|
+
function extractTokenState(url) {
|
|
63607
|
+
const m = url.match(TOKEN_SEGMENT_RE);
|
|
63608
|
+
if (!m)
|
|
63609
|
+
return null;
|
|
63610
|
+
return { token: m[1], expires: parseInt(m[2], 10) };
|
|
63611
|
+
}
|
|
63612
|
+
/** Replaces the exact `/{oldToken}/{oldExpires}/` segment in a URL. */
|
|
63613
|
+
function rewriteUrl(url, from, to) {
|
|
63614
|
+
const oldPart = `/${from.token}/${from.expires}/`;
|
|
63615
|
+
const newPart = `/${to.token}/${to.expires}/`;
|
|
63616
|
+
return url.includes(oldPart) ? url.replace(oldPart, newPart) : url;
|
|
63617
|
+
}
|
|
63618
|
+
/**
|
|
63619
|
+
* Normalises a URL by removing the `/{token}/{expires}/` segment so two URLs
|
|
63620
|
+
* for the same stream with different token pairs compare equal.
|
|
63621
|
+
* Returns `null` for unparseable input.
|
|
63622
|
+
*/
|
|
63623
|
+
function streamKey(url) {
|
|
63624
|
+
try {
|
|
63625
|
+
const u = new URL(url);
|
|
63626
|
+
return u.origin + u.pathname.replace(TOKEN_SEGMENT_RE, '/');
|
|
63627
|
+
}
|
|
63628
|
+
catch {
|
|
63629
|
+
return null;
|
|
63630
|
+
}
|
|
63631
|
+
}
|
|
63632
|
+
/**
|
|
63633
|
+
* Returns a custom hls.js loader class that transparently rewrites the
|
|
63634
|
+
* token/expires path segments in every request URL.
|
|
63635
|
+
*
|
|
63636
|
+
* The returned class extends the default hls.js XhrLoader so all native
|
|
63637
|
+
* hls.js behaviour (retry, timeout, range requests …) is preserved.
|
|
63638
|
+
*/
|
|
63639
|
+
function createTokenRewritingLoader(getOriginal, getCurrent) {
|
|
63640
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63641
|
+
const DefaultLoader = Hls.DefaultConfig.loader;
|
|
63642
|
+
return class TokenRewritingLoader extends DefaultLoader {
|
|
63643
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63644
|
+
load(context, config, callbacks) {
|
|
63645
|
+
const original = getOriginal();
|
|
63646
|
+
const current = getCurrent();
|
|
63647
|
+
if (original && current && context.url) {
|
|
63648
|
+
context.url = rewriteUrl(context.url, original, current);
|
|
63649
|
+
}
|
|
63650
|
+
super.load(context, config, callbacks);
|
|
63651
|
+
}
|
|
63652
|
+
};
|
|
63653
|
+
}
|
|
63654
|
+
/**
|
|
63655
|
+
* `PLUGIN` — automatic token refresh for Gcore protected-content streams.
|
|
63656
|
+
*
|
|
63657
|
+
* Supports all three playback engines:
|
|
63658
|
+
*
|
|
63659
|
+
* | Engine | Mechanism | Interruption |
|
|
63660
|
+
* |--------|-----------|--------------|
|
|
63661
|
+
* | **hls.js** | Custom loader rewrites every request URL | None |
|
|
63662
|
+
* | **dash.js** | `addRequestInterceptor` rewrites every request URL | None |
|
|
63663
|
+
* | **Native `<video>`** (Safari ≤ iOS 14.4) | Source reload + seek restore | Brief |
|
|
63664
|
+
*
|
|
63665
|
+
* @public
|
|
63666
|
+
* @remarks
|
|
63667
|
+
* Register the plugin once before creating any player instance:
|
|
63668
|
+
* ```ts
|
|
63669
|
+
* import { Player, TokenRefreshPlugin } from '@gcorevideo/player'
|
|
63670
|
+
* Player.registerPlugin(TokenRefreshPlugin)
|
|
63671
|
+
* ```
|
|
63672
|
+
*
|
|
63673
|
+
* Then pass `tokenRefresh` in `PlayerConfig`:
|
|
63674
|
+
* ```ts
|
|
63675
|
+
* const player = new Player({
|
|
63676
|
+
* sources: [{ source: initialUrl, mimeType: 'application/x-mpegURL' }],
|
|
63677
|
+
* tokenRefresh: {
|
|
63678
|
+
* getToken: () => fetch('https://…/token').then(r => r.json()),
|
|
63679
|
+
* ipBound: false,
|
|
63680
|
+
* refreshLeadSeconds: 5,
|
|
63681
|
+
* onTokenRefreshed: (data) => console.log('new token expires', data.expires),
|
|
63682
|
+
* },
|
|
63683
|
+
* })
|
|
63684
|
+
* ```
|
|
63685
|
+
*
|
|
63686
|
+
* @example
|
|
63687
|
+
* Safari native — opt-in Service Worker for fully seamless refresh:
|
|
63688
|
+
* ```js
|
|
63689
|
+
* // Register token-refresh-sw.js (see example/ directory)
|
|
63690
|
+
* // and omit tokenRefresh config — the SW handles rewriting.
|
|
63691
|
+
* ```
|
|
63692
|
+
*/
|
|
63693
|
+
class TokenRefreshPlugin extends CorePlugin {
|
|
63694
|
+
/** @internal */
|
|
63695
|
+
static get type() {
|
|
63696
|
+
return 'core';
|
|
63697
|
+
}
|
|
63698
|
+
/** @internal */
|
|
63699
|
+
get name() {
|
|
63700
|
+
return 'token_refresh';
|
|
63701
|
+
}
|
|
63702
|
+
/** @internal */
|
|
63703
|
+
get supportedVersion() {
|
|
63704
|
+
return { min: CLAPPR_VERSION$1 };
|
|
63705
|
+
}
|
|
63706
|
+
/** Token state extracted from the currently-managed source URL */
|
|
63707
|
+
originalState = null;
|
|
63708
|
+
/** Latest token state (updated after each refresh) */
|
|
63709
|
+
currentState = null;
|
|
63710
|
+
/** Scheduled refresh timer handle */
|
|
63711
|
+
refreshTimer = null;
|
|
63712
|
+
/** Playback time (seconds) to restore after a native-video source reload */
|
|
63713
|
+
savedPosition = null;
|
|
63714
|
+
/** True when using native HTML5 Video playback (no request interception) */
|
|
63715
|
+
isNativePlayback = false;
|
|
63716
|
+
/** Set in destroy(); short-circuits late timer callbacks and getToken() resolutions */
|
|
63717
|
+
destroyed = false;
|
|
63718
|
+
/** @internal */
|
|
63719
|
+
bindEvents() {
|
|
63720
|
+
this.listenTo(this.core, Events$1.CORE_CONTAINERS_CREATED, this.onContainersCreated);
|
|
63721
|
+
}
|
|
63722
|
+
/** @internal */
|
|
63723
|
+
destroy() {
|
|
63724
|
+
this.destroyed = true;
|
|
63725
|
+
this.clearTimer();
|
|
63726
|
+
super.destroy();
|
|
63727
|
+
}
|
|
63728
|
+
onContainersCreated() {
|
|
63729
|
+
const container = this.core.containers[0];
|
|
63730
|
+
if (!container)
|
|
63731
|
+
return;
|
|
63732
|
+
const playbackName = container.playback.name;
|
|
63733
|
+
const src = container.playback.options?.src ?? '';
|
|
63734
|
+
trace(`${T$1} onContainersCreated`, { playbackName, src: src.slice(0, 80) });
|
|
63735
|
+
this.isNativePlayback = playbackName !== 'hls' && playbackName !== 'dash';
|
|
63736
|
+
const state = extractTokenState(src);
|
|
63737
|
+
if (!state) {
|
|
63738
|
+
// Active source has no token pattern — drop any refresh state we were
|
|
63739
|
+
// holding for a previous stream (e.g. SourceController rotated away).
|
|
63740
|
+
if (this.originalState) {
|
|
63741
|
+
trace(`${T$1} active source has no token pattern — clearing refresh state`);
|
|
63742
|
+
this.clearTimer();
|
|
63743
|
+
this.originalState = null;
|
|
63744
|
+
this.currentState = null;
|
|
63745
|
+
}
|
|
63746
|
+
return;
|
|
63747
|
+
}
|
|
63748
|
+
// Adopt the new token state if this is the first source we see, or if
|
|
63749
|
+
// the stream has changed (SourceController rotated, consumer called
|
|
63750
|
+
// player.load() with a different stream, or the plugin itself reloaded
|
|
63751
|
+
// with a refreshed URL in the native path).
|
|
63752
|
+
const isNewStream = !this.originalState ||
|
|
63753
|
+
state.token !== this.originalState.token ||
|
|
63754
|
+
state.expires !== this.originalState.expires;
|
|
63755
|
+
if (isNewStream) {
|
|
63756
|
+
trace(`${T$1} adopting source token state`, {
|
|
63757
|
+
token: state.token.slice(0, 8) + '…',
|
|
63758
|
+
expires: new Date(state.expires * 1000).toISOString(),
|
|
63759
|
+
});
|
|
63760
|
+
this.originalState = { ...state };
|
|
63761
|
+
this.currentState = { ...state };
|
|
63762
|
+
this.scheduleRefresh();
|
|
63763
|
+
}
|
|
63764
|
+
// Inject the appropriate interception mechanism for this playback engine.
|
|
63765
|
+
switch (playbackName) {
|
|
63766
|
+
case 'hls':
|
|
63767
|
+
this.injectHlsLoader(container);
|
|
63768
|
+
break;
|
|
63769
|
+
case 'dash':
|
|
63770
|
+
this.injectDashInterceptor(container);
|
|
63771
|
+
break;
|
|
63772
|
+
default:
|
|
63773
|
+
// Native HTML5 Video — no request hooks available.
|
|
63774
|
+
// Seek restore after a token-triggered reload.
|
|
63775
|
+
this.listenToOnce(this.core, Events$1.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChangedForNative);
|
|
63776
|
+
break;
|
|
63777
|
+
}
|
|
63778
|
+
}
|
|
63779
|
+
injectHlsLoader(container) {
|
|
63780
|
+
const getOriginal = () => this.originalState;
|
|
63781
|
+
const getCurrent = () => this.currentState;
|
|
63782
|
+
const TokenLoader = createTokenRewritingLoader(getOriginal, getCurrent);
|
|
63783
|
+
$.extend(true, container.playback.options, {
|
|
63784
|
+
playback: {
|
|
63785
|
+
hlsjsConfig: {
|
|
63786
|
+
loader: TokenLoader,
|
|
63787
|
+
},
|
|
63788
|
+
},
|
|
63789
|
+
});
|
|
63790
|
+
trace(`${T$1} HLS custom loader injected`);
|
|
63791
|
+
}
|
|
63792
|
+
injectDashInterceptor(container) {
|
|
63793
|
+
$.extend(true, container.playback.options, {
|
|
63794
|
+
dash: {
|
|
63795
|
+
requestInterceptor: (request) => {
|
|
63796
|
+
if (this.originalState && this.currentState) {
|
|
63797
|
+
request.url = rewriteUrl(request.url, this.originalState, this.currentState);
|
|
63798
|
+
}
|
|
63799
|
+
return Promise.resolve(request);
|
|
63800
|
+
},
|
|
63801
|
+
},
|
|
63802
|
+
});
|
|
63803
|
+
trace(`${T$1} DASH request interceptor injected`);
|
|
63804
|
+
}
|
|
63805
|
+
async reloadNativeSource(data) {
|
|
63806
|
+
const container = this.core.activeContainer;
|
|
63807
|
+
const playback = container?.playback;
|
|
63808
|
+
if (!playback)
|
|
63809
|
+
return;
|
|
63810
|
+
// SourceController (or any other actor) may have switched the active
|
|
63811
|
+
// playback to hls/dash while getToken() was in flight. Those engines
|
|
63812
|
+
// have their own request-interception path; a native reload would
|
|
63813
|
+
// undo the switch.
|
|
63814
|
+
if (playback.name === 'hls' || playback.name === 'dash') {
|
|
63815
|
+
trace(`${T$1} skipping native reload — active playback is ${playback.name}`);
|
|
63816
|
+
return;
|
|
63817
|
+
}
|
|
63818
|
+
if (!this.isNativePlayback) {
|
|
63819
|
+
trace(`${T$1} skipping native reload — no longer in native playback mode`);
|
|
63820
|
+
return;
|
|
63821
|
+
}
|
|
63822
|
+
// Verify the URL we're about to load belongs to the same stream that's
|
|
63823
|
+
// currently active. If SourceController rotated to a different stream
|
|
63824
|
+
// during the getToken() await, the refreshed URL would silently swap
|
|
63825
|
+
// us back, undoing SourceController's decision.
|
|
63826
|
+
const currentSrc = playback.options?.src ?? '';
|
|
63827
|
+
const newUrl = this.opts.ipBound ? data.url_ip : data.url;
|
|
63828
|
+
const activeKey = streamKey(currentSrc);
|
|
63829
|
+
const nextKey = streamKey(newUrl);
|
|
63830
|
+
if (!activeKey || !nextKey || activeKey !== nextKey) {
|
|
63831
|
+
trace(`${T$1} skipping native reload — active source differs from refresh URL`, {
|
|
63832
|
+
activeKey,
|
|
63833
|
+
nextKey,
|
|
63834
|
+
});
|
|
63835
|
+
return;
|
|
63836
|
+
}
|
|
63837
|
+
// Capture current playback position before tearing down the container.
|
|
63838
|
+
const mediaEl = playback.el;
|
|
63839
|
+
const currentTime = mediaEl?.currentTime ?? 0;
|
|
63840
|
+
this.savedPosition = currentTime > 0 ? currentTime : null;
|
|
63841
|
+
trace(`${T$1} native reload`, { newUrl: newUrl.slice(0, 80), savedPosition: this.savedPosition });
|
|
63842
|
+
// core.load() destroys and recreates all containers.
|
|
63843
|
+
this.core.load([{ source: newUrl, mimeType: this.core.options.mimeType ?? 'application/x-mpegURL' }]);
|
|
63844
|
+
}
|
|
63845
|
+
onActiveContainerChangedForNative() {
|
|
63846
|
+
if (this.savedPosition === null)
|
|
63847
|
+
return;
|
|
63848
|
+
const pos = this.savedPosition;
|
|
63849
|
+
this.savedPosition = null;
|
|
63850
|
+
// Wait for the new container to be fully ready before seeking.
|
|
63851
|
+
const container = this.core.activeContainer;
|
|
63852
|
+
if (!container)
|
|
63853
|
+
return;
|
|
63854
|
+
this.listenToOnce(container, Events$1.CONTAINER_READY, () => {
|
|
63855
|
+
trace(`${T$1} native: restoring position`, { pos });
|
|
63856
|
+
container.seek(pos);
|
|
63857
|
+
container.play();
|
|
63858
|
+
});
|
|
63859
|
+
}
|
|
63860
|
+
scheduleRefresh() {
|
|
63861
|
+
this.clearTimer();
|
|
63862
|
+
if (this.destroyed || !this.currentState)
|
|
63863
|
+
return;
|
|
63864
|
+
const leadMs = (this.opts.refreshLeadSeconds ?? 5) * 1000;
|
|
63865
|
+
const msUntilRefresh = this.currentState.expires * 1000 - Date.now() - leadMs;
|
|
63866
|
+
trace(`${T$1} next refresh in`, {
|
|
63867
|
+
seconds: Math.round(msUntilRefresh / 1000),
|
|
63868
|
+
expires: new Date(this.currentState.expires * 1000).toISOString(),
|
|
63869
|
+
});
|
|
63870
|
+
this.refreshTimer = setTimeout(() => this.performRefresh(), Math.max(msUntilRefresh, 1000));
|
|
63871
|
+
}
|
|
63872
|
+
async performRefresh() {
|
|
63873
|
+
trace(`${T$1} fetching new token`);
|
|
63874
|
+
try {
|
|
63875
|
+
const data = await this.opts.getToken();
|
|
63876
|
+
// Plugin may have been destroyed while getToken() was in flight; drop the result.
|
|
63877
|
+
if (this.destroyed)
|
|
63878
|
+
return;
|
|
63879
|
+
const newToken = this.opts.ipBound ? data.token_ip : data.token;
|
|
63880
|
+
const newState = { token: newToken, expires: data.expires };
|
|
63881
|
+
if (this.isNativePlayback) {
|
|
63882
|
+
// Must reload source because the <video> element has no request hook.
|
|
63883
|
+
await this.reloadNativeSource(data);
|
|
63884
|
+
}
|
|
63885
|
+
// originalState is never changed after init — it holds the token that was
|
|
63886
|
+
// baked into every URL in the initial manifest. hls.js/dash.js always
|
|
63887
|
+
// produces request URLs based on that manifest, so every segment URL
|
|
63888
|
+
// still contains the original token regardless of how many refreshes
|
|
63889
|
+
// have already happened. The loader replaces original→current on each
|
|
63890
|
+
// request, so updating only currentState is sufficient.
|
|
63891
|
+
this.currentState = newState;
|
|
63892
|
+
this.opts.onTokenRefreshed?.(data);
|
|
63893
|
+
trace(`${T$1} token refreshed`, {
|
|
63894
|
+
token: newToken.slice(0, 8) + '…',
|
|
63895
|
+
expires: new Date(data.expires * 1000).toISOString(),
|
|
63896
|
+
});
|
|
63897
|
+
}
|
|
63898
|
+
catch (err) {
|
|
63899
|
+
trace(`${T$1} token refresh failed`, { err });
|
|
63900
|
+
}
|
|
63901
|
+
// Always reschedule, even after an error (will retry near next expiry).
|
|
63902
|
+
this.scheduleRefresh();
|
|
63903
|
+
}
|
|
63904
|
+
get opts() {
|
|
63905
|
+
return this.options.tokenRefresh;
|
|
63906
|
+
}
|
|
63907
|
+
clearTimer() {
|
|
63908
|
+
if (this.refreshTimer !== null) {
|
|
63909
|
+
clearTimeout(this.refreshTimer);
|
|
63910
|
+
this.refreshTimer = null;
|
|
63911
|
+
}
|
|
63912
|
+
}
|
|
63913
|
+
}
|
|
63914
|
+
|
|
63585
63915
|
/**
|
|
63586
63916
|
* Events emitted by the VolumeFade plugin.
|
|
63587
63917
|
* @public
|
|
@@ -63690,4 +64020,4 @@ class VolumeFade extends UICorePlugin {
|
|
|
63690
64020
|
}
|
|
63691
64021
|
}
|
|
63692
64022
|
|
|
63693
|
-
export { AudioTracks as AudioSelector, AudioTracks, BigMuteButton, BottomGear, ChainedTracer, NerdStats as ClapprNerdStats, ClapprStats, ClapprStatsChronograph, ClapprStatsCounter, ClapprStatsEvents, ClickToPause, Clips, ClosedCaptions, CmcdConfig, ContextMenu, DvrControls, ErrorScreen, ExtendedEvents, Favicon, GearEvents, GoogleAnalytics, QualityLevels as LevelSelector, LogTracer, Logger$1 as Logger, Logo, MediaControl, MultiCamera, NerdStats, PictureInPicture, PlaybackErrorCode, PlaybackRate, Player, PlayerEvent, Poster, QualityLevels, RemoteTracer, SeekTime, SentryTracer, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, VolumeFade, VolumeFadeEvents, reportError, setTracer, trace, version };
|
|
64023
|
+
export { AudioTracks as AudioSelector, AudioTracks, BigMuteButton, BottomGear, ChainedTracer, NerdStats as ClapprNerdStats, ClapprStats, ClapprStatsChronograph, ClapprStatsCounter, ClapprStatsEvents, ClickToPause, Clips, ClosedCaptions, CmcdConfig, ContextMenu, DvrControls, ErrorScreen, ExtendedEvents, Favicon, GearEvents, GoogleAnalytics, QualityLevels as LevelSelector, LogTracer, Logger$1 as Logger, Logo, MediaControl, MultiCamera, NerdStats, PictureInPicture, PlaybackErrorCode, PlaybackRate, Player, PlayerEvent, Poster, QualityLevels, RemoteTracer, SeekTime, SentryTracer, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, TokenRefreshPlugin, VolumeFade, VolumeFadeEvents, reportError, setTracer, trace, version };
|