From 4909b6fe12d0ee36cf4dcefbfc0d9776d6367565 Mon Sep 17 00:00:00 2001 From: Borewit Date: Wed, 28 Jan 2026 19:49:43 +0100 Subject: [PATCH] fix(id3v2): prevent crash when no tag frames present Fix TypeError: Cannot read properties of undefined (reading 'filter') by defaulting missing frame/tag collections to empty arrays/objects. - Add MP3 sample without tags + regression test - Update related TS configs and dependencies/lockfile --- lib/id3v2/ID3v2Parser.ts | 9 ++++----- test/samples/mp3/notags.mp3 | Bin 0 -> 12636 bytes test/test-file-mp3.ts | 8 ++++++++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 test/samples/mp3/notags.mp3 diff --git a/lib/id3v2/ID3v2Parser.ts b/lib/id3v2/ID3v2Parser.ts index 8f89c4d6..93c29ac9 100644 --- a/lib/id3v2/ID3v2Parser.ts +++ b/lib/id3v2/ID3v2Parser.ts @@ -104,7 +104,7 @@ export class ID3v2Parser { await (id3Header.flags.isExtendedHeader ? this.parseExtendedHeader() : this.parseId3Data(id3Header.size)); // Post process - const chapters = ID3v2Parser.mapId3v2Chapters(this.metadata.native[this.headerType], this.metadata.format.sampleRate); + const chapters = ID3v2Parser.mapId3v2Chapters(this.metadata.native[this.headerType]); this.metadata.setFormat('chapters', chapters); } @@ -178,10 +178,9 @@ export class ID3v2Parser { * This function expects the `native` tags already to contain parsed `CHAP` and `CTOC` frame values, * as produced by `FrameParser.readData`. */ - private static mapId3v2Chapters( - id3Tags: ITag[], - sampleRate?: number - ): IChapter[] | undefined { + private static mapId3v2Chapters(id3Tags?: ITag[]): IChapter[] | undefined { + + if (!id3Tags) return; const chapFrames = id3Tags.filter(t => t.id === 'CHAP') as any[] | undefined; if (!chapFrames?.length) return; diff --git a/test/samples/mp3/notags.mp3 b/test/samples/mp3/notags.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..40a38fb15c63d13273583d589e54299d3ea11170 GIT binary patch literal 12636 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 DPa*XPEN^Os6jRZI-o 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 literal 0 HcmV?d00001 diff --git a/test/test-file-mp3.ts b/test/test-file-mp3.ts index 0ffcefc0..44da780e 100644 --- a/test/test-file-mp3.ts +++ b/test/test-file-mp3.ts @@ -24,6 +24,14 @@ describe('Parse MP3 files', () => { const mp3SamplePath = path.join(samplePath, 'mp3'); + it('should handle MP3 without any tags', async () => { + const filePath = path.join(mp3SamplePath, 'notags.mp3'); + const {format, native} = await mm.parseFile(filePath, {includeChapters: true}); + assert.strictEqual(format.container, 'MPEG'); + assert.strictEqual(format.codec, 'MPEG 2 Layer 3'); + assert.strictEqual(Object.keys(native).length, 0, 'Should be empty'); + }); + describe('Test patterns for ISO/MPEG ', () => { it.skip('ISO/MPEG 1 Layer 1', async () => {