hls.js 1.6.0-rc.1.0.canary.11079 → 1.6.0-rc.1.0.canary.11080

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/hls.mjs CHANGED
@@ -402,7 +402,7 @@ function enableLogs(debugConfig, context, id) {
402
402
  // Some browsers don't allow to use bind on console object anyway
403
403
  // fallback to default if needed
404
404
  try {
405
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-rc.1.0.canary.11079"}`);
405
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-rc.1.0.canary.11080"}`);
406
406
  } catch (e) {
407
407
  /* log fn threw an exception. All logger methods are no-ops. */
408
408
  return createLogger();
@@ -8453,6 +8453,14 @@ class BaseStreamController extends TaskLoop {
8453
8453
  return (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details;
8454
8454
  }
8455
8455
  }
8456
+ get timelineOffset() {
8457
+ const configuredTimelineOffset = this.config.timelineOffset;
8458
+ if (configuredTimelineOffset) {
8459
+ var _this$getLevelDetails;
8460
+ return ((_this$getLevelDetails = this.getLevelDetails()) == null ? void 0 : _this$getLevelDetails.appliedTimelineOffset) || configuredTimelineOffset;
8461
+ }
8462
+ return 0;
8463
+ }
8456
8464
  onMediaAttached(event, data) {
8457
8465
  const media = this.media = this.mediaBuffer = data.media;
8458
8466
  media.removeEventListener('seeking', this.onMediaSeeking);
@@ -9150,8 +9158,8 @@ class BaseStreamController extends TaskLoop {
9150
9158
  const end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd;
9151
9159
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
9152
9160
  }
9153
- frag = this.filterReplacedPrimary(frag, levelDetails);
9154
- return this.mapToInitFragWhenRequired(frag);
9161
+ const programFrag = this.filterReplacedPrimary(frag, levelDetails);
9162
+ return this.mapToInitFragWhenRequired(programFrag);
9155
9163
  }
9156
9164
  isLoopLoading(frag, targetBufferTime) {
9157
9165
  const trackerState = this.fragmentTracker.getState(frag);
@@ -9178,12 +9186,21 @@ class BaseStreamController extends TaskLoop {
9178
9186
  this.loopSn = undefined;
9179
9187
  return nextFragment;
9180
9188
  }
9189
+ get primaryPrefetch() {
9190
+ if (interstitialsEnabled(this.hls.config)) {
9191
+ var _this$hls$interstitia, _this$hls$interstitia2;
9192
+ const playingInterstitial = (_this$hls$interstitia = this.hls.interstitialsManager) == null ? void 0 : (_this$hls$interstitia2 = _this$hls$interstitia.playingItem) == null ? void 0 : _this$hls$interstitia2.event;
9193
+ if (playingInterstitial) {
9194
+ return true;
9195
+ }
9196
+ }
9197
+ return false;
9198
+ }
9181
9199
  filterReplacedPrimary(frag, details) {
9182
9200
  if (!frag) {
9183
9201
  return frag;
9184
9202
  }
9185
- const config = this.hls.config;
9186
- if (config.interstitialsController && config.enableInterstitialPlayback !== false && frag.type !== PlaylistLevelType.SUBTITLE) {
9203
+ if (interstitialsEnabled(this.hls.config) && frag.type !== PlaylistLevelType.SUBTITLE) {
9187
9204
  // Do not load fragments outside the buffering schedule segment
9188
9205
  const interstitials = this.hls.interstitialsManager;
9189
9206
  const bufferingItem = interstitials == null ? void 0 : interstitials.bufferingItem;
@@ -9203,7 +9220,10 @@ class BaseStreamController extends TaskLoop {
9203
9220
  }
9204
9221
  if (frag.start > bufferingItem.end && bufferingItem.nextEvent) {
9205
9222
  // fragment is past schedule item end
9206
- return null;
9223
+ // allow some overflow when not appending in place to prevent stalls
9224
+ if (bufferingItem.nextEvent.appendInPlace || frag.start - bufferingItem.end > 1) {
9225
+ return null;
9226
+ }
9207
9227
  }
9208
9228
  }
9209
9229
  }
@@ -9389,6 +9409,7 @@ class BaseStreamController extends TaskLoop {
9389
9409
  if (startPosition < sliding) {
9390
9410
  startPosition = -1;
9391
9411
  }
9412
+ const timelineOffset = this.timelineOffset;
9392
9413
  if (startPosition === -1) {
9393
9414
  // Use Playlist EXT-X-START:TIME-OFFSET when set
9394
9415
  // Prioritize Multivariant Playlist offset so that main, audio, and subtitle stream-controller start times match
@@ -9412,9 +9433,9 @@ class BaseStreamController extends TaskLoop {
9412
9433
  this.log(`setting startPosition to 0 by default`);
9413
9434
  this.startPosition = startPosition = 0;
9414
9435
  }
9415
- this.lastCurrentTime = startPosition;
9436
+ this.lastCurrentTime = startPosition + timelineOffset;
9416
9437
  }
9417
- this.nextLoadPosition = startPosition;
9438
+ this.nextLoadPosition = startPosition + timelineOffset;
9418
9439
  }
9419
9440
  getLoadPosition() {
9420
9441
  var _this$hls;
@@ -9694,6 +9715,9 @@ class BaseStreamController extends TaskLoop {
9694
9715
  return this._state;
9695
9716
  }
9696
9717
  }
9718
+ function interstitialsEnabled(config) {
9719
+ return !!config.interstitialsController && config.enableInterstitialPlayback !== false;
9720
+ }
9697
9721
 
9698
9722
  class ChunkCache {
9699
9723
  constructor() {
@@ -10086,7 +10110,7 @@ function requireEventemitter3 () {
10086
10110
  var eventemitter3Exports = requireEventemitter3();
10087
10111
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
10088
10112
 
10089
- const version = "1.6.0-rc.1.0.canary.11079";
10113
+ const version = "1.6.0-rc.1.0.canary.11080";
10090
10114
 
10091
10115
  // ensure the worker ends up in the bundle
10092
10116
  // If the worker should not be included this gets aliased to empty.js
@@ -16441,7 +16465,7 @@ class AudioStreamController extends BaseStreamController {
16441
16465
  const cc = mainFrag.cc;
16442
16466
  return findNearestWithCC(trackDetails, cc, mainFrag) || trackDetails && findFragWithCC(trackDetails.fragments, cc) || mainFrag;
16443
16467
  }
16444
- startLoad(startPosition) {
16468
+ startLoad(startPosition, skipSeekToStartPosition) {
16445
16469
  if (!this.levels) {
16446
16470
  this.startPosition = startPosition;
16447
16471
  this.state = State.STOPPED;
@@ -16457,7 +16481,8 @@ class AudioStreamController extends BaseStreamController {
16457
16481
  } else {
16458
16482
  this.state = State.WAITING_TRACK;
16459
16483
  }
16460
- this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;
16484
+ this.nextLoadPosition = this.lastCurrentTime = startPosition + this.timelineOffset;
16485
+ this.startPosition = skipSeekToStartPosition ? -1 : startPosition;
16461
16486
  this.tick();
16462
16487
  }
16463
16488
  doTick() {
@@ -16581,7 +16606,7 @@ class AudioStreamController extends BaseStreamController {
16581
16606
  // 3. if tracks or track not loaded and selected
16582
16607
  // then exit loop
16583
16608
  // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
16584
- if (!this.buffering || !media && (this.startFragRequested || !config.startFragPrefetch) || !(levels != null && levels[trackId])) {
16609
+ if (!this.buffering || !media && !this.primaryPrefetch && (this.startFragRequested || !config.startFragPrefetch) || !(levels != null && levels[trackId])) {
16585
16610
  return;
16586
16611
  }
16587
16612
  const levelInfo = levels[trackId];
@@ -18822,17 +18847,10 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
18822
18847
  return type && !((_this$tracks$type4 = this.tracks[type]) != null && _this$tracks$type4.ended);
18823
18848
  });
18824
18849
  if (allTracksEnding) {
18825
- this.log(`Queueing EOS`);
18826
- this.blockUntilOpen(() => {
18827
- this.sourceBuffers.forEach(([type]) => {
18828
- if (type !== null) {
18829
- const track = this.tracks[type];
18830
- if (track) {
18831
- track.ending = false;
18832
- }
18833
- }
18834
- });
18835
- if (allowEndOfStream) {
18850
+ if (allowEndOfStream) {
18851
+ this.log(`Queueing EOS`);
18852
+ this.blockUntilOpen(() => {
18853
+ this.tracksEnded();
18836
18854
  const {
18837
18855
  mediaSource
18838
18856
  } = this;
@@ -18845,11 +18863,24 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => key === 'initSeg
18845
18863
  this.log(`Calling mediaSource.endOfStream()`);
18846
18864
  // Allow this to throw and be caught by the enqueueing function
18847
18865
  mediaSource.endOfStream();
18848
- }
18866
+ this.hls.trigger(Events.BUFFERED_TO_END, undefined);
18867
+ });
18868
+ } else {
18869
+ this.tracksEnded();
18849
18870
  this.hls.trigger(Events.BUFFERED_TO_END, undefined);
18850
- });
18871
+ }
18851
18872
  }
18852
18873
  }
18874
+ tracksEnded() {
18875
+ this.sourceBuffers.forEach(([type]) => {
18876
+ if (type !== null) {
18877
+ const track = this.tracks[type];
18878
+ if (track) {
18879
+ track.ending = false;
18880
+ }
18881
+ }
18882
+ });
18883
+ }
18853
18884
  onLevelUpdated(event, {
18854
18885
  details
18855
18886
  }) {
@@ -22980,7 +23011,7 @@ function hash(text) {
22980
23011
  return (hash >>> 0).toString();
22981
23012
  }
22982
23013
 
22983
- const ALIGNED_END_THRESHOLD_SECONDS = 0.02;
23014
+ const ALIGNED_END_THRESHOLD_SECONDS = 0.025;
22984
23015
  let TimelineOccupancy = /*#__PURE__*/function (TimelineOccupancy) {
22985
23016
  TimelineOccupancy[TimelineOccupancy["Point"] = 0] = "Point";
22986
23017
  TimelineOccupancy[TimelineOccupancy["Range"] = 1] = "Range";
@@ -23014,6 +23045,7 @@ class InterstitialEvent {
23014
23045
  this.assetListResponse = null;
23015
23046
  this.resumeAnchor = void 0;
23016
23047
  this.error = void 0;
23048
+ this.resetOnResume = void 0;
23017
23049
  this.base = base;
23018
23050
  this.dateRange = dateRange;
23019
23051
  this.setDateRange(dateRange);
@@ -23073,6 +23105,18 @@ class InterstitialEvent {
23073
23105
  get startOffset() {
23074
23106
  return this.cue.pre ? 0 : this.startTime;
23075
23107
  }
23108
+ get startIsAligned() {
23109
+ if (this.startTime === 0 || this.snapOptions.out) {
23110
+ return true;
23111
+ }
23112
+ const frag = this.dateRange.tagAnchor;
23113
+ if (frag) {
23114
+ const startTime = this.dateRange.startTime;
23115
+ const snappedStart = getSnapToFragmentTime(startTime, frag);
23116
+ return startTime - snappedStart < 0.1;
23117
+ }
23118
+ return false;
23119
+ }
23076
23120
  get resumptionOffset() {
23077
23121
  const resumeOffset = this.resumeOffset;
23078
23122
  const offset = isFiniteNumber(resumeOffset) ? resumeOffset : this.duration;
@@ -23090,18 +23134,22 @@ class InterstitialEvent {
23090
23134
  return resumeTime;
23091
23135
  }
23092
23136
  get appendInPlace() {
23137
+ if (this.appendInPlaceStarted) {
23138
+ return true;
23139
+ }
23093
23140
  if (this.appendInPlaceDisabled) {
23094
23141
  return false;
23095
23142
  }
23096
- if (!this.cue.once && !this.cue.pre && (
23143
+ if (!this.cue.once && !this.cue.pre &&
23097
23144
  // preroll starts at startPosition before startPosition is known (live)
23098
- this.startTime === 0 || this.snapOptions.out) && (isNaN(this.playoutLimit) && isNaN(this.resumeOffset) || this.resumeOffset && this.duration && Math.abs(this.resumeOffset - this.duration) < ALIGNED_END_THRESHOLD_SECONDS)) {
23145
+ this.startIsAligned && (isNaN(this.playoutLimit) && isNaN(this.resumeOffset) || this.resumeOffset && this.duration && Math.abs(this.resumeOffset - this.duration) < ALIGNED_END_THRESHOLD_SECONDS)) {
23099
23146
  return true;
23100
23147
  }
23101
23148
  return false;
23102
23149
  }
23103
23150
  set appendInPlace(value) {
23104
23151
  if (this.appendInPlaceStarted) {
23152
+ this.resetOnResume = !value;
23105
23153
  return;
23106
23154
  }
23107
23155
  this.appendInPlaceDisabled = !value;
@@ -23194,6 +23242,7 @@ class HlsAssetPlayer {
23194
23242
  this.hasDetails = false;
23195
23243
  this.mediaAttached = null;
23196
23244
  this._currentTime = void 0;
23245
+ this._bufferedEosTime = void 0;
23197
23246
  this.checkPlayout = () => {
23198
23247
  const interstitial = this.interstitial;
23199
23248
  const playoutLimit = interstitial.playoutLimit;
@@ -23230,9 +23279,25 @@ class HlsAssetPlayer {
23230
23279
  }
23231
23280
  });
23232
23281
  }
23233
- get destroyed() {
23282
+ bufferedInPlaceToEnd(media) {
23234
23283
  var _this$hls;
23235
- return !((_this$hls = this.hls) != null && _this$hls.userConfig);
23284
+ if (!this.interstitial.appendInPlace) {
23285
+ return false;
23286
+ }
23287
+ if ((_this$hls = this.hls) != null && _this$hls.bufferedToEnd) {
23288
+ return true;
23289
+ }
23290
+ if (!media || !this._bufferedEosTime) {
23291
+ return false;
23292
+ }
23293
+ const start = this.timelineOffset;
23294
+ const bufferInfo = BufferHelper.bufferInfo(media, start, 0);
23295
+ const bufferedEnd = this.getAssetTime(bufferInfo.end);
23296
+ return bufferedEnd >= this._bufferedEosTime - 0.02;
23297
+ }
23298
+ get destroyed() {
23299
+ var _this$hls2;
23300
+ return !((_this$hls2 = this.hls) != null && _this$hls2.userConfig);
23236
23301
  }
23237
23302
  get assetId() {
23238
23303
  return this.assetItem.identifier;
@@ -23241,12 +23306,15 @@ class HlsAssetPlayer {
23241
23306
  return this.assetItem.parentIdentifier;
23242
23307
  }
23243
23308
  get media() {
23244
- var _this$hls2;
23245
- return ((_this$hls2 = this.hls) == null ? void 0 : _this$hls2.media) || null;
23309
+ var _this$hls3;
23310
+ return ((_this$hls3 = this.hls) == null ? void 0 : _this$hls3.media) || null;
23246
23311
  }
23247
23312
  get bufferedEnd() {
23248
23313
  const media = this.media || this.mediaAttached;
23249
23314
  if (!media) {
23315
+ if (this._bufferedEosTime) {
23316
+ return this._bufferedEosTime;
23317
+ }
23250
23318
  return this.currentTime;
23251
23319
  }
23252
23320
  const bufferInfo = BufferHelper.bufferInfo(media, media.currentTime, 0.001);
@@ -23277,8 +23345,8 @@ class HlsAssetPlayer {
23277
23345
  return this.assetItem.startOffset;
23278
23346
  }
23279
23347
  get timelineOffset() {
23280
- var _this$hls3;
23281
- return ((_this$hls3 = this.hls) == null ? void 0 : _this$hls3.config.timelineOffset) || 0;
23348
+ var _this$hls4;
23349
+ return ((_this$hls4 = this.hls) == null ? void 0 : _this$hls4.config.timelineOffset) || 0;
23282
23350
  }
23283
23351
  set timelineOffset(value) {
23284
23352
  const timelineOffset = this.timelineOffset;
@@ -23301,9 +23369,18 @@ class HlsAssetPlayer {
23301
23369
  const media = this.mediaAttached;
23302
23370
  if (media) {
23303
23371
  this._currentTime = media.currentTime;
23372
+ this.bufferSnapShot();
23304
23373
  media.removeEventListener('timeupdate', this.checkPlayout);
23305
23374
  }
23306
23375
  }
23376
+ bufferSnapShot() {
23377
+ if (this.mediaAttached) {
23378
+ var _this$hls5;
23379
+ if ((_this$hls5 = this.hls) != null && _this$hls5.bufferedToEnd) {
23380
+ this._bufferedEosTime = this.bufferedEnd;
23381
+ }
23382
+ }
23383
+ }
23307
23384
  destroy() {
23308
23385
  this.removeMediaListeners();
23309
23386
  this.hls.destroy();
@@ -23327,6 +23404,7 @@ class HlsAssetPlayer {
23327
23404
  this.hls.pauseBuffering();
23328
23405
  }
23329
23406
  transferMedia() {
23407
+ this.bufferSnapShot();
23330
23408
  return this.hls.transferMedia();
23331
23409
  }
23332
23410
  on(event, listener, context) {
@@ -23339,8 +23417,8 @@ class HlsAssetPlayer {
23339
23417
  this.hls.off(event, listener);
23340
23418
  }
23341
23419
  toString() {
23342
- var _this$hls4, _this$interstitial;
23343
- return `HlsAssetPlayer: ${eventAssetToString(this.assetItem)} ${(_this$hls4 = this.hls) == null ? void 0 : _this$hls4.sessionId} ${(_this$interstitial = this.interstitial) != null && _this$interstitial.appendInPlace ? 'append-in-place' : ''}`;
23420
+ var _this$hls6, _this$interstitial;
23421
+ return `HlsAssetPlayer: ${eventAssetToString(this.assetItem)} ${(_this$hls6 = this.hls) == null ? void 0 : _this$hls6.sessionId} ${(_this$interstitial = this.interstitial) != null && _this$interstitial.appendInPlace ? 'append-in-place' : ''}`;
23344
23422
  }
23345
23423
  }
23346
23424
 
@@ -23787,6 +23865,9 @@ class InterstitialsSchedule extends Logger {
23787
23865
  const timeBetween = interstitialEvents[i + 1].startTime - interstitialEvents[i].resumeTime;
23788
23866
  if (timeBetween < ABUTTING_THRESHOLD_SECONDS) {
23789
23867
  interstitialEvents[i + 1].appendInPlace = false;
23868
+ if (interstitialEvents[i + 1].appendInPlace) {
23869
+ this.warn(`Could not change append strategy for abutting event ${interstitial}`);
23870
+ }
23790
23871
  }
23791
23872
  }
23792
23873
  // Update cumulativeDuration for next abutting interstitial with the same start date
@@ -23810,19 +23891,18 @@ class InterstitialsSchedule extends Logger {
23810
23891
  const details = mediaSelection[playlistType].details;
23811
23892
  const playlistEnd = details.edge;
23812
23893
  if (resumeTime > playlistEnd) {
23813
- if (playlists.length > 1) {
23814
- this.log(`"${interstitial.identifier}" resumption ${resumeTime} past ${playlistType} playlist end ${playlistEnd}`);
23815
- return true;
23816
- }
23894
+ // Live playback - resumption segments are not yet available
23895
+ this.log(`"${interstitial.identifier}" resumption ${resumeTime} past ${playlistType} playlist end ${playlistEnd}`);
23896
+ // Assume alignment is possible (or reset can take place)
23817
23897
  return false;
23818
23898
  }
23819
23899
  const startFragment = findFragmentByPTS(null, details.fragments, resumeTime);
23820
23900
  if (!startFragment) {
23821
- this.log(`"${interstitial.identifier}" resumption ${resumeTime} does not align with any fragments in ${playlistType} playlist`);
23901
+ this.log(`"${interstitial.identifier}" resumption ${resumeTime} does not align with any fragments in ${playlistType} playlist (${details.fragStart}-${details.fragmentEnd})`);
23822
23902
  return true;
23823
23903
  }
23824
- const endAllowance = playlistType === 'audio' ? 0.175 : 0;
23825
- const alignedWithSegment = Math.abs(startFragment.start - resumeTime) < ALIGNED_END_THRESHOLD_SECONDS || Math.abs(startFragment.end - resumeTime) < ALIGNED_END_THRESHOLD_SECONDS + endAllowance;
23904
+ const allowance = playlistType === 'audio' ? 0.175 : 0;
23905
+ const alignedWithSegment = Math.abs(startFragment.start - resumeTime) < ALIGNED_END_THRESHOLD_SECONDS + allowance || Math.abs(startFragment.end - resumeTime) < ALIGNED_END_THRESHOLD_SECONDS + allowance;
23826
23906
  if (!alignedWithSegment) {
23827
23907
  this.log(`"${interstitial.identifier}" resumption ${resumeTime} not aligned with ${playlistType} fragment bounds (${startFragment.start}-${startFragment.end} sn: ${startFragment.sn} cc: ${startFragment.cc})`);
23828
23908
  return true;
@@ -23872,7 +23952,7 @@ class AssetListLoader {
23872
23952
  // @ts-ignore
23873
23953
  this.hls = null;
23874
23954
  }
23875
- loadAssetList(interstitial, liveStartPosition) {
23955
+ loadAssetList(interstitial, hlsStartOffset) {
23876
23956
  const assetListUrl = interstitial.assetListUrl;
23877
23957
  let url;
23878
23958
  try {
@@ -23882,11 +23962,8 @@ class AssetListLoader {
23882
23962
  this.hls.trigger(Events.ERROR, errorData);
23883
23963
  return;
23884
23964
  }
23885
- if (liveStartPosition && !(interstitial.cue.pre || interstitial.cue.post) && url.protocol !== 'data:') {
23886
- const startOffset = liveStartPosition - interstitial.startTime;
23887
- if (startOffset > 0) {
23888
- url.searchParams.set('_HLS_start_offset', '' + Math.round(startOffset * 1000) / 1000);
23889
- }
23965
+ if (hlsStartOffset && url.protocol !== 'data:') {
23966
+ url.searchParams.set('_HLS_start_offset', '' + hlsStartOffset);
23890
23967
  }
23891
23968
  const config = this.hls.config;
23892
23969
  const Loader = config.loader;
@@ -24400,7 +24477,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
24400
24477
  if (media) {
24401
24478
  const currentTime = timelineType === 'primary' ? media.currentTime : getMappedTime(playingItem, timelineType, c.playingAsset, 'timelinePos', 'currentTime');
24402
24479
  const diff = time - currentTime;
24403
- const seekToTime = media.currentTime + diff;
24480
+ const seekToTime = (appendInPlace ? currentTime : media.currentTime) + diff;
24404
24481
  if (seekToTime >= 0 && (!assetPlayer || appendInPlace || seekToTime <= assetPlayer.duration)) {
24405
24482
  media.currentTime = seekToTime;
24406
24483
  return;
@@ -24550,12 +24627,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
24550
24627
  return getBufferedEnd();
24551
24628
  },
24552
24629
  get currentTime() {
24553
- var _playingItem$event;
24554
24630
  const timelinePos = c.timelinePos;
24555
- const playingItem = c.effectivePlayingItem;
24556
- if (playingItem != null && (_playingItem$event = playingItem.event) != null && _playingItem$event.appendInPlace) {
24557
- return playingItem.start;
24558
- }
24559
24631
  return timelinePos > 0 ? timelinePos : 0;
24560
24632
  },
24561
24633
  set currentTime(time) {
@@ -24724,6 +24796,9 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
24724
24796
  const interstitial = queuedPlayer.interstitial;
24725
24797
  this.clearInterstitial(queuedPlayer.interstitial, null);
24726
24798
  interstitial.appendInPlace = false;
24799
+ if (interstitial.appendInPlace) {
24800
+ this.warn(`Could not change append strategy for queued assets ${interstitial}`);
24801
+ }
24727
24802
  }
24728
24803
  });
24729
24804
  }
@@ -24900,7 +24975,9 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
24900
24975
  }
24901
24976
  // Ensure Interstitial is enqueued
24902
24977
  const waitingItem = this.waitingItem;
24903
- this.setBufferingItem(scheduledItem);
24978
+ if (!this.assetsBuffered(scheduledItem, media)) {
24979
+ this.setBufferingItem(scheduledItem);
24980
+ }
24904
24981
  let player = this.preloadAssets(interstitial, assetListIndex);
24905
24982
  if (!this.eventItemsMatch(scheduledItem, waitingItem || currentItem)) {
24906
24983
  this.waitingItem = scheduledItem;
@@ -25256,6 +25333,16 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25256
25333
  this.bufferedToItem(playingItem);
25257
25334
  }
25258
25335
  }
25336
+ assetsBuffered(item, media) {
25337
+ const assetList = item.event.assetList;
25338
+ if (assetList.length === 0) {
25339
+ return false;
25340
+ }
25341
+ return !item.event.assetList.some(asset => {
25342
+ const player = this.getAssetPlayer(asset.identifier);
25343
+ return !(player != null && player.bufferedInPlaceToEnd(media));
25344
+ });
25345
+ }
25259
25346
  setBufferingItem(item) {
25260
25347
  const bufferingLast = this.bufferingItem;
25261
25348
  const schedule = this.schedule;
@@ -25351,14 +25438,11 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25351
25438
  const neverLoaded = assetListLength === 0 && !interstitial.assetListLoader;
25352
25439
  const playOnce = interstitial.cue.once;
25353
25440
  if (neverLoaded) {
25354
- this.log(`Load interstitial asset ${assetListIndex + 1}/${uri ? 1 : assetListLength} ${interstitial}`);
25355
25441
  const timelineStart = interstitial.timelineStart;
25356
25442
  if (interstitial.appendInPlace) {
25357
25443
  this.flushFrontBuffer(timelineStart + 0.25);
25358
25444
  }
25359
- if (uri) {
25360
- return this.createAsset(interstitial, 0, 0, timelineStart, interstitial.duration, uri);
25361
- }
25445
+ let hlsStartOffset;
25362
25446
  let liveStartPosition = 0;
25363
25447
  if (!this.playingItem && this.primaryLive) {
25364
25448
  liveStartPosition = this.hls.startPosition;
@@ -25366,7 +25450,17 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25366
25450
  liveStartPosition = this.hls.liveSyncPosition || 0;
25367
25451
  }
25368
25452
  }
25369
- const assetListLoader = this.assetListLoader.loadAssetList(interstitial, liveStartPosition);
25453
+ if (liveStartPosition && !(interstitial.cue.pre || interstitial.cue.post)) {
25454
+ const startOffset = liveStartPosition - timelineStart;
25455
+ if (startOffset > 0) {
25456
+ hlsStartOffset = Math.round(startOffset * 1000) / 1000;
25457
+ }
25458
+ }
25459
+ this.log(`Load interstitial asset ${assetListIndex + 1}/${uri ? 1 : assetListLength} ${interstitial}${hlsStartOffset ? ` live-start: ${liveStartPosition} start-offset: ${hlsStartOffset}` : ''}`);
25460
+ if (uri) {
25461
+ return this.createAsset(interstitial, 0, 0, timelineStart, interstitial.duration, uri);
25462
+ }
25463
+ const assetListLoader = this.assetListLoader.loadAssetList(interstitial, hlsStartOffset);
25370
25464
  if (assetListLoader) {
25371
25465
  interstitial.assetListLoader = assetListLoader;
25372
25466
  }
@@ -25456,7 +25550,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25456
25550
  const selectedAudio = primary.audioTracks[primary.audioTrack];
25457
25551
  const selectedSubtitle = primary.subtitleTracks[primary.subtitleTrack];
25458
25552
  let startPosition = 0;
25459
- if (this.primaryLive) {
25553
+ if (this.primaryLive || interstitial.appendInPlace) {
25460
25554
  const timePastStart = this.timelinePos - assetItem.timelineStart;
25461
25555
  if (timePastStart > 1) {
25462
25556
  const duration = assetItem.duration;
@@ -25651,9 +25745,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25651
25745
  player
25652
25746
  });
25653
25747
  }
25654
-
25655
- // detach media and attach to interstitial player if it does not have another element attached
25656
- this.bufferAssetPlayer(player, media);
25748
+ if (!player.bufferedInPlaceToEnd(media)) {
25749
+ // detach media and attach to interstitial player if it does not have another element attached
25750
+ this.bufferAssetPlayer(player, media);
25751
+ }
25657
25752
  }
25658
25753
  bufferAssetPlayer(player, media) {
25659
25754
  var _this$schedule$items4, _this$detachedData5;
@@ -25781,6 +25876,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25781
25876
  return;
25782
25877
  }
25783
25878
  const eventStart = interstitial.timelineStart;
25879
+ const previousDuration = interstitial.duration;
25784
25880
  let sumDuration = 0;
25785
25881
  assets.forEach((asset, assetListIndex) => {
25786
25882
  const duration = parseFloat(asset.DURATION);
@@ -25788,6 +25884,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25788
25884
  sumDuration += duration;
25789
25885
  });
25790
25886
  interstitial.duration = sumDuration;
25887
+ this.log(`Loaded asset-list with duration: ${sumDuration} (was: ${previousDuration}) ${interstitial}`);
25791
25888
  const waitingItem = this.waitingItem;
25792
25889
  const waitingForItem = (waitingItem == null ? void 0 : waitingItem.event.identifier) === interstitialId;
25793
25890
 
@@ -25802,6 +25899,15 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))}`);
25802
25899
  const scheduleIndex = this.schedule.findEventIndex(interstitialId);
25803
25900
  const item = (_this$schedule$items5 = this.schedule.items) == null ? void 0 : _this$schedule$items5[scheduleIndex];
25804
25901
  if (item) {
25902
+ if (!this.playingItem && this.timelinePos > item.end) {
25903
+ // Abandon if new duration is reduced enough to land playback in primary start
25904
+ const index = this.schedule.findItemIndexAtTime(this.timelinePos);
25905
+ if (index !== scheduleIndex) {
25906
+ interstitial.error = new Error(`Interstitial no longer within playback range ${this.timelinePos} ${interstitial}`);
25907
+ this.primaryFallback(interstitial);
25908
+ return;
25909
+ }
25910
+ }
25805
25911
  this.setBufferingItem(item);
25806
25912
  }
25807
25913
  this.setSchedulePosition(scheduleIndex);
@@ -25869,11 +25975,12 @@ class SubtitleStreamController extends BaseStreamController {
25869
25975
  hls.off(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this);
25870
25976
  hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
25871
25977
  }
25872
- startLoad(startPosition) {
25978
+ startLoad(startPosition, skipSeekToStartPosition) {
25873
25979
  this.stopLoad();
25874
25980
  this.state = State.IDLE;
25875
25981
  this.setInterval(TICK_INTERVAL$2);
25876
- this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;
25982
+ this.nextLoadPosition = this.lastCurrentTime = startPosition + this.timelineOffset;
25983
+ this.startPosition = skipSeekToStartPosition ? -1 : startPosition;
25877
25984
  this.tick();
25878
25985
  }
25879
25986
  onManifestLoading() {
@@ -32016,7 +32123,7 @@ class StreamController extends BaseStreamController {
32016
32123
  startPosition = lastCurrentTime;
32017
32124
  }
32018
32125
  this.state = State.IDLE;
32019
- this.nextLoadPosition = this.lastCurrentTime = startPosition;
32126
+ this.nextLoadPosition = this.lastCurrentTime = startPosition + this.timelineOffset;
32020
32127
  this.startPosition = skipSeekToStartPosition ? -1 : startPosition;
32021
32128
  this.tick();
32022
32129
  } else {
@@ -32092,7 +32199,7 @@ class StreamController extends BaseStreamController {
32092
32199
  // if start level not parsed yet OR
32093
32200
  // if video not attached AND start fragment already requested OR start frag prefetch not enabled
32094
32201
  // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment
32095
- if (levelLastLoaded === null || !media && (this.startFragRequested || !hls.config.startFragPrefetch)) {
32202
+ if (levelLastLoaded === null || !media && !this.primaryPrefetch && (this.startFragRequested || !hls.config.startFragPrefetch)) {
32096
32203
  return;
32097
32204
  }
32098
32205
 
@@ -32722,11 +32829,11 @@ class StreamController extends BaseStreamController {
32722
32829
  }
32723
32830
 
32724
32831
  // Offset start position by timeline offset
32725
- const details = this.getLevelDetails();
32726
- const configuredTimelineOffset = this.config.timelineOffset;
32727
- if (configuredTimelineOffset && startPosition) {
32728
- startPosition += (details == null ? void 0 : details.appliedTimelineOffset) || configuredTimelineOffset;
32832
+ const timelineOffset = this.timelineOffset;
32833
+ if (timelineOffset && startPosition) {
32834
+ startPosition += timelineOffset;
32729
32835
  }
32836
+ const details = this.getLevelDetails();
32730
32837
  const buffered = BufferHelper.getBuffered(media);
32731
32838
  const bufferStart = buffered.length ? buffered.start(0) : 0;
32732
32839
  const delta = bufferStart - startPosition;