From e5cc111dbbf562f2c3e3c808e2352b88bbc85d8d Mon Sep 17 00:00:00 2001 From: Borewit Date: Tue, 26 Nov 2024 21:28:00 +0100 Subject: [PATCH] Utilize `strtok3` random access reading --- lib/ParserFactory.ts | 10 ++++- lib/apev2/APEv2Parser.ts | 11 ++++-- lib/common/RandomFileReader.ts | 34 ---------------- lib/common/RandomUint8ArrayReader.ts | 26 ------------ lib/core.ts | 18 ++++----- lib/id3v1/ID3v1Parser.ts | 13 +++--- lib/index.ts | 10 +---- lib/lyrics3/Lyrics3.ts | 11 ++++-- lib/type.ts | 22 ----------- package.json | 3 +- test/metadata-parsers.ts | 35 +++++++++++------ test/test-file-aac.ts | 4 +- test/test-file-aiff.ts | 16 ++++---- test/test-file-amr.ts | 9 +++-- test/test-file-ape.ts | 2 +- test/test-file-asf.ts | 4 +- test/test-file-flac.ts | 6 +-- test/test-file-mp3.ts | 7 ++-- test/test-file-mp4.ts | 59 +++++++++++++++------------- test/test-file-mpeg.ts | 2 +- test/test-file-musepack.ts | 59 ++++++++++++++-------------- test/test-file-ogg.ts | 6 +-- test/test-file-wavpack.ts | 6 +-- test/test-id3v1.1.ts | 15 ++++--- test/test-regress-GH-56.ts | 31 +++++++++------ yarn.lock | 22 ++++++++++- 26 files changed, 205 insertions(+), 236 deletions(-) delete mode 100644 lib/common/RandomFileReader.ts delete mode 100644 lib/common/RandomUint8ArrayReader.ts diff --git a/lib/ParserFactory.ts b/lib/ParserFactory.ts index 9541c4952..b716cb79e 100644 --- a/lib/ParserFactory.ts +++ b/lib/ParserFactory.ts @@ -6,7 +6,7 @@ import initDebug from 'debug'; import { type INativeMetadataCollector, MetadataCollector } from './common/MetadataCollector.js'; import type { IAudioMetadata, IOptions, ParserType } from './type.js'; -import type { ITokenizer } from 'strtok3'; +import type { IRandomAccessTokenizer, ITokenizer } from 'strtok3'; import { mpegParserLoader } from './mpeg/MpegLoader.js'; import { CouldNotDetermineFileTypeError, UnsupportedFileTypeError } from './ParseError.js'; import { apeParserLoader } from './apev2/Apev2Loader.js'; @@ -22,6 +22,7 @@ import { oggParserLoader } from './ogg/OggLoader.js'; import { wavpackParserLoader } from './wavpack/WavPackLoader.js'; import { riffParserLoader } from './wav/WaveLoader.js'; import { amrParserLoader } from './amr/AmrLoader.js'; +import { scanAppendingHeaders } from './core.js'; const debug = initDebug('music-metadata:parser:factory'); @@ -93,6 +94,13 @@ export class ParserFactory { async parse(tokenizer: ITokenizer, parserLoader: IParserLoader | undefined, opts?: IOptions): Promise { + if (tokenizer.supportsRandomAccess()) { + debug('tokenizer supports random-access, scanning for appending headers'); + await scanAppendingHeaders(tokenizer as IRandomAccessTokenizer, opts); + } else { + debug('tokenizer does not support random-access, cannot scan for appending headers'); + } + if (!parserLoader) { const buf = new Uint8Array(4100); if (tokenizer.fileInfo.mimeType) { diff --git a/lib/apev2/APEv2Parser.ts b/lib/apev2/APEv2Parser.ts index ca6496d4e..22232d9ef 100644 --- a/lib/apev2/APEv2Parser.ts +++ b/lib/apev2/APEv2Parser.ts @@ -4,7 +4,7 @@ import { StringType } from 'token-types'; import { uint8ArrayToString } from 'uint8array-extras'; import * as util from '../common/Util.js'; -import type { IOptions, IRandomReader, IApeHeader } from '../type.js'; +import type { IOptions, IApeHeader } from '../type.js'; import type { INativeMetadataCollector } from '../common/MetadataCollector.js'; import { BasicParser } from '../common/BasicParser.js'; import { @@ -18,6 +18,7 @@ import { TagItemHeader } from './APEv2Token.js'; import { makeUnexpectedFileContentError } from '../ParseError.js'; +import type { IRandomAccessTokenizer } from 'strtok3'; const debug = initDebug('music-metadata:parser:APEv2'); @@ -54,13 +55,15 @@ export class APEv2Parser extends BasicParser { /** * Calculates the APEv1 / APEv2 first field offset - * @param reader + * @param tokenizer * @param offset */ - public static async findApeFooterOffset(reader: IRandomReader, offset: number): Promise { + public static async findApeFooterOffset(tokenizer: IRandomAccessTokenizer, offset: number): Promise { // Search for APE footer header at the end of the file const apeBuf = new Uint8Array(TagFooter.len); - await reader.randomRead(apeBuf, 0, TagFooter.len, offset - TagFooter.len); + const position = tokenizer.position; + await tokenizer.readBuffer(apeBuf, {position: offset - TagFooter.len}); + tokenizer.setPosition(position); const tagFooter = TagFooter.get(apeBuf, 0); if (tagFooter.ID === 'APETAGEX') { if (tagFooter.flags.isHeader) { diff --git a/lib/common/RandomFileReader.ts b/lib/common/RandomFileReader.ts deleted file mode 100644 index 17c2b0162..000000000 --- a/lib/common/RandomFileReader.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as fs from 'node:fs'; - -import type { IRandomReader } from '../type.js'; - -/** - * Provides abstract file access via the IRandomRead interface - */ -export class RandomFileReader implements IRandomReader { - - private constructor(private readonly fileHandle: fs.promises.FileHandle, public filePath: string, public fileSize: number) { - } - - /** - * Read from a given position of an abstracted file or buffer. - * @param buffer {Uint8Array} is the buffer that the data will be written to. - * @param offset {number} is the offset in the buffer to start writing at. - * @param length {number}is an integer specifying the number of bytes to read. - * @param position {number} is an argument specifying where to begin reading from in the file. - * @return {Promise} bytes read - */ - public async randomRead(buffer: Uint8Array, offset: number, length: number, position: number): Promise { - const result = await this.fileHandle.read(buffer, offset, length, position); - return result.bytesRead; - } - - public async close() { - return this.fileHandle.close(); - } - - public static async init(filePath: string, fileSize: number): Promise { - const fileHandle = await fs.promises.open(filePath, 'r'); - return new RandomFileReader(fileHandle, filePath, fileSize); - } -} diff --git a/lib/common/RandomUint8ArrayReader.ts b/lib/common/RandomUint8ArrayReader.ts deleted file mode 100644 index 3341fe2a1..000000000 --- a/lib/common/RandomUint8ArrayReader.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { IRandomReader } from '../type.js'; - -/** - * Provides abstract Uint8Array access via the IRandomRead interface - */ -export class RandomUint8ArrayReader implements IRandomReader { - - public readonly fileSize: number; - - constructor(private readonly uint8Array: Uint8Array) { - this.fileSize = uint8Array.length; - } - - /** - * Read from a given position of an abstracted file or buffer. - * @param uint8Array - Uint8Array that the data will be written to. - * @param offset - Offset in the buffer to start writing at. - * @param length - Integer specifying the number of bytes to read. - * @param position - Specifies where to begin reading from in the file. - * @return Promise providing bytes read - */ - public async randomRead(uint8Array: Uint8Array, offset: number, length: number, position: number): Promise { - uint8Array.set(this.uint8Array.subarray(position, position + length), offset); - return length; - } -} diff --git a/lib/core.ts b/lib/core.ts index 18ed59962..91e787530 100644 --- a/lib/core.ts +++ b/lib/core.ts @@ -2,15 +2,14 @@ * Primary entry point, Node.js specific entry point is MusepackParser.ts */ -import {type AnyWebByteStream, type IFileInfo, type ITokenizer, fromWebStream, fromBuffer} from 'strtok3'; +import { type AnyWebByteStream, type IFileInfo, type ITokenizer, fromWebStream, fromBuffer, type IRandomAccessTokenizer } from 'strtok3'; import { ParserFactory } from './ParserFactory.js'; -import { RandomUint8ArrayReader } from './common/RandomUint8ArrayReader.js'; import { APEv2Parser } from './apev2/APEv2Parser.js'; import { hasID3v1Header } from './id3v1/ID3v1Parser.js'; import { getLyricsHeaderLength } from './lyrics3/Lyrics3.js'; -import type { IAudioMetadata, INativeTagDict, IOptions, IPicture, IPrivateOptions, IRandomReader, ITag } from './type.js'; +import type { IAudioMetadata, INativeTagDict, IOptions, IPicture, IPrivateOptions, ITag } from './type.js'; export type { IFileInfo } from 'strtok3'; @@ -54,9 +53,6 @@ export function parseWebStream(webStream: AnyWebByteStream, fileInfo?: IFileInfo */ export async function parseBuffer(uint8Array: Uint8Array, fileInfo?: IFileInfo | string, options: IOptions = {}): Promise { - const bufferReader = new RandomUint8ArrayReader(uint8Array); - await scanAppendingHeaders(bufferReader, options); - const tokenizer = fromBuffer(uint8Array, {fileInfo: typeof fileInfo === 'string' ? {mimeType: fileInfo} : fileInfo}); return parseFromTokenizer(tokenizer, options); } @@ -112,16 +108,16 @@ export function selectCover(pictures?: IPicture[]): IPicture | null { }) : null; } -export async function scanAppendingHeaders(randomReader: IRandomReader, options: IPrivateOptions = {}) { +export async function scanAppendingHeaders(tokenizer: IRandomAccessTokenizer, options: IPrivateOptions = {}) { - let apeOffset = randomReader.fileSize; - if (await hasID3v1Header(randomReader)) { + let apeOffset = tokenizer.fileInfo.size; + if (await hasID3v1Header(tokenizer)) { apeOffset -= 128; - const lyricsLen = await getLyricsHeaderLength(randomReader); + const lyricsLen = await getLyricsHeaderLength(tokenizer); apeOffset -= lyricsLen; } - options.apeHeader = await APEv2Parser.findApeFooterOffset(randomReader, apeOffset); + options.apeHeader = await APEv2Parser.findApeFooterOffset(tokenizer, apeOffset); } export declare function loadMusicMetadata(): Promise; diff --git a/lib/id3v1/ID3v1Parser.ts b/lib/id3v1/ID3v1Parser.ts index 8bbb34a49..931aac30d 100644 --- a/lib/id3v1/ID3v1Parser.ts +++ b/lib/id3v1/ID3v1Parser.ts @@ -3,10 +3,10 @@ import { StringType, UINT8 } from 'token-types'; import * as util from '../common/Util.js'; -import type { IGetToken, ITokenizer } from 'strtok3'; +import type { IGetToken, IRandomAccessTokenizer, ITokenizer } from 'strtok3'; import { BasicParser } from '../common/BasicParser.js'; import { APEv2Parser } from '../apev2/APEv2Parser.js'; -import type { AnyTagValue, IApeHeader, IPrivateOptions, IRandomReader } from '../type.js'; +import type { AnyTagValue, IApeHeader, IPrivateOptions } from '../type.js'; import type { INativeMetadataCollector } from '../common/MetadataCollector.js'; const debug = initDebug('music-metadata:parser:ID3v1'); @@ -160,13 +160,14 @@ export class ID3v1Parser extends BasicParser { private async addTag(id: string, value: AnyTagValue): Promise { await this.metadata.addTag('ID3v1', id, value); } - } -export async function hasID3v1Header(reader: IRandomReader): Promise { - if (reader.fileSize >= 128) { +export async function hasID3v1Header(tokenizer: IRandomAccessTokenizer): Promise { + if (tokenizer.fileInfo.size >= 128) { const tag = new Uint8Array(3); - await reader.randomRead(tag, 0, tag.length, reader.fileSize - 128); + const position = tokenizer.position; + await tokenizer.readBuffer(tag, {position: tokenizer.fileInfo.size - 128}); + tokenizer.setPosition(position); // Restore tokenizer position return new TextDecoder('latin1').decode(tag) === 'TAG'; } return false; diff --git a/lib/index.ts b/lib/index.ts index e2ac351da..667e125b9 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -6,10 +6,9 @@ import type { Readable } from 'node:stream'; import { fromFile, fromStream, type IFileInfo } from 'strtok3'; import initDebug from 'debug'; -import { parseFromTokenizer, scanAppendingHeaders } from './core.js'; +import { parseFromTokenizer, } from './core.js'; import { ParserFactory } from './ParserFactory.js'; import type { IAudioMetadata, IOptions } from './type.js'; -import { RandomFileReader } from './common/RandomFileReader.js'; export * from './core.js'; @@ -39,13 +38,6 @@ export async function parseFile(filePath: string, options: IOptions = {}): Promi const fileTokenizer = await fromFile(filePath); - const fileReader = await RandomFileReader.init(filePath, fileTokenizer.fileInfo.size as number); - try { - await scanAppendingHeaders(fileReader, options); - } finally { - await fileReader.close(); - } - const parserFactory = new ParserFactory(); try { diff --git a/lib/lyrics3/Lyrics3.ts b/lib/lyrics3/Lyrics3.ts index f94d28fe5..5028763b7 100644 --- a/lib/lyrics3/Lyrics3.ts +++ b/lib/lyrics3/Lyrics3.ts @@ -1,11 +1,14 @@ -import type { IRandomReader } from '../type.js'; +import type {IRandomAccessTokenizer} from 'strtok3'; export const endTag2 = 'LYRICS200'; -export async function getLyricsHeaderLength(reader: IRandomReader): Promise { - if (reader.fileSize >= 143) { +export async function getLyricsHeaderLength(tokenizer: IRandomAccessTokenizer): Promise { + const fileSize = tokenizer.fileInfo.size; + if (fileSize >= 143) { const buf = new Uint8Array(15); - await reader.randomRead(buf, 0, buf.length, reader.fileSize - 143); + const position = tokenizer.position; + await tokenizer.readBuffer(buf, {position: fileSize - 143}); + tokenizer.setPosition(position); // Restore position const txt = new TextDecoder('latin1').decode(buf); const tag = txt.slice(6); if (tag === endTag2) { diff --git a/lib/type.ts b/lib/type.ts index 727a51b5e..3e67ff1e2 100644 --- a/lib/type.ts +++ b/lib/type.ts @@ -694,28 +694,6 @@ export interface IMetadataEvent { export type Observer = (update: IMetadataEvent) => void; -/** - * Provides random data read access - * Used read operations on file of buffers - */ -export interface IRandomReader { - - /** - * Total length of file or buffer - */ - fileSize: number; - - /** - * Read from a given position of an abstracted file or buffer. - * @param {Uint8Array} buffer the buffer that the data will be written to. - * @param {number} offset the offset in the buffer to start writing at. - * @param {number} length an integer specifying the number of bytes to read. - * @param {number} position an argument specifying where to begin reading from in the file. - * @return {Promise} bytes read - */ - randomRead(buffer: Uint8Array, offset: number, length: number, position: number): Promise; -} - export interface ILyricsText { text: string; timestamp?: number; diff --git a/package.json b/package.json index 6be3f2b86..7edfe9a3c 100644 --- a/package.json +++ b/package.json @@ -109,8 +109,9 @@ "content-type": "^1.0.5", "debug": "^4.3.7", "file-type": "^19.6.0", + "link": "^2.1.1", "media-typer": "^1.1.0", - "strtok3": "^9.0.1", + "strtok3": "^9.1.0", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" }, diff --git a/test/metadata-parsers.ts b/test/metadata-parsers.ts index 116ed42e3..da7c1a5f9 100644 --- a/test/metadata-parsers.ts +++ b/test/metadata-parsers.ts @@ -3,7 +3,7 @@ import fs from 'node:fs'; import * as mm from '../lib/index.js'; import type { IAudioMetadata, IOptions } from '../lib/index.js'; -type ParseFileMethod = (skipTest: () => void, filePath: string, mimeType?: string, options?: IOptions) => Promise; +type ParseFileMethod = (skipTest: () => void, filePath: string, mimeType?: string, options?: IOptions) => Promise<{metadata: IAudioMetadata, randomRead: boolean}>; interface IParser { description: string; @@ -18,33 +18,46 @@ const [nodeMajorVersion] = process.versions.node.split('.').map(Number); export const Parsers: IParser[] = [ { description: 'parseFile', - initParser: (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { - return mm.parseFile(filePath, options); + initParser: async (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { + return { + metadata: await mm.parseFile(filePath, options), + randomRead: true + }; } }, { description: 'parseStream (Node.js)', - initParser: (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { + initParser: async (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { const stream = fs.createReadStream(filePath); - return mm.parseStream(stream, {mimeType}, options).then(metadata => { + try { + return { + metadata: await mm.parseStream(stream, {mimeType: mimeType}, options), + randomRead: false + }; + } finally { stream.close(); - return metadata; - }); + } } }, { description: 'parseBlob', - initParser: (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { + initParser: async (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { if (nodeMajorVersion < 20) { skipTest(); } const buffer = fs.readFileSync(filePath); - return mm.parseBlob(new Blob([buffer], {type: mimeType}), options); + return { + metadata: await mm.parseBlob(new Blob([buffer], {type: mimeType}), options), + randomRead: false + }; } }, { description: 'parseBuffer', - initParser: (skipTest, filePath: string, mimeType?: string, options?: IOptions) => { + initParser: async(skipTest, filePath: string, mimeType?: string, options?: IOptions) => { const buffer = fs.readFileSync(filePath); const array = new Uint8Array(buffer); - return mm.parseBuffer(array, {mimeType}, options); + return { + metadata: await mm.parseBuffer(array, {mimeType}, options), + randomRead: true + }; } } ]; diff --git a/test/test-file-aac.ts b/test/test-file-aac.ts index 708d612f1..2a14232ab 100644 --- a/test/test-file-aac.ts +++ b/test/test-file-aac.ts @@ -29,7 +29,7 @@ describe('Parse ADTS/AAC', () => { if (parser.description === 'parseBlob') { this.skip(); // ToDo: fix different behaviour parseFromWebStream() } - const metadata = await parser.initParser(() => this.skip(), path.join(aacSamplePath, 'adts-mpeg4.aac'), 'audio/aac', { + const {metadata} = await parser.initParser(() => this.skip(), path.join(aacSamplePath, 'adts-mpeg4.aac'), 'audio/aac', { duration: true }); checkFormat(metadata.format, 'ADTS/MPEG-4', 'AAC', 'AAC LC', 16000, 1, 20399, 256000); @@ -41,7 +41,7 @@ describe('Parse ADTS/AAC', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aacSamplePath, 'adts-mpeg4-2.aac'), 'audio/aac', { + const {metadata} = await parser.initParser(() => this.skip(), path.join(aacSamplePath, 'adts-mpeg4-2.aac'), 'audio/aac', { duration: true }); checkFormat(metadata.format, 'ADTS/MPEG-4', 'AAC', 'AAC LC', 44100, 2, 128000, 14336); diff --git a/test/test-file-aiff.ts b/test/test-file-aiff.ts index c9c203cf8..1660d3b85 100644 --- a/test/test-file-aiff.ts +++ b/test/test-file-aiff.ts @@ -30,7 +30,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { it(parser.description, async function(){ // AIFF file, AIFF file, stereo 8-bit data // Source: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/Samples.html - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'M1F1-int8-AFsp.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'M1F1-int8-AFsp.aif'), 'audio/aiff'); checkFormat(metadata.format, 'PCM', 8000, 2, 8, 23493); }); }); @@ -42,7 +42,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { it(parser.description, async function(){ // AIFF-C file, stereo A-law data (compression type: alaw) // Source: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/Samples.html - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'M1F1-AlawC-AFsp.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'M1F1-AlawC-AFsp.aif'), 'audio/aiff'); checkFormat(metadata.format, 'Alaw 2:1', 8000, 2, 16, 23493); }); }); @@ -67,7 +67,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Pmiscck.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Pmiscck.aif'), 'audio/aiff'); checkFormat(metadata.format, ULAW, 8000, 1, 16, 9); }); }); @@ -77,7 +77,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Pnossnd.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Pnossnd.aif'), 'audio/aiff'); checkFormat(metadata.format, ULAW, 8000, 1, 16, 0); }); }); @@ -87,7 +87,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Poffset.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Poffset.aif'), 'audio/aiff'); checkFormat(metadata.format, ULAW, 8000, 1, 16, 9); }); }); @@ -97,7 +97,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Porder.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Porder.aif'), 'audio/aiff'); checkFormat(metadata.format, ULAW, 8000, 1, 16, 9); }); }); @@ -107,7 +107,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Ptjunk.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Ptjunk.aif'), 'audio/aiff'); checkFormat(metadata.format, ULAW, 8000, 1, 16, 9); }); }); @@ -117,7 +117,7 @@ describe('Parse AIFF (Audio Interchange File Format)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Fnonull.aif'), 'audio/aiff'); + const { metadata } = await parser.initParser(() => this.skip(), path.join(aiffSamplePath, 'Fnonull.aif'), 'audio/aiff'); checkFormat(metadata.format, ULAW, 8000, 1, 16, 9); }); }); diff --git a/test/test-file-amr.ts b/test/test-file-amr.ts index 632fd9caa..c0e837a9a 100644 --- a/test/test-file-amr.ts +++ b/test/test-file-amr.ts @@ -12,7 +12,8 @@ describe('Adaptive Multi-Rate (AMR) audio file', () => { describe('parser.description', () => { it('parse: sample.amr', async function () { - const {format} = await parser.initParser(() => this.skip(), path.join(amrPath, 'sample.amr'), 'audio/amr', {duration: true}); + const {metadata} = await parser.initParser(() => this.skip(), path.join(amrPath, 'sample.amr'), 'audio/amr', {duration: true}); + const {format} = metadata; assert.strictEqual(format.sampleRate, 8000, 'format.sampleRate'); assert.strictEqual(format.numberOfChannels, 1, 'format.numberOfChannels'); assert.strictEqual(format.bitrate, 64000, 'format.bitrate'); @@ -20,7 +21,8 @@ describe('Adaptive Multi-Rate (AMR) audio file', () => { }); it('parse: gs-16b-1c-8000hz.amr', async function () { - const {format} = await parser.initParser(() => this.skip(), path.join(amrPath, 'gs-16b-1c-8000hz.amr'), 'audio/amr', {duration: true}); + const {metadata} = await parser.initParser(() => this.skip(), path.join(amrPath, 'gs-16b-1c-8000hz.amr'), 'audio/amr', {duration: true}); + const {format} = metadata; assert.strictEqual(format.sampleRate, 8000, 'format.sampleRate'); assert.strictEqual(format.numberOfChannels, 1, 'format.numberOfChannels'); assert.strictEqual(format.bitrate, 64000, 'format.bitrate'); @@ -29,7 +31,8 @@ describe('Adaptive Multi-Rate (AMR) audio file', () => { it('parse: ff-16b-1c-8000hz.amr', async function () { - const {format} = await parser.initParser(() => this.skip(), path.join(amrPath, 'ff-16b-1c-8000hz.amr'), 'audio/amr', {duration: true}); + const {metadata} = await parser.initParser(() => this.skip(), path.join(amrPath, 'ff-16b-1c-8000hz.amr'), 'audio/amr', {duration: true}); + const {format} = metadata; assert.strictEqual(format.sampleRate, 8000, 'format.sampleRate'); assert.strictEqual(format.numberOfChannels, 1, 'format.numberOfChannels'); assert.strictEqual(format.bitrate, 64000, 'format.bitrate'); diff --git a/test/test-file-ape.ts b/test/test-file-ape.ts index e89f39068..3af52876d 100644 --- a/test/test-file-ape.ts +++ b/test/test-file-ape.ts @@ -41,7 +41,7 @@ describe('Parse APE (Monkey\'s Audio)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(samplePath, 'monkeysaudio.ape'), 'audio/ape'); + const {metadata} = await parser.initParser(() => this.skip(), path.join(samplePath, 'monkeysaudio.ape'), 'audio/ape'); assert.isDefined(metadata, 'metadata should be defined'); checkFormat(metadata.format); checkCommon(metadata.common); diff --git a/test/test-file-asf.ts b/test/test-file-asf.ts index 9b76d6453..abcab879e 100644 --- a/test/test-file-asf.ts +++ b/test/test-file-asf.ts @@ -108,7 +108,7 @@ describe('Parse ASF', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), path.join(asfFilePath, 'asf.wma'), 'audio/x-ms-wma'); + const {metadata} = await parser.initParser(() => this.skip(), path.join(asfFilePath, 'asf.wma'), 'audio/x-ms-wma'); assert.isDefined(metadata, 'metadata'); checkFormat(metadata.format); checkCommon(metadata.common); @@ -125,7 +125,7 @@ describe('Parse ASF', () => { Parsers.forEach(parser => { it(parser.description, async function(){ const filePath = path.join(asfFilePath, 'issue_57.wma'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/x-ms-wma'); + const {metadata} = await parser.initParser(() => this.skip(), filePath, 'audio/x-ms-wma'); const asf = mm.orderTags(metadata.native.asf); assert.exists(asf['WM/Picture'][0], 'ASF WM/Picture should be set'); const nativePicture = asf['WM/Picture'][0]; diff --git a/test/test-file-flac.ts b/test/test-file-flac.ts index fa19f3d23..be946a713 100644 --- a/test/test-file-flac.ts +++ b/test/test-file-flac.ts @@ -60,7 +60,7 @@ describe('Parse FLAC Vorbis comment', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(this.skip(), path.join(samplePath, 'flac.flac'), 'audio/flac'); + const {metadata} = await parser.initParser(this.skip(), path.join(samplePath, 'flac.flac'), 'audio/flac'); checkFormat(metadata.format); checkCommon(metadata.common); checkNative(mm.orderTags(metadata.native.vorbis)); @@ -75,7 +75,7 @@ describe('Parse FLAC Vorbis comment', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(this.skip(), filePath, 'audio/flac'); + const {metadata} = await parser.initParser(this.skip(), filePath, 'audio/flac'); t.deepEqual(metadata.format.tagTypes, ['ID3v2.3', 'vorbis', 'ID3v1'], 'File has 3 tag types: "vorbis", "ID3v2.3" & "ID3v1"'); }); }); @@ -88,7 +88,7 @@ describe('Parse FLAC Vorbis comment', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/flac'); + const {metadata} = await parser.initParser(() => this.skip(), filePath, 'audio/flac'); assert.approximately(496000, metadata.format.bitrate, 500); }); }); diff --git a/test/test-file-mp3.ts b/test/test-file-mp3.ts index 3b093f792..d9a965362 100644 --- a/test/test-file-mp3.ts +++ b/test/test-file-mp3.ts @@ -256,8 +256,8 @@ describe('Parse MP3 files', () => { Parsers.slice(0, 1) .forEach(parser => { it(parser.description, async function(){ - const { format } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: false}); - assert.isUndefined(format.duration, 'Don\'t expect a duration'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: false}); + assert.isUndefined(metadata.format.duration, 'Don\'t expect a duration'); }); }); }); @@ -269,7 +269,8 @@ describe('Parse MP3 files', () => { Parsers .forEach(parser => { it(parser.description, async function(){ - const { format } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: true}); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: true}); + const { format } = metadata; assert.approximately(format.duration, durationSleepAwayMp3, 1 / 10, 'Expect a duration'); assert.strictEqual(format.numberOfSamples, 8831232, 'format.numberOfSamples'); }); diff --git a/test/test-file-mp4.ts b/test/test-file-mp4.ts index d658376fc..665fcbb9e 100644 --- a/test/test-file-mp4.ts +++ b/test/test-file-mp4.ts @@ -72,7 +72,7 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'id4.m4a'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); const native = metadata.native.iTunes; assert.ok(native, 'Native m4a tags should be present'); @@ -93,7 +93,7 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'issue-74.m4a'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); const {format, common, native} = metadata; assert.deepEqual(format.container, 'isom/iso2/mp41', 'format.container'); @@ -122,7 +122,7 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'issue-79.m4a'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); const {common, format} = metadata; assert.deepEqual(format.container, 'M4A/mp42/isom', 'format.container'); @@ -152,8 +152,8 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'issue-127.m4b'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); - const {common, format} = metadata; + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mp4'); + const { common, format} = metadata; assert.deepEqual(format.container, 'M4A/3gp5/isom', 'format.container'); assert.deepEqual(format.codec, 'MPEG-4/AAC', 'format.codec'); @@ -292,19 +292,20 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'Mr. Pickles S02E07 My Dear Boy.mp4'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'video/mp4'); - assert.deepEqual(metadata.common.title, 'My Dear Boy'); - assert.deepEqual(metadata.common.tvEpisode, 7); - assert.deepEqual(metadata.common.tvEpisodeId, '017'); - assert.deepEqual(metadata.common.tvSeason, 2); - assert.deepEqual(metadata.common.tvShow, 'Mr. Pickles'); - - assert.deepEqual(metadata.format.container, 'mp42/isom', 'format.container'); - assert.deepEqual(metadata.format.codec, 'MPEG-4/AAC+AC-3+CEA-608', 'format.codec'); - assert.deepEqual(metadata.common.artist, 'Mr. Pickles'); - assert.deepEqual(metadata.common.artists, ['Mr. Pickles']); - assert.deepEqual(metadata.common.albumartist, 'Mr. Pickles'); - assert.deepEqual(metadata.common.copyright, '© & TM - Cartoon Network - 2016'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'video/mp4'); + const { common, format } = metadata; + assert.deepEqual(common.title, 'My Dear Boy'); + assert.deepEqual(common.tvEpisode, 7); + assert.deepEqual(common.tvEpisodeId, '017'); + assert.deepEqual(common.tvSeason, 2); + assert.deepEqual(common.tvShow, 'Mr. Pickles'); + + assert.deepEqual(format.container, 'mp42/isom', 'format.container'); + assert.deepEqual(format.codec, 'MPEG-4/AAC+AC-3+CEA-608', 'format.codec'); + assert.deepEqual(common.artist, 'Mr. Pickles'); + assert.deepEqual(common.artists, ['Mr. Pickles']); + assert.deepEqual(common.albumartist, 'Mr. Pickles'); + assert.deepEqual(common.copyright, '© & TM - Cartoon Network - 2016'); const iTunes = mm.orderTags(metadata.native.iTunes); assert.deepEqual(iTunes.stik, [10], 'iTunes.stik = 10 = TV Show'); // Ref: http://www.zoyinc.com/?p=1004 @@ -320,7 +321,7 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'issue-133.m4a'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'video/mp4'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'video/mp4'); assert.deepEqual(metadata.format.container, 'M4A/mp42/isom', 'format.container'); assert.deepEqual(metadata.format.codec, 'MPEG-4/AAC', 'format.codec'); }); @@ -334,7 +335,7 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { const filePath = path.join(mp4Samples, 'issue-151.m4a'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'video/mp4'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'video/mp4'); assert.deepEqual(metadata.format.container, 'mp42/isom', 'format.container'); assert.deepEqual(metadata.format.codec, 'MPEG-4/AAC+MP4S', 'format.codec'); @@ -356,15 +357,17 @@ describe('Parse MPEG-4 files with iTunes metadata', () => { it(parser.description, async function(){ const filePath = path.join(mp4Samples, '01. Trumpsta (Djuro Remix).m4a'); - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/m4a'); - assert.deepEqual(metadata.format.container, 'M4A/mp42/isom', 'format.container'); - assert.deepEqual(metadata.format.codec, 'MPEG-4/AAC', 'format.codec'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/m4a'); + const { format, common } = metadata; + + assert.deepEqual(format.container, 'M4A/mp42/isom', 'format.container'); + assert.deepEqual(format.codec, 'MPEG-4/AAC', 'format.codec'); - assert.deepEqual(metadata.common.album, 'Trumpsta (Remixes)'); - assert.deepEqual(metadata.common.albumartist, 'Contiez'); - assert.deepEqual(metadata.common.artist, 'Contiez'); - assert.deepEqual(metadata.common.artists, ['Contiez']); - assert.strictEqual(metadata.common.title, 'Trumpsta (Djuro Remix)'); + assert.deepEqual(common.album, 'Trumpsta (Remixes)'); + assert.deepEqual(common.albumartist, 'Contiez'); + assert.deepEqual(common.artist, 'Contiez'); + assert.deepEqual(common.artists, ['Contiez']); + assert.strictEqual(common.title, 'Trumpsta (Djuro Remix)'); }); }); diff --git a/test/test-file-mpeg.ts b/test/test-file-mpeg.ts index b33bf900e..d2c154dec 100644 --- a/test/test-file-mpeg.ts +++ b/test/test-file-mpeg.ts @@ -343,7 +343,7 @@ describe('Parse MPEG', () => { Parsers .forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: false}); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: false}); assert.strictEqual(metadata.format.duration, 0.4963265306122449); }); }); diff --git a/test/test-file-musepack.ts b/test/test-file-musepack.ts index 90f4cd5e7..850e47c21 100644 --- a/test/test-file-musepack.ts +++ b/test/test-file-musepack.ts @@ -15,9 +15,9 @@ describe('Parse Musepack (.mpc)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/musepac'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/musepac'); // Check format - const format = metadata.format; + const { format, common } = metadata; assert.deepEqual(format.container, 'Musepack, SV7'); assert.strictEqual(format.sampleRate, 44100); assert.strictEqual(format.numberOfSamples, 11940); @@ -25,7 +25,6 @@ describe('Parse Musepack (.mpc)', () => { assert.strictEqual(format.codec, '1.15'); // Check generic metadata - const common = metadata.common; assert.strictEqual(common.title, 'God Inside'); assert.strictEqual(common.artist, 'Faze Action'); assert.strictEqual(common.album, 'Broad Souls'); @@ -50,20 +49,21 @@ describe('Parse Musepack (.mpc)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/musepac'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/musepac'); + const { format, common } = metadata; // Check format - assert.deepEqual(metadata.format.container, 'Musepack, SV7'); - assert.strictEqual(metadata.format.sampleRate, 44100); - assert.strictEqual(metadata.format.numberOfSamples, 11940); - assert.approximately(metadata.format.bitrate, 269649, 1); - assert.strictEqual(metadata.format.codec, '1.15'); + assert.deepEqual(format.container, 'Musepack, SV7'); + assert.strictEqual(format.sampleRate, 44100); + assert.strictEqual(format.numberOfSamples, 11940); + assert.approximately(format.bitrate, 269649, 1); + assert.strictEqual(format.codec, '1.15'); // Check generic metadata - assert.strictEqual(metadata.common.title, 'God Inside'); - assert.strictEqual(metadata.common.artist, 'Faze Action'); - assert.strictEqual(metadata.common.album, 'Broad Souls'); - assert.strictEqual(metadata.common.date, '2004'); - assert.deepEqual(metadata.common.track, {no: 9, of: null}); + assert.strictEqual(common.title, 'God Inside'); + assert.strictEqual(common.artist, 'Faze Action'); + assert.strictEqual(common.album, 'Broad Souls'); + assert.strictEqual(common.date, '2004'); + assert.deepEqual(common.track, {no: 9, of: null}); }); }); @@ -76,24 +76,25 @@ describe('Parse Musepack (.mpc)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/musepac'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/musepac'); + const { format, common } = metadata; // Check format - assert.deepEqual(metadata.format.container, 'Musepack, SV8'); - assert.strictEqual(metadata.format.sampleRate, 48000); - assert.strictEqual(metadata.format.numberOfSamples, 24000); - assert.strictEqual(metadata.format.numberOfChannels, 2); - assert.approximately(metadata.format.duration, 0.5, 1 / 2000); - assert.approximately(metadata.format.bitrate, 32368, 1); + assert.deepEqual(format.container, 'Musepack, SV8'); + assert.strictEqual(format.sampleRate, 48000); + assert.strictEqual(format.numberOfSamples, 24000); + assert.strictEqual(format.numberOfChannels, 2); + assert.approximately(format.duration, 0.5, 1 / 2000); + assert.approximately(format.bitrate, 32368, 1); // Check generic metadata - assert.strictEqual(metadata.common.title, 'Goldberg Variations, BWV 988: Variatio 4 a 1 Clav.'); - assert.strictEqual(metadata.common.artist, 'Johann Sebastian Bach'); - assert.deepEqual(metadata.common.artists, ['Johann Sebastian Bach']); - assert.deepEqual(metadata.common.isrc, ['QMNYZ1200005']); - assert.deepEqual(metadata.common.license, 'https://creativecommons.org/publicdomain/zero/1.0/'); - assert.strictEqual(metadata.common.album, 'Open Goldberg Variations'); - assert.strictEqual(metadata.common.date, '2012-05-28'); - assert.deepEqual(metadata.common.track, {no: 5, of: 32}); + assert.strictEqual(common.title, 'Goldberg Variations, BWV 988: Variatio 4 a 1 Clav.'); + assert.strictEqual(common.artist, 'Johann Sebastian Bach'); + assert.deepEqual(common.artists, ['Johann Sebastian Bach']); + assert.deepEqual(common.isrc, ['QMNYZ1200005']); + assert.deepEqual(common.license, 'https://creativecommons.org/publicdomain/zero/1.0/'); + assert.strictEqual(common.album, 'Open Goldberg Variations'); + assert.strictEqual(common.date, '2012-05-28'); + assert.deepEqual(common.track, {no: 5, of: 32}); }); }); diff --git a/test/test-file-ogg.ts b/test/test-file-ogg.ts index 9390f42b5..526cadc41 100644 --- a/test/test-file-ogg.ts +++ b/test/test-file-ogg.ts @@ -66,7 +66,7 @@ describe('Parse Ogg', function() { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/ogg'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/ogg'); checkFormat(metadata.format); check_Nirvana_In_Bloom_VorbisTags(mm.orderTags(metadata.native.vorbis)); check_Nirvana_In_Bloom_commonTags(metadata.common); @@ -149,7 +149,7 @@ describe('Parse Ogg', function() { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/ogg'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/ogg'); checkFormat(metadata.format); check_Nirvana_In_Bloom_VorbisTags(mm.orderTags(metadata.native.vorbis)); check_Nirvana_In_Bloom_commonTags(metadata.common); @@ -173,7 +173,7 @@ describe('Parse Ogg', function() { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/ogg'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/ogg'); checkFormat(metadata.format); }); }); diff --git a/test/test-file-wavpack.ts b/test/test-file-wavpack.ts index f2dde85ce..fd748f952 100644 --- a/test/test-file-wavpack.ts +++ b/test/test-file-wavpack.ts @@ -28,7 +28,7 @@ describe('Parse WavPack (audio/x-wavpack)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), wv1, 'audio/x-wavpack'); + const { metadata } = await parser.initParser(() => this.skip(), wv1, 'audio/x-wavpack'); checkFormat(metadata.format); checkCommon(metadata.common); }); @@ -50,7 +50,7 @@ describe('Parse WavPack (audio/x-wavpack)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), wv1, 'audio/x-wavpack'); + const { metadata } = await parser.initParser(() => this.skip(), wv1, 'audio/x-wavpack'); checkFormat(metadata.format); }); }); @@ -72,7 +72,7 @@ describe('Parse WavPack (audio/x-wavpack)', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), wv1, 'audio/x-wavpack'); + const { metadata } = await parser.initParser(() => this.skip(), wv1, 'audio/x-wavpack'); checkFormat(metadata.format); }); }); diff --git a/test/test-id3v1.1.ts b/test/test-id3v1.1.ts index 84ae32a3f..134c7133d 100644 --- a/test/test-id3v1.1.ts +++ b/test/test-id3v1.1.ts @@ -40,10 +40,9 @@ describe('Parsing MPEG / ID3v1', () => { */ Parsers.forEach(parser => { it(parser.description, async function(){ - return parser.initParser(() => this.skip(), fileBloodSugar, 'audio/mpeg').then(metadata => { - checkFormat(metadata.format); - checkCommon(metadata.common); - }); + const { metadata } = await parser.initParser(() => this.skip(), fileBloodSugar, 'audio/mpeg'); + checkFormat(metadata.format); + checkCommon(metadata.common); }); }); }); @@ -54,7 +53,7 @@ describe('Parsing MPEG / ID3v1', () => { Parsers.forEach(parser => { it(parser.description, async function(){ this.timeout(15000); // Can take a bit longer - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {skipPostHeaders: true}); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {skipPostHeaders: true}); assert.deepEqual(metadata.format.tagTypes, ['ID3v2.3'], 'format.tagTypes'); }); }); @@ -77,7 +76,7 @@ describe('Parsing MPEG / ID3v1', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); checkFormat(metadata.format); }); }); @@ -115,7 +114,7 @@ describe('Parsing MPEG / ID3v1', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); assert.isDefined(metadata, 'should provide metadata'); checkFormat(metadata.format); checkCommon(metadata.common); @@ -133,7 +132,7 @@ describe('Parsing MPEG / ID3v1', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - const metadata = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: true}); + const { metadata } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg', {duration: true}); const id3v1 = mm.orderTags(metadata.native.ID3v1); assert.deepEqual(id3v1.title, ['Skupinove foto'], 'id3v1.title'); assert.deepEqual(id3v1.artist, ['Pavel Dobes'], 'id3v1.artist'); diff --git a/test/test-regress-GH-56.ts b/test/test-regress-GH-56.ts index f39588193..a9f8c24e6 100644 --- a/test/test-regress-GH-56.ts +++ b/test/test-regress-GH-56.ts @@ -34,23 +34,30 @@ describe('should calculate duration for a CBR encoded MP3', () => { Parsers.forEach(parser => { it(parser.description, async function(){ - return parser.initParser(() => this.skip(), filePath, 'audio/mpeg').then(metadata => { - const expectedTags = (parser.description === 'parseFile' || parser.description === 'parseBuffer') ? ['ID3v2.3', 'APEv2'] : ['ID3v2.3']; - t.deepEqual(metadata.format.tagTypes, expectedTags, 'format.tagTypes'); - t.strictEqual(metadata.format.sampleRate, 44100, 'format.sampleRate'); - t.strictEqual(metadata.format.duration, 16462080 / metadata.format.sampleRate, 'format.duration'); - }); + const { metadata, randomRead } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); + const expectedTags = randomRead ? ['ID3v2.3', 'APEv2'] : ['ID3v2.3']; + t.deepEqual(metadata.format.tagTypes, expectedTags, 'format.tagTypes'); + t.strictEqual(metadata.format.sampleRate, 44100, 'format.sampleRate'); + t.strictEqual(metadata.format.duration, 16462080 / metadata.format.sampleRate, 'format.duration'); }); }); it('_parseFile', async function(){ const parser = Parsers[0]; - return parser.initParser(() => this.skip(), filePath, 'audio/mpeg').then(metadata => { - const expectedTags = (parser.description === 'parseFile' || parser.description === 'parseBuffer') ? ['ID3v2.3', 'APEv2'] : ['ID3v2.3']; - t.deepEqual(metadata.format.tagTypes, expectedTags, 'format.tagTypes'); - t.strictEqual(metadata.format.sampleRate, 44100, 'format.sampleRate'); - t.strictEqual(metadata.format.duration, 16462080 / metadata.format.sampleRate, 'format.duration'); - }); + const { metadata, randomRead } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); + const expectedTags = randomRead ? ['ID3v2.3', 'APEv2'] : ['ID3v2.3']; + t.deepEqual(metadata.format.tagTypes, expectedTags, 'format.tagTypes'); + t.strictEqual(metadata.format.sampleRate, 44100, 'format.sampleRate'); + t.strictEqual(metadata.format.duration, 16462080 / metadata.format.sampleRate, 'format.duration'); + }); + + it('debug single', async function(){ + const parser = Parsers[3]; + const { metadata, randomRead } = await parser.initParser(() => this.skip(), filePath, 'audio/mpeg'); + const expectedTags = randomRead ? ['ID3v2.3', 'APEv2'] : ['ID3v2.3']; + t.deepEqual(metadata.format.tagTypes, expectedTags, 'format.tagTypes'); + t.strictEqual(metadata.format.sampleRate, 44100, 'format.sampleRate'); + t.strictEqual(metadata.format.duration, 16462080 / metadata.format.sampleRate, 'format.duration'); }); }); diff --git a/yarn.lock b/yarn.lock index 162c01990..d9eb83ab7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1736,6 +1736,15 @@ __metadata: languageName: node linkType: hard +"link@npm:^2.1.1": + version: 2.1.1 + resolution: "link@npm:2.1.1" + bin: + link: dist/cli.js + checksum: 10c0/2ab956715e77c4fb9d78e16172301b59e2c03611feae4df33f50a70351445da050bea4010c682a0e187a09b5f2ee4462bab842153c2767a3b018de00b0e8282a + languageName: node + linkType: hard + "load-plugin@npm:^6.0.0": version: 6.0.3 resolution: "load-plugin@npm:6.0.3" @@ -2378,12 +2387,13 @@ __metadata: debug: "npm:^4.3.7" del-cli: "npm:^6.0.0" file-type: "npm:^19.6.0" + link: "npm:^2.1.1" media-typer: "npm:^1.1.0" mime: "npm:^4.0.4" mocha: "npm:^10.8.2" remark-cli: "npm:^12.0.1" remark-preset-lint-consistent: "npm:^6.0.0" - strtok3: "npm:^9.0.1" + strtok3: "npm:^9.1.0" token-types: "npm:^6.0.0" ts-node: "npm:^10.9.2" typescript: "npm:^5.6.3" @@ -3222,6 +3232,16 @@ __metadata: languageName: node linkType: hard +"strtok3@npm:^9.1.0": + version: 9.1.0 + resolution: "strtok3@npm:9.1.0" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + peek-readable: "npm:^5.3.1" + checksum: 10c0/703699e49dd205bdf353ea974a12cbe858c0fd5d1f3ee42ca7314805f6b65aae968ff85b7dbb28d981b1f9bd909da2ec4132c58a9476cb155a86c9ca324604b8 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0"