hls-streamer 4.5.1 → 4.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -335,9 +335,11 @@ Provide either `filePath` **or** `storageProvider` — not both.
335
335
  ### API Surface
336
336
 
337
337
  - `createM3U8(): Promise<string>` – Full HLS playlist with frame-accurate durations. Audio → HLS v6; Video → HLS v7 + `EXT-X-MAP`.
338
- - `getFileBuffer(start: number, end: number): Promise<Buffer>` – Byte-range read from the source file (used for both segments and the init segment).
338
+ - `getFileBuffer(start: number, end: number): Promise<Buffer>` – Byte-range read from the source file (used for both segments and the init segment). Validates the range against file size only — does not trigger a full parse.
339
339
  - `getSegmentDuration(index: number): Promise<number>` – Duration in seconds for a specific segment.
340
- - `getMediaType(): Promise<'audio' | 'video'>` – Returns `'video'` for MP4/MOV/M4V, `'audio'` for everything else.
340
+ - `getMediaType(): Promise<'audio' | 'video'>` – Returns `'video'` for MP4/MOV/M4V, `'audio'` for everything else. Reads only the file header (~64 bytes) when possible.
341
+ - `getFileInfo(): Promise<MediaFileInfo>` – Parsed metadata (size, duration, frame table, etc.). Cached on the instance.
342
+ - `restoreFileInfo(fileInfo: MediaFileInfo): void` – Hydrate a previously-parsed `MediaFileInfo` into a fresh instance, skipping the underlying parse. Pair with an external metadata cache (LRU, Redis) to avoid re-downloading + re-parsing the same file across short-lived instances. Safe to round-trip via `JSON.stringify` / `JSON.parse`.
341
343
 
342
344
  Custom error classes: `FileNotFoundError`, `InvalidFileError`, `InvalidRangeError`, `InvalidParameterError`, `UnsupportedFormatError`, `StorageProviderError`.
343
345
 
@@ -397,6 +399,19 @@ Custom error classes: `FileNotFoundError`, `InvalidFileError`, `InvalidRangeErro
397
399
  ## Operational Tips
398
400
 
399
401
  - **Caching** – Construct the streamer once per unique file and reuse it. Segment planning caches metadata after the first call.
402
+ - **External metadata cache** – If you cannot keep `HlsStreamer` instances alive between requests (e.g. serverless, multi-process workers), use `getFileInfo()` to capture the parsed `MediaFileInfo` once and `restoreFileInfo()` to hydrate fresh instances on subsequent requests. Subsequent calls to `createM3U8()`, `getMediaType()`, and `getFileBuffer()` then issue zero parse-related reads against your storage backend:
403
+
404
+ ```ts
405
+ const cached = lruCache.get(s3Key); // your cache
406
+ const streamer = new HlsStreamer({ storageProvider: new S3Provider({ bucket, key: s3Key, client: s3 }) });
407
+ if (cached) {
408
+ streamer.restoreFileInfo(cached);
409
+ } else {
410
+ lruCache.set(s3Key, await streamer.getFileInfo());
411
+ }
412
+ // From here, segment requests do one ranged GET, nothing more.
413
+ ```
414
+
400
415
  - **Video segment size** – Use a larger `segmentSizeKB` (1024–4096) for video to avoid excessive HTTP requests. Audio works well at 512.
401
416
  - **CDN friendliness** – Segment URLs are deterministic byte ranges, making them ideal for edge caching. Use `Cache-Control: public, max-age=86400`.
402
417
  - **Serverless** – Zero-dependency design works in Lambda/Cloud Functions. `getFileBuffer` reads only the bytes needed, keeping memory usage low.
@@ -433,6 +448,28 @@ Contributions are welcome! Please open an issue to discuss substantial changes b
433
448
 
434
449
  ## Release Notes
435
450
 
451
+ For the complete history, see [CHANGELOG.md](./CHANGELOG.md).
452
+
453
+ ### Version 4.6.0
454
+
455
+ #### New Features
456
+
457
+ - **`HlsStreamer.getFileInfo()`** is now public — returns the parsed `MediaFileInfo` (size, duration, frame table, etc.). Result is cached on the instance.
458
+ - **`HlsStreamer.restoreFileInfo(fileInfo)`** hydrates a previously-parsed `MediaFileInfo` into a fresh instance, skipping the underlying parse. Designed to pair with an external cache (LRU, Redis) so short-lived instances avoid redundant downloads + re-parsing of the same file. Round-trips cleanly through `JSON.stringify` / `JSON.parse`.
459
+
460
+ #### Performance
461
+
462
+ - **`getMediaType()`** no longer triggers a full file read in the common path. It now reads only the file header (~64 bytes) and classifies via magic bytes; falls back to a full parse only when the header is inconclusive. With a `format` override, classification happens with no read at all.
463
+ - **`getFileBuffer()`** no longer triggers a full file parse. It validates the requested range using only `provider.getSize()` (a HEAD-equivalent), then issues the ranged read. The size is memoized on the instance — repeated segment requests against the same `HlsStreamer` issue exactly one `getSize()` call regardless of how many ranges are fetched.
464
+
465
+ #### Migration
466
+
467
+ Fully backward compatible — no signatures or behaviors changed. All additions are additive.
468
+
469
+ Full details: [CHANGELOG.md](./CHANGELOG.md#460---2026-05-02).
470
+
471
+ ---
472
+
436
473
  ### Version 4.5.0
437
474
 
438
475
  #### New Features
@@ -1,8 +1,6 @@
1
1
  import { Mp3FileInfo, MediaFileInfo } from '../Interfaces/HlsStreamer';
2
2
  import { MediaFormat } from '../Parsers/IMediaParser';
3
3
  export declare class FileLib {
4
- private static readonly BITRATE_INDEX;
5
- private static readonly SAMPLE_RATE_INDEX;
6
4
  static getFileSizeInBytes(buffer: Buffer): number;
7
5
  static getMP3DurationFromBuffer(mp3Buffer: Buffer): Promise<number>;
8
6
  static analyzeMP3Buffer(buffer: Buffer, opts?: {
@@ -21,13 +19,5 @@ export declare class FileLib {
21
19
  filePath?: string;
22
20
  format?: MediaFormat;
23
21
  }): MediaFileInfo;
24
- private static calculateFrameLength;
25
- private static getId3Offsets;
26
- private static syncSafeInteger;
27
- private static isFrameSync;
28
- private static parseFrameHeader;
29
- private static decodeVersion;
30
- private static decodeLayer;
31
- private static getSamplesPerFrame;
32
22
  }
33
23
  //# sourceMappingURL=FileLib.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileLib.d.ts","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAgB,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAGrF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAqBtD,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAgBnC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAIvC;IAKF,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;WAWpC,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,WAAW;WA8HzE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;WAetD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;WAmBhF,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IAU7F,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACxE,aAAa;IA8BhB,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACxE,aAAa;IAIhB,OAAO,CAAC,MAAM,CAAC,oBAAoB;IASnC,OAAO,CAAC,MAAM,CAAC,aAAa;IA8B5B,OAAO,CAAC,MAAM,CAAC,eAAe;IAO9B,OAAO,CAAC,MAAM,CAAC,WAAW;IAW1B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAqC/B,OAAO,CAAC,MAAM,CAAC,aAAa;IAa5B,OAAO,CAAC,MAAM,CAAC,WAAW;IAa1B,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAWlC"}
1
+ {"version":3,"file":"FileLib.d.ts","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAgB,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAErF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAMtD,qBAAa,OAAO;IAIlB,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;WAWpC,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQzE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,WAAW;WAiDzE,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;WAetD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;WAmBhF,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IAU7F,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACxE,aAAa;IA8BhB,MAAM,CAAC,kBAAkB,CACvB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACxE,aAAa;CAGjB"}
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import { ParserFactory } from '../Parsers/ParserFactory';
3
+ import { Mp3Parser } from '../Parsers/Mp3Parser';
3
4
  export class FileLib {
4
5
  static getFileSizeInBytes(buffer) {
5
6
  if (Buffer.isBuffer(buffer)) {
@@ -14,98 +15,39 @@ export class FileLib {
14
15
  return duration;
15
16
  }
16
17
  static analyzeMP3Buffer(buffer, opts = {}) {
17
- const warnings = [];
18
- const size = opts.fileSize ?? buffer.length;
19
- if (buffer.length === 0) {
20
- return {
21
- size,
22
- duration: 0,
23
- audioDataSize: 0,
24
- frames: []
25
- };
26
- }
27
- const offsets = this.getId3Offsets(buffer);
28
- const frames = [];
29
- const payloadSize = Math.max(0, offsets.audioEnd - offsets.startOffset);
30
- let offset = offsets.startOffset;
31
- let frameIndex = 0;
32
- let sampleRate;
33
- let totalDuration = 0;
34
- let totalAudioBytes = 0;
35
- while (offset + 4 <= offsets.audioEnd) {
36
- if (!this.isFrameSync(buffer, offset)) {
37
- offset++;
38
- continue;
39
- }
40
- const header = buffer.readUInt32BE(offset);
41
- const parsed = this.parseFrameHeader(header);
42
- if (!parsed) {
43
- offset++;
44
- continue;
45
- }
46
- const frameLength = this.calculateFrameLength(parsed);
47
- if (frameLength <= 0) {
48
- warnings.push(`Encountered zero-length frame at offset ${offset}`);
49
- offset++;
50
- continue;
51
- }
52
- if (offset + frameLength > buffer.length) {
53
- warnings.push(`Truncated frame at offset ${offset}`);
54
- break;
55
- }
56
- sampleRate = sampleRate ?? parsed.sampleRate;
57
- const frameDuration = parsed.samplesPerFrame / parsed.sampleRate;
58
- frames.push({
59
- index: frameIndex,
60
- offset,
61
- length: frameLength,
62
- duration: frameDuration,
63
- samples: parsed.samplesPerFrame,
64
- sampleRate: parsed.sampleRate,
65
- bitrate: parsed.bitrate,
66
- padding: parsed.padding
67
- });
68
- frameIndex++;
69
- totalDuration += frameDuration;
70
- totalAudioBytes += frameLength;
71
- offset += frameLength;
72
- }
73
- if (frames.length === 0) {
74
- const estimatedDuration = buffer.length / 16000;
75
- warnings.push('Falling back to heuristic duration due to missing MP3 frames');
76
- const metadata = {
77
- size,
78
- duration: Math.max(0.001, estimatedDuration),
79
- audioDataSize: payloadSize,
80
- frames: []
81
- };
82
- if (warnings.length) {
83
- metadata.warnings = warnings;
84
- }
85
- return metadata;
86
- }
87
- const duration = totalDuration;
88
- const averageBitrate = duration > 0 ? (totalAudioBytes * 8) / duration / 1000 : undefined;
18
+ const parsed = new Mp3Parser().analyze(buffer, opts);
19
+ const frames = parsed.frames.map((frame) => ({
20
+ index: frame.index,
21
+ offset: frame.offset,
22
+ length: frame.length,
23
+ duration: frame.duration,
24
+ samples: frame.samples,
25
+ sampleRate: frame.sampleRate,
26
+ bitrate: frame.bitrate,
27
+ padding: (((buffer[frame.offset + 2] ?? 0) >> 1) & 0x1)
28
+ }));
89
29
  const metadata = {
90
- size,
91
- duration,
92
- audioDataSize: totalAudioBytes || payloadSize,
30
+ size: parsed.size,
31
+ duration: parsed.duration,
32
+ audioDataSize: parsed.audioDataSize,
93
33
  frames
94
34
  };
95
- if (sampleRate !== undefined) {
96
- metadata.sampleRate = sampleRate;
35
+ if (parsed.sampleRate !== undefined) {
36
+ metadata.sampleRate = parsed.sampleRate;
97
37
  }
98
- if (averageBitrate !== undefined) {
99
- metadata.averageBitrate = averageBitrate;
38
+ if (parsed.averageBitrate !== undefined) {
39
+ metadata.averageBitrate = parsed.averageBitrate;
100
40
  }
101
- if (offsets.id3v2Size > 0) {
102
- metadata.id3v2Size = offsets.id3v2Size;
41
+ const id3v2Size = parsed.metadata?.['id3v2Size'];
42
+ if (typeof id3v2Size === 'number' && id3v2Size > 0) {
43
+ metadata.id3v2Size = id3v2Size;
103
44
  }
104
- if (offsets.id3v1Size > 0) {
105
- metadata.id3v1Size = offsets.id3v1Size;
45
+ const id3v1Size = parsed.metadata?.['id3v1Size'];
46
+ if (typeof id3v1Size === 'number' && id3v1Size > 0) {
47
+ metadata.id3v1Size = id3v1Size;
106
48
  }
107
- if (warnings.length) {
108
- metadata.warnings = warnings;
49
+ if (parsed.warnings?.length) {
50
+ metadata.warnings = parsed.warnings;
109
51
  }
110
52
  return metadata;
111
53
  }
@@ -156,144 +98,5 @@ export class FileLib {
156
98
  static analyzeAudioBuffer(buffer, opts = {}) {
157
99
  return this.analyzeMediaBuffer(buffer, opts);
158
100
  }
159
- static calculateFrameLength(frame) {
160
- if (frame.layer === 1) {
161
- return Math.floor(((12 * frame.bitrate * 1000) / frame.sampleRate + frame.padding) * 4);
162
- }
163
- const multiplier = frame.layer === 3 && frame.version !== 1 ? 72 : 144;
164
- return Math.floor((multiplier * frame.bitrate * 1000) / frame.sampleRate + frame.padding);
165
- }
166
- static getId3Offsets(buffer) {
167
- let startOffset = 0;
168
- let id3v2Size = 0;
169
- if (buffer.length >= 10 && buffer.toString('ascii', 0, 3) === 'ID3') {
170
- const sizeBytes = buffer.subarray(6, 10);
171
- id3v2Size = this.syncSafeInteger(sizeBytes);
172
- startOffset = Math.min(buffer.length, 10 + id3v2Size);
173
- }
174
- let id3v1Size = 0;
175
- let audioEnd = buffer.length;
176
- if (buffer.length >= 128) {
177
- const tagOffset = buffer.length - 128;
178
- if (buffer.toString('ascii', tagOffset, tagOffset + 3) === 'TAG') {
179
- id3v1Size = 128;
180
- audioEnd = tagOffset;
181
- }
182
- }
183
- return {
184
- startOffset,
185
- id3v2Size,
186
- audioEnd,
187
- id3v1Size
188
- };
189
- }
190
- static syncSafeInteger(bytes) {
191
- return ((bytes[0] & 0x7f) << 21) |
192
- ((bytes[1] & 0x7f) << 14) |
193
- ((bytes[2] & 0x7f) << 7) |
194
- (bytes[3] & 0x7f);
195
- }
196
- static isFrameSync(buffer, offset) {
197
- const byte1 = buffer[offset];
198
- const byte2 = buffer[offset + 1];
199
- if (byte1 === undefined || byte2 === undefined) {
200
- return false;
201
- }
202
- return byte1 === 0xff && (byte2 & 0xe0) === 0xe0;
203
- }
204
- static parseFrameHeader(header) {
205
- const versionBits = (header >> 19) & 0x3;
206
- const layerBits = (header >> 17) & 0x3;
207
- const bitrateIndex = (header >> 12) & 0xf;
208
- const sampleRateIndex = (header >> 10) & 0x3;
209
- const padding = ((header >> 9) & 0x1);
210
- const version = this.decodeVersion(versionBits);
211
- const layer = this.decodeLayer(layerBits);
212
- if (!version || !layer) {
213
- return null;
214
- }
215
- if (bitrateIndex === 0 || bitrateIndex === 15 || sampleRateIndex === 3) {
216
- return null;
217
- }
218
- const bitrate = this.BITRATE_INDEX[version][layer][bitrateIndex];
219
- const sampleRate = this.SAMPLE_RATE_INDEX[version][sampleRateIndex];
220
- if (!bitrate || !sampleRate) {
221
- return null;
222
- }
223
- const samplesPerFrame = this.getSamplesPerFrame(version, layer);
224
- return {
225
- version,
226
- layer,
227
- bitrate,
228
- sampleRate,
229
- padding,
230
- samplesPerFrame
231
- };
232
- }
233
- static decodeVersion(bits) {
234
- switch (bits) {
235
- case 0b11:
236
- return 1;
237
- case 0b10:
238
- return 2;
239
- case 0b00:
240
- return 25;
241
- default:
242
- return null;
243
- }
244
- }
245
- static decodeLayer(bits) {
246
- switch (bits) {
247
- case 0b11:
248
- return 1;
249
- case 0b10:
250
- return 2;
251
- case 0b01:
252
- return 3;
253
- default:
254
- return null;
255
- }
256
- }
257
- static getSamplesPerFrame(version, layer) {
258
- if (layer === 1) {
259
- return 384;
260
- }
261
- if (layer === 2) {
262
- return 1152;
263
- }
264
- return version === 1 ? 1152 : 576;
265
- }
266
101
  }
267
- Object.defineProperty(FileLib, "BITRATE_INDEX", {
268
- enumerable: true,
269
- configurable: true,
270
- writable: true,
271
- value: {
272
- 1: {
273
- 1: [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448],
274
- 2: [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384],
275
- 3: [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
276
- },
277
- 2: {
278
- 1: [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256],
279
- 2: [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
280
- 3: [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
281
- },
282
- 25: {
283
- 1: [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256],
284
- 2: [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
285
- 3: [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
286
- }
287
- }
288
- });
289
- Object.defineProperty(FileLib, "SAMPLE_RATE_INDEX", {
290
- enumerable: true,
291
- configurable: true,
292
- writable: true,
293
- value: {
294
- 1: [44100, 48000, 32000],
295
- 2: [22050, 24000, 16000],
296
- 25: [11025, 12000, 8000]
297
- }
298
- });
299
102
  //# sourceMappingURL=FileLib.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileLib.js","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FileLib.js","sourceRoot":"","sources":["../../src/Libs/FileLib.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { IMediaParser, MediaFileInfo, MediaFormat } from './IMediaParser';
2
2
  export declare class Mp3Parser implements IMediaParser {
3
+ private static readonly MIN_CONSECUTIVE_AUDIO_FRAMES;
3
4
  private static readonly BITRATE_INDEX;
4
5
  private static readonly SAMPLE_RATE_INDEX;
5
6
  getFormat(): MediaFormat;
@@ -9,7 +10,14 @@ export declare class Mp3Parser implements IMediaParser {
9
10
  }): MediaFileInfo;
10
11
  private calculateFrameLength;
11
12
  private getId3Offsets;
13
+ private hasId3v2Magic;
14
+ private parseId3v2Tag;
12
15
  private syncSafeInteger;
16
+ private isSyncSafeInteger;
17
+ private findFirstFrameOffset;
18
+ private getFrameCandidate;
19
+ private countConsecutiveFrames;
20
+ private isCompatibleFrame;
13
21
  private isFrameSync;
14
22
  private parseFrameHeader;
15
23
  private decodeVersion;
@@ -1 +1 @@
1
- {"version":3,"file":"Mp3Parser.d.ts","sourceRoot":"","sources":["../../src/Parsers/Mp3Parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAkB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAsB1F,qBAAa,SAAU,YAAW,YAAY;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAgBnC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAIvC;IAEF,SAAS,IAAI,WAAW;IAIxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAsBjC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,aAAa;IAgJxE,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,gBAAgB;IAqCxB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;CAW3B"}
1
+ {"version":3,"file":"Mp3Parser.d.ts","sourceRoot":"","sources":["../../src/Parsers/Mp3Parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAkB,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA4B1F,qBAAa,SAAU,YAAW,YAAY;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAAK;IAEzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAgBnC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAIvC;IAEF,SAAS,IAAI,WAAW;IAIxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAsBjC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,aAAa;IAwJxE,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,aAAa;IAoDrB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,aAAa;IA+BrB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,oBAAoB;IAkC5B,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,sBAAsB;IA4B9B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,gBAAgB;IAqCxB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;CAW3B"}
@@ -32,8 +32,13 @@ export class Mp3Parser {
32
32
  }
33
33
  const offsets = this.getId3Offsets(buffer);
34
34
  const frames = [];
35
- const payloadSize = Math.max(0, offsets.audioEnd - offsets.startOffset);
36
- let offset = offsets.startOffset;
35
+ const firstFrameOffset = this.findFirstFrameOffset(buffer, offsets.scanStartOffset, offsets.audioEnd);
36
+ const audioStartOffset = firstFrameOffset ?? offsets.startOffset;
37
+ const payloadSize = Math.max(0, offsets.audioEnd - audioStartOffset);
38
+ if (firstFrameOffset !== null && firstFrameOffset < offsets.startOffset) {
39
+ warnings.push(`ID3v2 tag size appears to overlap audio; using first verified MP3 frame at offset ${firstFrameOffset}`);
40
+ }
41
+ let offset = audioStartOffset;
37
42
  let frameIndex = 0;
38
43
  let sampleRate;
39
44
  let channels;
@@ -142,11 +147,27 @@ export class Mp3Parser {
142
147
  }
143
148
  getId3Offsets(buffer) {
144
149
  let startOffset = 0;
150
+ let scanStartOffset = 0;
145
151
  let id3v2Size = 0;
146
- if (buffer.length >= 10 && buffer.toString('ascii', 0, 3) === 'ID3') {
147
- const sizeBytes = buffer.subarray(6, 10);
148
- id3v2Size = this.syncSafeInteger(sizeBytes);
149
- startOffset = Math.min(buffer.length, 10 + id3v2Size);
152
+ if (this.hasId3v2Magic(buffer, 0)) {
153
+ scanStartOffset = Math.min(buffer.length, 10);
154
+ let offset = 0;
155
+ while (offset < buffer.length && this.hasId3v2Magic(buffer, offset)) {
156
+ const tag = this.parseId3v2Tag(buffer, offset);
157
+ if (!tag) {
158
+ break;
159
+ }
160
+ id3v2Size += tag.payloadSize;
161
+ const nextOffset = Math.min(buffer.length, offset + tag.totalSize);
162
+ if (nextOffset <= offset) {
163
+ break;
164
+ }
165
+ offset = nextOffset;
166
+ startOffset = offset;
167
+ }
168
+ if (startOffset === 0) {
169
+ startOffset = scanStartOffset;
170
+ }
150
171
  }
151
172
  let id3v1Size = 0;
152
173
  let audioEnd = buffer.length;
@@ -159,17 +180,113 @@ export class Mp3Parser {
159
180
  }
160
181
  return {
161
182
  startOffset,
183
+ scanStartOffset,
162
184
  id3v2Size,
163
185
  audioEnd,
164
186
  id3v1Size
165
187
  };
166
188
  }
189
+ hasId3v2Magic(buffer, offset) {
190
+ return offset + 10 <= buffer.length && buffer.toString('ascii', offset, offset + 3) === 'ID3';
191
+ }
192
+ parseId3v2Tag(buffer, offset) {
193
+ if (!this.hasId3v2Magic(buffer, offset)) {
194
+ return null;
195
+ }
196
+ const majorVersion = buffer[offset + 3];
197
+ const revision = buffer[offset + 4];
198
+ const flags = buffer[offset + 5];
199
+ const sizeBytes = buffer.subarray(offset + 6, offset + 10);
200
+ if (majorVersion === undefined ||
201
+ revision === undefined ||
202
+ flags === undefined ||
203
+ majorVersion < 2 ||
204
+ majorVersion > 4 ||
205
+ revision === 0xff ||
206
+ !this.isSyncSafeInteger(sizeBytes)) {
207
+ return null;
208
+ }
209
+ const payloadSize = this.syncSafeInteger(sizeBytes);
210
+ const footerSize = majorVersion === 4 && (flags & 0x10) !== 0 ? 10 : 0;
211
+ return {
212
+ payloadSize,
213
+ totalSize: 10 + payloadSize + footerSize
214
+ };
215
+ }
167
216
  syncSafeInteger(bytes) {
168
217
  return ((bytes[0] & 0x7f) << 21) |
169
218
  ((bytes[1] & 0x7f) << 14) |
170
219
  ((bytes[2] & 0x7f) << 7) |
171
220
  (bytes[3] & 0x7f);
172
221
  }
222
+ isSyncSafeInteger(bytes) {
223
+ return bytes.length === 4 && bytes.every((byte) => (byte & 0x80) === 0);
224
+ }
225
+ findFirstFrameOffset(buffer, startOffset, audioEnd) {
226
+ let offset = Math.max(0, Math.min(startOffset, audioEnd));
227
+ let fallbackOffset = null;
228
+ while (offset + 4 <= audioEnd) {
229
+ if (buffer[offset] !== 0xff) {
230
+ const next = buffer.indexOf(0xff, offset + 1);
231
+ if (next === -1 || next + 4 > audioEnd) {
232
+ break;
233
+ }
234
+ offset = next;
235
+ }
236
+ const candidate = this.getFrameCandidate(buffer, offset, audioEnd);
237
+ if (!candidate) {
238
+ offset++;
239
+ continue;
240
+ }
241
+ fallbackOffset = fallbackOffset ?? offset;
242
+ if (this.countConsecutiveFrames(buffer, offset, audioEnd) >=
243
+ Mp3Parser.MIN_CONSECUTIVE_AUDIO_FRAMES) {
244
+ return offset;
245
+ }
246
+ offset++;
247
+ }
248
+ return fallbackOffset;
249
+ }
250
+ getFrameCandidate(buffer, offset, audioEnd) {
251
+ if (offset + 4 > audioEnd || !this.isFrameSync(buffer, offset)) {
252
+ return null;
253
+ }
254
+ const header = this.parseFrameHeader(buffer.readUInt32BE(offset));
255
+ if (!header) {
256
+ return null;
257
+ }
258
+ const length = this.calculateFrameLength(header);
259
+ if (length <= 0) {
260
+ return null;
261
+ }
262
+ return { header, length };
263
+ }
264
+ countConsecutiveFrames(buffer, offset, audioEnd) {
265
+ let count = 0;
266
+ let cursor = offset;
267
+ let firstHeader;
268
+ while (count < Mp3Parser.MIN_CONSECUTIVE_AUDIO_FRAMES) {
269
+ const candidate = this.getFrameCandidate(buffer, cursor, audioEnd);
270
+ if (!candidate) {
271
+ break;
272
+ }
273
+ if (firstHeader && !this.isCompatibleFrame(firstHeader, candidate.header)) {
274
+ break;
275
+ }
276
+ firstHeader = firstHeader ?? candidate.header;
277
+ count++;
278
+ if (cursor + candidate.length > audioEnd || cursor + candidate.length > buffer.length) {
279
+ break;
280
+ }
281
+ cursor += candidate.length;
282
+ }
283
+ return count;
284
+ }
285
+ isCompatibleFrame(first, next) {
286
+ return first.version === next.version &&
287
+ first.layer === next.layer &&
288
+ first.sampleRate === next.sampleRate;
289
+ }
173
290
  isFrameSync(buffer, offset) {
174
291
  const byte1 = buffer[offset];
175
292
  const byte2 = buffer[offset + 1];
@@ -241,6 +358,12 @@ export class Mp3Parser {
241
358
  return version === 1 ? 1152 : 576;
242
359
  }
243
360
  }
361
+ Object.defineProperty(Mp3Parser, "MIN_CONSECUTIVE_AUDIO_FRAMES", {
362
+ enumerable: true,
363
+ configurable: true,
364
+ writable: true,
365
+ value: 3
366
+ });
244
367
  Object.defineProperty(Mp3Parser, "BITRATE_INDEX", {
245
368
  enumerable: true,
246
369
  configurable: true,