@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/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$e = 'playback.dash';
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
- }, this.options.dash);
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$e} _onPlaybackError`, { type: event.type, code: event.error.code, message: event.error.message });
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$d = 'playback.hls';
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$d} _recover failed to recover`, {
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$d} _onHLSJSError`, {
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$d} _onHLSJSError trying to recover from network error`, {
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$d} _onHLSJSError trying to recover from media error`, {
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$d} _onHLSJSError non-fatal error occurred`, {
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$d} onAudioTrackSwitched`);
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$c = 'playback.html5_video';
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$c} switchAudioTrack`, {
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$b = 'gplayer';
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$b} triggerAutoPlay`);
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$b} onReady`, {
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$b} buildCoreOptions`, {
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.29.0";
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$a = 'plugins.media_control';
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$a} enable`, {
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$a} playerResize`, {
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$9 = 'plugins.big_mute_button';
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$9} onPlay`, {
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$9} onStop`, { ui });
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$8 = 'plugins.nerd_stats';
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$8} toggle`, {
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$8} speedtest error`, {
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$8} hide`);
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$7 = 'plugins.error_screen';
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$7} onPlay`);
57462
+ trace(`${T$8} onPlay`);
57448
57463
  this.unmount();
57449
57464
  }
57450
57465
  unmount() {
57451
- trace(`${T$7} unmount`);
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$7} reload`);
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$7} onError`, { err });
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$6 = 'plugins.multicamera';
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$6} onCameraSelect`, { value });
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$6} changeById`, { id });
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$6} changeById`, { currentCamera: this.currentCamera });
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$5 = `plugins.pip`;
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$5} isPiPSupported`, {
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$5} togglePictureInPicture`);
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$5} requestPictureInPicture`, {
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$5} exitPictureInPicture`);
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$4 = 'plugins.playback_rate';
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$4} onCoreReady`);
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$4} onActiveContainerChange`);
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$4} onMediaControlRendered`);
58518
+ trace(`${T$5} onMediaControlRendered`);
58504
58519
  this.render();
58505
58520
  }
58506
58521
  onGearRendered() {
58507
- trace(`${T$4} onGearRendered`);
58522
+ trace(`${T$5} onGearRendered`);
58508
58523
  this.mount();
58509
58524
  }
58510
58525
  mount() {
58511
- trace(`${T$4} mount`, {
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$4} onMetaDataLoaded`, {
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$4} onPlaybackRateChange not steering to the selected rate, it is seemingly a catchup algorithm working`, {
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$4} syncRate`, {
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$4} resetPlaybackRate`, {
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$4} highlightCurrentRate`, {
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$3 = 'plugins.source_controller';
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$3} on PLAYBACK_ERROR`, {
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$3} onContainerPlay`, {
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$3} retryPlayback enter`, {
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$3} retryPlayback syncing...`, {
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$3} retryPlayback loaded`, {
59899
+ trace(`${T$4} retryPlayback loaded`, {
59885
59900
  nextSource,
59886
59901
  });
59887
59902
  setTimeout(() => {
59888
- trace(`${T$3} retryPlayback playing`, {
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$2 = 'plugins.telemetry';
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$2} onReady`, {
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$1 = 'plugins.thumbnails';
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$1} misconfigured: options.thumbnails.sprite and options.thumbnails.vtt are required`);
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$1} failed to parse the sprite sheet`);
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 };