@remotion/media-parser 4.0.268 → 4.0.270
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/containers/flac/get-channel-count.d.ts +1 -1
- package/dist/containers/iso-base-media/get-moov-atom.js +1 -0
- package/dist/containers/m3u/after-manifest-fetch.d.ts +5 -7
- package/dist/containers/m3u/after-manifest-fetch.js +29 -43
- package/dist/containers/m3u/fetch-m3u8-stream.d.ts +1 -2
- package/dist/containers/m3u/fetch-m3u8-stream.js +4 -4
- package/dist/containers/m3u/get-duration-from-m3u.d.ts +2 -2
- package/dist/containers/m3u/get-duration-from-m3u.js +8 -3
- package/dist/containers/m3u/get-playlist.d.ts +6 -1
- package/dist/containers/m3u/get-playlist.js +18 -9
- package/dist/containers/m3u/get-streams.d.ts +3 -2
- package/dist/containers/m3u/get-streams.js +10 -3
- package/dist/containers/m3u/m3u-child-stream.d.ts +0 -0
- package/dist/containers/m3u/m3u-child-stream.js +1 -0
- package/dist/containers/m3u/parse-directive.js +28 -1
- package/dist/containers/m3u/parse-m3u-manifest.js +1 -1
- package/dist/containers/m3u/parse-m3u.js +8 -1
- package/dist/containers/m3u/return-packets.d.ts +3 -2
- package/dist/containers/m3u/return-packets.js +83 -54
- package/dist/containers/m3u/run-over-m3u.d.ts +9 -0
- package/dist/containers/m3u/run-over-m3u.js +88 -0
- package/dist/containers/m3u/sample-sorter.d.ts +13 -0
- package/dist/containers/m3u/sample-sorter.js +64 -0
- package/dist/containers/m3u/select-stream.d.ts +9 -1
- package/dist/containers/m3u/select-stream.js +21 -1
- package/dist/containers/m3u/types.d.ts +15 -1
- package/dist/containers/wav/parse-list.js +9 -1
- package/dist/download-and-parse-media.js +31 -30
- package/dist/esm/index.mjs +542 -212
- package/dist/get-duration.js +1 -1
- package/dist/index.d.ts +30 -26
- package/dist/index.js +2 -1
- package/dist/internal-parse-media.js +4 -2
- package/dist/options.d.ts +2 -1
- package/dist/parse-media.js +2 -1
- package/dist/state/m3u-state.d.ts +34 -16
- package/dist/state/m3u-state.js +89 -25
- package/dist/state/parser-state.d.ts +30 -26
- package/dist/state/parser-state.js +3 -2
- package/dist/throttled-progress.js +20 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/esm/index.mjs
CHANGED
|
@@ -5824,20 +5824,21 @@ var getM3uStreams = (structure, originalSrc) => {
|
|
|
5824
5824
|
if (next.type !== "m3u-text-value") {
|
|
5825
5825
|
throw new Error("Expected m3u-text-value");
|
|
5826
5826
|
}
|
|
5827
|
-
const
|
|
5827
|
+
const associatedPlaylists = [];
|
|
5828
5828
|
if (str.audio) {
|
|
5829
5829
|
const match = structure.boxes.filter((box) => {
|
|
5830
5830
|
return box.type === "m3u-media-info" && box.groupId === str.audio;
|
|
5831
5831
|
});
|
|
5832
5832
|
for (const audioTrack of match) {
|
|
5833
|
-
|
|
5833
|
+
associatedPlaylists.push({
|
|
5834
5834
|
autoselect: audioTrack.autoselect,
|
|
5835
5835
|
channels: audioTrack.channels,
|
|
5836
5836
|
default: audioTrack.default,
|
|
5837
5837
|
groupId: audioTrack.groupId,
|
|
5838
5838
|
language: audioTrack.language,
|
|
5839
5839
|
name: audioTrack.name,
|
|
5840
|
-
url: originalSrc && originalSrc.startsWith("http") ? new URL(audioTrack.uri, originalSrc).href : audioTrack.uri
|
|
5840
|
+
url: originalSrc && originalSrc.startsWith("http") ? new URL(audioTrack.uri, originalSrc).href : audioTrack.uri,
|
|
5841
|
+
id: associatedPlaylists.length
|
|
5841
5842
|
});
|
|
5842
5843
|
}
|
|
5843
5844
|
}
|
|
@@ -5847,7 +5848,7 @@ var getM3uStreams = (structure, originalSrc) => {
|
|
|
5847
5848
|
bandwidth: str.bandwidth,
|
|
5848
5849
|
codecs: str.codecs,
|
|
5849
5850
|
resolution: str.resolution,
|
|
5850
|
-
|
|
5851
|
+
associatedPlaylists
|
|
5851
5852
|
});
|
|
5852
5853
|
}
|
|
5853
5854
|
}
|
|
@@ -5857,6 +5858,11 @@ var getM3uStreams = (structure, originalSrc) => {
|
|
|
5857
5858
|
const sorted = boxes.slice().sort((a, b) => {
|
|
5858
5859
|
const aResolution = a.resolution ? a.resolution.width * a.resolution.height : 0;
|
|
5859
5860
|
const bResolution = b.resolution ? b.resolution.width * b.resolution.height : 0;
|
|
5861
|
+
if (aResolution === bResolution) {
|
|
5862
|
+
const bandwidthA = a.averageBandwidth ?? a.bandwidth ?? 0;
|
|
5863
|
+
const bandwidthB = b.averageBandwidth ?? b.bandwidth ?? 0;
|
|
5864
|
+
return bandwidthB - bandwidthA;
|
|
5865
|
+
}
|
|
5860
5866
|
return bResolution - aResolution;
|
|
5861
5867
|
});
|
|
5862
5868
|
return sorted.map((box, index) => ({ ...box, id: index }));
|
|
@@ -6171,19 +6177,30 @@ var getSamplePositionsFromTrack = ({
|
|
|
6171
6177
|
};
|
|
6172
6178
|
|
|
6173
6179
|
// src/containers/m3u/get-playlist.ts
|
|
6174
|
-
var
|
|
6180
|
+
var getAllPlaylists = ({
|
|
6181
|
+
structure,
|
|
6182
|
+
src
|
|
6183
|
+
}) => {
|
|
6175
6184
|
const isIndependent = isIndependentSegments(structure);
|
|
6176
6185
|
if (!isIndependent) {
|
|
6177
|
-
return
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6186
|
+
return [
|
|
6187
|
+
{
|
|
6188
|
+
type: "m3u-playlist",
|
|
6189
|
+
boxes: structure.boxes,
|
|
6190
|
+
src
|
|
6191
|
+
}
|
|
6192
|
+
];
|
|
6181
6193
|
}
|
|
6182
6194
|
const playlists = structure.boxes.filter((box) => box.type === "m3u-playlist");
|
|
6183
|
-
|
|
6184
|
-
|
|
6195
|
+
return playlists;
|
|
6196
|
+
};
|
|
6197
|
+
var getPlaylist = (structure, src) => {
|
|
6198
|
+
const allPlaylists = getAllPlaylists({ structure, src });
|
|
6199
|
+
const playlists = allPlaylists.find((box) => box.src === src);
|
|
6200
|
+
if (!playlists) {
|
|
6201
|
+
throw new Error(`Expected m3u-playlist with src ${src}`);
|
|
6185
6202
|
}
|
|
6186
|
-
return playlists
|
|
6203
|
+
return playlists;
|
|
6187
6204
|
};
|
|
6188
6205
|
var getDurationFromPlaylist = (playlist) => {
|
|
6189
6206
|
const duration2 = playlist.boxes.filter((box) => box.type === "m3u-extinf");
|
|
@@ -6194,9 +6211,14 @@ var getDurationFromPlaylist = (playlist) => {
|
|
|
6194
6211
|
};
|
|
6195
6212
|
|
|
6196
6213
|
// src/containers/m3u/get-duration-from-m3u.ts
|
|
6197
|
-
var getDurationFromM3u = (
|
|
6198
|
-
const
|
|
6199
|
-
|
|
6214
|
+
var getDurationFromM3u = (state) => {
|
|
6215
|
+
const playlists = getAllPlaylists({
|
|
6216
|
+
structure: state.getM3uStructure(),
|
|
6217
|
+
src: state.src
|
|
6218
|
+
});
|
|
6219
|
+
return Math.max(...playlists.map((p) => {
|
|
6220
|
+
return getDurationFromPlaylist(p);
|
|
6221
|
+
}));
|
|
6200
6222
|
};
|
|
6201
6223
|
|
|
6202
6224
|
// src/containers/mp3/get-frame-length.ts
|
|
@@ -6435,7 +6457,7 @@ var getDuration = (parserState) => {
|
|
|
6435
6457
|
return getDurationFromFlac(parserState);
|
|
6436
6458
|
}
|
|
6437
6459
|
if (structure.type === "m3u") {
|
|
6438
|
-
return getDurationFromM3u(parserState
|
|
6460
|
+
return getDurationFromM3u(parserState);
|
|
6439
6461
|
}
|
|
6440
6462
|
throw new Error("Has no duration " + structure);
|
|
6441
6463
|
};
|
|
@@ -8300,49 +8322,171 @@ var eventLoopState = (logLevel) => {
|
|
|
8300
8322
|
return { eventLoopBreakIfNeeded };
|
|
8301
8323
|
};
|
|
8302
8324
|
|
|
8325
|
+
// src/containers/m3u/sample-sorter.ts
|
|
8326
|
+
var sampleSorter = ({
|
|
8327
|
+
logLevel,
|
|
8328
|
+
getAllChunksProcessedForPlaylist
|
|
8329
|
+
}) => {
|
|
8330
|
+
const streamsWithTracks = [];
|
|
8331
|
+
const audioCallbacks = {};
|
|
8332
|
+
const videoCallbacks = {};
|
|
8333
|
+
const latestSample = {};
|
|
8334
|
+
return {
|
|
8335
|
+
addToStreamWithTrack: (src) => {
|
|
8336
|
+
streamsWithTracks.push(src);
|
|
8337
|
+
},
|
|
8338
|
+
addVideoStreamToConsider: (src, callback) => {
|
|
8339
|
+
videoCallbacks[src] = callback;
|
|
8340
|
+
},
|
|
8341
|
+
addAudioStreamToConsider: (src, callback) => {
|
|
8342
|
+
audioCallbacks[src] = callback;
|
|
8343
|
+
},
|
|
8344
|
+
addAudioSample: async (src, sample) => {
|
|
8345
|
+
const callback = audioCallbacks[src];
|
|
8346
|
+
if (!callback) {
|
|
8347
|
+
throw new Error("No callback found for audio sample");
|
|
8348
|
+
}
|
|
8349
|
+
latestSample[src] = sample.dts;
|
|
8350
|
+
await callback(sample);
|
|
8351
|
+
},
|
|
8352
|
+
addVideoSample: async (src, sample) => {
|
|
8353
|
+
const callback = videoCallbacks[src];
|
|
8354
|
+
if (!callback) {
|
|
8355
|
+
throw new Error("No callback found for audio sample");
|
|
8356
|
+
}
|
|
8357
|
+
latestSample[src] = sample.dts;
|
|
8358
|
+
await callback(sample);
|
|
8359
|
+
},
|
|
8360
|
+
getNextStreamToRun: (streams) => {
|
|
8361
|
+
for (const stream of streams) {
|
|
8362
|
+
if (!streamsWithTracks.includes(stream)) {
|
|
8363
|
+
Log.trace(logLevel, `Did not yet detect track of ${stream}, working on that`);
|
|
8364
|
+
return stream;
|
|
8365
|
+
}
|
|
8366
|
+
}
|
|
8367
|
+
let smallestDts = Infinity;
|
|
8368
|
+
for (const stream of streams) {
|
|
8369
|
+
if (getAllChunksProcessedForPlaylist(stream)) {
|
|
8370
|
+
continue;
|
|
8371
|
+
}
|
|
8372
|
+
if ((latestSample[stream] ?? 0) < smallestDts) {
|
|
8373
|
+
smallestDts = latestSample[stream] ?? 0;
|
|
8374
|
+
}
|
|
8375
|
+
}
|
|
8376
|
+
for (const stream of streams) {
|
|
8377
|
+
if ((latestSample[stream] ?? 0) === smallestDts) {
|
|
8378
|
+
Log.trace(logLevel, `Working on ${stream} because it has the smallest DTS`);
|
|
8379
|
+
return stream;
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
8382
|
+
throw new Error("should be done with parsing now");
|
|
8383
|
+
}
|
|
8384
|
+
};
|
|
8385
|
+
};
|
|
8386
|
+
|
|
8303
8387
|
// src/state/m3u-state.ts
|
|
8304
|
-
var m3uState = () => {
|
|
8305
|
-
let
|
|
8306
|
-
let
|
|
8307
|
-
|
|
8308
|
-
|
|
8388
|
+
var m3uState = (logLevel) => {
|
|
8389
|
+
let selectedMainPlaylist = null;
|
|
8390
|
+
let associatedPlaylists = null;
|
|
8391
|
+
const hasEmittedVideoTrack = {};
|
|
8392
|
+
const hasEmittedAudioTrack = {};
|
|
8393
|
+
const hasEmittedDoneWithTracks = {};
|
|
8309
8394
|
let hasFinishedManifest = false;
|
|
8310
8395
|
let readyToIterateOverM3u = false;
|
|
8311
|
-
|
|
8312
|
-
|
|
8396
|
+
const allChunksProcessed = {};
|
|
8397
|
+
const m3uStreamRuns = {};
|
|
8398
|
+
const tracksDone = {};
|
|
8399
|
+
const getMainPlaylistUrl = () => {
|
|
8400
|
+
if (!selectedMainPlaylist) {
|
|
8401
|
+
throw new Error("No main playlist selected");
|
|
8402
|
+
}
|
|
8403
|
+
const playlistUrl = selectedMainPlaylist.type === "initial-url" ? selectedMainPlaylist.url : selectedMainPlaylist.stream.url;
|
|
8404
|
+
return playlistUrl;
|
|
8405
|
+
};
|
|
8406
|
+
const getSelectedPlaylists = () => {
|
|
8407
|
+
return [
|
|
8408
|
+
getMainPlaylistUrl(),
|
|
8409
|
+
...(associatedPlaylists ?? []).map((p) => p.url)
|
|
8410
|
+
];
|
|
8411
|
+
};
|
|
8412
|
+
const getAllChunksProcessedForPlaylist = (src) => allChunksProcessed[src];
|
|
8313
8413
|
return {
|
|
8314
|
-
|
|
8315
|
-
|
|
8414
|
+
setSelectedMainPlaylist: (stream) => {
|
|
8415
|
+
selectedMainPlaylist = stream;
|
|
8416
|
+
},
|
|
8417
|
+
getSelectedMainPlaylist: () => selectedMainPlaylist,
|
|
8418
|
+
setHasEmittedVideoTrack: (src, callback) => {
|
|
8419
|
+
hasEmittedVideoTrack[src] = callback;
|
|
8420
|
+
},
|
|
8421
|
+
hasEmittedVideoTrack: (src) => {
|
|
8422
|
+
const value = hasEmittedVideoTrack[src];
|
|
8423
|
+
if (value === undefined) {
|
|
8424
|
+
return false;
|
|
8425
|
+
}
|
|
8426
|
+
return value;
|
|
8316
8427
|
},
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
hasEmittedVideoTrack = callback;
|
|
8428
|
+
setHasEmittedAudioTrack: (src, callback) => {
|
|
8429
|
+
hasEmittedAudioTrack[src] = callback;
|
|
8320
8430
|
},
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8431
|
+
hasEmittedAudioTrack: (src) => {
|
|
8432
|
+
const value = hasEmittedAudioTrack[src];
|
|
8433
|
+
if (value === undefined) {
|
|
8434
|
+
return false;
|
|
8435
|
+
}
|
|
8436
|
+
return value;
|
|
8324
8437
|
},
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
hasEmittedDoneWithTracks = true;
|
|
8438
|
+
setHasEmittedDoneWithTracks: (src) => {
|
|
8439
|
+
hasEmittedDoneWithTracks[src] = true;
|
|
8328
8440
|
},
|
|
8329
|
-
hasEmittedDoneWithTracks: () => hasEmittedDoneWithTracks,
|
|
8441
|
+
hasEmittedDoneWithTracks: (src) => hasEmittedDoneWithTracks[src],
|
|
8330
8442
|
setReadyToIterateOverM3u: () => {
|
|
8331
8443
|
readyToIterateOverM3u = true;
|
|
8332
8444
|
},
|
|
8333
8445
|
isReadyToIterateOverM3u: () => readyToIterateOverM3u,
|
|
8334
|
-
|
|
8335
|
-
|
|
8446
|
+
setAllChunksProcessed: (src) => {
|
|
8447
|
+
allChunksProcessed[src] = true;
|
|
8336
8448
|
},
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8449
|
+
getAllChunksProcessedForPlaylist,
|
|
8450
|
+
getAllChunksProcessedOverall: () => {
|
|
8451
|
+
if (!selectedMainPlaylist) {
|
|
8452
|
+
return false;
|
|
8453
|
+
}
|
|
8454
|
+
const selectedPlaylists = getSelectedPlaylists();
|
|
8455
|
+
return selectedPlaylists.every((url) => allChunksProcessed[url]);
|
|
8341
8456
|
},
|
|
8342
8457
|
setHasFinishedManifest: () => {
|
|
8343
8458
|
hasFinishedManifest = true;
|
|
8344
8459
|
},
|
|
8345
|
-
hasFinishedManifest: () => hasFinishedManifest
|
|
8460
|
+
hasFinishedManifest: () => hasFinishedManifest,
|
|
8461
|
+
setM3uStreamRun: (playlistUrl, run) => {
|
|
8462
|
+
if (!run) {
|
|
8463
|
+
delete m3uStreamRuns[playlistUrl];
|
|
8464
|
+
return;
|
|
8465
|
+
}
|
|
8466
|
+
m3uStreamRuns[playlistUrl] = run;
|
|
8467
|
+
},
|
|
8468
|
+
setTracksDone: (playlistUrl) => {
|
|
8469
|
+
tracksDone[playlistUrl] = true;
|
|
8470
|
+
const selectedPlaylists = getSelectedPlaylists();
|
|
8471
|
+
return selectedPlaylists.every((url) => tracksDone[url]);
|
|
8472
|
+
},
|
|
8473
|
+
getM3uStreamRun: (playlistUrl) => m3uStreamRuns[playlistUrl] ?? null,
|
|
8474
|
+
abortM3UStreamRuns: () => {
|
|
8475
|
+
const values = Object.values(m3uStreamRuns);
|
|
8476
|
+
if (values.length === 0) {
|
|
8477
|
+
return;
|
|
8478
|
+
}
|
|
8479
|
+
Log.trace(logLevel, `Aborting ${values.length} M3U stream runs`);
|
|
8480
|
+
values.forEach((run) => {
|
|
8481
|
+
run.abort();
|
|
8482
|
+
});
|
|
8483
|
+
},
|
|
8484
|
+
setAssociatedPlaylists: (playlists) => {
|
|
8485
|
+
associatedPlaylists = playlists;
|
|
8486
|
+
},
|
|
8487
|
+
getAssociatedPlaylists: () => associatedPlaylists,
|
|
8488
|
+
getSelectedPlaylists,
|
|
8489
|
+
sampleSorter: sampleSorter({ logLevel, getAllChunksProcessedForPlaylist })
|
|
8346
8490
|
};
|
|
8347
8491
|
};
|
|
8348
8492
|
|
|
@@ -8890,7 +9034,8 @@ var makeParserState = ({
|
|
|
8890
9034
|
src,
|
|
8891
9035
|
readerInterface,
|
|
8892
9036
|
onDiscardedData,
|
|
8893
|
-
selectM3uStreamFn
|
|
9037
|
+
selectM3uStreamFn,
|
|
9038
|
+
selectM3uAssociatedPlaylistsFn
|
|
8894
9039
|
}) => {
|
|
8895
9040
|
let skippedBytes = 0;
|
|
8896
9041
|
const iterator = getArrayBufferIterator(new Uint8Array([]), contentLength);
|
|
@@ -8920,7 +9065,7 @@ var makeParserState = ({
|
|
|
8920
9065
|
mp3Info,
|
|
8921
9066
|
aac: aacState(),
|
|
8922
9067
|
flac: flacState(),
|
|
8923
|
-
m3u: m3uState(),
|
|
9068
|
+
m3u: m3uState(logLevel),
|
|
8924
9069
|
callbacks: sampleCallback({
|
|
8925
9070
|
controller,
|
|
8926
9071
|
hasAudioTrackHandlers,
|
|
@@ -8955,7 +9100,8 @@ var makeParserState = ({
|
|
|
8955
9100
|
src,
|
|
8956
9101
|
readerInterface,
|
|
8957
9102
|
discardReadBytes,
|
|
8958
|
-
selectM3uStreamFn
|
|
9103
|
+
selectM3uStreamFn,
|
|
9104
|
+
selectM3uAssociatedPlaylistsFn
|
|
8959
9105
|
};
|
|
8960
9106
|
};
|
|
8961
9107
|
|
|
@@ -8992,7 +9138,8 @@ var getMoovAtom = async ({
|
|
|
8992
9138
|
readerInterface: state.readerInterface,
|
|
8993
9139
|
src: state.src,
|
|
8994
9140
|
onDiscardedData: null,
|
|
8995
|
-
selectM3uStreamFn: state.selectM3uStreamFn
|
|
9141
|
+
selectM3uStreamFn: state.selectM3uStreamFn,
|
|
9142
|
+
selectM3uAssociatedPlaylistsFn: state.selectM3uAssociatedPlaylistsFn
|
|
8996
9143
|
});
|
|
8997
9144
|
while (true) {
|
|
8998
9145
|
const result = await reader.reader.read();
|
|
@@ -9189,7 +9336,7 @@ var parseM3uMediaDirective = (str) => {
|
|
|
9189
9336
|
// src/containers/m3u/parse-directive.ts
|
|
9190
9337
|
var parseM3uDirective = (str) => {
|
|
9191
9338
|
const firstColon = str.indexOf(":");
|
|
9192
|
-
const directive = firstColon === -1 ? str : str.slice(0, firstColon);
|
|
9339
|
+
const directive = (firstColon === -1 ? str : str.slice(0, firstColon)).trim();
|
|
9193
9340
|
const value = firstColon === -1 ? null : str.slice(firstColon + 1);
|
|
9194
9341
|
if (directive === "#EXT-X-VERSION") {
|
|
9195
9342
|
if (!value) {
|
|
@@ -9244,6 +9391,24 @@ var parseM3uDirective = (str) => {
|
|
|
9244
9391
|
playlistType: value
|
|
9245
9392
|
};
|
|
9246
9393
|
}
|
|
9394
|
+
if (directive === "#EXT-X-MEDIA-SEQUENCE") {
|
|
9395
|
+
if (!value) {
|
|
9396
|
+
throw new Error("#EXT-X-MEDIA-SEQUENCE directive must have a value");
|
|
9397
|
+
}
|
|
9398
|
+
return {
|
|
9399
|
+
type: "m3u-media-sequence",
|
|
9400
|
+
value: Number(value)
|
|
9401
|
+
};
|
|
9402
|
+
}
|
|
9403
|
+
if (directive === "#EXT-X-DISCONTINUITY-SEQUENCE") {
|
|
9404
|
+
if (!value) {
|
|
9405
|
+
throw new Error("#EXT-X-DISCONTINUITY-SEQUENCE directive must have a value");
|
|
9406
|
+
}
|
|
9407
|
+
return {
|
|
9408
|
+
type: "m3u-discontinuity-sequence",
|
|
9409
|
+
value: Number(value)
|
|
9410
|
+
};
|
|
9411
|
+
}
|
|
9247
9412
|
if (directive === "#EXT-X-STREAM-INF") {
|
|
9248
9413
|
if (!value) {
|
|
9249
9414
|
throw new Error("EXT-X-STREAM-INF directive must have a value");
|
|
@@ -9251,6 +9416,15 @@ var parseM3uDirective = (str) => {
|
|
|
9251
9416
|
const res = parseStreamInf(value);
|
|
9252
9417
|
return res;
|
|
9253
9418
|
}
|
|
9419
|
+
if (directive === "#EXT-X-MAP") {
|
|
9420
|
+
if (!value) {
|
|
9421
|
+
throw new Error("#EXT-X-MAP directive must have a value");
|
|
9422
|
+
}
|
|
9423
|
+
return {
|
|
9424
|
+
type: "m3u-map",
|
|
9425
|
+
value: Number(value)
|
|
9426
|
+
};
|
|
9427
|
+
}
|
|
9254
9428
|
throw new Error(`Unknown directive ${directive}. Value: ${value}`);
|
|
9255
9429
|
};
|
|
9256
9430
|
|
|
@@ -9275,21 +9449,134 @@ var parseM3u8Text = (line, boxes) => {
|
|
|
9275
9449
|
};
|
|
9276
9450
|
|
|
9277
9451
|
// src/containers/m3u/fetch-m3u8-stream.ts
|
|
9278
|
-
var fetchM3u8Stream = async (
|
|
9279
|
-
const res = await fetch(
|
|
9452
|
+
var fetchM3u8Stream = async (url) => {
|
|
9453
|
+
const res = await fetch(url);
|
|
9280
9454
|
if (!res.ok) {
|
|
9281
|
-
throw new Error(`Failed to fetch ${
|
|
9455
|
+
throw new Error(`Failed to fetch ${url} (HTTP code: ${res.status})`);
|
|
9282
9456
|
}
|
|
9283
9457
|
const text = await res.text();
|
|
9284
9458
|
const lines = text.split(`
|
|
9285
9459
|
`);
|
|
9286
9460
|
const boxes = [];
|
|
9287
9461
|
for (const line of lines) {
|
|
9288
|
-
parseM3u8Text(line, boxes);
|
|
9462
|
+
parseM3u8Text(line.trim(), boxes);
|
|
9289
9463
|
}
|
|
9290
9464
|
return boxes;
|
|
9291
9465
|
};
|
|
9292
9466
|
|
|
9467
|
+
// src/containers/m3u/select-stream.ts
|
|
9468
|
+
var selectAssociatedPlaylists = async ({
|
|
9469
|
+
playlists,
|
|
9470
|
+
fn
|
|
9471
|
+
}) => {
|
|
9472
|
+
if (playlists.length < 1) {
|
|
9473
|
+
return Promise.resolve([]);
|
|
9474
|
+
}
|
|
9475
|
+
const streams = await fn({ associatedPlaylists: playlists });
|
|
9476
|
+
if (!Array.isArray(streams)) {
|
|
9477
|
+
throw new Error("Expected an array of associated playlists");
|
|
9478
|
+
}
|
|
9479
|
+
for (const stream of streams) {
|
|
9480
|
+
if (!playlists.find((playlist) => playlist.url === stream.url)) {
|
|
9481
|
+
throw new Error(`The associated playlist ${JSON.stringify(streams)} cannot be selected because it was not in the list of selectable playlists`);
|
|
9482
|
+
}
|
|
9483
|
+
}
|
|
9484
|
+
return streams;
|
|
9485
|
+
};
|
|
9486
|
+
var defaultSelectM3uAssociatedPlaylists = ({ associatedPlaylists }) => {
|
|
9487
|
+
return Promise.resolve(associatedPlaylists.filter((playlist) => playlist.default));
|
|
9488
|
+
};
|
|
9489
|
+
var selectStream = async ({
|
|
9490
|
+
streams,
|
|
9491
|
+
fn
|
|
9492
|
+
}) => {
|
|
9493
|
+
if (streams.length < 1) {
|
|
9494
|
+
throw new Error("No streams found");
|
|
9495
|
+
}
|
|
9496
|
+
const selectedStreamId = await fn({ streams });
|
|
9497
|
+
const selectedStream = streams.find((stream) => stream.id === selectedStreamId);
|
|
9498
|
+
if (!selectedStream) {
|
|
9499
|
+
throw new Error(`No stream with the id ${selectedStreamId} found`);
|
|
9500
|
+
}
|
|
9501
|
+
return Promise.resolve(selectedStream);
|
|
9502
|
+
};
|
|
9503
|
+
var defaultSelectM3uStreamFn = ({ streams }) => {
|
|
9504
|
+
return Promise.resolve(streams[0].id);
|
|
9505
|
+
};
|
|
9506
|
+
|
|
9507
|
+
// src/containers/m3u/after-manifest-fetch.ts
|
|
9508
|
+
var afterManifestFetch = async ({
|
|
9509
|
+
structure,
|
|
9510
|
+
m3uState: m3uState2,
|
|
9511
|
+
src,
|
|
9512
|
+
selectM3uStreamFn,
|
|
9513
|
+
logLevel,
|
|
9514
|
+
selectAssociatedPlaylists: selectAssociatedPlaylistsFn
|
|
9515
|
+
}) => {
|
|
9516
|
+
const independentSegments = isIndependentSegments(structure);
|
|
9517
|
+
if (!independentSegments) {
|
|
9518
|
+
if (!src) {
|
|
9519
|
+
throw new Error("No src");
|
|
9520
|
+
}
|
|
9521
|
+
m3uState2.setSelectedMainPlaylist({
|
|
9522
|
+
type: "initial-url",
|
|
9523
|
+
url: src
|
|
9524
|
+
});
|
|
9525
|
+
return m3uState2.setReadyToIterateOverM3u();
|
|
9526
|
+
}
|
|
9527
|
+
const streams = getM3uStreams(structure, src);
|
|
9528
|
+
if (streams === null) {
|
|
9529
|
+
throw new Error("No streams found");
|
|
9530
|
+
}
|
|
9531
|
+
const selectedPlaylist = await selectStream({ streams, fn: selectM3uStreamFn });
|
|
9532
|
+
if (!selectedPlaylist.resolution) {
|
|
9533
|
+
throw new Error("Stream does not have a resolution");
|
|
9534
|
+
}
|
|
9535
|
+
m3uState2.setSelectedMainPlaylist({
|
|
9536
|
+
type: "selected-stream",
|
|
9537
|
+
stream: selectedPlaylist
|
|
9538
|
+
});
|
|
9539
|
+
const associatedPlaylists = await selectAssociatedPlaylists({
|
|
9540
|
+
playlists: selectedPlaylist.associatedPlaylists,
|
|
9541
|
+
fn: selectAssociatedPlaylistsFn
|
|
9542
|
+
});
|
|
9543
|
+
m3uState2.setAssociatedPlaylists(associatedPlaylists);
|
|
9544
|
+
const playlistUrls = [
|
|
9545
|
+
selectedPlaylist.url,
|
|
9546
|
+
...associatedPlaylists.map((p) => p.url)
|
|
9547
|
+
];
|
|
9548
|
+
const struc = await Promise.all(playlistUrls.map(async (url) => {
|
|
9549
|
+
Log.verbose(logLevel, `Fetching playlist ${url}`);
|
|
9550
|
+
const boxes = await fetchM3u8Stream(url);
|
|
9551
|
+
return {
|
|
9552
|
+
type: "m3u-playlist",
|
|
9553
|
+
boxes,
|
|
9554
|
+
src: url
|
|
9555
|
+
};
|
|
9556
|
+
}));
|
|
9557
|
+
structure.boxes.push(...struc);
|
|
9558
|
+
m3uState2.setReadyToIterateOverM3u();
|
|
9559
|
+
};
|
|
9560
|
+
|
|
9561
|
+
// src/containers/m3u/parse-m3u-manifest.ts
|
|
9562
|
+
var parseM3uManifest = ({
|
|
9563
|
+
iterator,
|
|
9564
|
+
structure,
|
|
9565
|
+
contentLength
|
|
9566
|
+
}) => {
|
|
9567
|
+
const start = iterator.startCheckpoint();
|
|
9568
|
+
const line = iterator.readUntilLineEnd();
|
|
9569
|
+
if (iterator.counter.getOffset() > contentLength) {
|
|
9570
|
+
throw new Error("Unexpected end of file");
|
|
9571
|
+
}
|
|
9572
|
+
if (line === null) {
|
|
9573
|
+
start.returnToCheckpoint();
|
|
9574
|
+
return Promise.resolve(null);
|
|
9575
|
+
}
|
|
9576
|
+
parseM3u8Text(line.trim(), structure.boxes);
|
|
9577
|
+
return Promise.resolve(null);
|
|
9578
|
+
};
|
|
9579
|
+
|
|
9293
9580
|
// src/forward-controller.ts
|
|
9294
9581
|
var forwardMediaParserController = ({
|
|
9295
9582
|
parentController,
|
|
@@ -9316,25 +9603,6 @@ var forwardMediaParserController = ({
|
|
|
9316
9603
|
};
|
|
9317
9604
|
};
|
|
9318
9605
|
|
|
9319
|
-
// src/containers/m3u/select-stream.ts
|
|
9320
|
-
var selectStream = async ({
|
|
9321
|
-
streams,
|
|
9322
|
-
fn
|
|
9323
|
-
}) => {
|
|
9324
|
-
if (streams.length < 1) {
|
|
9325
|
-
throw new Error("No streams found");
|
|
9326
|
-
}
|
|
9327
|
-
const selectedStreamId = await fn({ streams });
|
|
9328
|
-
const selectedStream = streams.find((stream) => stream.id === selectedStreamId);
|
|
9329
|
-
if (!selectedStream) {
|
|
9330
|
-
throw new Error(`No stream with the id ${selectedStreamId} found`);
|
|
9331
|
-
}
|
|
9332
|
-
return Promise.resolve(selectedStream);
|
|
9333
|
-
};
|
|
9334
|
-
var defaultSelectM3uStreamFn = ({ streams }) => {
|
|
9335
|
-
return Promise.resolve(streams[0].id);
|
|
9336
|
-
};
|
|
9337
|
-
|
|
9338
9606
|
// src/readers/fetch/get-body-and-reader.ts
|
|
9339
9607
|
var getLengthAndReader = async ({
|
|
9340
9608
|
canLiveWithoutContentLength,
|
|
@@ -9531,6 +9799,7 @@ var parseMedia = (options) => {
|
|
|
9531
9799
|
reader: options.reader ?? fetchReader,
|
|
9532
9800
|
controller: options.controller ?? undefined,
|
|
9533
9801
|
selectM3uStream: options.selectM3uStream ?? defaultSelectM3uStreamFn,
|
|
9802
|
+
selectM3uAssociatedPlaylists: options.selectM3uAssociatedPlaylists ?? defaultSelectM3uAssociatedPlaylists,
|
|
9534
9803
|
src: options.src,
|
|
9535
9804
|
mode: "query",
|
|
9536
9805
|
onDiscardedData: null,
|
|
@@ -9567,161 +9836,198 @@ var iteratorOverTsFiles = async ({
|
|
|
9567
9836
|
onDoneWithTracks,
|
|
9568
9837
|
playlistUrl,
|
|
9569
9838
|
logLevel,
|
|
9570
|
-
parentController
|
|
9839
|
+
parentController,
|
|
9840
|
+
onInitialProgress
|
|
9571
9841
|
}) => {
|
|
9572
|
-
const playlist = getPlaylist(structure);
|
|
9842
|
+
const playlist = getPlaylist(structure, playlistUrl);
|
|
9573
9843
|
const chunks = getChunks(playlist);
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
const isLastChunk = chunkIndex === chunks.length - 1;
|
|
9578
|
-
const src = new URL(chunk.url, playlistUrl).toString();
|
|
9844
|
+
let resolver = onInitialProgress;
|
|
9845
|
+
let rejector = (_e) => {
|
|
9846
|
+
};
|
|
9579
9847
|
const childController = mediaParserController();
|
|
9580
9848
|
const forwarded = forwardMediaParserController({
|
|
9581
9849
|
childController,
|
|
9582
9850
|
parentController
|
|
9583
9851
|
});
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
},
|
|
9596
|
-
onAudioTrack: async ({ track }) => {
|
|
9597
|
-
const callbackOrFalse = m3uState2.hasEmittedAudioTrack();
|
|
9598
|
-
if (callbackOrFalse === false) {
|
|
9599
|
-
const callback = await onAudioTrack(track);
|
|
9600
|
-
if (!callback) {
|
|
9601
|
-
m3uState2.setHasEmittedAudioTrack(null);
|
|
9602
|
-
return null;
|
|
9603
|
-
}
|
|
9604
|
-
m3uState2.setHasEmittedAudioTrack(callback);
|
|
9605
|
-
return (sample) => {
|
|
9606
|
-
return callback(sample);
|
|
9607
|
-
};
|
|
9852
|
+
const makeContinuationFn = () => {
|
|
9853
|
+
return {
|
|
9854
|
+
continue() {
|
|
9855
|
+
const { promise, reject, resolve } = Promise.withResolvers();
|
|
9856
|
+
resolver = resolve;
|
|
9857
|
+
rejector = reject;
|
|
9858
|
+
childController.resume();
|
|
9859
|
+
return promise;
|
|
9860
|
+
},
|
|
9861
|
+
abort() {
|
|
9862
|
+
childController.abort();
|
|
9608
9863
|
}
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9864
|
+
};
|
|
9865
|
+
};
|
|
9866
|
+
for (const chunk of chunks) {
|
|
9867
|
+
const isLastChunk = chunk === chunks[chunks.length - 1];
|
|
9868
|
+
await childController._internals.checkForAbortAndPause();
|
|
9869
|
+
const src = new URL(chunk.url, playlistUrl).toString();
|
|
9870
|
+
try {
|
|
9871
|
+
await parseMedia({
|
|
9872
|
+
src,
|
|
9873
|
+
acknowledgeRemotionLicense: true,
|
|
9874
|
+
logLevel,
|
|
9875
|
+
controller: childController,
|
|
9876
|
+
progressIntervalInMs: 0,
|
|
9877
|
+
onParseProgress: () => {
|
|
9878
|
+
childController.pause();
|
|
9879
|
+
resolver(makeContinuationFn());
|
|
9880
|
+
},
|
|
9881
|
+
onTracks: () => {
|
|
9882
|
+
if (!m3uState2.hasEmittedDoneWithTracks(playlistUrl)) {
|
|
9883
|
+
m3uState2.setHasEmittedDoneWithTracks(playlistUrl);
|
|
9884
|
+
onDoneWithTracks();
|
|
9885
|
+
return null;
|
|
9886
|
+
}
|
|
9887
|
+
},
|
|
9888
|
+
onAudioTrack: async ({ track }) => {
|
|
9889
|
+
const callbackOrFalse = m3uState2.hasEmittedAudioTrack(playlistUrl);
|
|
9890
|
+
if (callbackOrFalse === false) {
|
|
9891
|
+
const callback = await onAudioTrack(track);
|
|
9892
|
+
if (!callback) {
|
|
9893
|
+
m3uState2.setHasEmittedAudioTrack(playlistUrl, null);
|
|
9894
|
+
return null;
|
|
9895
|
+
}
|
|
9896
|
+
m3uState2.setHasEmittedAudioTrack(playlistUrl, callback);
|
|
9897
|
+
return (sample) => {
|
|
9898
|
+
return callback(sample);
|
|
9899
|
+
};
|
|
9900
|
+
}
|
|
9901
|
+
return callbackOrFalse;
|
|
9902
|
+
},
|
|
9903
|
+
onVideoTrack: async ({ track }) => {
|
|
9904
|
+
const callbackOrFalse = m3uState2.hasEmittedVideoTrack(playlistUrl);
|
|
9905
|
+
if (callbackOrFalse === false) {
|
|
9906
|
+
const callback = await onVideoTrack(track);
|
|
9907
|
+
if (!callback) {
|
|
9908
|
+
m3uState2.setHasEmittedVideoTrack(playlistUrl, null);
|
|
9909
|
+
return null;
|
|
9910
|
+
}
|
|
9911
|
+
m3uState2.setHasEmittedVideoTrack(playlistUrl, callback);
|
|
9912
|
+
return (sample) => {
|
|
9913
|
+
return callback(sample);
|
|
9914
|
+
};
|
|
9915
|
+
}
|
|
9916
|
+
return callbackOrFalse;
|
|
9618
9917
|
}
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
}
|
|
9624
|
-
return callbackOrFalse;
|
|
9918
|
+
});
|
|
9919
|
+
} catch (e) {
|
|
9920
|
+
rejector(e);
|
|
9921
|
+
throw e;
|
|
9625
9922
|
}
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
}
|
|
9631
|
-
forwarded.cleanup();
|
|
9632
|
-
};
|
|
9633
|
-
|
|
9634
|
-
// src/containers/m3u/after-manifest-fetch.ts
|
|
9635
|
-
var afterManifestFetch = async ({
|
|
9636
|
-
structure,
|
|
9637
|
-
m3uState: m3uState2,
|
|
9638
|
-
src,
|
|
9639
|
-
selectM3uStreamFn
|
|
9640
|
-
}) => {
|
|
9641
|
-
const independentSegments = isIndependentSegments(structure);
|
|
9642
|
-
if (!independentSegments) {
|
|
9643
|
-
if (!src) {
|
|
9644
|
-
throw new Error("No src");
|
|
9923
|
+
forwarded.cleanup();
|
|
9924
|
+
if (!isLastChunk) {
|
|
9925
|
+
childController.pause();
|
|
9926
|
+
resolver(makeContinuationFn());
|
|
9645
9927
|
}
|
|
9646
|
-
m3uState2.setSelectedStream({
|
|
9647
|
-
type: "initial-url",
|
|
9648
|
-
url: src
|
|
9649
|
-
});
|
|
9650
|
-
return m3uState2.setReadyToIterateOverM3u();
|
|
9651
|
-
}
|
|
9652
|
-
const streams = getM3uStreams(structure, src);
|
|
9653
|
-
if (streams === null) {
|
|
9654
|
-
throw new Error("No streams found");
|
|
9655
|
-
}
|
|
9656
|
-
const selectedStream = await selectStream({ streams, fn: selectM3uStreamFn });
|
|
9657
|
-
m3uState2.setSelectedStream({ type: "selected-stream", stream: selectedStream });
|
|
9658
|
-
if (!selectedStream.resolution) {
|
|
9659
|
-
throw new Error("Stream does not have a resolution");
|
|
9660
9928
|
}
|
|
9661
|
-
|
|
9662
|
-
structure.boxes.push({ type: "m3u-playlist", boxes });
|
|
9663
|
-
m3uState2.setReadyToIterateOverM3u();
|
|
9929
|
+
resolver(null);
|
|
9664
9930
|
};
|
|
9931
|
+
|
|
9932
|
+
// src/containers/m3u/run-over-m3u.ts
|
|
9665
9933
|
var runOverM3u = async ({
|
|
9666
9934
|
state,
|
|
9667
|
-
structure
|
|
9668
|
-
}) => {
|
|
9669
|
-
const selectedStream = state.m3u.getSelectedStream();
|
|
9670
|
-
if (!selectedStream) {
|
|
9671
|
-
throw new Error("No stream selected");
|
|
9672
|
-
}
|
|
9673
|
-
await iteratorOverTsFiles({
|
|
9674
|
-
playlistUrl: selectedStream.type === "initial-url" ? selectedStream.url : selectedStream.stream.url,
|
|
9675
|
-
structure,
|
|
9676
|
-
logLevel: state.logLevel,
|
|
9677
|
-
onDoneWithTracks() {
|
|
9678
|
-
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
9679
|
-
},
|
|
9680
|
-
onAudioTrack: (track) => {
|
|
9681
|
-
return registerAudioTrack({
|
|
9682
|
-
container: "m3u8",
|
|
9683
|
-
state,
|
|
9684
|
-
track
|
|
9685
|
-
});
|
|
9686
|
-
},
|
|
9687
|
-
onVideoTrack: (track) => {
|
|
9688
|
-
return registerVideoTrack({
|
|
9689
|
-
container: "m3u8",
|
|
9690
|
-
state,
|
|
9691
|
-
track
|
|
9692
|
-
});
|
|
9693
|
-
},
|
|
9694
|
-
m3uState: state.m3u,
|
|
9695
|
-
parentController: state.controller
|
|
9696
|
-
});
|
|
9697
|
-
};
|
|
9698
|
-
|
|
9699
|
-
// src/containers/m3u/parse-m3u-manifest.ts
|
|
9700
|
-
var parseM3uManifest = ({
|
|
9701
|
-
iterator,
|
|
9702
9935
|
structure,
|
|
9703
|
-
|
|
9936
|
+
playlistUrl,
|
|
9937
|
+
logLevel
|
|
9704
9938
|
}) => {
|
|
9705
|
-
const
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9939
|
+
const existingRun = state.m3u.getM3uStreamRun(playlistUrl);
|
|
9940
|
+
if (existingRun) {
|
|
9941
|
+
Log.trace(logLevel, "Existing M3U parsing process found for", playlistUrl);
|
|
9942
|
+
const run = await existingRun.continue();
|
|
9943
|
+
state.m3u.setM3uStreamRun(playlistUrl, run);
|
|
9944
|
+
if (!run) {
|
|
9945
|
+
state.m3u.setAllChunksProcessed(playlistUrl);
|
|
9946
|
+
}
|
|
9947
|
+
return;
|
|
9713
9948
|
}
|
|
9714
|
-
|
|
9715
|
-
return Promise
|
|
9949
|
+
Log.trace(logLevel, "Starting new M3U parsing process for", playlistUrl);
|
|
9950
|
+
return new Promise((resolve, reject) => {
|
|
9951
|
+
const run = iteratorOverTsFiles({
|
|
9952
|
+
playlistUrl,
|
|
9953
|
+
structure,
|
|
9954
|
+
onInitialProgress: (newRun) => {
|
|
9955
|
+
state.m3u.setM3uStreamRun(playlistUrl, newRun);
|
|
9956
|
+
resolve();
|
|
9957
|
+
},
|
|
9958
|
+
logLevel: state.logLevel,
|
|
9959
|
+
onDoneWithTracks() {
|
|
9960
|
+
const allDone = state.m3u.setTracksDone(playlistUrl);
|
|
9961
|
+
if (allDone) {
|
|
9962
|
+
state.callbacks.tracks.setIsDone(state.logLevel);
|
|
9963
|
+
}
|
|
9964
|
+
},
|
|
9965
|
+
onAudioTrack: async (track) => {
|
|
9966
|
+
const existingTracks = state.callbacks.tracks.getTracks();
|
|
9967
|
+
let { trackId } = track;
|
|
9968
|
+
while (existingTracks.find((t) => t.trackId === trackId)) {
|
|
9969
|
+
trackId++;
|
|
9970
|
+
}
|
|
9971
|
+
const onAudioSample = await registerAudioTrack({
|
|
9972
|
+
container: "m3u8",
|
|
9973
|
+
state,
|
|
9974
|
+
track: {
|
|
9975
|
+
...track,
|
|
9976
|
+
trackId
|
|
9977
|
+
}
|
|
9978
|
+
});
|
|
9979
|
+
state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
|
|
9980
|
+
if (onAudioSample === null) {
|
|
9981
|
+
return null;
|
|
9982
|
+
}
|
|
9983
|
+
state.m3u.sampleSorter.addAudioStreamToConsider(playlistUrl, onAudioSample);
|
|
9984
|
+
return async (sample) => {
|
|
9985
|
+
await state.m3u.sampleSorter.addAudioSample(playlistUrl, sample);
|
|
9986
|
+
};
|
|
9987
|
+
},
|
|
9988
|
+
onVideoTrack: async (track) => {
|
|
9989
|
+
const existingTracks = state.callbacks.tracks.getTracks();
|
|
9990
|
+
let { trackId } = track;
|
|
9991
|
+
while (existingTracks.find((t) => t.trackId === trackId)) {
|
|
9992
|
+
trackId++;
|
|
9993
|
+
}
|
|
9994
|
+
const onVideoSample = await registerVideoTrack({
|
|
9995
|
+
container: "m3u8",
|
|
9996
|
+
state,
|
|
9997
|
+
track: {
|
|
9998
|
+
...track,
|
|
9999
|
+
trackId
|
|
10000
|
+
}
|
|
10001
|
+
});
|
|
10002
|
+
state.m3u.sampleSorter.addToStreamWithTrack(playlistUrl);
|
|
10003
|
+
if (onVideoSample === null) {
|
|
10004
|
+
return null;
|
|
10005
|
+
}
|
|
10006
|
+
state.m3u.sampleSorter.addVideoStreamToConsider(playlistUrl, onVideoSample);
|
|
10007
|
+
return async (sample) => {
|
|
10008
|
+
await state.m3u.sampleSorter.addVideoSample(playlistUrl, sample);
|
|
10009
|
+
};
|
|
10010
|
+
},
|
|
10011
|
+
m3uState: state.m3u,
|
|
10012
|
+
parentController: state.controller
|
|
10013
|
+
});
|
|
10014
|
+
run.catch((err) => {
|
|
10015
|
+
reject(err);
|
|
10016
|
+
});
|
|
10017
|
+
});
|
|
9716
10018
|
};
|
|
9717
10019
|
|
|
9718
10020
|
// src/containers/m3u/parse-m3u.ts
|
|
9719
10021
|
var parseM3u = async ({ state }) => {
|
|
9720
10022
|
const structure = state.getM3uStructure();
|
|
9721
10023
|
if (state.m3u.isReadyToIterateOverM3u()) {
|
|
10024
|
+
const selectedPlaylists = state.m3u.getSelectedPlaylists();
|
|
10025
|
+
const whichPlaylistToRunOver = state.m3u.sampleSorter.getNextStreamToRun(selectedPlaylists);
|
|
9722
10026
|
await runOverM3u({
|
|
9723
10027
|
state,
|
|
9724
|
-
structure
|
|
10028
|
+
structure,
|
|
10029
|
+
playlistUrl: whichPlaylistToRunOver,
|
|
10030
|
+
logLevel: state.logLevel
|
|
9725
10031
|
});
|
|
9726
10032
|
return null;
|
|
9727
10033
|
}
|
|
@@ -9730,7 +10036,9 @@ var parseM3u = async ({ state }) => {
|
|
|
9730
10036
|
structure,
|
|
9731
10037
|
m3uState: state.m3u,
|
|
9732
10038
|
src: typeof state.src === "string" ? state.src : null,
|
|
9733
|
-
selectM3uStreamFn: state.selectM3uStreamFn
|
|
10039
|
+
selectM3uStreamFn: state.selectM3uStreamFn,
|
|
10040
|
+
logLevel: state.logLevel,
|
|
10041
|
+
selectAssociatedPlaylists: state.selectM3uAssociatedPlaylistsFn
|
|
9734
10042
|
});
|
|
9735
10043
|
return null;
|
|
9736
10044
|
}
|
|
@@ -11411,7 +11719,12 @@ var parseList = ({
|
|
|
11411
11719
|
throw new Error(`Only supporting LIST INFO, but got ${type}`);
|
|
11412
11720
|
}
|
|
11413
11721
|
const metadata = [];
|
|
11414
|
-
|
|
11722
|
+
const remainingBytes = () => ckSize - (iterator.counter.getOffset() - startOffset);
|
|
11723
|
+
while (remainingBytes() > 0) {
|
|
11724
|
+
if (remainingBytes() < 4) {
|
|
11725
|
+
iterator.discard(remainingBytes());
|
|
11726
|
+
break;
|
|
11727
|
+
}
|
|
11415
11728
|
const key = iterator.getByteString(4, false);
|
|
11416
11729
|
const size = iterator.getUint32Le();
|
|
11417
11730
|
const value = iterator.getByteString(size, true);
|
|
@@ -11802,21 +12115,33 @@ var throttledStateUpdate = ({
|
|
|
11802
12115
|
updateFn(currentState);
|
|
11803
12116
|
lastUpdated = currentState;
|
|
11804
12117
|
};
|
|
11805
|
-
|
|
11806
|
-
callUpdateIfChanged();
|
|
11807
|
-
}, everyMilliseconds);
|
|
11808
|
-
const onAbort = () => {
|
|
11809
|
-
clearInterval(interval);
|
|
12118
|
+
let cleanup = () => {
|
|
11810
12119
|
};
|
|
11811
|
-
|
|
12120
|
+
if (everyMilliseconds > 0) {
|
|
12121
|
+
const interval = setInterval(() => {
|
|
12122
|
+
callUpdateIfChanged();
|
|
12123
|
+
}, everyMilliseconds);
|
|
12124
|
+
const onAbort = () => {
|
|
12125
|
+
clearInterval(interval);
|
|
12126
|
+
};
|
|
12127
|
+
controller._internals.signal.addEventListener("abort", onAbort, {
|
|
12128
|
+
once: true
|
|
12129
|
+
});
|
|
12130
|
+
cleanup = () => {
|
|
12131
|
+
clearInterval(interval);
|
|
12132
|
+
controller._internals.signal.removeEventListener("abort", onAbort);
|
|
12133
|
+
};
|
|
12134
|
+
}
|
|
11812
12135
|
return {
|
|
11813
12136
|
get: () => currentState,
|
|
11814
12137
|
update: (fn) => {
|
|
11815
12138
|
currentState = fn(currentState);
|
|
12139
|
+
if (everyMilliseconds === 0) {
|
|
12140
|
+
callUpdateIfChanged();
|
|
12141
|
+
}
|
|
11816
12142
|
},
|
|
11817
12143
|
stopAndGetLastProgress: () => {
|
|
11818
|
-
|
|
11819
|
-
controller._internals.signal.removeEventListener("abort", onAbort);
|
|
12144
|
+
cleanup();
|
|
11820
12145
|
return currentState;
|
|
11821
12146
|
}
|
|
11822
12147
|
};
|
|
@@ -11839,6 +12164,7 @@ var internalParseMedia = async function({
|
|
|
11839
12164
|
acknowledgeRemotionLicense,
|
|
11840
12165
|
apiName,
|
|
11841
12166
|
selectM3uStream: selectM3uStreamFn,
|
|
12167
|
+
selectM3uAssociatedPlaylists: selectM3uAssociatedPlaylistsFn,
|
|
11842
12168
|
...more
|
|
11843
12169
|
}) {
|
|
11844
12170
|
warnIfRemotionLicenseNotAcknowledged({
|
|
@@ -11890,7 +12216,8 @@ var internalParseMedia = async function({
|
|
|
11890
12216
|
readerInterface,
|
|
11891
12217
|
src,
|
|
11892
12218
|
onDiscardedData,
|
|
11893
|
-
selectM3uStreamFn
|
|
12219
|
+
selectM3uStreamFn,
|
|
12220
|
+
selectM3uAssociatedPlaylistsFn
|
|
11894
12221
|
});
|
|
11895
12222
|
const { iterator } = state;
|
|
11896
12223
|
let currentReader = readerInstance;
|
|
@@ -11930,7 +12257,7 @@ var internalParseMedia = async function({
|
|
|
11930
12257
|
return true;
|
|
11931
12258
|
}
|
|
11932
12259
|
if (state.iterator.counter.getOffset() === contentLength) {
|
|
11933
|
-
if (state.getStructure().type === "m3u" && !state.m3u.
|
|
12260
|
+
if (state.getStructure().type === "m3u" && !state.m3u.getAllChunksProcessedOverall()) {
|
|
11934
12261
|
return false;
|
|
11935
12262
|
}
|
|
11936
12263
|
Log.verbose(logLevel, "Reached end of file");
|
|
@@ -12053,6 +12380,7 @@ var internalParseMedia = async function({
|
|
|
12053
12380
|
currentReader.abort();
|
|
12054
12381
|
iterator?.destroy();
|
|
12055
12382
|
state.callbacks.tracks.ensureHasTracksAtEnd(fields);
|
|
12383
|
+
state.m3u.abortM3UStreamRuns();
|
|
12056
12384
|
if (errored) {
|
|
12057
12385
|
throw errored;
|
|
12058
12386
|
}
|
|
@@ -12075,6 +12403,7 @@ var downloadAndParseMedia = async (options) => {
|
|
|
12075
12403
|
onContainer: options.onContainer ?? null,
|
|
12076
12404
|
onDimensions: options.onDimensions ?? null,
|
|
12077
12405
|
selectM3uStream: options.selectM3uStream ?? defaultSelectM3uStreamFn,
|
|
12406
|
+
selectM3uAssociatedPlaylists: options.selectM3uAssociatedPlaylists ?? defaultSelectM3uAssociatedPlaylists,
|
|
12078
12407
|
onDiscardedData: async (data) => {
|
|
12079
12408
|
await content.write(data);
|
|
12080
12409
|
},
|
|
@@ -12125,7 +12454,7 @@ var downloadAndParseMedia = async (options) => {
|
|
|
12125
12454
|
return returnValue;
|
|
12126
12455
|
};
|
|
12127
12456
|
// src/version.ts
|
|
12128
|
-
var VERSION = "4.0.
|
|
12457
|
+
var VERSION = "4.0.270";
|
|
12129
12458
|
|
|
12130
12459
|
// src/index.ts
|
|
12131
12460
|
var MediaParserInternals = {
|
|
@@ -12149,6 +12478,7 @@ export {
|
|
|
12149
12478
|
hasBeenAborted,
|
|
12150
12479
|
downloadAndParseMedia,
|
|
12151
12480
|
defaultSelectM3uStreamFn,
|
|
12481
|
+
defaultSelectM3uAssociatedPlaylists,
|
|
12152
12482
|
VERSION,
|
|
12153
12483
|
MediaParserInternals,
|
|
12154
12484
|
MediaParserAbortError,
|