@remotion/media-parser 4.0.266 → 4.0.267

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.
Files changed (62) hide show
  1. package/dist/containers/flac/get-channel-count.d.ts +1 -1
  2. package/dist/containers/m3u/get-streams.d.ts +10 -0
  3. package/dist/containers/m3u/get-streams.js +20 -0
  4. package/dist/containers/m3u/parse-directive.js +8 -0
  5. package/dist/containers/m3u/parse-m3u-media-directive.d.ts +2 -0
  6. package/dist/containers/m3u/parse-m3u-media-directive.js +31 -0
  7. package/dist/containers/m3u/parse-stream-inf.js +1 -0
  8. package/dist/containers/m3u/types.d.ts +15 -1
  9. package/dist/esm/{from-fetch.mjs → fetch.mjs} +3 -119
  10. package/dist/esm/index.mjs +322 -269
  11. package/dist/esm/node-writer.mjs +113 -0
  12. package/dist/esm/node.mjs +37 -106
  13. package/dist/fetch.d.ts +1 -0
  14. package/dist/fetch.js +17 -0
  15. package/dist/make-hvc1-codec-strings.js +3 -3
  16. package/dist/node-writer.d.ts +1 -0
  17. package/dist/node-writer.js +17 -0
  18. package/dist/node.d.ts +1 -0
  19. package/dist/node.js +17 -0
  20. package/dist/readers/fetch/get-body-and-reader.js +3 -2
  21. package/dist/state/parser-state.d.ts +2 -2
  22. package/dist/version.d.ts +1 -1
  23. package/dist/version.js +1 -1
  24. package/dist/web-file.d.ts +1 -0
  25. package/dist/web-file.js +17 -0
  26. package/package.json +32 -32
  27. package/dist/containers/flac/m3u/after-manifest-fetch.d.ts +0 -14
  28. package/dist/containers/flac/m3u/after-manifest-fetch.js +0 -53
  29. package/dist/containers/flac/m3u/fetch-m3u8-stream.d.ts +0 -3
  30. package/dist/containers/flac/m3u/fetch-m3u8-stream.js +0 -15
  31. package/dist/containers/flac/m3u/get-chunks.d.ts +0 -6
  32. package/dist/containers/flac/m3u/get-chunks.js +0 -20
  33. package/dist/containers/flac/m3u/get-duration-from-m3u.d.ts +0 -2
  34. package/dist/containers/flac/m3u/get-duration-from-m3u.js +0 -9
  35. package/dist/containers/flac/m3u/get-playlist.d.ts +0 -3
  36. package/dist/containers/flac/m3u/get-playlist.js +0 -19
  37. package/dist/containers/flac/m3u/get-streams.d.ts +0 -13
  38. package/dist/containers/flac/m3u/get-streams.js +0 -41
  39. package/dist/containers/flac/m3u/parse-directive.d.ts +0 -2
  40. package/dist/containers/flac/m3u/parse-directive.js +0 -64
  41. package/dist/containers/flac/m3u/parse-m3u-manifest.d.ts +0 -8
  42. package/dist/containers/flac/m3u/parse-m3u-manifest.js +0 -18
  43. package/dist/containers/flac/m3u/parse-m3u.d.ts +0 -4
  44. package/dist/containers/flac/m3u/parse-m3u.js +0 -35
  45. package/dist/containers/flac/m3u/parse-m3u8-text.d.ts +0 -2
  46. package/dist/containers/flac/m3u/parse-m3u8-text.js +0 -23
  47. package/dist/containers/flac/m3u/parse-stream-inf.d.ts +0 -3
  48. package/dist/containers/flac/m3u/parse-stream-inf.js +0 -61
  49. package/dist/containers/flac/m3u/return-packets.d.ts +0 -14
  50. package/dist/containers/flac/m3u/return-packets.js +0 -63
  51. package/dist/containers/flac/m3u/select-stream.d.ts +0 -10
  52. package/dist/containers/flac/m3u/select-stream.js +0 -15
  53. package/dist/containers/flac/m3u/types.d.ts +0 -48
  54. package/dist/containers/flac/m3u/types.js +0 -2
  55. package/dist/containers/mp3/get-tracks-from-mp3.d.ts +0 -4
  56. package/dist/containers/mp3/get-tracks-from-mp3.js +0 -25
  57. package/dist/does-need-contentlength-contentrange.d.ts +0 -2
  58. package/dist/does-need-contentlength-contentrange.js +0 -10
  59. package/dist/esm/from-node.mjs +0 -44
  60. package/dist/readers/from-uintarray.d.ts +0 -2
  61. package/dist/readers/from-uintarray.js +0 -27
  62. /package/dist/esm/{from-web-file.mjs → web-file.mjs} +0 -0
@@ -1,264 +1,3 @@
1
- // src/errors.ts
2
- class IsAGifError extends Error {
3
- mimeType;
4
- sizeInBytes;
5
- fileName;
6
- constructor({
7
- message,
8
- mimeType,
9
- sizeInBytes,
10
- fileName
11
- }) {
12
- super(message);
13
- this.fileName = "IsAGifError";
14
- this.mimeType = mimeType;
15
- this.sizeInBytes = sizeInBytes;
16
- this.fileName = fileName;
17
- if (Error.captureStackTrace) {
18
- Error.captureStackTrace(this, IsAGifError);
19
- }
20
- }
21
- }
22
-
23
- class IsAnImageError extends Error {
24
- imageType;
25
- dimensions;
26
- mimeType;
27
- sizeInBytes;
28
- fileName;
29
- constructor({
30
- dimensions,
31
- imageType,
32
- message,
33
- mimeType,
34
- sizeInBytes,
35
- fileName
36
- }) {
37
- super(message);
38
- this.name = "IsAnImageError";
39
- this.imageType = imageType;
40
- this.dimensions = dimensions;
41
- this.mimeType = mimeType;
42
- this.sizeInBytes = sizeInBytes;
43
- this.fileName = fileName;
44
- if (Error.captureStackTrace) {
45
- Error.captureStackTrace(this, IsAnImageError);
46
- }
47
- }
48
- }
49
-
50
- class IsAPdfError extends Error {
51
- mimeType;
52
- sizeInBytes;
53
- fileName;
54
- constructor({
55
- message,
56
- mimeType,
57
- sizeInBytes,
58
- fileName
59
- }) {
60
- super(message);
61
- this.name = "IsAPdfError";
62
- this.mimeType = mimeType;
63
- this.sizeInBytes = sizeInBytes;
64
- this.fileName = fileName;
65
- if (Error.captureStackTrace) {
66
- Error.captureStackTrace(this, IsAPdfError);
67
- }
68
- }
69
- }
70
-
71
- class IsAnUnsupportedFileTypeError extends Error {
72
- mimeType;
73
- sizeInBytes;
74
- fileName;
75
- constructor({
76
- message,
77
- mimeType,
78
- sizeInBytes,
79
- fileName
80
- }) {
81
- super(message);
82
- this.name = "IsAnUnsupportedFileTypeError";
83
- this.mimeType = mimeType;
84
- this.sizeInBytes = sizeInBytes;
85
- this.fileName = fileName;
86
- if (Error.captureStackTrace) {
87
- Error.captureStackTrace(this, IsAnUnsupportedFileTypeError);
88
- }
89
- }
90
- }
91
-
92
- class IsAnUnsupportedAudioTypeError extends Error {
93
- mimeType;
94
- sizeInBytes;
95
- fileName;
96
- audioType;
97
- constructor({
98
- message,
99
- mimeType,
100
- sizeInBytes,
101
- fileName,
102
- audioType
103
- }) {
104
- super(message);
105
- this.name = "IsAnUnsupportedAudioTypeError";
106
- this.mimeType = mimeType;
107
- this.sizeInBytes = sizeInBytes;
108
- this.fileName = fileName;
109
- this.audioType = audioType;
110
- if (Error.captureStackTrace) {
111
- Error.captureStackTrace(this, IsAnUnsupportedAudioTypeError);
112
- }
113
- }
114
- }
115
-
116
- class MediaParserAbortError extends Error {
117
- constructor(message) {
118
- super(message);
119
- this.name = "MediaParserAbortError";
120
- this.cause = undefined;
121
- }
122
- }
123
- var hasBeenAborted = (error) => {
124
- return error instanceof MediaParserAbortError;
125
- };
126
-
127
- // src/readers/fetch/get-body-and-reader.ts
128
- var getLengthAndReader = async (endsWithM3u8, res, ownController) => {
129
- if (endsWithM3u8) {
130
- const text = await res.text();
131
- const stream = new ReadableStream({
132
- start(controller) {
133
- controller.enqueue(new TextEncoder().encode(text));
134
- controller.close();
135
- }
136
- });
137
- return {
138
- contentLength: text.length,
139
- reader: {
140
- reader: stream.getReader(),
141
- abort() {
142
- ownController.abort();
143
- }
144
- },
145
- needsContentRange: false
146
- };
147
- }
148
- const length = res.headers.get("content-length");
149
- const contentLength = length === null ? null : parseInt(length, 10);
150
- if (!res.body) {
151
- throw new Error("No body");
152
- }
153
- const reader = res.body.getReader();
154
- return {
155
- reader: {
156
- reader,
157
- abort: () => {
158
- ownController.abort();
159
- }
160
- },
161
- contentLength,
162
- needsContentRange: true
163
- };
164
- };
165
-
166
- // src/readers/fetch/resolve-url.ts
167
- var resolveUrl = (src) => {
168
- try {
169
- const resolvedUrl = typeof window !== "undefined" && typeof window.location !== "undefined" ? new URL(src, window.location.origin) : new URL(src);
170
- return resolvedUrl;
171
- } catch {
172
- return src;
173
- }
174
- };
175
-
176
- // src/readers/from-fetch.ts
177
- function parseContentRange(input) {
178
- const matches = input.match(/^(\w+) ((\d+)-(\d+)|\*)\/(\d+|\*)$/);
179
- if (!matches)
180
- return null;
181
- const [, unit, , start, end, size] = matches;
182
- const range = {
183
- unit,
184
- start: start != null ? Number(start) : null,
185
- end: end != null ? Number(end) : null,
186
- size: size === "*" ? null : Number(size)
187
- };
188
- if (range.start === null && range.end === null && range.size === null) {
189
- return null;
190
- }
191
- return range;
192
- }
193
- var validateContentRangeAndDetectIfSupported = (actualRange, parsedContentRange, statusCode) => {
194
- if (statusCode === 206) {
195
- return { supportsContentRange: true };
196
- }
197
- if (typeof actualRange === "number" && parsedContentRange?.start !== actualRange) {
198
- if (actualRange === 0) {
199
- return { supportsContentRange: false };
200
- }
201
- throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
202
- }
203
- if (actualRange !== null && typeof actualRange !== "number" && (parsedContentRange?.start !== actualRange[0] || parsedContentRange?.end !== actualRange[1])) {
204
- throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
205
- }
206
- return { supportsContentRange: true };
207
- };
208
- var fetchReader = {
209
- read: async ({ src, range, controller }) => {
210
- if (typeof src !== "string") {
211
- throw new Error("src must be a string when using `fetchReader`");
212
- }
213
- const resolvedUrl = resolveUrl(src);
214
- const resolvedUrlString = resolvedUrl.toString();
215
- if (!resolvedUrlString.startsWith("https://") && !resolvedUrlString.startsWith("blob:") && !resolvedUrlString.startsWith("http://")) {
216
- return Promise.reject(new Error(`${resolvedUrlString} is not a URL - needs to start with http:// or https:// or blob:. If you want to read a local file, pass \`reader: nodeReader\` to parseMedia().`));
217
- }
218
- const ownController = new AbortController;
219
- const cache = typeof navigator !== "undefined" && navigator.userAgent.includes("Cloudflare-Workers") ? undefined : "no-store";
220
- const actualRange = range === null ? 0 : range;
221
- const endsWithM3u8 = (typeof resolvedUrl === "string" ? resolvedUrl : resolvedUrl.pathname).endsWith(".m3u8");
222
- const headers = actualRange === 0 && endsWithM3u8 ? {} : typeof actualRange === "number" ? {
223
- Range: `bytes=${actualRange}-`
224
- } : {
225
- Range: `bytes=${`${actualRange[0]}-${actualRange[1]}`}`
226
- };
227
- const res = await fetch(resolvedUrl, {
228
- headers,
229
- signal: ownController.signal,
230
- cache
231
- });
232
- const contentRange = res.headers.get("content-range");
233
- const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
234
- const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
235
- controller._internals.signal.addEventListener("abort", () => {
236
- ownController.abort(new MediaParserAbortError("Aborted by user"));
237
- }, { once: true });
238
- if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
239
- throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
240
- }
241
- const contentDisposition = res.headers.get("content-disposition");
242
- const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
243
- const fallbackName = src.split("/").pop();
244
- const { contentLength, needsContentRange, reader } = await getLengthAndReader(endsWithM3u8, res, ownController);
245
- if (controller) {
246
- controller._internals.signal.addEventListener("abort", () => {
247
- reader.reader.cancel().catch(() => {
248
- });
249
- }, { once: true });
250
- }
251
- return {
252
- reader,
253
- contentLength,
254
- contentType: res.headers.get("content-type"),
255
- name: name ?? fallbackName,
256
- supportsContentRange,
257
- needsContentRange
258
- };
259
- }
260
- };
261
-
262
1
  // src/aac-codecprivate.ts
263
2
  var getSampleRateFromSampleFrequencyIndex = (samplingFrequencyIndex) => {
264
3
  switch (samplingFrequencyIndex) {
@@ -2766,8 +2505,8 @@ var getHvc1CodecString = (data) => {
2766
2505
  const generalProfileSpaceTierFlagAndIdc = data.getUint8();
2767
2506
  let generalProfileCompatibility = data.getUint32();
2768
2507
  const generalProfileSpace = generalProfileSpaceTierFlagAndIdc >> 6;
2769
- const generalTierFlag = generalProfileSpaceTierFlagAndIdc >> 5;
2770
- const generalProfileIdc = generalProfileSpaceTierFlagAndIdc >> 0;
2508
+ const generalTierFlag = (generalProfileSpaceTierFlagAndIdc & 32) >> 5;
2509
+ const generalProfileIdc = generalProfileSpaceTierFlagAndIdc & 31;
2771
2510
  const generalConstraintIndicator = data.getSlice(6);
2772
2511
  const generalLevelIdc = data.getUint8();
2773
2512
  let profileId = 0;
@@ -2788,7 +2527,7 @@ var getHvc1CodecString = (data) => {
2788
2527
  hasByte = true;
2789
2528
  }
2790
2529
  }
2791
- return `${profileSpaceChar}${generalProfileIdc.toString(16)}.${profileId.toString(16)}.${generalTierChar}${generalLevelIdc}.${generalConstraintString}`;
2530
+ return `${profileSpaceChar}${generalProfileIdc.toString(16)}.${profileId.toString(16)}.${generalTierChar}${generalLevelIdc}${generalConstraintString ? "." : ""}${generalConstraintString}`;
2792
2531
  };
2793
2532
 
2794
2533
  // src/containers/webm/av1-codec-private.ts
@@ -5808,12 +5547,30 @@ var getM3uStreams = (structure, originalSrc) => {
5808
5547
  if (next.type !== "m3u-text-value") {
5809
5548
  throw new Error("Expected m3u-text-value");
5810
5549
  }
5811
- boxes.push({
5812
- url: originalSrc && originalSrc.startsWith("http") ? new URL(next.value, originalSrc).href : next.value,
5550
+ const dedicatedAudioTracks = [];
5551
+ if (str.audio) {
5552
+ const match = structure.boxes.filter((box) => {
5553
+ return box.type === "m3u-media-info" && box.groupId === str.audio;
5554
+ });
5555
+ for (const audioTrack of match) {
5556
+ dedicatedAudioTracks.push({
5557
+ autoselect: audioTrack.autoselect,
5558
+ channels: audioTrack.channels,
5559
+ default: audioTrack.default,
5560
+ groupId: audioTrack.groupId,
5561
+ language: audioTrack.language,
5562
+ name: audioTrack.name,
5563
+ url: originalSrc && originalSrc.startsWith("http") ? new URL(audioTrack.uri, originalSrc).href : audioTrack.uri
5564
+ });
5565
+ }
5566
+ }
5567
+ boxes.push({
5568
+ url: originalSrc && originalSrc.startsWith("http") ? new URL(next.value, originalSrc).href : next.value,
5813
5569
  averageBandwidth: str.averageBandwidth,
5814
5570
  bandwidth: str.bandwidth,
5815
5571
  codecs: str.codecs,
5816
- resolution: str.resolution
5572
+ resolution: str.resolution,
5573
+ dedicatedAudioTracks
5817
5574
  });
5818
5575
  }
5819
5576
  }
@@ -7344,6 +7101,132 @@ class MediaParserEmitter {
7344
7101
  };
7345
7102
  }
7346
7103
 
7104
+ // src/errors.ts
7105
+ class IsAGifError extends Error {
7106
+ mimeType;
7107
+ sizeInBytes;
7108
+ fileName;
7109
+ constructor({
7110
+ message,
7111
+ mimeType,
7112
+ sizeInBytes,
7113
+ fileName
7114
+ }) {
7115
+ super(message);
7116
+ this.fileName = "IsAGifError";
7117
+ this.mimeType = mimeType;
7118
+ this.sizeInBytes = sizeInBytes;
7119
+ this.fileName = fileName;
7120
+ if (Error.captureStackTrace) {
7121
+ Error.captureStackTrace(this, IsAGifError);
7122
+ }
7123
+ }
7124
+ }
7125
+
7126
+ class IsAnImageError extends Error {
7127
+ imageType;
7128
+ dimensions;
7129
+ mimeType;
7130
+ sizeInBytes;
7131
+ fileName;
7132
+ constructor({
7133
+ dimensions,
7134
+ imageType,
7135
+ message,
7136
+ mimeType,
7137
+ sizeInBytes,
7138
+ fileName
7139
+ }) {
7140
+ super(message);
7141
+ this.name = "IsAnImageError";
7142
+ this.imageType = imageType;
7143
+ this.dimensions = dimensions;
7144
+ this.mimeType = mimeType;
7145
+ this.sizeInBytes = sizeInBytes;
7146
+ this.fileName = fileName;
7147
+ if (Error.captureStackTrace) {
7148
+ Error.captureStackTrace(this, IsAnImageError);
7149
+ }
7150
+ }
7151
+ }
7152
+
7153
+ class IsAPdfError extends Error {
7154
+ mimeType;
7155
+ sizeInBytes;
7156
+ fileName;
7157
+ constructor({
7158
+ message,
7159
+ mimeType,
7160
+ sizeInBytes,
7161
+ fileName
7162
+ }) {
7163
+ super(message);
7164
+ this.name = "IsAPdfError";
7165
+ this.mimeType = mimeType;
7166
+ this.sizeInBytes = sizeInBytes;
7167
+ this.fileName = fileName;
7168
+ if (Error.captureStackTrace) {
7169
+ Error.captureStackTrace(this, IsAPdfError);
7170
+ }
7171
+ }
7172
+ }
7173
+
7174
+ class IsAnUnsupportedFileTypeError extends Error {
7175
+ mimeType;
7176
+ sizeInBytes;
7177
+ fileName;
7178
+ constructor({
7179
+ message,
7180
+ mimeType,
7181
+ sizeInBytes,
7182
+ fileName
7183
+ }) {
7184
+ super(message);
7185
+ this.name = "IsAnUnsupportedFileTypeError";
7186
+ this.mimeType = mimeType;
7187
+ this.sizeInBytes = sizeInBytes;
7188
+ this.fileName = fileName;
7189
+ if (Error.captureStackTrace) {
7190
+ Error.captureStackTrace(this, IsAnUnsupportedFileTypeError);
7191
+ }
7192
+ }
7193
+ }
7194
+
7195
+ class IsAnUnsupportedAudioTypeError extends Error {
7196
+ mimeType;
7197
+ sizeInBytes;
7198
+ fileName;
7199
+ audioType;
7200
+ constructor({
7201
+ message,
7202
+ mimeType,
7203
+ sizeInBytes,
7204
+ fileName,
7205
+ audioType
7206
+ }) {
7207
+ super(message);
7208
+ this.name = "IsAnUnsupportedAudioTypeError";
7209
+ this.mimeType = mimeType;
7210
+ this.sizeInBytes = sizeInBytes;
7211
+ this.fileName = fileName;
7212
+ this.audioType = audioType;
7213
+ if (Error.captureStackTrace) {
7214
+ Error.captureStackTrace(this, IsAnUnsupportedAudioTypeError);
7215
+ }
7216
+ }
7217
+ }
7218
+
7219
+ class MediaParserAbortError extends Error {
7220
+ constructor(message) {
7221
+ super(message);
7222
+ this.name = "MediaParserAbortError";
7223
+ this.cause = undefined;
7224
+ }
7225
+ }
7226
+ var hasBeenAborted = (error) => {
7227
+ return error instanceof MediaParserAbortError;
7228
+ };
7229
+
7347
7230
  // src/pause-signal.ts
7348
7231
  var makePauseSignal = (emitter) => {
7349
7232
  const waiterFns = [];
@@ -8975,7 +8858,34 @@ var parseStreamInf = (str) => {
8975
8858
  resolution: map.RESOLUTION ? {
8976
8859
  width: parseInt(map.RESOLUTION.split("x")[0], 10),
8977
8860
  height: parseInt(map.RESOLUTION.split("x")[1], 10)
8978
- } : null
8861
+ } : null,
8862
+ audio: map.AUDIO || null
8863
+ };
8864
+ };
8865
+
8866
+ // src/containers/m3u/parse-m3u-media-directive.ts
8867
+ var parseM3uMediaDirective = (str) => {
8868
+ const quotes = splitRespectingQuotes(str);
8869
+ const map = {};
8870
+ for (const quote of quotes) {
8871
+ const firstColon = quote.indexOf("=");
8872
+ const key = firstColon === -1 ? quote : quote.slice(0, firstColon);
8873
+ const value = firstColon === -1 ? null : quote.slice(firstColon + 1);
8874
+ if (value === null) {
8875
+ throw new Error("Value is null");
8876
+ }
8877
+ const actualValue = value?.startsWith('"') && value?.endsWith('"') ? value.slice(1, -1) : value;
8878
+ map[key] = actualValue;
8879
+ }
8880
+ return {
8881
+ type: "m3u-media-info",
8882
+ autoselect: map.AUTOSELECT === "YES",
8883
+ channels: map.CHANNELS ? parseInt(map.CHANNELS, 10) : null,
8884
+ default: map.DEFAULT === "YES",
8885
+ groupId: map["GROUP-ID"],
8886
+ language: map.LANGUAGE || null,
8887
+ name: map.NAME || null,
8888
+ uri: map.URI
8979
8889
  };
8980
8890
  };
8981
8891
 
@@ -8998,6 +8908,13 @@ var parseM3uDirective = (str) => {
8998
8908
  type: "m3u-independent-segments"
8999
8909
  };
9000
8910
  }
8911
+ if (directive === "#EXT-X-MEDIA") {
8912
+ if (!value) {
8913
+ throw new Error("EXT-X-MEDIA directive must have a value");
8914
+ }
8915
+ const parsed = parseM3uMediaDirective(value);
8916
+ return parsed;
8917
+ }
9001
8918
  if (directive === "#EXT-X-TARGETDURATION") {
9002
8919
  if (!value) {
9003
8920
  throw new Error("EXT-X-TARGETDURATION directive must have a value");
@@ -9121,6 +9038,142 @@ var defaultSelectM3uStreamFn = ({ streams }) => {
9121
9038
  return Promise.resolve(streams[0].id);
9122
9039
  };
9123
9040
 
9041
+ // src/readers/fetch/get-body-and-reader.ts
9042
+ var getLengthAndReader = async (endsWithM3u8, res, ownController) => {
9043
+ if (endsWithM3u8) {
9044
+ const text = await res.text();
9045
+ const encoded = new TextEncoder().encode(text);
9046
+ const stream = new ReadableStream({
9047
+ start(controller) {
9048
+ controller.enqueue(encoded);
9049
+ controller.close();
9050
+ }
9051
+ });
9052
+ return {
9053
+ contentLength: encoded.byteLength,
9054
+ reader: {
9055
+ reader: stream.getReader(),
9056
+ abort() {
9057
+ ownController.abort();
9058
+ }
9059
+ },
9060
+ needsContentRange: false
9061
+ };
9062
+ }
9063
+ const length = res.headers.get("content-length");
9064
+ const contentLength = length === null ? null : parseInt(length, 10);
9065
+ if (!res.body) {
9066
+ throw new Error("No body");
9067
+ }
9068
+ const reader = res.body.getReader();
9069
+ return {
9070
+ reader: {
9071
+ reader,
9072
+ abort: () => {
9073
+ ownController.abort();
9074
+ }
9075
+ },
9076
+ contentLength,
9077
+ needsContentRange: true
9078
+ };
9079
+ };
9080
+
9081
+ // src/readers/fetch/resolve-url.ts
9082
+ var resolveUrl = (src) => {
9083
+ try {
9084
+ const resolvedUrl = typeof window !== "undefined" && typeof window.location !== "undefined" ? new URL(src, window.location.origin) : new URL(src);
9085
+ return resolvedUrl;
9086
+ } catch {
9087
+ return src;
9088
+ }
9089
+ };
9090
+
9091
+ // src/readers/from-fetch.ts
9092
+ function parseContentRange(input) {
9093
+ const matches = input.match(/^(\w+) ((\d+)-(\d+)|\*)\/(\d+|\*)$/);
9094
+ if (!matches)
9095
+ return null;
9096
+ const [, unit, , start, end, size] = matches;
9097
+ const range2 = {
9098
+ unit,
9099
+ start: start != null ? Number(start) : null,
9100
+ end: end != null ? Number(end) : null,
9101
+ size: size === "*" ? null : Number(size)
9102
+ };
9103
+ if (range2.start === null && range2.end === null && range2.size === null) {
9104
+ return null;
9105
+ }
9106
+ return range2;
9107
+ }
9108
+ var validateContentRangeAndDetectIfSupported = (actualRange, parsedContentRange, statusCode) => {
9109
+ if (statusCode === 206) {
9110
+ return { supportsContentRange: true };
9111
+ }
9112
+ if (typeof actualRange === "number" && parsedContentRange?.start !== actualRange) {
9113
+ if (actualRange === 0) {
9114
+ return { supportsContentRange: false };
9115
+ }
9116
+ throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
9117
+ }
9118
+ if (actualRange !== null && typeof actualRange !== "number" && (parsedContentRange?.start !== actualRange[0] || parsedContentRange?.end !== actualRange[1])) {
9119
+ throw new Error(`Range header (${actualRange}) does not match content-range header (${parsedContentRange?.start})`);
9120
+ }
9121
+ return { supportsContentRange: true };
9122
+ };
9123
+ var fetchReader = {
9124
+ read: async ({ src, range: range2, controller }) => {
9125
+ if (typeof src !== "string") {
9126
+ throw new Error("src must be a string when using `fetchReader`");
9127
+ }
9128
+ const resolvedUrl = resolveUrl(src);
9129
+ const resolvedUrlString = resolvedUrl.toString();
9130
+ if (!resolvedUrlString.startsWith("https://") && !resolvedUrlString.startsWith("blob:") && !resolvedUrlString.startsWith("http://")) {
9131
+ return Promise.reject(new Error(`${resolvedUrlString} is not a URL - needs to start with http:// or https:// or blob:. If you want to read a local file, pass \`reader: nodeReader\` to parseMedia().`));
9132
+ }
9133
+ const ownController = new AbortController;
9134
+ const cache = typeof navigator !== "undefined" && navigator.userAgent.includes("Cloudflare-Workers") ? undefined : "no-store";
9135
+ const actualRange = range2 === null ? 0 : range2;
9136
+ const endsWithM3u8 = (typeof resolvedUrl === "string" ? resolvedUrl : resolvedUrl.pathname).endsWith(".m3u8");
9137
+ const headers = actualRange === 0 && endsWithM3u8 ? {} : typeof actualRange === "number" ? {
9138
+ Range: `bytes=${actualRange}-`
9139
+ } : {
9140
+ Range: `bytes=${`${actualRange[0]}-${actualRange[1]}`}`
9141
+ };
9142
+ const res = await fetch(resolvedUrl, {
9143
+ headers,
9144
+ signal: ownController.signal,
9145
+ cache
9146
+ });
9147
+ const contentRange = res.headers.get("content-range");
9148
+ const parsedContentRange = contentRange ? parseContentRange(contentRange) : null;
9149
+ const { supportsContentRange } = validateContentRangeAndDetectIfSupported(actualRange, parsedContentRange, res.status);
9150
+ controller._internals.signal.addEventListener("abort", () => {
9151
+ ownController.abort(new MediaParserAbortError("Aborted by user"));
9152
+ }, { once: true });
9153
+ if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
9154
+ throw new Error(`Server returned status code ${res.status} for ${src} and range ${actualRange}`);
9155
+ }
9156
+ const contentDisposition = res.headers.get("content-disposition");
9157
+ const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
9158
+ const fallbackName = src.split("/").pop();
9159
+ const { contentLength, needsContentRange, reader } = await getLengthAndReader(endsWithM3u8, res, ownController);
9160
+ if (controller) {
9161
+ controller._internals.signal.addEventListener("abort", () => {
9162
+ reader.reader.cancel().catch(() => {
9163
+ });
9164
+ }, { once: true });
9165
+ }
9166
+ return {
9167
+ reader,
9168
+ contentLength,
9169
+ contentType: res.headers.get("content-type"),
9170
+ name: name ?? fallbackName,
9171
+ supportsContentRange,
9172
+ needsContentRange
9173
+ };
9174
+ }
9175
+ };
9176
+
9124
9177
  // src/parse-media.ts
9125
9178
  var parseMedia = (options) => {
9126
9179
  return internalParseMedia({
@@ -11969,7 +12022,7 @@ var downloadAndParseMedia = async (options) => {
11969
12022
  return returnValue;
11970
12023
  };
11971
12024
  // src/version.ts
11972
- var VERSION = "4.0.266";
12025
+ var VERSION = "4.0.267";
11973
12026
 
11974
12027
  // src/index.ts
11975
12028
  var MediaParserInternals = {