@gcorevideo/player 2.30.0 → 2.30.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/assets/audio-tracks/template.ejs +1 -1
  2. package/dist/core.js +65 -21
  3. package/dist/index.css +265 -265
  4. package/dist/index.embed.js +99 -34
  5. package/dist/index.js +125 -53
  6. package/docs/api/player.md +37 -0
  7. package/docs/api/player.player.getplugin.md +59 -0
  8. package/docs/api/player.player.md +14 -0
  9. package/docs/api/player.tokenrefreshoptions.gettoken.md +13 -0
  10. package/docs/api/player.tokenrefreshoptions.ipbound.md +13 -0
  11. package/docs/api/player.tokenrefreshoptions.md +115 -0
  12. package/docs/api/player.tokenrefreshoptions.ontokenrefreshed.md +13 -0
  13. package/docs/api/player.tokenrefreshoptions.refreshleadseconds.md +13 -0
  14. package/docs/api/player.tokenrefreshplugin.md +50 -0
  15. package/docs/api/player.tokenresponse.client_ip.md +13 -0
  16. package/docs/api/player.tokenresponse.expires.md +13 -0
  17. package/docs/api/player.tokenresponse.md +153 -0
  18. package/docs/api/player.tokenresponse.token.md +13 -0
  19. package/docs/api/player.tokenresponse.token_ip.md +13 -0
  20. package/docs/api/player.tokenresponse.url.md +13 -0
  21. package/docs/api/player.tokenresponse.url_ip.md +13 -0
  22. package/lib/playback/hls-playback/HlsPlayback.d.ts +1 -1
  23. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  24. package/lib/playback/hls-playback/HlsPlayback.js +23 -20
  25. package/lib/playback/hls-playback/RangesList.d.ts +7 -0
  26. package/lib/playback/hls-playback/RangesList.d.ts.map +1 -0
  27. package/lib/playback/hls-playback/RangesList.js +41 -0
  28. package/lib/plugins/audio-selector/AudioTracks.d.ts +4 -0
  29. package/lib/plugins/audio-selector/AudioTracks.d.ts.map +1 -1
  30. package/lib/plugins/audio-selector/AudioTracks.js +42 -12
  31. package/lib/plugins/subtitles/ClosedCaptions.d.ts.map +1 -1
  32. package/lib/plugins/subtitles/ClosedCaptions.js +0 -2
  33. package/package.json +1 -1
  34. package/src/playback/hls-playback/HlsPlayback.ts +40 -37
  35. package/src/playback/hls-playback/RangesList.ts +45 -0
  36. package/src/playback/hls-playback/__tests__/RangesList.test.ts +60 -0
  37. package/src/plugins/audio-selector/AudioTracks.ts +51 -16
  38. package/src/plugins/audio-selector/__tests__/__snapshots__/AudioTracks.test.ts.snap +9 -9
  39. package/src/plugins/subtitles/ClosedCaptions.ts +0 -5
  40. 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$f = 'playback.dash';
12925
+ const T$g = 'playback.dash';
12926
12926
  class DashPlayback extends BasePlayback {
12927
12927
  _levels = [];
12928
12928
  _currentLevel = AUTO$1;
@@ -13199,7 +13199,7 @@ class DashPlayback extends BasePlayback {
13199
13199
  this.trigger(Events$1.PLAYBACK_SETTINGSUPDATE);
13200
13200
  }
13201
13201
  _onPlaybackError = (event) => {
13202
- trace(`${T$f} _onPlaybackError`, { type: event.type, code: event.error.code, message: event.error.message });
13202
+ trace(`${T$g} _onPlaybackError`, { type: event.type, code: event.error.code, message: event.error.message });
13203
13203
  };
13204
13204
  _onDASHJSSError = (event) => {
13205
13205
  this._stopTimeUpdateTimer();
@@ -50092,6 +50092,48 @@ Hls.defaultConfig = void 0;
50092
50092
  // export const CLAPPR_VERSION: string = process.env.CLAPPR_VERSION || '0.11.3';
50093
50093
  const CLAPPR_VERSION$1 = '0.13.0';
50094
50094
 
50095
+ class RangesList {
50096
+ // TODO write an efficient implementation
50097
+ items = [];
50098
+ insert(start, end, value) {
50099
+ const index = this.findIndex((start + end) / 2);
50100
+ this.items.splice(index, 0, [start, end, value]);
50101
+ }
50102
+ find(position) {
50103
+ const index = this.findIndex(position);
50104
+ const item = this.items[index];
50105
+ if (!item || item[0] > position || item[1] < position) {
50106
+ return null;
50107
+ }
50108
+ return item[2];
50109
+ }
50110
+ findIndex(position) {
50111
+ let low = 0;
50112
+ let high = this.items.length;
50113
+ let index = 0;
50114
+ while (low < high) {
50115
+ index = low + Math.floor((high - low) / 2);
50116
+ const item = this.items[index];
50117
+ if (item[0] > position) {
50118
+ if (index === low) {
50119
+ return index;
50120
+ }
50121
+ high = index;
50122
+ continue;
50123
+ }
50124
+ if (item[1] <= position) {
50125
+ if (index === high - 1) {
50126
+ return index + 1;
50127
+ }
50128
+ low = index + 1;
50129
+ continue;
50130
+ }
50131
+ break;
50132
+ }
50133
+ return index;
50134
+ }
50135
+ }
50136
+
50095
50137
  // Copyright 2014 Globo.com Player authors. All rights reserved.
50096
50138
  // Use of this source code is governed by a BSD-style
50097
50139
  // license that can be found on https://github.com/clappr/hlsjs-playback/blob/main/LICENSE
@@ -50099,9 +50141,8 @@ const { now } = Utils;
50099
50141
  const AUTO = -1;
50100
50142
  const DEFAULT_RECOVER_ATTEMPTS = 16;
50101
50143
  Events$1.register('PLAYBACK_FRAGMENT_PARSING_METADATA');
50102
- const T$e = 'playback.hls';
50144
+ const T$f = 'playback.hls';
50103
50145
  class HlsPlayback extends BasePlayback {
50104
- _ccTracksUpdated = false;
50105
50146
  _currentFragment = null;
50106
50147
  _currentLevel = null;
50107
50148
  _durationExcludesAfterLiveSyncPoint = false;
@@ -50126,7 +50167,8 @@ class HlsPlayback extends BasePlayback {
50126
50167
  _timeUpdateTimer = null;
50127
50168
  oncueenter = null;
50128
50169
  oncueexit = null;
50129
- cues = []; // TODO check the list size and use BST if needed
50170
+ cues = null;
50171
+ cuesByTrack = {};
50130
50172
  currentCueId = null;
50131
50173
  /**
50132
50174
  * @internal
@@ -50323,7 +50365,6 @@ class HlsPlayback extends BasePlayback {
50323
50365
  }
50324
50366
  this._manifestParsed = false;
50325
50367
  // this._ccIsSetup = false
50326
- this._ccTracksUpdated = false;
50327
50368
  this._setInitialState();
50328
50369
  this._hls.destroy();
50329
50370
  this._hls = null;
@@ -50377,12 +50418,20 @@ class HlsPlayback extends BasePlayback {
50377
50418
  this._hls.on(Events.AUDIO_TRACK_SWITCHED, (evt, data) => this._onAudioTrackSwitched(evt, data));
50378
50419
  this._hls.on(Events.CUES_PARSED, (evt, data) => {
50379
50420
  data.cues?.forEach((cue) => {
50380
- this.cues.push({
50421
+ if (!this.cues) {
50422
+ const trackId = this._hls.subtitleTrack;
50423
+ if (!this.cuesByTrack[trackId]) {
50424
+ this.cuesByTrack[trackId] = new RangesList();
50425
+ }
50426
+ this.cues = this.cuesByTrack[trackId];
50427
+ }
50428
+ const cueInfo = {
50381
50429
  id: cue.id,
50382
50430
  start: cue.startTime,
50383
50431
  end: cue.endTime,
50384
50432
  text: cue.text,
50385
- });
50433
+ };
50434
+ this.cues.insert(cue.startTime, cue.endTime, cueInfo);
50386
50435
  });
50387
50436
  });
50388
50437
  this.bindCustomListeners();
@@ -50424,7 +50473,7 @@ class HlsPlayback extends BasePlayback {
50424
50473
  }
50425
50474
  else {
50426
50475
  Log.error('hlsjs: failed to recover', { evt, data });
50427
- trace(`${T$e} _recover failed to recover`, {
50476
+ trace(`${T$f} _recover failed to recover`, {
50428
50477
  type: data.type,
50429
50478
  details: data.details,
50430
50479
  });
@@ -50511,7 +50560,7 @@ class HlsPlayback extends BasePlayback {
50511
50560
  this.trigger(Events$1.PLAYBACK_SETTINGSUPDATE);
50512
50561
  }
50513
50562
  _onHLSJSError(evt, data) {
50514
- trace(`${T$e} _onHLSJSError`, {
50563
+ trace(`${T$f} _onHLSJSError`, {
50515
50564
  fatal: data.fatal,
50516
50565
  type: data.type,
50517
50566
  details: data.details,
@@ -50559,7 +50608,7 @@ class HlsPlayback extends BasePlayback {
50559
50608
  evt,
50560
50609
  data,
50561
50610
  });
50562
- trace(`${T$e} _onHLSJSError trying to recover from network error`, {
50611
+ trace(`${T$f} _onHLSJSError trying to recover from network error`, {
50563
50612
  details: data.details,
50564
50613
  });
50565
50614
  error.level = PlayerError.Levels.WARN;
@@ -50572,7 +50621,7 @@ class HlsPlayback extends BasePlayback {
50572
50621
  evt,
50573
50622
  data,
50574
50623
  });
50575
- trace(`${T$e} _onHLSJSError trying to recover from media error`, {
50624
+ trace(`${T$f} _onHLSJSError trying to recover from media error`, {
50576
50625
  details: data.details,
50577
50626
  });
50578
50627
  error.level = PlayerError.Levels.WARN;
@@ -50602,14 +50651,14 @@ class HlsPlayback extends BasePlayback {
50602
50651
  return;
50603
50652
  }
50604
50653
  Log.warn('hlsjs: non-fatal error occurred', { evt, data });
50605
- trace(`${T$e} _onHLSJSError non-fatal error occurred`, {
50654
+ trace(`${T$f} _onHLSJSError non-fatal error occurred`, {
50606
50655
  type: data.type,
50607
50656
  details: data.details,
50608
50657
  });
50609
50658
  }
50610
50659
  }
50611
50660
  reload() {
50612
- this.cues = [];
50661
+ this.cues = null;
50613
50662
  this.currentCueId = null;
50614
50663
  this._hls?.startLoad(-1);
50615
50664
  }
@@ -50674,9 +50723,7 @@ class HlsPlayback extends BasePlayback {
50674
50723
  }
50675
50724
  triggerCues() {
50676
50725
  const currentTime = this.getCurrentTime();
50677
- // const cues = Object.values(this.cues)
50678
- // TODO build a search tree
50679
- const cue = this.cues.find((cue) => currentTime >= cue.start && currentTime <= cue.end);
50726
+ const cue = this.cues?.find(currentTime);
50680
50727
  if (cue) {
50681
50728
  this.currentCueId = cue.id;
50682
50729
  this.oncueenter?.(cue);
@@ -50719,20 +50766,14 @@ class HlsPlayback extends BasePlayback {
50719
50766
  destroy() {
50720
50767
  this._stopTimeUpdateTimer();
50721
50768
  this._destroyHLSInstance();
50769
+ this.cues = null;
50770
+ this.cuesByTrack = {};
50722
50771
  return super.destroy();
50723
50772
  }
50724
50773
  _updatePlaybackType(evt, data) {
50725
50774
  const prevPlaybackType = this._playbackType;
50726
50775
  this._playbackType = (data.details.live ? Playback.LIVE : Playback.VOD);
50727
50776
  this._onLevelUpdated(evt, data);
50728
- // Live stream subtitle tracks detection hack (may not immediately available)
50729
- // if (
50730
- // this._ccTracksUpdated &&
50731
- // this._playbackType === Playback.LIVE &&
50732
- // this.hasClosedCaptionsTracks
50733
- // ) {
50734
- // this._onSubtitleLoaded()
50735
- // }
50736
50777
  if (prevPlaybackType !== this._playbackType) {
50737
50778
  this._updateSettings();
50738
50779
  }
@@ -50943,7 +50984,7 @@ class HlsPlayback extends BasePlayback {
50943
50984
  this.trigger(Events$1.PLAYBACK_AUDIO_AVAILABLE, data.audioTracks.map(toClapprTrack));
50944
50985
  }
50945
50986
  _onAudioTrackSwitched(_, data) {
50946
- trace(`${T$e} onAudioTrackSwitched`);
50987
+ trace(`${T$f} onAudioTrackSwitched`);
50947
50988
  // @ts-ignore
50948
50989
  const track = this._hls.audioTracks[data.id];
50949
50990
  this.trigger(Events$1.PLAYBACK_AUDIO_CHANGED, toClapprTrack(track));
@@ -50953,7 +50994,10 @@ class HlsPlayback extends BasePlayback {
50953
50994
  return;
50954
50995
  }
50955
50996
  this._hls.subtitleTrack = id;
50956
- this.cues = [];
50997
+ if (!this.cuesByTrack[id]) {
50998
+ this.cuesByTrack[id] = new RangesList();
50999
+ }
51000
+ this.cues = this.cuesByTrack[id];
50957
51001
  }
50958
51002
  /**
50959
51003
  * @override
@@ -50962,7 +51006,7 @@ class HlsPlayback extends BasePlayback {
50962
51006
  return this.getTextTracks();
50963
51007
  }
50964
51008
  getTextTracks() {
50965
- return this._hls?.subtitleTracks.map((t) => ({
51009
+ return (this._hls?.subtitleTracks.map((t) => ({
50966
51010
  id: t.id,
50967
51011
  name: t.name,
50968
51012
  track: {
@@ -50970,7 +51014,7 @@ class HlsPlayback extends BasePlayback {
50970
51014
  label: t.name,
50971
51015
  language: t.lang,
50972
51016
  },
50973
- })) || [];
51017
+ })) || []);
50974
51018
  }
50975
51019
  }
50976
51020
  HlsPlayback.canPlay = function (resource, mimeType) {
@@ -50988,7 +51032,7 @@ function toClapprTrack(t) {
50988
51032
  };
50989
51033
  }
50990
51034
 
50991
- const T$d = 'playback.html5_video';
51035
+ const T$e = 'playback.html5_video';
50992
51036
  const STALL_TIMEOUT = 15000;
50993
51037
  class HTML5Video extends BasePlayback {
50994
51038
  stallTimerId = null;
@@ -51089,7 +51133,7 @@ class HTML5Video extends BasePlayback {
51089
51133
  switchAudioTrack(id) {
51090
51134
  const tracks = this.el.audioTracks;
51091
51135
  const supported = !!tracks;
51092
- trace(`${T$d} switchAudioTrack`, {
51136
+ trace(`${T$e} switchAudioTrack`, {
51093
51137
  supported,
51094
51138
  });
51095
51139
  if (supported) {
@@ -51108,7 +51152,7 @@ function registerPlaybacks() {
51108
51152
  Loader.registerPlayback(DashPlayback);
51109
51153
  }
51110
51154
 
51111
- const T$c = 'gplayer';
51155
+ const T$d = 'gplayer';
51112
51156
  const DEFAULT_OPTIONS = {
51113
51157
  autoPlay: false,
51114
51158
  debug: 'none',
@@ -51455,7 +51499,7 @@ class Player {
51455
51499
  }
51456
51500
  }
51457
51501
  triggerAutoPlay() {
51458
- trace(`${T$c} triggerAutoPlay`);
51502
+ trace(`${T$d} triggerAutoPlay`);
51459
51503
  setTimeout(() => {
51460
51504
  this.player?.play({
51461
51505
  autoPlay: true,
@@ -51473,7 +51517,7 @@ class Player {
51473
51517
  // TODO test
51474
51518
  events = {
51475
51519
  onReady: () => {
51476
- trace(`${T$c} onReady`, {
51520
+ trace(`${T$d} onReady`, {
51477
51521
  ready: this.ready,
51478
51522
  });
51479
51523
  if (this.ready) {
@@ -51507,7 +51551,7 @@ class Player {
51507
51551
  buildCoreOptions(rootNode) {
51508
51552
  const sources = this.buildMediaSourcesList();
51509
51553
  const source = sources[0];
51510
- trace(`${T$c} buildCoreOptions`, {
51554
+ trace(`${T$d} buildCoreOptions`, {
51511
51555
  source,
51512
51556
  sources,
51513
51557
  });
@@ -51584,7 +51628,7 @@ class Player {
51584
51628
  }
51585
51629
  }
51586
51630
 
51587
- var version$1 = "2.30.0";
51631
+ var version$1 = "2.30.2";
51588
51632
 
51589
51633
  var packages = {
51590
51634
  "node_modules/@clappr/core": {
@@ -51608,7 +51652,7 @@ function version() {
51608
51652
  };
51609
51653
  }
51610
51654
 
51611
- const pluginHtml$6 = "<button class='gcore-skin-button-color media-control-dd' id=\"gplayer-audiotracks-button\" aria-haspopup=\"menu\" aria-expanded=\"false\">\n <span class=\"media-control-dd__text\" id=\"gplayer-audiotracks-button-text\"><%= title %></span>\n <span class=\"media-control-dd__arrow\"><%= icon %></span>\n</button>\n<ul class=\"gcore-skin-bg-color menu media-control-dd__popup\" id=\"gplayer-audiotracks-menu\" role=\"menu\">\n <% for (const track of tracks) { %>\n <li>\n <a href=\"#\" class=\"gcore-skin-text-color\" data-item=\"<%= track.id %>\" role=\"menuitemradio\" aria-checked=\"<%= track.id === current %>\">\n <%= track.label %>\n </a>\n </li>\n <% }; %>\n</ul>\n";
51655
+ const pluginHtml$6 = "<button class='gcore-skin-button-color media-control-dd' id=\"gplayer-audiotracks-button\" aria-haspopup=\"menu\" aria-expanded=\"false\">\n <span class=\"media-control-dd__text\" id=\"gplayer-audiotracks-button-text\"><%= title %></span>\n <span class=\"media-control-dd__arrow\"><%= icon %></span>\n</button>\n<ul class=\"gcore-skin-bg-color menu media-control-dd__popup\" id=\"gplayer-audiotracks-menu\" role=\"menu\">\n <% for (const track of tracks) { %>\n <li>\n <a href=\"#\" class=\"gcore-skin-text-color\" data-item=\"<%= track.id %>\" role=\"menuitemradio\" aria-checked=\"<%= track.id === current %>\">\n <%= track.label || track.language %>\n </a>\n </li>\n <% }; %>\n</ul>\n";
51612
51656
 
51613
51657
  const audioArrow = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"9px\" height=\"6px\" viewBox=\"0 0 9 6\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n <!-- Generator: Sketch 49 (51002) - http://www.bohemiancoding.com/sketch -->\n <title>quality-arrow</title>\n <desc>Created with Sketch.</desc>\n <defs></defs>\n <g id=\"quality-arrow\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <path\n d=\"M5.07079194,5.78553848 C4.91457318,5.94277844 4.70551573,6.00941824 4.50028717,5.99893557 C4.2950586,6.00941824 4.08676693,5.94277844 3.92978239,5.78553848 L0.221118462,1.2997069 C-0.0737061539,1.00469478 -0.0737061539,0.526236029 0.221118462,0.231972666 C0.515177299,-0.0630394586 1.23500883,0.00734414472 1.64852907,0.00734414472 L7.77475484,0.00734414472 C8.21201421,0.00734414472 8.48539703,-0.0630394586 8.77945587,0.231972666 C9.07351471,0.526236029 9.07351471,1.00469478 8.77945587,1.2997069 L5.07079194,5.78553848 Z\"\n fill=\"#FFFFFE\"></path>\n </g>\n</svg>\n";
51614
51658
 
@@ -52017,7 +52061,7 @@ const INITIAL_SETTINGS = {
52017
52061
  default: [],
52018
52062
  seekEnabled: false,
52019
52063
  };
52020
- const T$b = 'plugins.media_control';
52064
+ const T$c = 'plugins.media_control';
52021
52065
  /**
52022
52066
  * Extended events for the {@link MediaControl} plugin
52023
52067
  * @public
@@ -52310,7 +52354,7 @@ class MediaControl extends UICorePlugin {
52310
52354
  * Reenables the plugin disabled earlier with the {@link MediaControl.disable} method
52311
52355
  */
52312
52356
  enable() {
52313
- trace(`${T$b} enable`, {
52357
+ trace(`${T$c} enable`, {
52314
52358
  chromeless: this.options.chromeless,
52315
52359
  userDisabled: this.userDisabled,
52316
52360
  });
@@ -52467,7 +52511,7 @@ class MediaControl extends UICorePlugin {
52467
52511
  this.$el.removeClass('w370');
52468
52512
  this.$el.removeClass('w270');
52469
52513
  this.verticalVolume = false;
52470
- trace(`${T$b} playerResize`, {
52514
+ trace(`${T$c} playerResize`, {
52471
52515
  size,
52472
52516
  width: this.container.$el.width(),
52473
52517
  height: this.container.$el.height(),
@@ -53265,7 +53309,7 @@ function mergeElements(a, b) {
53265
53309
  }
53266
53310
 
53267
53311
  const VERSION$7 = '2.22.4';
53268
- // const T = 'plugins.audiotracks'
53312
+ const T$b = 'plugins.audiotracks';
53269
53313
  /**
53270
53314
  * `PLUGIN` that makes possible to switch audio tracks via the media control UI.
53271
53315
  * @public
@@ -53280,6 +53324,7 @@ class AudioTracks extends UICorePlugin {
53280
53324
  currentTrack = null;
53281
53325
  open = false;
53282
53326
  tracks = [];
53327
+ autoUpdateTimerId = null;
53283
53328
  /**
53284
53329
  * @internal
53285
53330
  */
@@ -53340,19 +53385,21 @@ class AudioTracks extends UICorePlugin {
53340
53385
  onActiveContainerChanged() {
53341
53386
  this.currentTrack = null;
53342
53387
  this.listenTo(this.core.activeContainer, Events$1.CONTAINER_AUDIO_AVAILABLE, (tracks) => {
53388
+ trace(`${T$b} on Events.CONTAINER_AUDIO_AVAILABLE`, {
53389
+ tracks,
53390
+ });
53391
+ const currentTrackId = this.core.activeContainer.currentAudioTrack?.id ??
53392
+ this.core.activePlayback?.currentAudioTrack?.id;
53343
53393
  this.currentTrack =
53344
- tracks.find((track) => track.kind === 'main') ?? null; // TODO test
53394
+ tracks.find((track) => track.id === currentTrackId) ??
53395
+ tracks.find((track) => track.kind === 'main') ??
53396
+ tracks[0] ??
53397
+ null;
53345
53398
  this.tracks = tracks;
53346
53399
  this.render();
53347
53400
  this.mount();
53348
53401
  });
53349
- this.listenTo(this.core.activeContainer, Events$1.CONTAINER_AUDIO_CHANGED, (track) => {
53350
- this.currentTrack = track;
53351
- this.highlightCurrentTrack();
53352
- this.buttonElement().removeClass('changing');
53353
- this.updateText();
53354
- });
53355
- // TODO test
53402
+ this.bindContainerAudioChanged();
53356
53403
  this.listenTo(this.core.activeContainer, Events$1.CONTAINER_CLICK, () => {
53357
53404
  this.hideMenu();
53358
53405
  });
@@ -53360,6 +53407,24 @@ class AudioTracks extends UICorePlugin {
53360
53407
  this.clickaway(null);
53361
53408
  });
53362
53409
  }
53410
+ bindContainerAudioChanged() {
53411
+ this.listenTo(this.core.activeContainer, Events$1.CONTAINER_AUDIO_CHANGED, (track) => {
53412
+ trace(`${T$b} on Events.CONTAINER_AUDIO_CHANGED`, {
53413
+ track,
53414
+ });
53415
+ this.setCurrentTrack(track);
53416
+ });
53417
+ }
53418
+ setCurrentTrack(track) {
53419
+ if (this.autoUpdateTimerId) {
53420
+ clearTimeout(this.autoUpdateTimerId);
53421
+ this.autoUpdateTimerId = null;
53422
+ }
53423
+ this.currentTrack = track;
53424
+ this.highlightCurrentTrack();
53425
+ this.buttonElement().removeClass('changing');
53426
+ this.updateText();
53427
+ }
53363
53428
  shouldRender() {
53364
53429
  // Render is called from the parent class constructor so tracks aren't available
53365
53430
  // Only care if we have at least 2 to choose from
@@ -53392,7 +53457,7 @@ class AudioTracks extends UICorePlugin {
53392
53457
  selectAudioTrack(id) {
53393
53458
  this.startTrackSwitching();
53394
53459
  this.core.activeContainer.switchAudioTrack(id);
53395
- this.updateText();
53460
+ this.autoUpdateSelected(id);
53396
53461
  }
53397
53462
  hideMenu() {
53398
53463
  this.open = false;
@@ -53443,7 +53508,7 @@ class AudioTracks extends UICorePlugin {
53443
53508
  if (!this.currentTrack) {
53444
53509
  return;
53445
53510
  }
53446
- this.buttonElementText().text(this.currentTrack.label);
53511
+ this.buttonElementText().text(this.getTitle());
53447
53512
  }
53448
53513
  highlightCurrentTrack() {
53449
53514
  this.trackElement().removeClass('current');
@@ -53464,6 +53529,15 @@ class AudioTracks extends UICorePlugin {
53464
53529
  this.core.getPlugin('media_control')?.slot('audiotracks', this.$el);
53465
53530
  }
53466
53531
  }
53532
+ autoUpdateSelected(id) {
53533
+ if (this.autoUpdateTimerId) {
53534
+ clearTimeout(this.autoUpdateTimerId);
53535
+ }
53536
+ this.autoUpdateTimerId = setTimeout(() => {
53537
+ const track = this.tracks.find(t => t.id === id) ?? null;
53538
+ this.setCurrentTrack(track);
53539
+ }, 500);
53540
+ }
53467
53541
  clickaway = mediaControlClickaway(() => this.hideMenu());
53468
53542
  }
53469
53543
 
@@ -60258,8 +60332,6 @@ class ClosedCaptions extends UICorePlugin {
60258
60332
  // event.target does not exist for some reason in tests
60259
60333
  const id = Number((event.target ?? event.currentTarget).dataset?.item ??
60260
60334
  '-1');
60261
- // TODO review, make configurable, and emit event in addition
60262
- // localStorage.setItem(LOCAL_STORAGE_CC_ID, id) // TODO store language instead?
60263
60335
  this.userSelectedItemId = id;
60264
60336
  this.selectItem(this.findById(id));
60265
60337
  this.hideMenu();
@@ -361,6 +361,21 @@ A `PLUGIN` that configures [CMCD](https://cdn.cta.tech/cta/media/media/resources
361
361
  `PLUGIN` that displays the thumbnails of the video when available.
362
362
 
363
363
 
364
+ </td></tr>
365
+ <tr><td>
366
+
367
+ [TokenRefreshPlugin](./player.tokenrefreshplugin.md)
368
+
369
+
370
+ </td><td>
371
+
372
+ `PLUGIN` — automatic token refresh for Gcore protected-content streams.
373
+
374
+ Supports all three playback engines:
375
+
376
+ \| Engine \| Mechanism \| Interruption \| \|\-\-\-\-\-\---\|\-\-\-\-\-\-\-\-\---\|\-\-\-\-\-\-\-\-\-\-\-\---\| \| \*\*hls.js\*\* \| Custom loader rewrites every request URL \| None \| \| \*\*dash.js\*\* \| `addRequestInterceptor` rewrites every request URL \| None \| \| \*\*Native `<video>`<!-- -->\*\* (Safari ≤ iOS 14.4) \| Source reload + seek restore \| Brief \|
377
+
378
+
364
379
  </td></tr>
365
380
  <tr><td>
366
381
 
@@ -721,6 +736,28 @@ Plugin configuration options for the thumbnails plugin.
721
736
  Current playback time and total duration of the media.
722
737
 
723
738
 
739
+ </td></tr>
740
+ <tr><td>
741
+
742
+ [TokenRefreshOptions](./player.tokenrefreshoptions.md)
743
+
744
+
745
+ </td><td>
746
+
747
+ Configuration options for [TokenRefreshPlugin](./player.tokenrefreshplugin.md)<!-- -->.
748
+
749
+
750
+ </td></tr>
751
+ <tr><td>
752
+
753
+ [TokenResponse](./player.tokenresponse.md)
754
+
755
+
756
+ </td><td>
757
+
758
+ Response shape expected from your token-refresh API endpoint.
759
+
760
+
724
761
  </td></tr>
725
762
  <tr><td>
726
763
 
@@ -0,0 +1,59 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [@gcorevideo/player](./player.md) &gt; [Player](./player.player.md) &gt; [getPlugin](./player.player.getplugin.md)
4
+
5
+ ## Player.getPlugin() method
6
+
7
+ Returns a registered core plugin instance by name, or `null` if not found.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ getPlugin(name: string): any;
13
+ ```
14
+
15
+ ## Parameters
16
+
17
+ <table><thead><tr><th>
18
+
19
+ Parameter
20
+
21
+
22
+ </th><th>
23
+
24
+ Type
25
+
26
+
27
+ </th><th>
28
+
29
+ Description
30
+
31
+
32
+ </th></tr></thead>
33
+ <tbody><tr><td>
34
+
35
+ name
36
+
37
+
38
+ </td><td>
39
+
40
+ string
41
+
42
+
43
+ </td><td>
44
+
45
+
46
+ </td></tr>
47
+ </tbody></table>
48
+
49
+ **Returns:**
50
+
51
+ any
52
+
53
+ ## Example
54
+
55
+
56
+ ```ts
57
+ const tokenRefresh = player.getPlugin('token_refresh') as TokenRefreshPlugin | null
58
+ ```
59
+
@@ -137,6 +137,20 @@ Current playback (time since the beginning of the stream), if appropriate.
137
137
  Duration of the current media in seconds, if appropriate.
138
138
 
139
139
 
140
+ </td></tr>
141
+ <tr><td>
142
+
143
+ [getPlugin(name)](./player.player.getplugin.md)
144
+
145
+
146
+ </td><td>
147
+
148
+
149
+ </td><td>
150
+
151
+ Returns a registered core plugin instance by name, or `null` if not found.
152
+
153
+
140
154
  </td></tr>
141
155
  <tr><td>
142
156
 
@@ -0,0 +1,13 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [@gcorevideo/player](./player.md) &gt; [TokenRefreshOptions](./player.tokenrefreshoptions.md) &gt; [getToken](./player.tokenrefreshoptions.gettoken.md)
4
+
5
+ ## TokenRefreshOptions.getToken property
6
+
7
+ Async function called each time a fresh token is needed. Must return a [TokenResponse](./player.tokenresponse.md)<!-- -->.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ getToken: () => Promise<TokenResponse>;
13
+ ```
@@ -0,0 +1,13 @@
1
+ <!-- Do not edit this file. It is automatically generated by API Documenter. -->
2
+
3
+ [Home](./index.md) &gt; [@gcorevideo/player](./player.md) &gt; [TokenRefreshOptions](./player.tokenrefreshoptions.md) &gt; [ipBound](./player.tokenrefreshoptions.ipbound.md)
4
+
5
+ ## TokenRefreshOptions.ipBound property
6
+
7
+ When `true`<!-- -->, the IP-bound variant (`token_ip` / `url_ip`<!-- -->) is used. Defaults to `false`<!-- -->.
8
+
9
+ **Signature:**
10
+
11
+ ```typescript
12
+ ipBound?: boolean;
13
+ ```