@gcorevideo/player 2.28.26 → 2.28.27

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.
@@ -13736,7 +13736,7 @@ function enableLogs(debugConfig, context, id) {
13736
13736
  // Some browsers don't allow to use bind on console object anyway
13737
13737
  // fallback to default if needed
13738
13738
  try {
13739
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.14"}`);
13739
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.15"}`);
13740
13740
  } catch (e) {
13741
13741
  /* log fn threw an exception. All logger methods are no-ops. */
13742
13742
  return createLogger();
@@ -18182,11 +18182,7 @@ class FragmentTracker {
18182
18182
  });
18183
18183
  fragmentEntity.loaded = null;
18184
18184
  if (Object.keys(fragmentEntity.range).length) {
18185
- fragmentEntity.buffered = true;
18186
- const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList;
18187
- if (endList) {
18188
- this.endListFragments[fragmentEntity.body.type] = fragmentEntity;
18189
- }
18185
+ this.bufferedEnd(fragmentEntity, frag);
18190
18186
  if (!isPartial(fragmentEntity)) {
18191
18187
  // Remove older fragment parts from lookup after frag is tracked as buffered
18192
18188
  this.removeParts(frag.sn - 1, frag.type);
@@ -18196,6 +18192,13 @@ class FragmentTracker {
18196
18192
  this.removeFragment(fragmentEntity.body);
18197
18193
  }
18198
18194
  }
18195
+ bufferedEnd(fragmentEntity, frag) {
18196
+ fragmentEntity.buffered = true;
18197
+ const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList;
18198
+ if (endList) {
18199
+ this.endListFragments[fragmentEntity.body.type] = fragmentEntity;
18200
+ }
18201
+ }
18199
18202
  removeParts(snToKeep, levelType) {
18200
18203
  const activeParts = this.activePartLists[levelType];
18201
18204
  if (!activeParts) {
@@ -18220,7 +18223,7 @@ class FragmentTracker {
18220
18223
  }
18221
18224
  if (fragmentEntity) {
18222
18225
  fragmentEntity.loaded = null;
18223
- fragmentEntity.buffered = true;
18226
+ this.bufferedEnd(fragmentEntity, frag);
18224
18227
  }
18225
18228
  }
18226
18229
  getBufferedTimes(fragment, part, partial, timeRange) {
@@ -20269,6 +20272,14 @@ class LevelKey {
20269
20272
  static setKeyIdForUri(uri, keyId) {
20270
20273
  keyUriToKeyIdMap[uri] = keyId;
20271
20274
  }
20275
+ static addKeyIdForUri(uri) {
20276
+ const val = Object.keys(keyUriToKeyIdMap).length % Number.MAX_SAFE_INTEGER;
20277
+ const keyId = new Uint8Array(16);
20278
+ const dv = new DataView(keyId.buffer, 12, 4); // Just set the last 4 bytes
20279
+ dv.setUint32(0, val);
20280
+ keyUriToKeyIdMap[uri] = keyId;
20281
+ return keyId;
20282
+ }
20272
20283
  constructor(method, uri, format, formatversions = [1], iv = null, keyId) {
20273
20284
  this.uri = void 0;
20274
20285
  this.method = void 0;
@@ -22433,7 +22444,7 @@ class BaseStreamController extends TaskLoop {
22433
22444
  this.state = State.FRAG_LOADING;
22434
22445
 
22435
22446
  // Load key before streaming fragment data
22436
- const dataOnProgress = this.config.progressive;
22447
+ const dataOnProgress = this.config.progressive && frag.type !== PlaylistLevelType.SUBTITLE;
22437
22448
  let result;
22438
22449
  if (dataOnProgress && keyLoadingPromise) {
22439
22450
  result = keyLoadingPromise.then(keyLoadedData => {
@@ -23281,11 +23292,12 @@ class BaseStreamController extends TaskLoop {
23281
23292
  }, false);
23282
23293
  if (!parsed) {
23283
23294
  var _this$transmuxer;
23284
- if (level.fragmentError === 0) {
23285
- // Mark and track the odd empty segment as a gap to avoid reloading
23295
+ const mediaNotFound = ((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null;
23296
+ if (level.fragmentError === 0 || mediaNotFound && (level.fragmentError < 2 || frag.endList)) {
23297
+ // Mark and track the odd (or last) empty segment as a gap to avoid reloading
23286
23298
  this.treatAsGap(frag, level);
23287
23299
  }
23288
- if (((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null) {
23300
+ if (mediaNotFound) {
23289
23301
  const error = new Error(`Found no media in fragment ${frag.sn} of ${this.playlistLabel()} ${frag.level} resetting transmuxer to fallback to playlist timing`);
23290
23302
  this.warn(error.message);
23291
23303
  this.hls.trigger(Events.ERROR, {
@@ -23746,7 +23758,7 @@ function requireEventemitter3 () {
23746
23758
  var eventemitter3Exports = requireEventemitter3();
23747
23759
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
23748
23760
 
23749
- const version = "1.6.14";
23761
+ const version = "1.6.15";
23750
23762
 
23751
23763
  // ensure the worker ends up in the bundle
23752
23764
  // If the worker should not be included this gets aliased to empty.js
@@ -28924,7 +28936,7 @@ class MP4Remuxer extends Logger {
28924
28936
  // Clear the track samples. This also clears the samples array in the demuxer, since the reference is shared
28925
28937
  track.samples = [];
28926
28938
  const start = (firstPTS - initTime) / inputTimeScale;
28927
- const end = nextAudioTs / inputTimeScale;
28939
+ const end = this.nextAudioTs / inputTimeScale;
28928
28940
  const type = 'audio';
28929
28941
  const audioData = {
28930
28942
  data1: moof,
@@ -36088,6 +36100,11 @@ class EMEController extends Logger {
36088
36100
  }
36089
36101
  const keyIdArray = 'buffer' in keyId ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) : new Uint8Array(keyId);
36090
36102
  if (mediaKeySessionContext.keySystem === KeySystems.PLAYREADY && keyIdArray.length === 16) {
36103
+ // On some devices, the key ID has already been converted for endianness.
36104
+ // In such cases, this key ID is the one we need to cache.
36105
+ const originKeyIdWithStatusChange = arrayToHex(keyIdArray);
36106
+ // Cache the original key IDs to ensure compatibility across all cases.
36107
+ keyStatuses[originKeyIdWithStatusChange] = status;
36091
36108
  changeEndianness(keyIdArray);
36092
36109
  }
36093
36110
  const keyIdWithStatusChange = arrayToHex(keyIdArray);
@@ -38429,7 +38446,8 @@ class InterstitialsController extends Logger {
38429
38446
  if (backwardSeek && currentTime < start || currentTime >= start + duration) {
38430
38447
  var _playingItem$event;
38431
38448
  if ((_playingItem$event = playingItem.event) != null && _playingItem$event.appendInPlace) {
38432
- this.clearInterstitial(playingItem.event, playingItem);
38449
+ // Return SourceBuffer(s) to primary player and flush
38450
+ this.clearAssetPlayers(playingItem.event, playingItem);
38433
38451
  this.flushFrontBuffer(currentTime);
38434
38452
  }
38435
38453
  this.setScheduleToAssetAtTime(currentTime, playingAsset);
@@ -40139,11 +40157,14 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40139
40157
  return player;
40140
40158
  }
40141
40159
  clearInterstitial(interstitial, toSegment) {
40160
+ this.clearAssetPlayers(interstitial, toSegment);
40161
+ // Remove asset list and resolved duration
40162
+ interstitial.reset();
40163
+ }
40164
+ clearAssetPlayers(interstitial, toSegment) {
40142
40165
  interstitial.assetList.forEach(asset => {
40143
40166
  this.clearAssetPlayer(asset.identifier, toSegment);
40144
40167
  });
40145
- // Remove asset list and resolved duration
40146
- interstitial.reset();
40147
40168
  }
40148
40169
  resetAssetPlayer(assetId) {
40149
40170
  // Reset asset player so that it's timeline can be adjusted without reloading the MVP
@@ -40335,10 +40356,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40335
40356
  // Fallback to Primary by on current or future events by updating schedule to skip errored interstitials/assets
40336
40357
  const flushStart = interstitial.timelineStart;
40337
40358
  const playingItem = this.effectivePlayingItem;
40359
+ let timelinePos = this.timelinePos;
40338
40360
  // Update schedule now that interstitial/assets are flagged with `error` for fallback
40339
40361
  if (playingItem) {
40340
- this.log(`Fallback to primary from event "${interstitial.identifier}" start: ${flushStart} pos: ${this.timelinePos} playing: ${segmentToString(playingItem)} error: ${interstitial.error}`);
40341
- let timelinePos = this.timelinePos;
40362
+ this.log(`Fallback to primary from event "${interstitial.identifier}" start: ${flushStart} pos: ${timelinePos} playing: ${segmentToString(playingItem)} error: ${interstitial.error}`);
40342
40363
  if (timelinePos === -1) {
40343
40364
  timelinePos = this.hls.startPosition;
40344
40365
  }
@@ -40350,14 +40371,15 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40350
40371
  this.attachPrimary(flushStart, null);
40351
40372
  this.flushFrontBuffer(flushStart);
40352
40373
  }
40353
- if (!this.schedule) {
40354
- return;
40355
- }
40356
- const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos);
40357
- this.setSchedulePosition(scheduleIndex);
40358
- } else {
40374
+ } else if (timelinePos === -1) {
40359
40375
  this.checkStart();
40376
+ return;
40360
40377
  }
40378
+ if (!this.schedule) {
40379
+ return;
40380
+ }
40381
+ const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos);
40382
+ this.setSchedulePosition(scheduleIndex);
40361
40383
  }
40362
40384
 
40363
40385
  // Asset List loading
@@ -40398,7 +40420,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40398
40420
  // Abandon if new duration is reduced enough to land playback in primary start
40399
40421
  const index = this.schedule.findItemIndexAtTime(this.timelinePos);
40400
40422
  if (index !== scheduleIndex) {
40401
- interstitial.error = new Error(`Interstitial no longer within playback range ${this.timelinePos} ${interstitial}`);
40423
+ interstitial.error = new Error(`Interstitial ${assets.length ? 'no longer within playback range' : 'asset-list is empty'} ${this.timelinePos} ${interstitial}`);
40424
+ this.log(interstitial.error.message);
40402
40425
  this.updateSchedule(true);
40403
40426
  this.primaryFallback(interstitial);
40404
40427
  return;
@@ -47206,7 +47229,7 @@ class StreamController extends BaseStreamController {
47206
47229
  onAudioTrackSwitching(event, data) {
47207
47230
  const hls = this.hls;
47208
47231
  // if any URL found on new audio track, it is an alternate audio track
47209
- const fromAltAudio = this.altAudio === 2;
47232
+ const fromAltAudio = this.altAudio !== 0;
47210
47233
  const altAudio = useAlternateAudio(data.url, hls);
47211
47234
  // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
47212
47235
  // don't do anything if we switch to alt audio: audio stream controller is handling it.
@@ -47232,6 +47255,7 @@ class StreamController extends BaseStreamController {
47232
47255
  }
47233
47256
  // If switching from alt to main audio, flush all audio and trigger track switched
47234
47257
  if (fromAltAudio) {
47258
+ this.altAudio = 0;
47235
47259
  this.fragmentTracker.removeAllFragments();
47236
47260
  hls.once(Events.BUFFER_FLUSHED, () => {
47237
47261
  if (!this.hls) {
@@ -48069,14 +48093,22 @@ class KeyLoader extends Logger {
48069
48093
  if (!keyInfo.decryptdata.keyId && (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data) {
48070
48094
  const keyIds = parseKeyIdsFromTenc(frag.initSegment.data);
48071
48095
  if (keyIds.length) {
48072
- const keyId = keyIds[0];
48096
+ let keyId = keyIds[0];
48073
48097
  if (keyId.some(b => b !== 0)) {
48074
48098
  this.log(`Using keyId found in init segment ${arrayToHex(keyId)}`);
48075
- keyInfo.decryptdata.keyId = keyId;
48076
48099
  LevelKey.setKeyIdForUri(keyInfo.decryptdata.uri, keyId);
48100
+ } else {
48101
+ keyId = LevelKey.addKeyIdForUri(keyInfo.decryptdata.uri);
48102
+ this.log(`Generating keyId to patch media ${arrayToHex(keyId)}`);
48077
48103
  }
48104
+ keyInfo.decryptdata.keyId = keyId;
48078
48105
  }
48079
48106
  }
48107
+ if (!keyInfo.decryptdata.keyId && !isMediaFragment(frag)) {
48108
+ // Resolve so that unencrypted init segment is loaded
48109
+ // key id is extracted from tenc box when processing key for next segment above
48110
+ return Promise.resolve(keyLoadedData);
48111
+ }
48080
48112
  const keySessionContextPromise = this.emeController.loadKey(keyLoadedData);
48081
48113
  return (keyInfo.keyLoadPromise = keySessionContextPromise.then(keySessionContext => {
48082
48114
  keyInfo.mediaKeySessionContext = keySessionContext;
package/dist/index.js CHANGED
@@ -118,6 +118,73 @@ class LogTracer {
118
118
  }
119
119
  }
120
120
 
121
+ const DEFAULT_DELAY = 1000;
122
+ const MIN_DELAY = 1000;
123
+ /**
124
+ * A tracer the pushes the records to a remote server.
125
+ * Used with fullstack Node.js applications (Nuxt.js, Next.js, etc.)
126
+ * @beta
127
+ */
128
+ class RemoteTracer {
129
+ baseTracer;
130
+ tags;
131
+ buffer = [];
132
+ timerId = null;
133
+ delay;
134
+ /**
135
+ *
136
+ * @param baseTracer An additional tracer to be called next to this one. Deprecated. Use {@link ChainedTracer} instead.
137
+ * @param tags
138
+ * @param options
139
+ */
140
+ constructor(baseTracer, tags = {}, options = {}) {
141
+ this.baseTracer = baseTracer;
142
+ this.tags = tags;
143
+ this.delay = Math.max(options.delay ?? DEFAULT_DELAY, MIN_DELAY);
144
+ }
145
+ reportError(e) {
146
+ if (this.baseTracer) {
147
+ this.baseTracer.reportError(e);
148
+ }
149
+ const message = String(e);
150
+ const detail = e instanceof Error && 'detail' in e
151
+ ? e.detail
152
+ : undefined;
153
+ this.push(message, detail);
154
+ }
155
+ trace(message, detail) {
156
+ if (this.baseTracer) {
157
+ this.baseTracer.trace(message, detail);
158
+ }
159
+ this.push(message, detail);
160
+ }
161
+ setTag(key, value) {
162
+ this.tags[key] = value;
163
+ }
164
+ push(message, detail) {
165
+ const time = new Date().getTime();
166
+ this.buffer.push({ message, detail, time });
167
+ if (!this.timerId) {
168
+ this.timerId = setTimeout(() => {
169
+ this.timerId = null;
170
+ this.send(this.buffer.splice(0, this.buffer.length));
171
+ }, this.delay);
172
+ }
173
+ }
174
+ send(records) {
175
+ fetch('/api/traceb', {
176
+ method: 'POST',
177
+ body: JSON.stringify({
178
+ records,
179
+ tags: this.tags,
180
+ }),
181
+ headers: {
182
+ 'Content-Type': 'application/json',
183
+ },
184
+ }).catch((e) => console.error(e));
185
+ }
186
+ }
187
+
121
188
  /**
122
189
  * @beta
123
190
  */
@@ -13897,7 +13964,7 @@ function enableLogs(debugConfig, context, id) {
13897
13964
  // Some browsers don't allow to use bind on console object anyway
13898
13965
  // fallback to default if needed
13899
13966
  try {
13900
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.14"}`);
13967
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.15"}`);
13901
13968
  } catch (e) {
13902
13969
  /* log fn threw an exception. All logger methods are no-ops. */
13903
13970
  return createLogger();
@@ -18343,11 +18410,7 @@ class FragmentTracker {
18343
18410
  });
18344
18411
  fragmentEntity.loaded = null;
18345
18412
  if (Object.keys(fragmentEntity.range).length) {
18346
- fragmentEntity.buffered = true;
18347
- const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList;
18348
- if (endList) {
18349
- this.endListFragments[fragmentEntity.body.type] = fragmentEntity;
18350
- }
18413
+ this.bufferedEnd(fragmentEntity, frag);
18351
18414
  if (!isPartial(fragmentEntity)) {
18352
18415
  // Remove older fragment parts from lookup after frag is tracked as buffered
18353
18416
  this.removeParts(frag.sn - 1, frag.type);
@@ -18357,6 +18420,13 @@ class FragmentTracker {
18357
18420
  this.removeFragment(fragmentEntity.body);
18358
18421
  }
18359
18422
  }
18423
+ bufferedEnd(fragmentEntity, frag) {
18424
+ fragmentEntity.buffered = true;
18425
+ const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList;
18426
+ if (endList) {
18427
+ this.endListFragments[fragmentEntity.body.type] = fragmentEntity;
18428
+ }
18429
+ }
18360
18430
  removeParts(snToKeep, levelType) {
18361
18431
  const activeParts = this.activePartLists[levelType];
18362
18432
  if (!activeParts) {
@@ -18381,7 +18451,7 @@ class FragmentTracker {
18381
18451
  }
18382
18452
  if (fragmentEntity) {
18383
18453
  fragmentEntity.loaded = null;
18384
- fragmentEntity.buffered = true;
18454
+ this.bufferedEnd(fragmentEntity, frag);
18385
18455
  }
18386
18456
  }
18387
18457
  getBufferedTimes(fragment, part, partial, timeRange) {
@@ -20430,6 +20500,14 @@ class LevelKey {
20430
20500
  static setKeyIdForUri(uri, keyId) {
20431
20501
  keyUriToKeyIdMap[uri] = keyId;
20432
20502
  }
20503
+ static addKeyIdForUri(uri) {
20504
+ const val = Object.keys(keyUriToKeyIdMap).length % Number.MAX_SAFE_INTEGER;
20505
+ const keyId = new Uint8Array(16);
20506
+ const dv = new DataView(keyId.buffer, 12, 4); // Just set the last 4 bytes
20507
+ dv.setUint32(0, val);
20508
+ keyUriToKeyIdMap[uri] = keyId;
20509
+ return keyId;
20510
+ }
20433
20511
  constructor(method, uri, format, formatversions = [1], iv = null, keyId) {
20434
20512
  this.uri = void 0;
20435
20513
  this.method = void 0;
@@ -22594,7 +22672,7 @@ class BaseStreamController extends TaskLoop {
22594
22672
  this.state = State.FRAG_LOADING;
22595
22673
 
22596
22674
  // Load key before streaming fragment data
22597
- const dataOnProgress = this.config.progressive;
22675
+ const dataOnProgress = this.config.progressive && frag.type !== PlaylistLevelType.SUBTITLE;
22598
22676
  let result;
22599
22677
  if (dataOnProgress && keyLoadingPromise) {
22600
22678
  result = keyLoadingPromise.then(keyLoadedData => {
@@ -23442,11 +23520,12 @@ class BaseStreamController extends TaskLoop {
23442
23520
  }, false);
23443
23521
  if (!parsed) {
23444
23522
  var _this$transmuxer;
23445
- if (level.fragmentError === 0) {
23446
- // Mark and track the odd empty segment as a gap to avoid reloading
23523
+ const mediaNotFound = ((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null;
23524
+ if (level.fragmentError === 0 || mediaNotFound && (level.fragmentError < 2 || frag.endList)) {
23525
+ // Mark and track the odd (or last) empty segment as a gap to avoid reloading
23447
23526
  this.treatAsGap(frag, level);
23448
23527
  }
23449
- if (((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null) {
23528
+ if (mediaNotFound) {
23450
23529
  const error = new Error(`Found no media in fragment ${frag.sn} of ${this.playlistLabel()} ${frag.level} resetting transmuxer to fallback to playlist timing`);
23451
23530
  this.warn(error.message);
23452
23531
  this.hls.trigger(Events.ERROR, {
@@ -23907,7 +23986,7 @@ function requireEventemitter3 () {
23907
23986
  var eventemitter3Exports = requireEventemitter3();
23908
23987
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
23909
23988
 
23910
- const version$2 = "1.6.14";
23989
+ const version$2 = "1.6.15";
23911
23990
 
23912
23991
  // ensure the worker ends up in the bundle
23913
23992
  // If the worker should not be included this gets aliased to empty.js
@@ -29085,7 +29164,7 @@ class MP4Remuxer extends Logger {
29085
29164
  // Clear the track samples. This also clears the samples array in the demuxer, since the reference is shared
29086
29165
  track.samples = [];
29087
29166
  const start = (firstPTS - initTime) / inputTimeScale;
29088
- const end = nextAudioTs / inputTimeScale;
29167
+ const end = this.nextAudioTs / inputTimeScale;
29089
29168
  const type = 'audio';
29090
29169
  const audioData = {
29091
29170
  data1: moof,
@@ -36249,6 +36328,11 @@ class EMEController extends Logger {
36249
36328
  }
36250
36329
  const keyIdArray = 'buffer' in keyId ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) : new Uint8Array(keyId);
36251
36330
  if (mediaKeySessionContext.keySystem === KeySystems.PLAYREADY && keyIdArray.length === 16) {
36331
+ // On some devices, the key ID has already been converted for endianness.
36332
+ // In such cases, this key ID is the one we need to cache.
36333
+ const originKeyIdWithStatusChange = arrayToHex(keyIdArray);
36334
+ // Cache the original key IDs to ensure compatibility across all cases.
36335
+ keyStatuses[originKeyIdWithStatusChange] = status;
36252
36336
  changeEndianness(keyIdArray);
36253
36337
  }
36254
36338
  const keyIdWithStatusChange = arrayToHex(keyIdArray);
@@ -38590,7 +38674,8 @@ class InterstitialsController extends Logger {
38590
38674
  if (backwardSeek && currentTime < start || currentTime >= start + duration) {
38591
38675
  var _playingItem$event;
38592
38676
  if ((_playingItem$event = playingItem.event) != null && _playingItem$event.appendInPlace) {
38593
- this.clearInterstitial(playingItem.event, playingItem);
38677
+ // Return SourceBuffer(s) to primary player and flush
38678
+ this.clearAssetPlayers(playingItem.event, playingItem);
38594
38679
  this.flushFrontBuffer(currentTime);
38595
38680
  }
38596
38681
  this.setScheduleToAssetAtTime(currentTime, playingAsset);
@@ -40300,11 +40385,14 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40300
40385
  return player;
40301
40386
  }
40302
40387
  clearInterstitial(interstitial, toSegment) {
40388
+ this.clearAssetPlayers(interstitial, toSegment);
40389
+ // Remove asset list and resolved duration
40390
+ interstitial.reset();
40391
+ }
40392
+ clearAssetPlayers(interstitial, toSegment) {
40303
40393
  interstitial.assetList.forEach(asset => {
40304
40394
  this.clearAssetPlayer(asset.identifier, toSegment);
40305
40395
  });
40306
- // Remove asset list and resolved duration
40307
- interstitial.reset();
40308
40396
  }
40309
40397
  resetAssetPlayer(assetId) {
40310
40398
  // Reset asset player so that it's timeline can be adjusted without reloading the MVP
@@ -40496,10 +40584,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40496
40584
  // Fallback to Primary by on current or future events by updating schedule to skip errored interstitials/assets
40497
40585
  const flushStart = interstitial.timelineStart;
40498
40586
  const playingItem = this.effectivePlayingItem;
40587
+ let timelinePos = this.timelinePos;
40499
40588
  // Update schedule now that interstitial/assets are flagged with `error` for fallback
40500
40589
  if (playingItem) {
40501
- this.log(`Fallback to primary from event "${interstitial.identifier}" start: ${flushStart} pos: ${this.timelinePos} playing: ${segmentToString(playingItem)} error: ${interstitial.error}`);
40502
- let timelinePos = this.timelinePos;
40590
+ this.log(`Fallback to primary from event "${interstitial.identifier}" start: ${flushStart} pos: ${timelinePos} playing: ${segmentToString(playingItem)} error: ${interstitial.error}`);
40503
40591
  if (timelinePos === -1) {
40504
40592
  timelinePos = this.hls.startPosition;
40505
40593
  }
@@ -40511,14 +40599,15 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40511
40599
  this.attachPrimary(flushStart, null);
40512
40600
  this.flushFrontBuffer(flushStart);
40513
40601
  }
40514
- if (!this.schedule) {
40515
- return;
40516
- }
40517
- const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos);
40518
- this.setSchedulePosition(scheduleIndex);
40519
- } else {
40602
+ } else if (timelinePos === -1) {
40520
40603
  this.checkStart();
40604
+ return;
40605
+ }
40606
+ if (!this.schedule) {
40607
+ return;
40521
40608
  }
40609
+ const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos);
40610
+ this.setSchedulePosition(scheduleIndex);
40522
40611
  }
40523
40612
 
40524
40613
  // Asset List loading
@@ -40559,7 +40648,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
40559
40648
  // Abandon if new duration is reduced enough to land playback in primary start
40560
40649
  const index = this.schedule.findItemIndexAtTime(this.timelinePos);
40561
40650
  if (index !== scheduleIndex) {
40562
- interstitial.error = new Error(`Interstitial no longer within playback range ${this.timelinePos} ${interstitial}`);
40651
+ interstitial.error = new Error(`Interstitial ${assets.length ? 'no longer within playback range' : 'asset-list is empty'} ${this.timelinePos} ${interstitial}`);
40652
+ this.log(interstitial.error.message);
40563
40653
  this.updateSchedule(true);
40564
40654
  this.primaryFallback(interstitial);
40565
40655
  return;
@@ -47367,7 +47457,7 @@ class StreamController extends BaseStreamController {
47367
47457
  onAudioTrackSwitching(event, data) {
47368
47458
  const hls = this.hls;
47369
47459
  // if any URL found on new audio track, it is an alternate audio track
47370
- const fromAltAudio = this.altAudio === 2;
47460
+ const fromAltAudio = this.altAudio !== 0;
47371
47461
  const altAudio = useAlternateAudio(data.url, hls);
47372
47462
  // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
47373
47463
  // don't do anything if we switch to alt audio: audio stream controller is handling it.
@@ -47393,6 +47483,7 @@ class StreamController extends BaseStreamController {
47393
47483
  }
47394
47484
  // If switching from alt to main audio, flush all audio and trigger track switched
47395
47485
  if (fromAltAudio) {
47486
+ this.altAudio = 0;
47396
47487
  this.fragmentTracker.removeAllFragments();
47397
47488
  hls.once(Events.BUFFER_FLUSHED, () => {
47398
47489
  if (!this.hls) {
@@ -48230,14 +48321,22 @@ class KeyLoader extends Logger {
48230
48321
  if (!keyInfo.decryptdata.keyId && (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data) {
48231
48322
  const keyIds = parseKeyIdsFromTenc(frag.initSegment.data);
48232
48323
  if (keyIds.length) {
48233
- const keyId = keyIds[0];
48324
+ let keyId = keyIds[0];
48234
48325
  if (keyId.some(b => b !== 0)) {
48235
48326
  this.log(`Using keyId found in init segment ${arrayToHex(keyId)}`);
48236
- keyInfo.decryptdata.keyId = keyId;
48237
48327
  LevelKey.setKeyIdForUri(keyInfo.decryptdata.uri, keyId);
48328
+ } else {
48329
+ keyId = LevelKey.addKeyIdForUri(keyInfo.decryptdata.uri);
48330
+ this.log(`Generating keyId to patch media ${arrayToHex(keyId)}`);
48238
48331
  }
48332
+ keyInfo.decryptdata.keyId = keyId;
48239
48333
  }
48240
48334
  }
48335
+ if (!keyInfo.decryptdata.keyId && !isMediaFragment(frag)) {
48336
+ // Resolve so that unencrypted init segment is loaded
48337
+ // key id is extracted from tenc box when processing key for next segment above
48338
+ return Promise.resolve(keyLoadedData);
48339
+ }
48241
48340
  const keySessionContextPromise = this.emeController.loadKey(keyLoadedData);
48242
48341
  return (keyInfo.keyLoadPromise = keySessionContextPromise.then(keySessionContext => {
48243
48342
  keyInfo.mediaKeySessionContext = keySessionContext;
@@ -51424,7 +51523,7 @@ class Player {
51424
51523
  }
51425
51524
  }
51426
51525
 
51427
- var version$1 = "2.28.26";
51526
+ var version$1 = "2.28.27";
51428
51527
 
51429
51528
  var packages = {
51430
51529
  "node_modules/@clappr/core": {
@@ -51432,7 +51531,7 @@ var packages = {
51432
51531
  "node_modules/dashjs": {
51433
51532
  version: "4.7.4"},
51434
51533
  "node_modules/hls.js": {
51435
- version: "1.6.14"}};
51534
+ version: "1.6.15"}};
51436
51535
 
51437
51536
  /**
51438
51537
  * Version information about the gplayer and its main dependencies
@@ -63620,4 +63719,4 @@ class VolumeFade extends UICorePlugin {
63620
63719
  }
63621
63720
  }
63622
63721
 
63623
- 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, SeekTime, SentryTracer, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, VolumeFade, VolumeFadeEvents, reportError, setTracer, trace, version };
63722
+ 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 };
@@ -1,4 +1,4 @@
1
- export { ChainedTracer, LogTracer, Logger, SentryTracer, reportError, setTracer, trace, } from '@gcorevideo/utils';
1
+ export { ChainedTracer, LogTracer, Logger, RemoteTracer, SentryTracer, reportError, setTracer, trace, } from '@gcorevideo/utils';
2
2
  export * from './Player.js';
3
3
  export * from './playback.types.js';
4
4
  export * from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.core.d.ts","sourceRoot":"","sources":["../src/index.core.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,YAAY,EACZ,WAAW,EACX,SAAS,EACT,KAAK,GACN,MAAM,mBAAmB,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
1
+ {"version":3,"file":"index.core.d.ts","sourceRoot":"","sources":["../src/index.core.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,SAAS,EACT,KAAK,GACN,MAAM,mBAAmB,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
package/lib/index.core.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ChainedTracer, LogTracer, Logger, SentryTracer, reportError, setTracer, trace, } from '@gcorevideo/utils';
1
+ export { ChainedTracer, LogTracer, Logger, RemoteTracer, SentryTracer, reportError, setTracer, trace, } from '@gcorevideo/utils';
2
2
  export * from './Player.js';
3
3
  export * from './playback.types.js';
4
4
  export * from './types.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gcorevideo/player",
3
- "version": "2.28.26",
3
+ "version": "2.28.27",
4
4
  "description": "Gcore JavaScript video player",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -62,7 +62,7 @@
62
62
  "@gcorevideo/utils": "^0.7.1",
63
63
  "@sentry/types": "^8.47.0",
64
64
  "dashjs": "^4.7.4",
65
- "hls.js": "^1.5.17",
65
+ "hls.js": "^1.6.15",
66
66
  "human-format": "^1.2.1",
67
67
  "mousetrap": "^1.6.5",
68
68
  "videojs-vtt.js": "^0.15.5"
package/src/index.core.ts CHANGED
@@ -2,6 +2,7 @@ export {
2
2
  ChainedTracer,
3
3
  LogTracer,
4
4
  Logger,
5
+ RemoteTracer,
5
6
  SentryTracer,
6
7
  reportError,
7
8
  setTracer,