@remotion/media-parser 4.0.192 → 4.0.193

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/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/ftype.d.ts +9 -0
  7. package/dist/boxes/iso-base-media/ftype.js +31 -0
  8. package/dist/boxes/iso-base-media/process-box.js +17 -0
  9. package/dist/boxes/iso-base-media/stsd/samples.d.ts +2 -0
  10. package/dist/boxes/iso-base-media/stsd/samples.js +27 -8
  11. package/dist/boxes/webm/parse-webm-header.js +3 -3
  12. package/dist/boxes/webm/segments/track-entry.d.ts +20 -0
  13. package/dist/boxes/webm/segments/track-entry.js +37 -2
  14. package/dist/boxes/webm/segments.d.ts +2 -2
  15. package/dist/boxes/webm/segments.js +12 -0
  16. package/dist/buffer-iterator.d.ts +2 -1
  17. package/dist/buffer-iterator.js +27 -8
  18. package/dist/from-node.js +6 -2
  19. package/dist/from-web.js +6 -1
  20. package/dist/get-audio-codec.d.ts +4 -0
  21. package/dist/get-audio-codec.js +106 -0
  22. package/dist/get-dimensions.js +6 -2
  23. package/dist/get-fps.d.ts +1 -0
  24. package/dist/get-fps.js +34 -1
  25. package/dist/get-video-codec.js +3 -0
  26. package/dist/get-video-metadata.d.ts +2 -0
  27. package/dist/get-video-metadata.js +44 -0
  28. package/dist/has-all-info.d.ts +1 -1
  29. package/dist/has-all-info.js +4 -0
  30. package/dist/options.d.ts +7 -3
  31. package/dist/parse-media.js +23 -6
  32. package/dist/parse-result.d.ts +2 -1
  33. package/dist/read-and-increment-offset.d.ts +28 -0
  34. package/dist/read-and-increment-offset.js +177 -0
  35. package/dist/reader.d.ts +5 -1
  36. package/package.json +2 -2
  37. package/src/boxes/iso-base-media/esds/esds-descriptors.ts +104 -0
  38. package/src/boxes/iso-base-media/esds/esds.ts +49 -0
  39. package/src/boxes/iso-base-media/process-box.ts +20 -0
  40. package/src/boxes/iso-base-media/stsd/samples.ts +35 -8
  41. package/src/boxes/webm/parse-webm-header.ts +3 -3
  42. package/src/boxes/webm/segments/track-entry.ts +66 -1
  43. package/src/boxes/webm/segments.ts +29 -1
  44. package/src/buffer-iterator.ts +34 -10
  45. package/src/from-node.ts +6 -4
  46. package/src/from-web.ts +8 -1
  47. package/src/get-audio-codec.ts +143 -0
  48. package/src/get-dimensions.ts +11 -4
  49. package/src/get-fps.ts +48 -0
  50. package/src/get-video-codec.ts +4 -0
  51. package/src/has-all-info.ts +14 -2
  52. package/src/options.ts +18 -3
  53. package/src/parse-media.ts +30 -7
  54. package/src/parse-result.ts +3 -1
  55. package/src/reader.ts +5 -1
  56. package/src/test/matroska.test.ts +6 -7
  57. package/src/test/parse-esds.test.ts +75 -0
  58. package/src/test/stream-local.test.ts +79 -2
  59. package/src/test/stsd.test.ts +52 -5
  60. package/tsconfig.tsbuildinfo +1 -1
  61. package/dist/get-codec.d.ts +0 -4
  62. package/dist/get-codec.js +0 -22
@@ -0,0 +1,104 @@
1
+ import type {BufferIterator} from '../../../buffer-iterator';
2
+
3
+ type AudioObjectType = 'aac' | 'mp3' | 'unknown';
4
+
5
+ type DecoderConfigDescriptor = {
6
+ type: 'decoder-config-descriptor';
7
+ objectTypeIndication: AudioObjectType;
8
+ };
9
+
10
+ const mapToObjectAudioIndicator = (num: number): AudioObjectType => {
11
+ // https://chromium.googlesource.com/chromium/src/media/+/master/formats/mp4/es_descriptor.h
12
+ // http://netmedia.zju.edu.cn/multimedia2013/mpeg-4/ISO%20IEC%2014496-1%20MPEG-4%20System%20Standard.pdf
13
+ // Page 42, table 8
14
+ if (num === 0x40) {
15
+ return 'aac';
16
+ }
17
+
18
+ if (num === 0x6b) {
19
+ return 'mp3';
20
+ }
21
+
22
+ return 'unknown';
23
+ };
24
+
25
+ type SlConfigDescriptor = {
26
+ type: 'sl-config-descriptor';
27
+ };
28
+
29
+ type UnknownDescriptor = {
30
+ type: 'unknown-descriptor';
31
+ };
32
+
33
+ export type Descriptor =
34
+ | DecoderConfigDescriptor
35
+ | SlConfigDescriptor
36
+ | UnknownDescriptor;
37
+
38
+ type DescriptorAndNext = {
39
+ descriptor: Descriptor | null;
40
+ };
41
+
42
+ export const processDescriptor = ({
43
+ iterator,
44
+ }: {
45
+ iterator: BufferIterator;
46
+ }): DescriptorAndNext => {
47
+ const tag = iterator.getUint8();
48
+
49
+ if (tag === 4) {
50
+ const size = iterator.getPaddedFourByteNumber();
51
+ const initialOffset = iterator.counter.getOffset();
52
+
53
+ const objectTypeIndication = iterator.getUint8();
54
+
55
+ const remaining = size - (iterator.counter.getOffset() - initialOffset);
56
+ iterator.discard(remaining);
57
+ return {
58
+ descriptor: {
59
+ type: 'decoder-config-descriptor',
60
+ objectTypeIndication: mapToObjectAudioIndicator(objectTypeIndication),
61
+ },
62
+ };
63
+ }
64
+
65
+ if (tag === 6) {
66
+ const size = iterator.getPaddedFourByteNumber();
67
+
68
+ iterator.discard(size);
69
+ return {
70
+ descriptor: {
71
+ type: 'sl-config-descriptor',
72
+ },
73
+ };
74
+ }
75
+
76
+ return {
77
+ descriptor: null,
78
+ };
79
+ };
80
+
81
+ export const parseDescriptors = (
82
+ iterator: BufferIterator,
83
+ maxBytes: number,
84
+ ): Descriptor[] => {
85
+ const descriptors: Descriptor[] = [];
86
+ const initialOffset = iterator.counter.getOffset();
87
+
88
+ while (
89
+ iterator.bytesRemaining() > 0 &&
90
+ iterator.counter.getOffset() - initialOffset < maxBytes
91
+ ) {
92
+ const {descriptor} = processDescriptor({
93
+ iterator,
94
+ });
95
+
96
+ if (descriptor) {
97
+ descriptors.push(descriptor);
98
+ } else {
99
+ break;
100
+ }
101
+ }
102
+
103
+ return descriptors;
104
+ };
@@ -0,0 +1,49 @@
1
+ import type {BufferIterator} from '../../../buffer-iterator';
2
+ import type {Descriptor} from './esds-descriptors';
3
+ import {parseDescriptors} from './esds-descriptors';
4
+
5
+ export interface EsdsBox {
6
+ type: 'esds-box';
7
+ version: number;
8
+ tag: number;
9
+ sizeOfInstance: number;
10
+ esId: number;
11
+ descriptors: Descriptor[];
12
+ }
13
+
14
+ export const parseEsds = ({
15
+ data,
16
+ size,
17
+ fileOffset,
18
+ }: {
19
+ data: BufferIterator;
20
+ size: number;
21
+ fileOffset: number;
22
+ }): EsdsBox => {
23
+ const version = data.getUint8();
24
+ // Flags, we discard them
25
+ data.discard(3);
26
+ const tag = data.getUint8();
27
+
28
+ const sizeOfInstance = data.getPaddedFourByteNumber();
29
+ const esId = data.getUint16();
30
+
31
+ // disard 1 byte, currently unknown
32
+ data.discard(1);
33
+
34
+ const remaining = size - (data.counter.getOffset() - fileOffset);
35
+ const descriptors = parseDescriptors(data, remaining);
36
+
37
+ const remainingNow = size - (data.counter.getOffset() - fileOffset);
38
+
39
+ data.discard(remainingNow);
40
+
41
+ return {
42
+ type: 'esds-box',
43
+ version,
44
+ tag,
45
+ sizeOfInstance,
46
+ esId,
47
+ descriptors,
48
+ };
49
+ };
@@ -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
  };
@@ -177,6 +183,20 @@ const processBox = ({
177
183
  };
178
184
  }
179
185
 
186
+ if (boxType === 'esds') {
187
+ const box = parseEsds({
188
+ data: iterator,
189
+ size: boxSize,
190
+ fileOffset,
191
+ });
192
+
193
+ return {
194
+ type: 'complete',
195
+ box,
196
+ size: boxSize,
197
+ };
198
+ }
199
+
180
200
  const bytesRemainingInBox =
181
201
  boxSize - (iterator.counter.getOffset() - fileOffset);
182
202
 
@@ -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,16 @@ 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
+ let data = new Uint8Array(buf);
59
+ data.set(initialData);
60
+
53
61
  let view = new DataView(data.buffer);
54
62
  const counter = makeOffsetCounter();
55
63
 
@@ -76,6 +84,15 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
76
84
  );
77
85
  };
78
86
 
87
+ const getPaddedFourByteNumber = () => {
88
+ let lastInt = 128;
89
+ while (((lastInt = getUint8()), lastInt === 128)) {
90
+ // Do nothing
91
+ }
92
+
93
+ return lastInt;
94
+ };
95
+
79
96
  const getUint32 = () => {
80
97
  const val = view.getUint32(counter.getDiscardedOffset());
81
98
  counter.increment(4);
@@ -83,11 +100,11 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
83
100
  };
84
101
 
85
102
  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);
103
+ const oldLength = buf.byteLength;
104
+ const newLength = oldLength + newData.byteLength;
105
+ buf.resize(newLength);
106
+ const newArray = new Uint8Array(buf);
107
+ newArray.set(newData, oldLength);
91
108
  data = newArray;
92
109
  view = new DataView(data.buffer);
93
110
  };
@@ -111,9 +128,9 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
111
128
  const removeBytesRead = () => {
112
129
  const bytesToRemove = counter.getDiscardedOffset();
113
130
  counter.discardBytes(bytesToRemove);
114
- const newArray = new Uint8Array(data.buffer.byteLength - bytesToRemove);
115
- newArray.set(data.slice(bytesToRemove));
116
- data = newArray;
131
+ const newData = data.slice(bytesToRemove);
132
+ data.set(newData);
133
+ buf.resize(newData.byteLength);
117
134
  view = new DataView(data.buffer);
118
135
  };
119
136
 
@@ -134,6 +151,7 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
134
151
  const atom = getSlice(4);
135
152
  return new TextDecoder().decode(atom);
136
153
  },
154
+ getPaddedFourByteNumber,
137
155
  getMatroskaSegmentId: () => {
138
156
  const first = getSlice(1);
139
157
  const firstOneString = `0x${Array.from(new Uint8Array(first))
@@ -155,6 +173,9 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
155
173
  '0xb0',
156
174
  '0xba',
157
175
  '0x9a',
176
+ '0xe1',
177
+ '0xbf',
178
+ '0x88',
158
179
  ];
159
180
  if (knownIdsWithOneLength.includes(firstOneString)) {
160
181
  return firstOneString;
@@ -174,6 +195,8 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
174
195
  '0x55ee',
175
196
  '0x55b0',
176
197
  '0x7ba9',
198
+ '0x63a2',
199
+ '0x73a4',
177
200
  ];
178
201
 
179
202
  const firstTwoString = `${firstOneString}${Array.from(
@@ -267,6 +290,7 @@ export const getArrayBufferIterator = (initialData: Uint8Array) => {
267
290
  counter.increment(2);
268
291
  return val;
269
292
  },
293
+
270
294
  getInt16: () => {
271
295
  const val = view.getInt16(counter.getDiscardedOffset());
272
296
  counter.increment(2);
package/src/from-node.ts CHANGED
@@ -4,16 +4,18 @@ 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
9
  start: range === null ? 0 : range[0],
10
10
  end: range === null ? Infinity : range[1],
11
11
  });
12
- return Promise.resolve(
13
- Readable.toWeb(
12
+ const stats = await stat(src);
13
+ return {
14
+ reader: Readable.toWeb(
14
15
  stream,
15
16
  ).getReader() as ReadableStreamDefaultReader<Uint8Array>,
16
- );
17
+ contentLength: stats.size,
18
+ };
17
19
  },
18
20
  getLength: async (src) => {
19
21
  const stats = await stat(src);
package/src/from-web.ts CHANGED
@@ -31,9 +31,16 @@ export const webReader: ReaderInterface = {
31
31
  throw new Error('No body');
32
32
  }
33
33
 
34
+ const length = res.headers.get('content-length');
35
+ if (!length) {
36
+ throw new Error('No content-length');
37
+ }
38
+
39
+ const contentLength = length === null ? null : parseInt(length, 10);
40
+
34
41
  const reader = res.body.getReader();
35
42
 
36
- return reader;
43
+ return {reader, contentLength};
37
44
  },
38
45
  getLength: async (src) => {
39
46
  const res = await fetch(src, {