@remotion/media-parser 4.0.289 → 4.0.291

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 (167) hide show
  1. package/dist/containers/flac/get-channel-count.d.ts +1 -1
  2. package/dist/containers/iso-base-media/base-media-box.d.ts +0 -1
  3. package/dist/containers/iso-base-media/collect-sample-positions-from-moof-boxes.d.ts +4 -1
  4. package/dist/containers/iso-base-media/collect-sample-positions-from-moof-boxes.js +9 -5
  5. package/dist/containers/iso-base-media/find-keyframe-before-time.js +16 -11
  6. package/dist/containers/iso-base-media/find-track-to-seek.d.ts +14 -0
  7. package/dist/containers/iso-base-media/find-track-to-seek.js +39 -0
  8. package/dist/containers/iso-base-media/get-children.js +2 -2
  9. package/dist/containers/iso-base-media/get-keyframes.js +6 -1
  10. package/dist/containers/iso-base-media/get-mfra-seeking-box.d.ts +3 -1
  11. package/dist/containers/iso-base-media/get-mfra-seeking-box.js +5 -1
  12. package/dist/containers/iso-base-media/get-moov-atom.js +6 -3
  13. package/dist/containers/iso-base-media/get-sample-position-bounds.js +3 -1
  14. package/dist/containers/iso-base-media/get-sample-positions-from-track.js +1 -1
  15. package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.d.ts +14 -0
  16. package/dist/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.js +89 -0
  17. package/dist/containers/iso-base-media/get-seeking-byte.d.ts +3 -3
  18. package/dist/containers/iso-base-media/get-seeking-byte.js +32 -96
  19. package/dist/containers/iso-base-media/get-video-codec-from-iso-track.d.ts +1 -1
  20. package/dist/containers/iso-base-media/mdat/calculate-jump-marks.d.ts +6 -0
  21. package/dist/containers/iso-base-media/mdat/calculate-jump-marks.js +131 -0
  22. package/dist/containers/iso-base-media/mdat/mdat.d.ts +2 -2
  23. package/dist/containers/iso-base-media/mdat/mdat.js +18 -2
  24. package/dist/containers/iso-base-media/mfra/find-best-segment-from-tfra.d.ts +3 -3
  25. package/dist/containers/iso-base-media/mfra/find-best-segment-from-tfra.js +2 -2
  26. package/dist/containers/iso-base-media/mfra/get-mfra-atom.d.ts +5 -1
  27. package/dist/containers/iso-base-media/mfra/get-mfra-atom.js +3 -1
  28. package/dist/containers/iso-base-media/mfra/get-mfro-atom.d.ts +5 -1
  29. package/dist/containers/iso-base-media/mfra/get-mfro-atom.js +3 -1
  30. package/dist/containers/iso-base-media/parse-boxes.js +5 -2
  31. package/dist/containers/iso-base-media/process-box.d.ts +16 -5
  32. package/dist/containers/iso-base-media/process-box.js +206 -118
  33. package/dist/containers/iso-base-media/sample-positions.d.ts +25 -0
  34. package/dist/containers/iso-base-media/sample-positions.js +37 -0
  35. package/dist/containers/iso-base-media/seeking-hints.d.ts +1 -1
  36. package/dist/containers/iso-base-media/stsd/samples.js +1 -0
  37. package/dist/containers/iso-base-media/stsd/stsc.d.ts +1 -6
  38. package/dist/containers/iso-base-media/stsd/stsc.js +2 -5
  39. package/dist/containers/iso-base-media/stsd/stss.d.ts +1 -1
  40. package/dist/containers/iso-base-media/stsd/stss.js +2 -2
  41. package/dist/containers/iso-base-media/turn-sample-positions-into-array.d.ts +19 -0
  42. package/dist/containers/iso-base-media/turn-sample-positions-into-array.js +73 -0
  43. package/dist/containers/m3u/after-manifest-fetch.d.ts +5 -1
  44. package/dist/containers/m3u/after-manifest-fetch.js +3 -1
  45. package/dist/containers/m3u/first-sample-in-m3u-chunk.d.ts +13 -0
  46. package/dist/containers/m3u/first-sample-in-m3u-chunk.js +31 -0
  47. package/dist/containers/m3u/get-seeking-byte.d.ts +13 -0
  48. package/dist/containers/m3u/get-seeking-byte.js +32 -0
  49. package/dist/containers/m3u/get-streams.d.ts +1 -0
  50. package/dist/containers/m3u/get-streams.js +1 -0
  51. package/dist/containers/m3u/iterate-over-segment-files.d.ts +5 -3
  52. package/dist/containers/m3u/iterate-over-segment-files.js +11 -1
  53. package/dist/containers/m3u/parse-m3u-media-directive.js +1 -0
  54. package/dist/containers/m3u/parse-m3u.js +8 -0
  55. package/dist/containers/m3u/process-m3u-chunk.d.ts +12 -0
  56. package/dist/containers/m3u/process-m3u-chunk.js +274 -0
  57. package/dist/containers/m3u/run-over-m3u.js +7 -80
  58. package/dist/containers/m3u/sample-sorter.d.ts +1 -0
  59. package/dist/containers/m3u/sample-sorter.js +4 -1
  60. package/dist/containers/m3u/seek/get-chunk-to-seek-to.d.ts +5 -0
  61. package/dist/containers/m3u/seek/get-chunk-to-seek-to.js +14 -0
  62. package/dist/containers/m3u/seeking-hints.d.ts +2 -0
  63. package/dist/containers/m3u/seeking-hints.js +9 -0
  64. package/dist/containers/m3u/select-stream.d.ts +2 -1
  65. package/dist/containers/m3u/select-stream.js +7 -2
  66. package/dist/containers/m3u/types.d.ts +1 -0
  67. package/dist/containers/mp3/get-duration.d.ts +5 -0
  68. package/dist/containers/riff/seek/fetch-idx1.d.ts +3 -1
  69. package/dist/containers/riff/seek/fetch-idx1.js +3 -1
  70. package/dist/containers/transport-stream/handle-aac-packet.d.ts +2 -2
  71. package/dist/containers/transport-stream/handle-avc-packet.d.ts +2 -2
  72. package/dist/containers/transport-stream/process-audio.d.ts +2 -2
  73. package/dist/containers/transport-stream/process-stream-buffers.d.ts +3 -3
  74. package/dist/containers/transport-stream/process-video.d.ts +2 -2
  75. package/dist/containers/wav/get-duration-from-wav.d.ts +0 -1
  76. package/dist/containers/webm/get-sample-from-block.d.ts +12 -2
  77. package/dist/containers/webm/get-sample-from-block.js +40 -9
  78. package/dist/containers/webm/parse-ebml.js +28 -10
  79. package/dist/containers/webm/seek/fetch-web-cues.d.ts +3 -1
  80. package/dist/containers/webm/seek/fetch-web-cues.js +3 -1
  81. package/dist/containers/webm/state-for-processing.d.ts +2 -2
  82. package/dist/controller/media-parser-controller.d.ts +1 -1
  83. package/dist/controller/media-parser-controller.js +6 -2
  84. package/dist/controller/seek-signal.d.ts +1 -5
  85. package/dist/download-and-parse-media.js +1 -1
  86. package/dist/esm/index.mjs +1400 -611
  87. package/dist/esm/node.mjs +23 -3
  88. package/dist/esm/server-worker.mjs +8 -1
  89. package/dist/esm/universal.mjs +168 -15
  90. package/dist/esm/web.mjs +145 -13
  91. package/dist/esm/worker-server-entry.mjs +1467 -635
  92. package/dist/esm/worker-web-entry.mjs +1439 -634
  93. package/dist/esm/worker.mjs +8 -1
  94. package/dist/get-audio-codec.js +3 -0
  95. package/dist/get-duration.js +2 -1
  96. package/dist/get-fps.js +2 -1
  97. package/dist/get-sample-positions-from-mp4.js +10 -5
  98. package/dist/get-sample-positions.js +4 -4
  99. package/dist/get-seeking-byte.d.ts +5 -3
  100. package/dist/get-seeking-byte.js +19 -10
  101. package/dist/get-seeking-hints.d.ts +3 -3
  102. package/dist/get-seeking-hints.js +18 -13
  103. package/dist/get-tracks.d.ts +9 -1
  104. package/dist/get-tracks.js +13 -6
  105. package/dist/index.d.ts +21 -5
  106. package/dist/init-video.js +3 -2
  107. package/dist/internal-parse-media.js +13 -4
  108. package/dist/iterator/buffer-iterator.js +5 -3
  109. package/dist/metadata/metadata-from-iso.js +2 -1
  110. package/dist/options.d.ts +6 -1
  111. package/dist/parse-loop.js +22 -6
  112. package/dist/parse-media-on-worker-entry.js +1 -0
  113. package/dist/parse-media.js +1 -1
  114. package/dist/parse-result.d.ts +2 -2
  115. package/dist/perform-seek.d.ts +3 -1
  116. package/dist/perform-seek.js +3 -1
  117. package/dist/readers/fetch/get-body-and-reader.js +17 -2
  118. package/dist/readers/from-fetch.d.ts +17 -1
  119. package/dist/readers/from-fetch.js +68 -13
  120. package/dist/readers/from-node.js +24 -2
  121. package/dist/readers/from-web-file.js +3 -0
  122. package/dist/readers/reader.d.ts +19 -2
  123. package/dist/readers/universal.js +9 -0
  124. package/dist/readers/web.js +6 -0
  125. package/dist/register-track.d.ts +3 -3
  126. package/dist/seek-backwards.d.ts +3 -1
  127. package/dist/seek-backwards.js +4 -1
  128. package/dist/seek-forwards.d.ts +3 -1
  129. package/dist/seek-forwards.js +3 -1
  130. package/dist/seeking-hints.d.ts +4 -1
  131. package/dist/set-seeking-hints.js +4 -0
  132. package/dist/skip.d.ts +5 -0
  133. package/dist/skip.js +6 -1
  134. package/dist/state/can-skip-tracks.d.ts +1 -0
  135. package/dist/state/can-skip-tracks.js +10 -6
  136. package/dist/state/iso-base-media/cached-sample-positions.d.ts +15 -1
  137. package/dist/state/iso-base-media/cached-sample-positions.js +9 -4
  138. package/dist/state/iso-base-media/iso-state.d.ts +5 -1
  139. package/dist/state/iso-base-media/iso-state.js +2 -1
  140. package/dist/state/iso-base-media/lazy-mfra-load.d.ts +3 -1
  141. package/dist/state/iso-base-media/lazy-mfra-load.js +2 -1
  142. package/dist/state/keyframes.js +1 -0
  143. package/dist/state/m3u-state.d.ts +15 -4
  144. package/dist/state/m3u-state.js +20 -0
  145. package/dist/state/matroska/lazy-cues-fetch.d.ts +3 -1
  146. package/dist/state/matroska/lazy-cues-fetch.js +2 -1
  147. package/dist/state/matroska/webm.d.ts +3 -1
  148. package/dist/state/matroska/webm.js +2 -1
  149. package/dist/state/mp3.d.ts +16 -5
  150. package/dist/state/mp3.js +7 -5
  151. package/dist/state/parser-state.d.ts +31 -15
  152. package/dist/state/parser-state.js +19 -5
  153. package/dist/state/riff/lazy-idx1-fetch.d.ts +5 -3
  154. package/dist/state/riff/lazy-idx1-fetch.js +2 -1
  155. package/dist/state/riff.d.ts +5 -3
  156. package/dist/state/riff.js +2 -1
  157. package/dist/state/sample-callbacks.d.ts +3 -2
  158. package/dist/state/sample-callbacks.js +3 -3
  159. package/dist/version.d.ts +1 -1
  160. package/dist/version.js +1 -1
  161. package/dist/work-on-seek-request.d.ts +6 -3
  162. package/dist/work-on-seek-request.js +13 -13
  163. package/dist/worker/forward-controller-to-worker.js +1 -1
  164. package/dist/worker/serialize-error.js +26 -3
  165. package/dist/worker/worker-types.d.ts +7 -1
  166. package/dist/worker-server.js +2 -2
  167. package/package.json +3 -3
package/dist/esm/node.mjs CHANGED
@@ -1,7 +1,6 @@
1
1
  // src/readers/from-node.ts
2
2
  import { createReadStream, promises, statSync } from "fs";
3
3
  import { dirname, join, relative, sep } from "path";
4
- import { Readable } from "stream";
5
4
  var nodeReadContent = ({ src, range, controller }) => {
6
5
  if (typeof src !== "string") {
7
6
  throw new Error("src must be a string when using `nodeReader`");
@@ -16,7 +15,27 @@ var nodeReadContent = ({ src, range, controller }) => {
16
15
  ownController.abort();
17
16
  }, { once: true });
18
17
  const stats = statSync(src);
19
- const reader = Readable.toWeb(stream).getReader();
18
+ let readerCancelled = false;
19
+ const reader = new ReadableStream({
20
+ start(c) {
21
+ if (readerCancelled) {
22
+ return;
23
+ }
24
+ stream.on("data", (chunk) => {
25
+ c.enqueue(chunk);
26
+ });
27
+ stream.on("end", () => {
28
+ c.close();
29
+ });
30
+ stream.on("error", (err) => {
31
+ c.error(err);
32
+ });
33
+ },
34
+ cancel() {
35
+ readerCancelled = true;
36
+ stream.destroy();
37
+ }
38
+ }).getReader();
20
39
  if (controller) {
21
40
  controller._internals.signal.addEventListener("abort", () => {
22
41
  reader.cancel().catch(() => {});
@@ -56,7 +75,8 @@ var nodeCreateAdjacentFileSource = (relativePath, src) => {
56
75
  var nodeReader = {
57
76
  read: nodeReadContent,
58
77
  readWholeAsText: nodeReadWholeAsText,
59
- createAdjacentFileSource: nodeCreateAdjacentFileSource
78
+ createAdjacentFileSource: nodeCreateAdjacentFileSource,
79
+ preload: () => {}
60
80
  };
61
81
  export {
62
82
  nodeReader,
@@ -144,8 +144,14 @@ var deserializeError = (error) => {
144
144
  });
145
145
  case "MediaParserAbortError":
146
146
  return new MediaParserAbortError(error.errorMessage);
147
- default:
147
+ case "Error":
148
+ return new Error(error.errorMessage);
149
+ case "AbortError":
148
150
  return new Error(error.errorMessage);
151
+ case "NotReadableError":
152
+ return new Error(error.errorMessage);
153
+ default:
154
+ throw new Error(`Unknown error name: ${error}`);
149
155
  }
150
156
  };
151
157
 
@@ -280,6 +286,7 @@ var parseMediaOnWorkerImplementation = async ({ controller, reader, ...params },
280
286
  if (data.type === "response-error") {
281
287
  cleanup();
282
288
  const error = deserializeError(data);
289
+ error.stack = data.errorStack;
283
290
  reject(error);
284
291
  if (data.errorName === "MediaParserAbortError") {
285
292
  finalSeekingHints = data.seekingHints;
@@ -7,6 +7,40 @@ class MediaParserAbortError extends Error {
7
7
  }
8
8
  }
9
9
 
10
+ // src/log.ts
11
+ var logLevels = ["trace", "verbose", "info", "warn", "error"];
12
+ var getNumberForLogLevel = (level) => {
13
+ return logLevels.indexOf(level);
14
+ };
15
+ var isEqualOrBelowLogLevel = (currentLevel, level) => {
16
+ return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
17
+ };
18
+ var Log = {
19
+ trace: (logLevel, ...args) => {
20
+ if (isEqualOrBelowLogLevel(logLevel, "trace")) {
21
+ return console.log(...args);
22
+ }
23
+ },
24
+ verbose: (logLevel, ...args) => {
25
+ if (isEqualOrBelowLogLevel(logLevel, "verbose")) {
26
+ return console.log(...args);
27
+ }
28
+ },
29
+ info: (logLevel, ...args) => {
30
+ if (isEqualOrBelowLogLevel(logLevel, "info")) {
31
+ return console.log(...args);
32
+ }
33
+ },
34
+ warn: (logLevel, ...args) => {
35
+ if (isEqualOrBelowLogLevel(logLevel, "warn")) {
36
+ return console.warn(...args);
37
+ }
38
+ },
39
+ error: (...args) => {
40
+ return console.error(...args);
41
+ }
42
+ };
43
+
10
44
  // src/readers/fetch/get-body-and-reader.ts
11
45
  var getLengthAndReader = async ({
12
46
  canLiveWithoutContentLength,
@@ -19,10 +53,22 @@ var getLengthAndReader = async ({
19
53
  if (requestedWithoutRange || canLiveWithoutContentLength && contentLength === null) {
20
54
  const buffer = await res.arrayBuffer();
21
55
  const encoded = new Uint8Array(buffer);
56
+ let streamCancelled = false;
22
57
  const stream = new ReadableStream({
23
58
  start(controller) {
24
- controller.enqueue(encoded);
25
- controller.close();
59
+ if (ownController.signal.aborted) {
60
+ return;
61
+ }
62
+ if (streamCancelled) {
63
+ return;
64
+ }
65
+ try {
66
+ controller.enqueue(encoded);
67
+ controller.close();
68
+ } catch {}
69
+ },
70
+ cancel() {
71
+ streamCancelled = true;
26
72
  }
27
73
  });
28
74
  return {
@@ -98,14 +144,11 @@ var validateContentRangeAndDetectIfSupported = ({
98
144
  }
99
145
  return { supportsContentRange: true };
100
146
  };
101
- var fetchReadContent = async ({
102
- src,
147
+ var makeFetchRequest = async ({
103
148
  range,
149
+ src,
104
150
  controller
105
151
  }) => {
106
- if (typeof src !== "string" && src instanceof URL === false) {
107
- throw new Error("src must be a string when using `fetchReader`");
108
- }
109
152
  const resolvedUrl = resolveUrl(src);
110
153
  const resolvedUrlString = resolvedUrl.toString();
111
154
  if (!resolvedUrlString.startsWith("https://") && !resolvedUrlString.startsWith("blob:") && !resolvedUrlString.startsWith("http://")) {
@@ -134,21 +177,83 @@ var fetchReadContent = async ({
134
177
  parsedContentRange,
135
178
  statusCode: res.status
136
179
  });
137
- controller._internals.signal.addEventListener("abort", () => {
138
- ownController.abort(new MediaParserAbortError("Aborted by user"));
139
- }, { once: true });
180
+ if (controller) {
181
+ controller._internals.signal.addEventListener("abort", () => {
182
+ ownController.abort(new MediaParserAbortError("Aborted by user"));
183
+ }, { once: true });
184
+ }
140
185
  if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
141
- throw new Error(`Server returned status code ${res.status} for ${src} and range ${requestedRange}`);
186
+ throw new Error(`Server returned status code ${res.status} for ${resolvedUrl} and range ${requestedRange}`);
142
187
  }
143
188
  const contentDisposition = res.headers.get("content-disposition");
144
189
  const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
145
- const fallbackName = src.toString().split("/").pop();
146
190
  const { contentLength, needsContentRange, reader } = await getLengthAndReader({
147
191
  canLiveWithoutContentLength,
148
192
  res,
149
193
  ownController,
150
194
  requestedWithoutRange: requestWithoutRange
151
195
  });
196
+ const contentType = res.headers.get("content-type");
197
+ return {
198
+ contentLength,
199
+ needsContentRange,
200
+ reader,
201
+ name,
202
+ contentType,
203
+ supportsContentRange
204
+ };
205
+ };
206
+ var cacheKey = ({
207
+ src,
208
+ range
209
+ }) => {
210
+ return `${src}-${JSON.stringify(range)}`;
211
+ };
212
+ var makeFetchRequestOrGetCached = ({
213
+ range,
214
+ src,
215
+ controller,
216
+ logLevel,
217
+ prefetchCache
218
+ }) => {
219
+ const key = cacheKey({ src, range });
220
+ const cached = prefetchCache.get(key);
221
+ if (cached) {
222
+ Log.verbose(logLevel, `Reading from preload cache for ${key}`);
223
+ return cached;
224
+ }
225
+ Log.verbose(logLevel, `Fetching ${key}`);
226
+ const result = makeFetchRequest({ range, src, controller });
227
+ prefetchCache.set(key, result);
228
+ return result;
229
+ };
230
+ var fetchReadContent = async ({
231
+ src,
232
+ range,
233
+ controller,
234
+ logLevel,
235
+ prefetchCache
236
+ }) => {
237
+ if (typeof src !== "string" && src instanceof URL === false) {
238
+ throw new Error("src must be a string when using `fetchReader`");
239
+ }
240
+ const fallbackName = src.toString().split("/").pop();
241
+ const {
242
+ reader,
243
+ contentLength,
244
+ needsContentRange,
245
+ name,
246
+ supportsContentRange,
247
+ contentType
248
+ } = await makeFetchRequestOrGetCached({
249
+ range,
250
+ src,
251
+ controller,
252
+ logLevel,
253
+ prefetchCache
254
+ });
255
+ const key = cacheKey({ src, range });
256
+ prefetchCache.delete(key);
152
257
  if (controller) {
153
258
  controller._internals.signal.addEventListener("abort", () => {
154
259
  reader.reader.cancel().catch(() => {});
@@ -157,12 +262,33 @@ var fetchReadContent = async ({
157
262
  return {
158
263
  reader,
159
264
  contentLength,
160
- contentType: res.headers.get("content-type"),
265
+ contentType,
161
266
  name: name ?? fallbackName,
162
267
  supportsContentRange,
163
268
  needsContentRange
164
269
  };
165
270
  };
271
+ var fetchPreload = ({
272
+ src,
273
+ range,
274
+ logLevel,
275
+ prefetchCache
276
+ }) => {
277
+ if (typeof src !== "string" && src instanceof URL === false) {
278
+ throw new Error("src must be a string when using `fetchReader`");
279
+ }
280
+ const key = cacheKey({ src, range });
281
+ if (prefetchCache.has(key)) {
282
+ return prefetchCache.get(key);
283
+ }
284
+ makeFetchRequestOrGetCached({
285
+ range,
286
+ src,
287
+ controller: null,
288
+ logLevel,
289
+ prefetchCache
290
+ });
291
+ };
166
292
  var fetchReadWholeAsText = async (src) => {
167
293
  if (typeof src !== "string" && src instanceof URL === false) {
168
294
  throw new Error("src must be a string when using `fetchReader`");
@@ -183,7 +309,6 @@ var fetchCreateAdjacentFileSource = (relativePath, src) => {
183
309
  // src/readers/from-node.ts
184
310
  import { createReadStream, promises, statSync } from "fs";
185
311
  import { dirname, join, relative, sep } from "path";
186
- import { Readable } from "stream";
187
312
  var nodeReadContent = ({ src, range, controller }) => {
188
313
  if (typeof src !== "string") {
189
314
  throw new Error("src must be a string when using `nodeReader`");
@@ -198,7 +323,27 @@ var nodeReadContent = ({ src, range, controller }) => {
198
323
  ownController.abort();
199
324
  }, { once: true });
200
325
  const stats = statSync(src);
201
- const reader = Readable.toWeb(stream).getReader();
326
+ let readerCancelled = false;
327
+ const reader = new ReadableStream({
328
+ start(c) {
329
+ if (readerCancelled) {
330
+ return;
331
+ }
332
+ stream.on("data", (chunk) => {
333
+ c.enqueue(chunk);
334
+ });
335
+ stream.on("end", () => {
336
+ c.close();
337
+ });
338
+ stream.on("error", (err) => {
339
+ c.error(err);
340
+ });
341
+ },
342
+ cancel() {
343
+ readerCancelled = true;
344
+ stream.destroy();
345
+ }
346
+ }).getReader();
202
347
  if (controller) {
203
348
  controller._internals.signal.addEventListener("abort", () => {
204
349
  reader.cancel().catch(() => {});
@@ -314,6 +459,14 @@ var universalReader = {
314
459
  return fetchCreateAdjacentFileSource(relativePath, src);
315
460
  }
316
461
  return nodeCreateAdjacentFileSource(relativePath, src);
462
+ },
463
+ preload: ({ src, range, logLevel, prefetchCache }) => {
464
+ if (src instanceof Blob) {
465
+ return;
466
+ }
467
+ if (src.toString().startsWith("http") || src.toString().startsWith("blob:")) {
468
+ return fetchPreload({ range, src, logLevel, prefetchCache });
469
+ }
317
470
  }
318
471
  };
319
472
  export {
package/dist/esm/web.mjs CHANGED
@@ -7,6 +7,40 @@ class MediaParserAbortError extends Error {
7
7
  }
8
8
  }
9
9
 
10
+ // src/log.ts
11
+ var logLevels = ["trace", "verbose", "info", "warn", "error"];
12
+ var getNumberForLogLevel = (level) => {
13
+ return logLevels.indexOf(level);
14
+ };
15
+ var isEqualOrBelowLogLevel = (currentLevel, level) => {
16
+ return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
17
+ };
18
+ var Log = {
19
+ trace: (logLevel, ...args) => {
20
+ if (isEqualOrBelowLogLevel(logLevel, "trace")) {
21
+ return console.log(...args);
22
+ }
23
+ },
24
+ verbose: (logLevel, ...args) => {
25
+ if (isEqualOrBelowLogLevel(logLevel, "verbose")) {
26
+ return console.log(...args);
27
+ }
28
+ },
29
+ info: (logLevel, ...args) => {
30
+ if (isEqualOrBelowLogLevel(logLevel, "info")) {
31
+ return console.log(...args);
32
+ }
33
+ },
34
+ warn: (logLevel, ...args) => {
35
+ if (isEqualOrBelowLogLevel(logLevel, "warn")) {
36
+ return console.warn(...args);
37
+ }
38
+ },
39
+ error: (...args) => {
40
+ return console.error(...args);
41
+ }
42
+ };
43
+
10
44
  // src/readers/fetch/get-body-and-reader.ts
11
45
  var getLengthAndReader = async ({
12
46
  canLiveWithoutContentLength,
@@ -19,10 +53,22 @@ var getLengthAndReader = async ({
19
53
  if (requestedWithoutRange || canLiveWithoutContentLength && contentLength === null) {
20
54
  const buffer = await res.arrayBuffer();
21
55
  const encoded = new Uint8Array(buffer);
56
+ let streamCancelled = false;
22
57
  const stream = new ReadableStream({
23
58
  start(controller) {
24
- controller.enqueue(encoded);
25
- controller.close();
59
+ if (ownController.signal.aborted) {
60
+ return;
61
+ }
62
+ if (streamCancelled) {
63
+ return;
64
+ }
65
+ try {
66
+ controller.enqueue(encoded);
67
+ controller.close();
68
+ } catch {}
69
+ },
70
+ cancel() {
71
+ streamCancelled = true;
26
72
  }
27
73
  });
28
74
  return {
@@ -98,14 +144,11 @@ var validateContentRangeAndDetectIfSupported = ({
98
144
  }
99
145
  return { supportsContentRange: true };
100
146
  };
101
- var fetchReadContent = async ({
102
- src,
147
+ var makeFetchRequest = async ({
103
148
  range,
149
+ src,
104
150
  controller
105
151
  }) => {
106
- if (typeof src !== "string" && src instanceof URL === false) {
107
- throw new Error("src must be a string when using `fetchReader`");
108
- }
109
152
  const resolvedUrl = resolveUrl(src);
110
153
  const resolvedUrlString = resolvedUrl.toString();
111
154
  if (!resolvedUrlString.startsWith("https://") && !resolvedUrlString.startsWith("blob:") && !resolvedUrlString.startsWith("http://")) {
@@ -134,21 +177,83 @@ var fetchReadContent = async ({
134
177
  parsedContentRange,
135
178
  statusCode: res.status
136
179
  });
137
- controller._internals.signal.addEventListener("abort", () => {
138
- ownController.abort(new MediaParserAbortError("Aborted by user"));
139
- }, { once: true });
180
+ if (controller) {
181
+ controller._internals.signal.addEventListener("abort", () => {
182
+ ownController.abort(new MediaParserAbortError("Aborted by user"));
183
+ }, { once: true });
184
+ }
140
185
  if (res.status.toString().startsWith("4") || res.status.toString().startsWith("5")) {
141
- throw new Error(`Server returned status code ${res.status} for ${src} and range ${requestedRange}`);
186
+ throw new Error(`Server returned status code ${res.status} for ${resolvedUrl} and range ${requestedRange}`);
142
187
  }
143
188
  const contentDisposition = res.headers.get("content-disposition");
144
189
  const name = contentDisposition?.match(/filename="([^"]+)"/)?.[1];
145
- const fallbackName = src.toString().split("/").pop();
146
190
  const { contentLength, needsContentRange, reader } = await getLengthAndReader({
147
191
  canLiveWithoutContentLength,
148
192
  res,
149
193
  ownController,
150
194
  requestedWithoutRange: requestWithoutRange
151
195
  });
196
+ const contentType = res.headers.get("content-type");
197
+ return {
198
+ contentLength,
199
+ needsContentRange,
200
+ reader,
201
+ name,
202
+ contentType,
203
+ supportsContentRange
204
+ };
205
+ };
206
+ var cacheKey = ({
207
+ src,
208
+ range
209
+ }) => {
210
+ return `${src}-${JSON.stringify(range)}`;
211
+ };
212
+ var makeFetchRequestOrGetCached = ({
213
+ range,
214
+ src,
215
+ controller,
216
+ logLevel,
217
+ prefetchCache
218
+ }) => {
219
+ const key = cacheKey({ src, range });
220
+ const cached = prefetchCache.get(key);
221
+ if (cached) {
222
+ Log.verbose(logLevel, `Reading from preload cache for ${key}`);
223
+ return cached;
224
+ }
225
+ Log.verbose(logLevel, `Fetching ${key}`);
226
+ const result = makeFetchRequest({ range, src, controller });
227
+ prefetchCache.set(key, result);
228
+ return result;
229
+ };
230
+ var fetchReadContent = async ({
231
+ src,
232
+ range,
233
+ controller,
234
+ logLevel,
235
+ prefetchCache
236
+ }) => {
237
+ if (typeof src !== "string" && src instanceof URL === false) {
238
+ throw new Error("src must be a string when using `fetchReader`");
239
+ }
240
+ const fallbackName = src.toString().split("/").pop();
241
+ const {
242
+ reader,
243
+ contentLength,
244
+ needsContentRange,
245
+ name,
246
+ supportsContentRange,
247
+ contentType
248
+ } = await makeFetchRequestOrGetCached({
249
+ range,
250
+ src,
251
+ controller,
252
+ logLevel,
253
+ prefetchCache
254
+ });
255
+ const key = cacheKey({ src, range });
256
+ prefetchCache.delete(key);
152
257
  if (controller) {
153
258
  controller._internals.signal.addEventListener("abort", () => {
154
259
  reader.reader.cancel().catch(() => {});
@@ -157,12 +262,33 @@ var fetchReadContent = async ({
157
262
  return {
158
263
  reader,
159
264
  contentLength,
160
- contentType: res.headers.get("content-type"),
265
+ contentType,
161
266
  name: name ?? fallbackName,
162
267
  supportsContentRange,
163
268
  needsContentRange
164
269
  };
165
270
  };
271
+ var fetchPreload = ({
272
+ src,
273
+ range,
274
+ logLevel,
275
+ prefetchCache
276
+ }) => {
277
+ if (typeof src !== "string" && src instanceof URL === false) {
278
+ throw new Error("src must be a string when using `fetchReader`");
279
+ }
280
+ const key = cacheKey({ src, range });
281
+ if (prefetchCache.has(key)) {
282
+ return prefetchCache.get(key);
283
+ }
284
+ makeFetchRequestOrGetCached({
285
+ range,
286
+ src,
287
+ controller: null,
288
+ logLevel,
289
+ prefetchCache
290
+ });
291
+ };
166
292
  var fetchReadWholeAsText = async (src) => {
167
293
  if (typeof src !== "string" && src instanceof URL === false) {
168
294
  throw new Error("src must be a string when using `fetchReader`");
@@ -249,6 +375,12 @@ var webReader = {
249
375
  return webFileReadWholeAsText(src);
250
376
  }
251
377
  return fetchReadWholeAsText(src);
378
+ },
379
+ preload: ({ range, src, logLevel, prefetchCache }) => {
380
+ if (src instanceof Blob) {
381
+ return;
382
+ }
383
+ return fetchPreload({ range, src, logLevel, prefetchCache });
252
384
  }
253
385
  };
254
386
  export {