hls.js 1.6.0-beta.4.0.canary.11044 → 1.6.0-beta.4.0.canary.11045

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.
@@ -295,6 +295,7 @@ export default class BaseStreamController
295
295
  this.media = this.mediaBuffer = null;
296
296
  this.loopSn = undefined;
297
297
  if (transferringMedia) {
298
+ this.resetLoadingState();
298
299
  this.resetTransmuxer();
299
300
  return;
300
301
  }
@@ -448,67 +449,16 @@ export default class BaseStreamController
448
449
  }
449
450
 
450
451
  protected loadFragment(
451
- frag: Fragment,
452
+ frag: MediaFragment,
452
453
  level: Level,
453
454
  targetBufferTime: number,
454
455
  ) {
455
- const config = this.hls.config;
456
- if (
457
- __USE_INTERSTITIALS__ &&
458
- config.interstitialsController &&
459
- config.enableInterstitialPlayback !== false &&
460
- frag.type !== PlaylistLevelType.SUBTITLE
461
- ) {
462
- // Do not load fragments outside the buffering schedule segment
463
- const interstitials = this.hls.interstitialsManager;
464
- const bufferingItem = interstitials?.bufferingItem;
465
- if (bufferingItem) {
466
- const bufferingInterstitial = bufferingItem.event;
467
- if (bufferingInterstitial) {
468
- // Do not stream fragments while buffering Interstitial Events (except for overlap at the start)
469
- if (
470
- bufferingInterstitial.appendInPlace ||
471
- Math.abs(frag.start - bufferingItem.start) > 1 ||
472
- bufferingItem.start === 0
473
- ) {
474
- return;
475
- }
476
- } else {
477
- // Limit fragment loading to media in schedule item
478
- if (
479
- frag.end <= bufferingItem.start &&
480
- level.details?.live === false
481
- ) {
482
- // fragment ends by schedule item start
483
- return;
484
- }
485
- if (frag.start > bufferingItem.end && bufferingItem.nextEvent) {
486
- // fragment is past schedule item end
487
- return;
488
- }
489
- }
490
- }
491
- // Skip loading of fragments that overlap completely with appendInPlace interstitials
492
- const playerQueue = interstitials?.playerQueue;
493
- if (playerQueue) {
494
- for (let i = playerQueue.length; i--; ) {
495
- const interstitial = playerQueue[i].interstitial;
496
- if (
497
- interstitial.appendInPlace &&
498
- frag.start >= interstitial.startTime &&
499
- frag.end <= interstitial.resumeTime
500
- ) {
501
- return;
502
- }
503
- }
504
- }
505
- }
506
456
  this.startFragRequested = true;
507
457
  this._loadFragForPlayback(frag, level, targetBufferTime);
508
458
  }
509
459
 
510
460
  private _loadFragForPlayback(
511
- fragment: Fragment,
461
+ fragment: MediaFragment,
512
462
  level: Level,
513
463
  targetBufferTime: number,
514
464
  ) {
@@ -1352,6 +1302,7 @@ export default class BaseStreamController
1352
1302
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
1353
1303
  }
1354
1304
 
1305
+ frag = this.filterReplacedPrimary(frag, levelDetails);
1355
1306
  return this.mapToInitFragWhenRequired(frag);
1356
1307
  }
1357
1308
 
@@ -1402,6 +1353,65 @@ export default class BaseStreamController
1402
1353
  return nextFragment;
1403
1354
  }
1404
1355
 
1356
+ filterReplacedPrimary(
1357
+ frag: MediaFragment | null,
1358
+ details: LevelDetails | undefined,
1359
+ ): MediaFragment | null {
1360
+ if (!frag) {
1361
+ return frag;
1362
+ }
1363
+ const config = this.hls.config;
1364
+ if (
1365
+ __USE_INTERSTITIALS__ &&
1366
+ config.interstitialsController &&
1367
+ config.enableInterstitialPlayback !== false &&
1368
+ frag.type !== PlaylistLevelType.SUBTITLE
1369
+ ) {
1370
+ // Do not load fragments outside the buffering schedule segment
1371
+ const interstitials = this.hls.interstitialsManager;
1372
+ const bufferingItem = interstitials?.bufferingItem;
1373
+ if (bufferingItem) {
1374
+ const bufferingInterstitial = bufferingItem.event;
1375
+ if (bufferingInterstitial) {
1376
+ // Do not stream fragments while buffering Interstitial Events (except for overlap at the start)
1377
+ if (
1378
+ bufferingInterstitial.appendInPlace ||
1379
+ Math.abs(frag.start - bufferingItem.start) > 1 ||
1380
+ bufferingItem.start === 0
1381
+ ) {
1382
+ return null;
1383
+ }
1384
+ } else {
1385
+ // Limit fragment loading to media in schedule item
1386
+ if (frag.end <= bufferingItem.start && details?.live === false) {
1387
+ // fragment ends by schedule item start
1388
+ // this.fragmentTracker.fragBuffered(frag, true);
1389
+ return null;
1390
+ }
1391
+ if (frag.start > bufferingItem.end && bufferingItem.nextEvent) {
1392
+ // fragment is past schedule item end
1393
+ return null;
1394
+ }
1395
+ }
1396
+ }
1397
+ // Skip loading of fragments that overlap completely with appendInPlace interstitials
1398
+ const playerQueue = interstitials?.playerQueue;
1399
+ if (playerQueue) {
1400
+ for (let i = playerQueue.length; i--; ) {
1401
+ const interstitial = playerQueue[i].interstitial;
1402
+ if (
1403
+ interstitial.appendInPlace &&
1404
+ frag.start >= interstitial.startTime &&
1405
+ frag.end <= interstitial.resumeTime
1406
+ ) {
1407
+ return null;
1408
+ }
1409
+ }
1410
+ }
1411
+ }
1412
+ return frag;
1413
+ }
1414
+
1405
1415
  mapToInitFragWhenRequired(frag: Fragment | null): typeof frag {
1406
1416
  // If an initSegment is present, it must be buffered first
1407
1417
  if (frag?.initSegment && !frag?.initSegment.data && !this.bitrateTest) {
@@ -1486,6 +1486,8 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
1486
1486
  if (!media || !mediaSource) {
1487
1487
  return;
1488
1488
  }
1489
+ // once received, don't listen anymore to sourceopen event
1490
+ mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen);
1489
1491
  media.removeEventListener('emptied', this._onMediaEmptied);
1490
1492
  this.updateDuration();
1491
1493
  this.hls.trigger(Events.MEDIA_ATTACHED, {
@@ -1493,9 +1495,6 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
1493
1495
  mediaSource: mediaSource as MediaSource,
1494
1496
  });
1495
1497
 
1496
- // once received, don't listen anymore to sourceopen event
1497
- mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen);
1498
-
1499
1498
  if (this.mediaSource !== null) {
1500
1499
  this.checkPendingTracks();
1501
1500
  }
@@ -27,6 +27,7 @@ export class HlsAssetPlayer {
27
27
  public tracks: Partial<BufferCodecsData> | null = null;
28
28
  private hasDetails: boolean = false;
29
29
  private mediaAttached: HTMLMediaElement | null = null;
30
+ private _currentTime?: number;
30
31
 
31
32
  constructor(
32
33
  HlsPlayerClass: typeof Hls,
@@ -89,7 +90,7 @@ export class HlsAssetPlayer {
89
90
  get bufferedEnd(): number {
90
91
  const media = this.media || this.mediaAttached;
91
92
  if (!media) {
92
- return 0;
93
+ return this.currentTime;
93
94
  }
94
95
  const bufferInfo = BufferHelper.bufferInfo(media, media.currentTime, 0.001);
95
96
  return this.getAssetTime(bufferInfo.end);
@@ -98,7 +99,7 @@ export class HlsAssetPlayer {
98
99
  get currentTime(): number {
99
100
  const media = this.media || this.mediaAttached;
100
101
  if (!media) {
101
- return 0;
102
+ return this._currentTime || 0;
102
103
  }
103
104
  return this.getAssetTime(media.currentTime);
104
105
  }
@@ -151,6 +152,7 @@ export class HlsAssetPlayer {
151
152
  private removeMediaListeners() {
152
153
  const media = this.mediaAttached;
153
154
  if (media) {
155
+ this._currentTime = media.currentTime;
154
156
  media.removeEventListener('timeupdate', this.checkPlayout);
155
157
  }
156
158
  }
@@ -170,6 +172,7 @@ export class HlsAssetPlayer {
170
172
 
171
173
  detachMedia() {
172
174
  this.removeMediaListeners();
175
+ this.mediaAttached = null;
173
176
  this.hls.detachMedia();
174
177
  }
175
178
 
@@ -210,6 +213,6 @@ export class HlsAssetPlayer {
210
213
  }
211
214
 
212
215
  toString(): string {
213
- return `HlsAssetPlayer: ${eventAssetToString(this.assetItem)} ${this.hls.sessionId} ${this.interstitial.appendInPlace ? 'append-in-place' : ''}`;
216
+ return `HlsAssetPlayer: ${eventAssetToString(this.assetItem)} ${this.hls?.sessionId} ${this.interstitial?.appendInPlace ? 'append-in-place' : ''}`;
214
217
  }
215
218
  }
@@ -195,11 +195,11 @@ export default class InterstitialsController
195
195
  }
196
196
 
197
197
  resumeBuffering() {
198
- this.playerQueue.forEach((player) => player.resumeBuffering());
198
+ this.getBufferingPlayer()?.resumeBuffering();
199
199
  }
200
200
 
201
201
  pauseBuffering() {
202
- this.playerQueue.forEach((player) => player.pauseBuffering());
202
+ this.getBufferingPlayer()?.pauseBuffering();
203
203
  }
204
204
 
205
205
  destroy() {
@@ -760,6 +760,9 @@ export default class InterstitialsController
760
760
  player: Hls | HlsAssetPlayer,
761
761
  media: HTMLMediaElement,
762
762
  ) {
763
+ if (player.media === media) {
764
+ return;
765
+ }
763
766
  let attachMediaSourceData: MediaAttachingData | null = null;
764
767
  const primaryPlayer = this.hls;
765
768
  const isAssetPlayer = player !== primaryPlayer;
@@ -768,32 +771,44 @@ export default class InterstitialsController
768
771
  const detachedMediaSource = this.detachedData?.mediaSource;
769
772
 
770
773
  let logFromSource: string;
771
- if (primaryPlayer.media && appendInPlace) {
772
- attachMediaSourceData = primaryPlayer.transferMedia();
773
- this.detachedData = attachMediaSourceData;
774
+ if (primaryPlayer.media) {
775
+ if (appendInPlace) {
776
+ attachMediaSourceData = primaryPlayer.transferMedia();
777
+ this.detachedData = attachMediaSourceData;
778
+ }
774
779
  logFromSource = `Primary`;
775
780
  } else if (detachedMediaSource) {
776
781
  const bufferingPlayer = this.getBufferingPlayer();
777
782
  if (bufferingPlayer) {
778
783
  attachMediaSourceData = bufferingPlayer.transferMedia();
784
+ logFromSource = `${bufferingPlayer}`;
785
+ } else {
786
+ logFromSource = `detached MediaSource`;
779
787
  }
780
- logFromSource = `${bufferingPlayer}`;
781
788
  } else {
782
- logFromSource = `<unknown>`;
789
+ logFromSource = `detached media`;
783
790
  }
784
- this.log(
785
- `transferring to ${isAssetPlayer ? player : 'Primary'}
786
- MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
787
- );
788
-
789
791
  if (!attachMediaSourceData) {
790
792
  if (detachedMediaSource) {
791
793
  attachMediaSourceData = this.detachedData;
792
794
  this.log(
793
795
  `using detachedData: MediaSource ${stringify(attachMediaSourceData)}`,
794
796
  );
795
- } else if (!this.detachedData || this.hls.media === media) {
796
- // Media is attaching when `detachedData` and `hls.media` are populated. Detach to clear the MediaSource.
797
+ } else if (!this.detachedData || primaryPlayer.media === media) {
798
+ // Keep interstitial media transition consistent
799
+ const playerQueue = this.playerQueue;
800
+ if (playerQueue.length > 1) {
801
+ playerQueue.forEach((queuedPlayer) => {
802
+ if (
803
+ isAssetPlayer &&
804
+ queuedPlayer.interstitial.appendInPlace !== appendInPlace
805
+ ) {
806
+ const interstitial = queuedPlayer.interstitial;
807
+ this.clearInterstitial(queuedPlayer.interstitial, null);
808
+ interstitial.appendInPlace = false;
809
+ }
810
+ });
811
+ }
797
812
  this.hls.detachMedia();
798
813
  this.detachedData = { media };
799
814
  }
@@ -807,7 +822,7 @@ MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
807
822
  this.log(
808
823
  `${transferring ? 'transfering MediaSource' : 'attaching media'} to ${
809
824
  isAssetPlayer ? player : 'Primary'
810
- }`,
825
+ } from ${logFromSource}`,
811
826
  );
812
827
  if (dataToAttach === attachMediaSourceData) {
813
828
  const isAssetAtEndOfSchedule =
@@ -1068,7 +1083,7 @@ MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
1068
1083
  player,
1069
1084
  });
1070
1085
  this.retreiveMediaSource(assetId, scheduledItem);
1071
- if (player.media && !this.detachedData) {
1086
+ if (player.media && !this.detachedData?.mediaSource) {
1072
1087
  player.detachMedia();
1073
1088
  }
1074
1089
  }
@@ -1414,7 +1429,10 @@ MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
1414
1429
  main,
1415
1430
  };
1416
1431
  this.mediaSelection = currentSelection;
1417
- this.schedule.parseInterstitialDateRanges(currentSelection);
1432
+ this.schedule.parseInterstitialDateRanges(
1433
+ currentSelection,
1434
+ this.hls.config.interstitialAppendInPlace,
1435
+ );
1418
1436
 
1419
1437
  if (!this.effectivePlayingItem && this.schedule.items) {
1420
1438
  this.checkStart();
@@ -1532,6 +1550,8 @@ MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
1532
1550
  if (!this.playingLastItem && playingItem) {
1533
1551
  const playingIndex = this.findItemIndex(playingItem);
1534
1552
  this.setSchedulePosition(playingIndex + 1);
1553
+ } else {
1554
+ this.shouldPlay = false;
1535
1555
  }
1536
1556
  }
1537
1557
 
@@ -1552,9 +1572,6 @@ MediaSource ${stringify(attachMediaSourceData)} from ${logFromSource}`,
1552
1572
  interstitialEvents.length || removedIds.length
1553
1573
  );
1554
1574
  if (interstitialsUpdated) {
1555
- if (this.hls.config.interstitialAppendInPlace === false) {
1556
- interstitialEvents.forEach((event) => (event.appendInPlace = false));
1557
- }
1558
1575
  this.log(
1559
1576
  `INTERSTITIALS_UPDATED (${
1560
1577
  interstitialEvents.length
@@ -1888,18 +1905,18 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
1888
1905
  interstitial: InterstitialEvent,
1889
1906
  assetListIndex: number,
1890
1907
  ): HlsAssetPlayer | null {
1908
+ const uri = interstitial.assetUrl;
1891
1909
  const assetListLength = interstitial.assetList.length;
1892
1910
  const neverLoaded = assetListLength === 0 && !interstitial.assetListLoader;
1893
1911
  const playOnce = interstitial.cue.once;
1894
1912
  if (neverLoaded) {
1895
1913
  this.log(
1896
- `Load interstitial asset ${assetListIndex + 1}/${assetListLength} ${interstitial}`,
1914
+ `Load interstitial asset ${assetListIndex + 1}/${uri ? 1 : assetListLength} ${interstitial}`,
1897
1915
  );
1898
1916
  const timelineStart = interstitial.timelineStart;
1899
1917
  if (interstitial.appendInPlace) {
1900
1918
  this.flushFrontBuffer(timelineStart + 0.25);
1901
1919
  }
1902
- const uri = interstitial.assetUrl;
1903
1920
  if (uri) {
1904
1921
  return this.createAsset(
1905
1922
  interstitial,
@@ -2288,9 +2305,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
2288
2305
  }
2289
2306
 
2290
2307
  // detach media and attach to interstitial player if it does not have another element attached
2291
- if (!player.media) {
2292
- this.bufferAssetPlayer(player, media);
2293
- }
2308
+ this.bufferAssetPlayer(player, media);
2294
2309
  }
2295
2310
 
2296
2311
  private bufferAssetPlayer(player: HlsAssetPlayer, media: HTMLMediaElement) {
@@ -2306,11 +2321,19 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
2306
2321
  if (bufferingPlayer === player) {
2307
2322
  return;
2308
2323
  }
2324
+ const appendInPlaceNext = interstitial.appendInPlace;
2325
+ if (
2326
+ appendInPlaceNext &&
2327
+ bufferingPlayer?.interstitial.appendInPlace === false
2328
+ ) {
2329
+ // Media is detached and not available to append in place
2330
+ return;
2331
+ }
2309
2332
  const activeTracks =
2310
2333
  bufferingPlayer?.tracks ||
2311
2334
  this.detachedData?.tracks ||
2312
2335
  this.requiredTracks;
2313
- if (interstitial.appendInPlace && assetItem !== this.playingAsset) {
2336
+ if (appendInPlaceNext && assetItem !== this.playingAsset) {
2314
2337
  // Do not buffer another item if tracks are unknown or incompatible
2315
2338
  if (!player.tracks) {
2316
2339
  return;
@@ -2411,7 +2434,6 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
2411
2434
  } error: ${interstitial.error}`,
2412
2435
  );
2413
2436
  if (interstitial.appendInPlace) {
2414
- interstitial.appendInPlace = false;
2415
2437
  this.attachPrimary(flushStart, null);
2416
2438
  this.flushFrontBuffer(flushStart);
2417
2439
  }
@@ -2423,6 +2445,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
2423
2445
  if (!this.itemsMatch(playingItem, newPlayingItem)) {
2424
2446
  const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos);
2425
2447
  this.setSchedulePosition(scheduleIndex);
2448
+ } else {
2449
+ this.clearInterstitial(interstitial, null);
2426
2450
  }
2427
2451
  } else {
2428
2452
  this.checkStart();
@@ -239,13 +239,20 @@ export class InterstitialsSchedule extends Logger {
239
239
  return null;
240
240
  }
241
241
 
242
- public parseInterstitialDateRanges(mediaSelection: MediaSelection) {
242
+ public parseInterstitialDateRanges(
243
+ mediaSelection: MediaSelection,
244
+ enableAppendInPlace: boolean,
245
+ ) {
243
246
  const details = mediaSelection.main.details!;
244
247
  const { dateRanges } = details;
245
248
  const previousInterstitialEvents = this.events;
246
- const interstitialEvents = this.parseDateRanges(dateRanges, {
247
- url: details.url,
248
- });
249
+ const interstitialEvents = this.parseDateRanges(
250
+ dateRanges,
251
+ {
252
+ url: details.url,
253
+ },
254
+ enableAppendInPlace,
255
+ );
249
256
  const ids = Object.keys(dateRanges);
250
257
  const removedInterstitials = previousInterstitialEvents
251
258
  ? previousInterstitialEvents.filter(
@@ -321,6 +328,7 @@ export class InterstitialsSchedule extends Logger {
321
328
  private parseDateRanges(
322
329
  dateRanges: Record<string, DateRange>,
323
330
  baseData: BaseData,
331
+ enableAppendInPlace: boolean,
324
332
  ): InterstitialEvent[] {
325
333
  const interstitialEvents: InterstitialEvent[] = [];
326
334
  const ids = Object.keys(dateRanges);
@@ -336,6 +344,9 @@ export class InterstitialsSchedule extends Logger {
336
344
  } else {
337
345
  interstitial = new InterstitialEvent(dateRange, baseData);
338
346
  this.eventMap[id] = interstitial;
347
+ if (enableAppendInPlace === false) {
348
+ interstitial.appendInPlace = enableAppendInPlace;
349
+ }
339
350
  }
340
351
  interstitialEvents.push(interstitial);
341
352
  }
@@ -570,15 +581,13 @@ export class InterstitialsSchedule extends Logger {
570
581
  interstitial.appendInPlace = false;
571
582
  }
572
583
  }
573
- if (!interstitial.appendInPlace) {
584
+ if (!interstitial.appendInPlace && i + 1 < interstitialEvents.length) {
574
585
  // abutting Interstitials must use the same MediaSource strategy, this applies to all whether or not they are back to back:
575
- for (let j = i - 1; i--; ) {
576
- const timeBetween =
577
- interstitialEvents[j + 1].startTime -
578
- interstitialEvents[j].resumeTime;
579
- if (timeBetween < ABUTTING_THRESHOLD_SECONDS) {
580
- interstitialEvents[j].appendInPlace = false;
581
- }
586
+ const timeBetween =
587
+ interstitialEvents[i + 1].startTime -
588
+ interstitialEvents[i].resumeTime;
589
+ if (timeBetween < ABUTTING_THRESHOLD_SECONDS) {
590
+ interstitialEvents[i + 1].appendInPlace = false;
582
591
  }
583
592
  }
584
593
  // Update cumulativeDuration for next abutting interstitial with the same start date
@@ -447,7 +447,7 @@ export class SubtitleStreamController
447
447
  const fragLen = fragments.length;
448
448
  const end = trackDetails.edge;
449
449
 
450
- let foundFrag: Fragment | null = null;
450
+ let foundFrag: MediaFragment | null = null;
451
451
  const fragPrevious = this.fragPrevious;
452
452
  if (targetBufferTime < end) {
453
453
  const tolerance = config.maxFragLookUpTolerance;
@@ -469,27 +469,28 @@ export class SubtitleStreamController
469
469
  } else {
470
470
  foundFrag = fragments[fragLen - 1];
471
471
  }
472
+ foundFrag = this.filterReplacedPrimary(foundFrag, track.details);
472
473
  if (!foundFrag) {
473
474
  return;
474
475
  }
475
- foundFrag = this.mapToInitFragWhenRequired(foundFrag) as Fragment;
476
- if (isMediaFragment(foundFrag)) {
477
- // Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment
478
- const curSNIdx = foundFrag.sn - trackDetails.startSN;
479
- const prevFrag = fragments[curSNIdx - 1];
480
- if (
481
- prevFrag &&
482
- prevFrag.cc === foundFrag.cc &&
483
- this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED
484
- ) {
485
- foundFrag = prevFrag;
486
- }
476
+ // Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment
477
+ const curSNIdx = foundFrag.sn - trackDetails.startSN;
478
+ const prevFrag = fragments[curSNIdx - 1];
479
+ if (
480
+ prevFrag &&
481
+ prevFrag.cc === foundFrag.cc &&
482
+ this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED
483
+ ) {
484
+ foundFrag = prevFrag;
487
485
  }
488
486
  if (
489
487
  this.fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED
490
488
  ) {
491
489
  // only load if fragment is not loaded
492
- this.loadFragment(foundFrag, track, targetBufferTime);
490
+ const fragToLoad = this.mapToInitFragWhenRequired(foundFrag);
491
+ if (fragToLoad) {
492
+ this.loadFragment(fragToLoad, track, targetBufferTime);
493
+ }
493
494
  }
494
495
  }
495
496
  }
@@ -536,14 +536,11 @@ export function findPart(
536
536
 
537
537
  export function reassignFragmentLevelIndexes(levels: Level[]) {
538
538
  levels.forEach((level, index) => {
539
- const fragments = level.details?.fragments;
540
- if (fragments) {
541
- fragments.forEach((fragment) => {
542
- fragment.level = index;
543
- if (fragment.initSegment) {
544
- fragment.initSegment.level = index;
545
- }
546
- });
547
- }
539
+ level.details?.fragments.forEach((fragment) => {
540
+ fragment.level = index;
541
+ if (fragment.initSegment) {
542
+ fragment.initSegment.level = index;
543
+ }
544
+ });
548
545
  });
549
546
  }