@remotion/media-parser 4.0.192 → 4.0.194

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 (77) hide show
  1. package/boxes.json +1 -0
  2. package/dist/boxes/iso-base-media/esds/esds-descriptors.d.ts +21 -0
  3. package/dist/boxes/iso-base-media/esds/esds-descriptors.js +62 -0
  4. package/dist/boxes/iso-base-media/esds/esds.d.ts +15 -0
  5. package/dist/boxes/iso-base-media/esds/esds.js +27 -0
  6. package/dist/boxes/iso-base-media/esds-descriptors.d.ts +21 -0
  7. package/dist/boxes/iso-base-media/esds-descriptors.js +62 -0
  8. package/dist/boxes/iso-base-media/esds.d.ts +15 -0
  9. package/dist/boxes/iso-base-media/esds.js +27 -0
  10. package/dist/boxes/iso-base-media/mdat/mdat.d.ts +12 -0
  11. package/dist/boxes/iso-base-media/mdat/mdat.js +13 -0
  12. package/dist/boxes/iso-base-media/process-box.js +67 -0
  13. package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -0
  14. package/dist/boxes/iso-base-media/stsd/samples.js +27 -8
  15. package/dist/boxes/iso-base-media/stsd/stco.d.ts +14 -0
  16. package/dist/boxes/iso-base-media/stsd/stco.js +30 -0
  17. package/dist/boxes/iso-base-media/stsd/stsc.d.ts +19 -0
  18. package/dist/boxes/iso-base-media/stsd/stsc.js +34 -0
  19. package/dist/boxes/iso-base-media/stsd/stsz.d.ts +15 -0
  20. package/dist/boxes/iso-base-media/stsd/stsz.js +32 -0
  21. package/dist/boxes/webm/parse-webm-header.js +3 -3
  22. package/dist/boxes/webm/segments/track-entry.d.ts +20 -0
  23. package/dist/boxes/webm/segments/track-entry.js +37 -2
  24. package/dist/boxes/webm/segments.d.ts +2 -2
  25. package/dist/boxes/webm/segments.js +12 -0
  26. package/dist/buffer-iterator.d.ts +3 -1
  27. package/dist/buffer-iterator.js +41 -8
  28. package/dist/from-node.js +12 -4
  29. package/dist/from-web.js +12 -4
  30. package/dist/get-audio-codec.d.ts +4 -0
  31. package/dist/get-audio-codec.js +106 -0
  32. package/dist/get-dimensions.js +6 -2
  33. package/dist/get-fps.d.ts +1 -0
  34. package/dist/get-fps.js +34 -1
  35. package/dist/get-sample-positions.d.ts +12 -0
  36. package/dist/get-sample-positions.js +25 -0
  37. package/dist/get-samples.d.ts +0 -0
  38. package/dist/get-samples.js +1 -0
  39. package/dist/get-tracks.d.ts +10 -0
  40. package/dist/get-tracks.js +66 -0
  41. package/dist/get-video-codec.js +3 -0
  42. package/dist/has-all-info.d.ts +1 -1
  43. package/dist/has-all-info.js +4 -0
  44. package/dist/options.d.ts +7 -3
  45. package/dist/parse-media.js +37 -9
  46. package/dist/parse-result.d.ts +3 -1
  47. package/dist/parse-video.d.ts +1 -0
  48. package/dist/parse-video.js +1 -0
  49. package/dist/reader.d.ts +5 -1
  50. package/dist/traversal.d.ts +19 -0
  51. package/dist/traversal.js +88 -0
  52. package/package.json +2 -2
  53. package/src/boxes/iso-base-media/esds/esds-descriptors.ts +104 -0
  54. package/src/boxes/iso-base-media/esds/esds.ts +49 -0
  55. package/src/boxes/iso-base-media/process-box.ts +75 -0
  56. package/src/boxes/iso-base-media/stsd/samples.ts +35 -8
  57. package/src/boxes/webm/parse-webm-header.ts +3 -3
  58. package/src/boxes/webm/segments/track-entry.ts +66 -1
  59. package/src/boxes/webm/segments.ts +29 -1
  60. package/src/buffer-iterator.ts +54 -10
  61. package/src/from-node.ts +13 -6
  62. package/src/from-web.ts +14 -4
  63. package/src/get-audio-codec.ts +143 -0
  64. package/src/get-dimensions.ts +11 -4
  65. package/src/get-fps.ts +48 -0
  66. package/src/get-video-codec.ts +4 -0
  67. package/src/has-all-info.ts +14 -2
  68. package/src/options.ts +18 -3
  69. package/src/parse-media.ts +51 -10
  70. package/src/parse-result.ts +4 -1
  71. package/src/parse-video.ts +2 -0
  72. package/src/reader.ts +6 -2
  73. package/src/test/matroska.test.ts +6 -7
  74. package/src/test/parse-esds.test.ts +75 -0
  75. package/src/test/stream-local.test.ts +80 -3
  76. package/src/test/stsd.test.ts +52 -5
  77. package/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,7 @@
1
1
  import type {BufferIterator} from '../../buffer-iterator';
2
2
  import type {IsoBaseMediaBox, ParseResult} from '../../parse-result';
3
3
  import type {BoxAndNext} from '../../parse-video';
4
+ import {parseEsds} from './esds/esds';
4
5
  import {parseFtyp} from './ftyp';
5
6
  import {parseMdhd} from './mdhd';
6
7
  import {parseMoov} from './moov/moov';
@@ -25,6 +26,7 @@ const getChildren = ({
25
26
  boxType === 'minf' ||
26
27
  boxType === 'stbl' ||
27
28
  boxType === 'dims' ||
29
+ boxType === 'wave' ||
28
30
  boxType === 'stsb';
29
31
 
30
32
  if (parseChildren) {
@@ -42,6 +44,10 @@ const getChildren = ({
42
44
  return parsed.segments;
43
45
  }
44
46
 
47
+ if (bytesRemainingInBox < 0) {
48
+ throw new Error('Box size is too big ' + JSON.stringify({boxType}));
49
+ }
50
+
45
51
  iterator.discard(bytesRemainingInBox);
46
52
  return [];
47
53
  };
@@ -62,6 +68,32 @@ const processBox = ({
62
68
  }
63
69
 
64
70
  if (bytesRemaining < boxSize) {
71
+ if (bytesRemaining >= 4) {
72
+ const type = iterator.getByteString(4);
73
+ iterator.counter.decrement(4);
74
+
75
+ if (type === 'mdat') {
76
+ const skipTo = fileOffset + boxSize;
77
+ const bytesToSkip = skipTo - iterator.counter.getOffset();
78
+
79
+ // If there is a huge mdat chunk, we can skip it because we don't need it for the metadata
80
+ if (bytesToSkip > 1_000_000) {
81
+ return {
82
+ type: 'complete',
83
+ box: {
84
+ type: 'regular-box',
85
+ boxType: 'mdat',
86
+ children: [],
87
+ boxSize,
88
+ offset: fileOffset,
89
+ },
90
+ size: boxSize,
91
+ skipTo: fileOffset + boxSize,
92
+ };
93
+ }
94
+ }
95
+ }
96
+
65
97
  iterator.counter.decrement(iterator.counter.getOffset() - fileOffset);
66
98
  if (allowIncompleteBoxes) {
67
99
  return {
@@ -82,6 +114,7 @@ const processBox = ({
82
114
  type: 'complete',
83
115
  box,
84
116
  size: boxSize,
117
+ skipTo: null,
85
118
  };
86
119
  }
87
120
 
@@ -92,6 +125,7 @@ const processBox = ({
92
125
  type: 'complete',
93
126
  box,
94
127
  size: boxSize,
128
+ skipTo: null,
95
129
  };
96
130
  }
97
131
 
@@ -102,6 +136,7 @@ const processBox = ({
102
136
  type: 'complete',
103
137
  box,
104
138
  size: boxSize,
139
+ skipTo: null,
105
140
  };
106
141
  }
107
142
 
@@ -112,6 +147,7 @@ const processBox = ({
112
147
  type: 'complete',
113
148
  box,
114
149
  size: boxSize,
150
+ skipTo: null,
115
151
  };
116
152
  }
117
153
 
@@ -122,6 +158,7 @@ const processBox = ({
122
158
  type: 'complete',
123
159
  box,
124
160
  size: boxSize,
161
+ skipTo: null,
125
162
  };
126
163
  }
127
164
 
@@ -132,6 +169,7 @@ const processBox = ({
132
169
  type: 'complete',
133
170
  box,
134
171
  size: boxSize,
172
+ skipTo: null,
135
173
  };
136
174
  }
137
175
 
@@ -146,6 +184,7 @@ const processBox = ({
146
184
  type: 'complete',
147
185
  box,
148
186
  size: boxSize,
187
+ skipTo: null,
149
188
  };
150
189
  }
151
190
 
@@ -160,6 +199,7 @@ const processBox = ({
160
199
  type: 'complete',
161
200
  box,
162
201
  size: boxSize,
202
+ skipTo: null,
163
203
  };
164
204
  }
165
205
 
@@ -174,6 +214,22 @@ const processBox = ({
174
214
  type: 'complete',
175
215
  box,
176
216
  size: boxSize,
217
+ skipTo: null,
218
+ };
219
+ }
220
+
221
+ if (boxType === 'esds') {
222
+ const box = parseEsds({
223
+ data: iterator,
224
+ size: boxSize,
225
+ fileOffset,
226
+ });
227
+
228
+ return {
229
+ type: 'complete',
230
+ box,
231
+ size: boxSize,
232
+ skipTo: null,
177
233
  };
178
234
  }
179
235
 
@@ -196,6 +252,7 @@ const processBox = ({
196
252
  offset: fileOffset,
197
253
  },
198
254
  size: boxSize,
255
+ skipTo: null,
199
256
  };
200
257
  };
201
258
 
@@ -237,10 +294,28 @@ export const parseBoxes = ({
237
294
  initialBoxes: boxes,
238
295
  });
239
296
  },
297
+ skipTo: null,
240
298
  };
241
299
  }
242
300
 
243
301
  boxes.push(result.box);
302
+
303
+ if (result.skipTo !== null) {
304
+ return {
305
+ status: 'incomplete',
306
+ segments: boxes,
307
+ continueParsing: () => {
308
+ return parseBoxes({
309
+ iterator,
310
+ maxBytes,
311
+ allowIncompleteBoxes,
312
+ initialBoxes: boxes,
313
+ });
314
+ },
315
+ skipTo: result.skipTo,
316
+ };
317
+ }
318
+
244
319
  iterator.discardFirstBytes();
245
320
  }
246
321
 
@@ -1,4 +1,6 @@
1
1
  import type {BufferIterator} from '../../../buffer-iterator';
2
+ import type {AnySegment} from '../../../parse-result';
3
+ import {parseBoxes} from '../process-box';
2
4
 
3
5
  type SampleBase = {
4
6
  format: string;
@@ -21,6 +23,7 @@ type AudioSample = SampleBase & {
21
23
  bytesPerPacket: number | null;
22
24
  bytesPerFrame: number | null;
23
25
  bitsPerSample: number | null;
26
+ children: AnySegment[];
24
27
  };
25
28
 
26
29
  type VideoSample = SampleBase & {
@@ -164,7 +167,16 @@ export const processSample = ({
164
167
 
165
168
  const bytesRemainingInBox =
166
169
  boxSize - (iterator.counter.getOffset() - fileOffset);
167
- iterator.discard(bytesRemainingInBox);
170
+ const children = parseBoxes({
171
+ iterator,
172
+ allowIncompleteBoxes: false,
173
+ maxBytes: bytesRemainingInBox,
174
+ initialBoxes: [],
175
+ });
176
+
177
+ if (children.status === 'incomplete') {
178
+ throw new Error('Incomplete boxes are not allowed');
179
+ }
168
180
 
169
181
  return {
170
182
  sample: {
@@ -185,6 +197,7 @@ export const processSample = ({
185
197
  bytesPerPacket: null,
186
198
  bytesPerFrame: null,
187
199
  bitsPerSample: null,
200
+ children: children.segments,
188
201
  },
189
202
  };
190
203
  }
@@ -192,17 +205,29 @@ export const processSample = ({
192
205
  if (version === 1) {
193
206
  const numberOfChannels = iterator.getUint16();
194
207
  const sampleSize = iterator.getUint16();
195
- const compressionId = iterator.getUint16();
208
+ const compressionId = iterator.getInt16();
196
209
  const packetSize = iterator.getUint16();
197
210
  const sampleRate = iterator.getFixedPoint1616Number();
198
- const samplesPerPacket = iterator.getUint16();
199
- const bytesPerPacket = iterator.getUint16();
200
- const bytesPerFrame = iterator.getUint16();
201
- const bitsPerSample = iterator.getUint16();
211
+
212
+ const samplesPerPacket = iterator.getUint32();
213
+
214
+ const bytesPerPacket = iterator.getUint32();
215
+ const bytesPerFrame = iterator.getUint32();
216
+ const bytesPerSample = iterator.getUint32();
202
217
 
203
218
  const bytesRemainingInBox =
204
219
  boxSize - (iterator.counter.getOffset() - fileOffset);
205
- iterator.discard(bytesRemainingInBox);
220
+
221
+ const children = parseBoxes({
222
+ iterator,
223
+ allowIncompleteBoxes: false,
224
+ maxBytes: bytesRemainingInBox,
225
+ initialBoxes: [],
226
+ });
227
+
228
+ if (children.status === 'incomplete') {
229
+ throw new Error('Incomplete boxes are not allowed');
230
+ }
206
231
 
207
232
  return {
208
233
  sample: {
@@ -222,7 +247,8 @@ export const processSample = ({
222
247
  samplesPerPacket,
223
248
  bytesPerPacket,
224
249
  bytesPerFrame,
225
- bitsPerSample,
250
+ bitsPerSample: bytesPerSample,
251
+ children: children.segments,
226
252
  },
227
253
  };
228
254
  }
@@ -245,6 +271,7 @@ export const processSample = ({
245
271
 
246
272
  const bytesRemainingInBox =
247
273
  boxSize - (iterator.counter.getOffset() - fileOffset);
274
+
248
275
  iterator.discard(bytesRemainingInBox);
249
276
 
250
277
  return {
@@ -7,12 +7,12 @@ export const parseWebm = (counter: BufferIterator): ParseResult => {
7
7
  counter.discard(4);
8
8
  const length = counter.getVint();
9
9
 
10
- if (length !== 31) {
11
- throw new Error(`Expected header length 31, got ${length}`);
10
+ if (length !== 31 && length !== 35) {
11
+ throw new Error(`Expected header length 31 or 25, got ${length}`);
12
12
  }
13
13
 
14
14
  // Discard header for now
15
- counter.discard(31);
15
+ counter.discard(length);
16
16
 
17
17
  return {status: 'done', segments: [expectSegment(counter)]};
18
18
  };
@@ -278,7 +278,7 @@ export type ColorSegment = {
278
278
  export const parseColorSegment = (iterator: BufferIterator): ColorSegment => {
279
279
  const length = iterator.getVint();
280
280
 
281
- iterator.discard(length - 1);
281
+ iterator.discard(length);
282
282
 
283
283
  return {
284
284
  type: 'color-segment',
@@ -320,3 +320,68 @@ export const parseInterlacedSegment = (
320
320
  interlaced: Boolean(interlaced),
321
321
  };
322
322
  };
323
+
324
+ export type CodecPrivateSegment = {
325
+ type: 'codec-private-segment';
326
+ codecPrivateData: number[];
327
+ };
328
+
329
+ export const parseCodecPrivateSegment = (
330
+ iterator: BufferIterator,
331
+ ): CodecPrivateSegment => {
332
+ const length = iterator.getVint();
333
+
334
+ return {
335
+ type: 'codec-private-segment',
336
+ codecPrivateData: [...iterator.getSlice(length)],
337
+ };
338
+ };
339
+
340
+ export type Crc32Segment = {
341
+ type: 'crc32-segment';
342
+ crc32: number[];
343
+ };
344
+
345
+ export const parseCrc32Segment = (iterator: BufferIterator): Crc32Segment => {
346
+ const length = iterator.getVint();
347
+
348
+ return {
349
+ type: 'crc32-segment',
350
+ crc32: [...iterator.getSlice(length)],
351
+ };
352
+ };
353
+
354
+ export type SegmentUUIDSegment = {
355
+ type: 'segment-uuid-segment';
356
+ segmentUUID: string;
357
+ };
358
+
359
+ export const parseSegmentUUIDSegment = (
360
+ iterator: BufferIterator,
361
+ ): SegmentUUIDSegment => {
362
+ const length = iterator.getVint();
363
+
364
+ return {
365
+ type: 'segment-uuid-segment',
366
+ segmentUUID: iterator.getSlice(length).toString(),
367
+ };
368
+ };
369
+
370
+ export type DefaultFlagSegment = {
371
+ type: 'default-flag-segment';
372
+ defaultFlag: boolean;
373
+ };
374
+
375
+ export const parseDefaultFlagSegment = (
376
+ iterator: BufferIterator,
377
+ ): DefaultFlagSegment => {
378
+ const length = iterator.getVint();
379
+ if (length !== 1) {
380
+ throw new Error('Expected default flag segment to be 1 byte');
381
+ }
382
+
383
+ return {
384
+ type: 'default-flag-segment',
385
+ defaultFlag: Boolean(iterator.getUint8()),
386
+ };
387
+ };
@@ -17,14 +17,18 @@ import type {TimestampScaleSegment} from './segments/timestamp-scale';
17
17
  import {parseTimestampScaleSegment} from './segments/timestamp-scale';
18
18
  import type {
19
19
  AlphaModeSegment,
20
+ CodecPrivateSegment,
20
21
  CodecSegment,
21
22
  ColorSegment,
23
+ Crc32Segment,
22
24
  DefaultDurationSegment,
25
+ DefaultFlagSegment,
23
26
  FlagLacingSegment,
24
27
  HeightSegment,
25
28
  InterlacedSegment,
26
29
  LanguageSegment,
27
30
  MaxBlockAdditionId,
31
+ SegmentUUIDSegment,
28
32
  TitleSegment,
29
33
  TrackEntrySegment,
30
34
  TrackNumberSegment,
@@ -35,14 +39,18 @@ import type {
35
39
  } from './segments/track-entry';
36
40
  import {
37
41
  parseAlphaModeSegment,
42
+ parseCodecPrivateSegment,
38
43
  parseCodecSegment,
39
44
  parseColorSegment,
45
+ parseCrc32Segment,
40
46
  parseDefaultDurationSegment,
47
+ parseDefaultFlagSegment,
41
48
  parseFlagLacing,
42
49
  parseHeightSegment,
43
50
  parseInterlacedSegment,
44
51
  parseLanguageSegment,
45
52
  parseMaxBlockAdditionId,
53
+ parseSegmentUUIDSegment,
46
54
  parseTitleSegment,
47
55
  parseTrackEntry,
48
56
  parseTrackNumber,
@@ -88,7 +96,11 @@ export type MatroskaSegment =
88
96
  | MaxBlockAdditionId
89
97
  | ColorSegment
90
98
  | TitleSegment
91
- | InterlacedSegment;
99
+ | InterlacedSegment
100
+ | CodecPrivateSegment
101
+ | Crc32Segment
102
+ | SegmentUUIDSegment
103
+ | DefaultFlagSegment;
92
104
 
93
105
  export const expectSegment = (iterator: BufferIterator): MatroskaSegment => {
94
106
  const segmentId = iterator.getMatroskaSegmentId();
@@ -203,10 +215,26 @@ export const expectSegment = (iterator: BufferIterator): MatroskaSegment => {
203
215
  return parseAlphaModeSegment(iterator);
204
216
  }
205
217
 
218
+ if (segmentId === '0x63a2') {
219
+ return parseCodecPrivateSegment(iterator);
220
+ }
221
+
206
222
  if (segmentId === '0x7ba9') {
207
223
  return parseTitleSegment(iterator);
208
224
  }
209
225
 
226
+ if (segmentId === '0xbf') {
227
+ return parseCrc32Segment(iterator);
228
+ }
229
+
230
+ if (segmentId === '0x73a4') {
231
+ return parseSegmentUUIDSegment(iterator);
232
+ }
233
+
234
+ if (segmentId === '0x88') {
235
+ return parseDefaultFlagSegment(iterator);
236
+ }
237
+
210
238
  const length = iterator.getVint();
211
239
 
212
240
  const bytesRemaining = iterator.byteLength() - iterator.counter.getOffset();
@@ -48,8 +48,22 @@ const makeOffsetCounter = (): OffsetCounter => {
48
48
  return new OffsetCounter(0);
49
49
  };
50
50
 
51
- export const getArrayBufferIterator = (initialData: Uint8Array) => {
52
- let data = initialData;
51
+ export const getArrayBufferIterator = (
52
+ initialData: Uint8Array,
53
+ maxBytes?: number,
54
+ ) => {
55
+ const buf = new ArrayBuffer(initialData.byteLength, {
56
+ maxByteLength: maxBytes ?? 1_000_000_000,
57
+ });
58
+ if (!buf.resize) {
59
+ throw new Error(
60
+ '`ArrayBuffer.resize` is not supported in this Runtime. Use at least Node.js 20 or Bun.',
61
+ );
62
+ }
63
+
64
+ let data = new Uint8Array(buf);
65
+ data.set(initialData);
66
+
53
67
  let view = new DataView(data.buffer);
54
68
  const counter = makeOffsetCounter();
55
69
 
@@ -76,6 +90,15 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
76
90
  );
77
91
  };
78
92
 
93
+ const getPaddedFourByteNumber = () => {
94
+ let lastInt = 128;
95
+ while (((lastInt = getUint8()), lastInt === 128)) {
96
+ // Do nothing
97
+ }
98
+
99
+ return lastInt;
100
+ };
101
+
79
102
  const getUint32 = () => {
80
103
  const val = view.getUint32(counter.getDiscardedOffset());
81
104
  counter.increment(4);
@@ -83,11 +106,11 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
83
106
  };
84
107
 
85
108
  const addData = (newData: Uint8Array) => {
86
- const newArray = new Uint8Array(
87
- data.buffer.byteLength + newData.byteLength,
88
- );
89
- newArray.set(data);
90
- newArray.set(new Uint8Array(newData), data.byteLength);
109
+ const oldLength = buf.byteLength;
110
+ const newLength = oldLength + newData.byteLength;
111
+ buf.resize(newLength);
112
+ const newArray = new Uint8Array(buf);
113
+ newArray.set(newData, oldLength);
91
114
  data = newArray;
92
115
  view = new DataView(data.buffer);
93
116
  };
@@ -110,14 +133,28 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
110
133
 
111
134
  const removeBytesRead = () => {
112
135
  const bytesToRemove = counter.getDiscardedOffset();
136
+
137
+ // Only to this operation if it is really worth it 😇
138
+ if (bytesToRemove < 100_000) {
139
+ return;
140
+ }
141
+
113
142
  counter.discardBytes(bytesToRemove);
114
- const newArray = new Uint8Array(data.buffer.byteLength - bytesToRemove);
115
- newArray.set(data.slice(bytesToRemove));
116
- data = newArray;
143
+ const newData = data.slice(bytesToRemove);
144
+ data.set(newData);
145
+ buf.resize(newData.byteLength);
117
146
  view = new DataView(data.buffer);
118
147
  };
119
148
 
149
+ const skipTo = (offset: number) => {
150
+ buf.resize(offset);
151
+ const currentOffset = counter.getOffset();
152
+ counter.increment(offset - currentOffset);
153
+ removeBytesRead();
154
+ };
155
+
120
156
  return {
157
+ skipTo,
121
158
  addData,
122
159
  counter,
123
160
  byteLength,
@@ -134,6 +171,7 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
134
171
  const atom = getSlice(4);
135
172
  return new TextDecoder().decode(atom);
136
173
  },
174
+ getPaddedFourByteNumber,
137
175
  getMatroskaSegmentId: () => {
138
176
  const first = getSlice(1);
139
177
  const firstOneString = `0x${Array.from(new Uint8Array(first))
@@ -155,6 +193,9 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
155
193
  '0xb0',
156
194
  '0xba',
157
195
  '0x9a',
196
+ '0xe1',
197
+ '0xbf',
198
+ '0x88',
158
199
  ];
159
200
  if (knownIdsWithOneLength.includes(firstOneString)) {
160
201
  return firstOneString;
@@ -174,6 +215,8 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
174
215
  '0x55ee',
175
216
  '0x55b0',
176
217
  '0x7ba9',
218
+ '0x63a2',
219
+ '0x73a4',
177
220
  ];
178
221
 
179
222
  const firstTwoString = `${firstOneString}${Array.from(
@@ -267,6 +310,7 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
267
310
  counter.increment(2);
268
311
  return val;
269
312
  },
313
+
270
314
  getInt16: () => {
271
315
  const val = view.getInt16(counter.getDiscardedOffset());
272
316
  counter.increment(2);
package/src/from-node.ts CHANGED
@@ -4,16 +4,23 @@ import {Readable} from 'stream';
4
4
  import type {ReaderInterface} from './reader';
5
5
 
6
6
  export const nodeReader: ReaderInterface = {
7
- read: (src, range) => {
7
+ read: async (src, range) => {
8
8
  const stream = createReadStream(src, {
9
- start: range === null ? 0 : range[0],
10
- end: range === null ? Infinity : range[1],
9
+ start: range === null ? 0 : typeof range === 'number' ? range : range[0],
10
+ end:
11
+ range === null
12
+ ? Infinity
13
+ : typeof range === 'number'
14
+ ? Infinity
15
+ : range[1],
11
16
  });
12
- return Promise.resolve(
13
- Readable.toWeb(
17
+ const stats = await stat(src);
18
+ return {
19
+ reader: Readable.toWeb(
14
20
  stream,
15
21
  ).getReader() as ReadableStreamDefaultReader<Uint8Array>,
16
- );
22
+ contentLength: stats.size,
23
+ };
17
24
  },
18
25
  getLength: async (src) => {
19
26
  const stats = await stat(src);
package/src/from-web.ts CHANGED
@@ -23,17 +23,27 @@ export const webReader: ReaderInterface = {
23
23
  headers:
24
24
  range === null
25
25
  ? {}
26
- : {
27
- Range: `bytes=${`${range[0]}-${range[1]}`}`,
28
- },
26
+ : typeof range === 'number'
27
+ ? {
28
+ Range: `bytes=${range}`,
29
+ }
30
+ : {
31
+ Range: `bytes=${`${range[0]}-${range[1]}`}`,
32
+ },
33
+ // Disable Next.js caching
34
+ cache: 'no-store',
29
35
  });
30
36
  if (!res.body) {
31
37
  throw new Error('No body');
32
38
  }
33
39
 
40
+ const length = res.headers.get('content-length');
41
+
42
+ const contentLength = length === null ? null : parseInt(length, 10);
43
+
34
44
  const reader = res.body.getReader();
35
45
 
36
- return reader;
46
+ return {reader, contentLength};
37
47
  },
38
48
  getLength: async (src) => {
39
49
  const res = await fetch(src, {