hls.js 1.6.0-rc.1.0.canary.11073 → 1.6.0-rc.1.0.canary.11074

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.11073"}`);
405
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.0-rc.1.0.canary.11074"}`);
406
406
  } catch (e) {
407
407
  /* log fn threw an exception. All logger methods are no-ops. */
408
408
  return createLogger();
@@ -6915,7 +6915,7 @@ const LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*
6915
6915
  // segment URI, group 3 => the URI (note newline is not eaten)
6916
6916
  /#.*/.source // All other non-segment oriented tags will match with all groups empty
6917
6917
  ].join('|'), 'g');
6918
- const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#(EXTM3U)/.source, /#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\r?\n?/.source].join('|'));
6918
+ const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\r?\n?/.source].join('|'));
6919
6919
  class M3U8Parser {
6920
6920
  static findGroup(groups, mediaGroupId) {
6921
6921
  for (let i = 0; i < groups.length; i++) {
@@ -7110,6 +7110,7 @@ class M3U8Parser {
7110
7110
  return results;
7111
7111
  }
7112
7112
  static parseLevelPlaylist(string, baseurl, id, type, levelUrlId, multivariantVariableList) {
7113
+ var _LEVEL_PLAYLIST_REGEX;
7113
7114
  const base = {
7114
7115
  url: baseurl
7115
7116
  };
@@ -7131,9 +7132,14 @@ class M3U8Parser {
7131
7132
  let firstPdtIndex = -1;
7132
7133
  let createNextFrag = false;
7133
7134
  let nextByteRange = null;
7135
+ let serverControlAttrs;
7134
7136
  LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0;
7135
7137
  level.m3u8 = string;
7136
7138
  level.hasVariableRefs = hasVariableReferences(string) ;
7139
+ if (((_LEVEL_PLAYLIST_REGEX = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) == null ? void 0 : _LEVEL_PLAYLIST_REGEX[0]) !== '#EXTM3U') {
7140
+ level.playlistParsingError = new Error('Missing format identifier #EXTM3U');
7141
+ return level;
7142
+ }
7137
7143
  while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) {
7138
7144
  if (createNextFrag) {
7139
7145
  createNextFrag = false;
@@ -7222,15 +7228,23 @@ class M3U8Parser {
7222
7228
  }
7223
7229
  break;
7224
7230
  case 'PLAYLIST-TYPE':
7231
+ if (level.type) {
7232
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7233
+ }
7225
7234
  level.type = value1.toUpperCase();
7226
7235
  break;
7227
7236
  case 'MEDIA-SEQUENCE':
7237
+ if (level.startSN !== 0) {
7238
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7239
+ } else if (fragments.length > 0) {
7240
+ assignMustAppearBeforeSegmentsError(level, tag, result);
7241
+ }
7228
7242
  currentSN = level.startSN = parseInt(value1);
7229
7243
  break;
7230
7244
  case 'SKIP':
7231
7245
  {
7232
7246
  if (level.skippedSegments) {
7233
- level.playlistParsingError = new Error(`#EXT-X-SKIP MUST NOT appear more than once in a Playlist`);
7247
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7234
7248
  }
7235
7249
  const skipAttrs = new AttrList(value1, level);
7236
7250
  const skippedSegments = skipAttrs.decimalInteger('SKIPPED-SEGMENTS');
@@ -7249,15 +7263,23 @@ class M3U8Parser {
7249
7263
  break;
7250
7264
  }
7251
7265
  case 'TARGETDURATION':
7266
+ if (level.targetduration !== 0) {
7267
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7268
+ }
7252
7269
  level.targetduration = Math.max(parseInt(value1), 1);
7253
7270
  break;
7254
7271
  case 'VERSION':
7272
+ if (level.version !== null) {
7273
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7274
+ }
7255
7275
  level.version = parseInt(value1);
7256
7276
  break;
7257
7277
  case 'INDEPENDENT-SEGMENTS':
7258
- case 'EXTM3U':
7259
7278
  break;
7260
7279
  case 'ENDLIST':
7280
+ if (!level.live) {
7281
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7282
+ }
7261
7283
  level.live = false;
7262
7284
  break;
7263
7285
  case '#':
@@ -7309,6 +7331,11 @@ class M3U8Parser {
7309
7331
  break;
7310
7332
  }
7311
7333
  case 'DISCONTINUITY-SEQUENCE':
7334
+ if (level.startCC !== 0) {
7335
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7336
+ } else if (fragments.length > 0) {
7337
+ assignMustAppearBeforeSegmentsError(level, tag, result);
7338
+ }
7312
7339
  level.startCC = discontinuityCounter = parseInt(value1);
7313
7340
  break;
7314
7341
  case 'KEY':
@@ -7367,7 +7394,10 @@ class M3U8Parser {
7367
7394
  }
7368
7395
  case 'SERVER-CONTROL':
7369
7396
  {
7370
- const serverControlAttrs = new AttrList(value1);
7397
+ if (serverControlAttrs) {
7398
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7399
+ }
7400
+ serverControlAttrs = new AttrList(value1);
7371
7401
  level.canBlockReload = serverControlAttrs.bool('CAN-BLOCK-RELOAD');
7372
7402
  level.canSkipUntil = serverControlAttrs.optionalFloat('CAN-SKIP-UNTIL', 0);
7373
7403
  level.canSkipDateRanges = level.canSkipUntil > 0 && serverControlAttrs.bool('CAN-SKIP-DATERANGES');
@@ -7377,6 +7407,9 @@ class M3U8Parser {
7377
7407
  }
7378
7408
  case 'PART-INF':
7379
7409
  {
7410
+ if (level.partTarget) {
7411
+ assignMultipleMediaPlaylistTagOccuranceError(level, tag, result);
7412
+ }
7380
7413
  const partInfAttrs = new AttrList(value1);
7381
7414
  level.partTarget = partInfAttrs.decimalFloatingPoint('PART-TARGET');
7382
7415
  break;
@@ -7428,6 +7461,9 @@ class M3U8Parser {
7428
7461
  setFragLevelKeys(frag, levelkeys, level);
7429
7462
  }
7430
7463
  }
7464
+ if (!level.targetduration) {
7465
+ level.playlistParsingError = new Error(`#EXT-X-TARGETDURATION is required`);
7466
+ }
7431
7467
  const fragmentLength = fragments.length;
7432
7468
  const firstFragment = fragments[0];
7433
7469
  const lastFragment = fragments[fragmentLength - 1];
@@ -7605,6 +7641,12 @@ function setFragLevelKeys(frag, levelkeys, level) {
7605
7641
  encryptedFragments.push(frag);
7606
7642
  }
7607
7643
  }
7644
+ function assignMultipleMediaPlaylistTagOccuranceError(level, tag, result) {
7645
+ level.playlistParsingError = new Error(`#EXT-X-${tag} must not appear more than once (${result[0]})`);
7646
+ }
7647
+ function assignMustAppearBeforeSegmentsError(level, tag, result) {
7648
+ level.playlistParsingError = new Error(`#EXT-X-${tag} must appear before the first Media Segment (${result[0]})`);
7649
+ }
7608
7650
 
7609
7651
  function updateFromToPTS(fragFrom, fragTo) {
7610
7652
  const fragToPTS = fragTo.startPTS;
@@ -7892,9 +7934,24 @@ function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) {
7892
7934
  }
7893
7935
  if (oldFrag && newFrag) {
7894
7936
  intersectionFn(oldFrag, newFrag, i, newFrags);
7937
+ if (oldFrag.url && oldFrag.url !== newFrag.url) {
7938
+ newDetails.playlistParsingError = getSequenceError(`media sequence mismatch ${newFrag.sn}:`, oldDetails, newDetails, oldFrag, newFrag);
7939
+ return;
7940
+ } else if (oldFrag.cc !== newFrag.cc) {
7941
+ newDetails.playlistParsingError = getSequenceError(`discontinuity sequence mismatch (${oldFrag.cc}!=${newFrag.cc})`, oldDetails, newDetails, oldFrag, newFrag);
7942
+ return;
7943
+ }
7895
7944
  }
7896
7945
  }
7897
7946
  }
7947
+ function getSequenceError(message, oldDetails, newDetails, oldFrag, newFrag) {
7948
+ return new Error(`${message} ${newFrag.url}
7949
+ Playlist starting @${oldDetails.startSN}
7950
+ ${oldDetails.m3u8}
7951
+
7952
+ Playlist starting @${newDetails.startSN}
7953
+ ${newDetails.m3u8}`);
7954
+ }
7898
7955
  function adjustSliding(oldDetails, newDetails, matchingStableVariantOrRendition = true) {
7899
7956
  const delta = newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN;
7900
7957
  const oldFragments = oldDetails.fragments;
@@ -10001,7 +10058,7 @@ function requireEventemitter3 () {
10001
10058
  var eventemitter3Exports = requireEventemitter3();
10002
10059
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
10003
10060
 
10004
- const version = "1.6.0-rc.1.0.canary.11073";
10061
+ const version = "1.6.0-rc.1.0.canary.11074";
10005
10062
 
10006
10063
  // ensure the worker ends up in the bundle
10007
10064
  // If the worker should not be included this gets aliased to empty.js
@@ -17192,6 +17249,31 @@ class BasePlaylistController extends Logger {
17192
17249
  // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments
17193
17250
  if (previousDetails && details.fragments.length > 0) {
17194
17251
  mergeDetails(previousDetails, details);
17252
+ const error = details.playlistParsingError;
17253
+ if (error) {
17254
+ this.warn(error);
17255
+ const hls = this.hls;
17256
+ if (!hls.config.ignorePlaylistParsingErrors) {
17257
+ var _details$fragments$;
17258
+ const {
17259
+ networkDetails
17260
+ } = data;
17261
+ hls.trigger(Events.ERROR, {
17262
+ type: ErrorTypes.NETWORK_ERROR,
17263
+ details: ErrorDetails.LEVEL_PARSING_ERROR,
17264
+ fatal: false,
17265
+ url: details.url,
17266
+ error,
17267
+ reason: error.message,
17268
+ level: data.level || undefined,
17269
+ parent: (_details$fragments$ = details.fragments[0]) == null ? void 0 : _details$fragments$.type,
17270
+ networkDetails,
17271
+ stats
17272
+ });
17273
+ return;
17274
+ }
17275
+ details.playlistParsingError = null;
17276
+ }
17195
17277
  }
17196
17278
  if (details.requestScheduled === -1) {
17197
17279
  details.requestScheduled = stats.loading.start;
@@ -29695,6 +29777,7 @@ const hlsDefaultConfig = _objectSpread2(_objectSpread2({
29695
29777
  // used by fps-controller
29696
29778
  appendErrorMaxRetry: 3,
29697
29779
  // used by buffer-controller
29780
+ ignorePlaylistParsingErrors: false,
29698
29781
  loader: XhrLoader,
29699
29782
  // loader: FetchLoader,
29700
29783
  fLoader: undefined,
@@ -33791,21 +33874,25 @@ class PlaylistLoader {
33791
33874
  }
33792
33875
  const error = levelDetails.playlistParsingError;
33793
33876
  if (error) {
33794
- hls.trigger(Events.ERROR, {
33795
- type: ErrorTypes.NETWORK_ERROR,
33796
- details: ErrorDetails.LEVEL_PARSING_ERROR,
33797
- fatal: false,
33798
- url,
33799
- error,
33800
- reason: error.message,
33801
- response,
33802
- context,
33803
- level: levelIndex,
33804
- parent,
33805
- networkDetails,
33806
- stats
33807
- });
33808
- return;
33877
+ this.hls.logger.warn(error);
33878
+ if (!hls.config.ignorePlaylistParsingErrors) {
33879
+ hls.trigger(Events.ERROR, {
33880
+ type: ErrorTypes.NETWORK_ERROR,
33881
+ details: ErrorDetails.LEVEL_PARSING_ERROR,
33882
+ fatal: false,
33883
+ url,
33884
+ error,
33885
+ reason: error.message,
33886
+ response,
33887
+ context,
33888
+ level: levelIndex,
33889
+ parent,
33890
+ networkDetails,
33891
+ stats
33892
+ });
33893
+ return;
33894
+ }
33895
+ levelDetails.playlistParsingError = null;
33809
33896
  }
33810
33897
  if (levelDetails.live && loader) {
33811
33898
  if (loader.getCacheAge) {