music-metadata 10.3.1 → 10.5.0

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 (56) hide show
  1. package/README.md +8 -7
  2. package/lib/ParserFactory.d.ts +22 -20
  3. package/lib/ParserFactory.js +78 -131
  4. package/lib/aiff/AiffLoader.d.ts +2 -0
  5. package/lib/aiff/AiffLoader.js +8 -0
  6. package/lib/amr/AmrLoader.d.ts +2 -0
  7. package/lib/amr/AmrLoader.js +8 -0
  8. package/lib/amr/AmrParser.d.ts +7 -0
  9. package/lib/amr/AmrParser.js +49 -0
  10. package/lib/amr/AmrToken.d.ts +11 -0
  11. package/lib/amr/AmrToken.js +15 -0
  12. package/lib/apev2/APEv2Parser.js +2 -4
  13. package/lib/apev2/Apev2Loader.d.ts +2 -0
  14. package/lib/apev2/Apev2Loader.js +8 -0
  15. package/lib/asf/AsfLoader.d.ts +2 -0
  16. package/lib/asf/AsfLoader.js +8 -0
  17. package/lib/asf/AsfObject.d.ts +7 -1
  18. package/lib/common/BasicParser.d.ts +5 -5
  19. package/lib/common/BasicParser.js +1 -7
  20. package/lib/common/ParserLoader.d.ts +1 -0
  21. package/lib/common/ParserLoader.js +1 -0
  22. package/lib/core.d.ts +1 -1
  23. package/lib/core.js +4 -3
  24. package/lib/dsdiff/DsdiffLoader.d.ts +2 -0
  25. package/lib/dsdiff/DsdiffLoader.js +8 -0
  26. package/lib/dsf/DsfLoader.d.ts +2 -0
  27. package/lib/dsf/DsfLoader.js +8 -0
  28. package/lib/flac/FlacLoader.d.ts +2 -0
  29. package/lib/flac/FlacLoader.js +8 -0
  30. package/lib/flac/FlacParser.d.ts +0 -11
  31. package/lib/flac/FlacParser.js +1 -11
  32. package/lib/id3v1/ID3v1Parser.d.ts +5 -1
  33. package/lib/id3v1/ID3v1Parser.js +8 -5
  34. package/lib/id3v2/AbstractID3Parser.js +2 -2
  35. package/lib/index.js +5 -4
  36. package/lib/matroska/MatroskaLoader.d.ts +2 -0
  37. package/lib/matroska/MatroskaLoader.js +8 -0
  38. package/lib/matroska/MatroskaParser.d.ts +0 -11
  39. package/lib/matroska/MatroskaParser.js +1 -12
  40. package/lib/mp4/Atom.js +5 -5
  41. package/lib/mp4/Mp4Loader.d.ts +2 -0
  42. package/lib/mp4/Mp4Loader.js +8 -0
  43. package/lib/mpeg/MpegLoader.d.ts +2 -0
  44. package/lib/mpeg/MpegLoader.js +8 -0
  45. package/lib/musepack/MusepackLoader.d.ts +2 -0
  46. package/lib/musepack/MusepackLoader.js +8 -0
  47. package/lib/musepack/{index.d.ts → MusepackParser.d.ts} +1 -2
  48. package/lib/musepack/{index.js → MusepackParser.js} +4 -6
  49. package/lib/ogg/OggLoader.d.ts +2 -0
  50. package/lib/ogg/OggLoader.js +8 -0
  51. package/lib/type.d.ts +1 -1
  52. package/lib/wav/WaveLoader.d.ts +2 -0
  53. package/lib/wav/WaveLoader.js +8 -0
  54. package/lib/wavpack/WavPackLoader.d.ts +2 -0
  55. package/lib/wavpack/WavPackLoader.js +8 -0
  56. package/package.json +5 -5
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  [![Node.js CI](https://github.com/Borewit/music-metadata/actions/workflows/nodejs-ci.yml/badge.svg?branch=master)](https://github.com/Borewit/music-metadata/actions?query=branch%3Amaster)
2
2
  [![Build status](https://ci.appveyor.com/api/projects/status/tgtqynlon8t99qq5/branch/master?svg=true)](https://ci.appveyor.com/project/Borewit/music-metadata/branch/master)
3
3
  [![NPM version](https://img.shields.io/npm/v/music-metadata.svg)](https://npmjs.org/package/music-metadata)
4
- [![npm downloads](http://img.shields.io/npm/dm/music-metadata.svg)](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600)
4
+ [![npm downloads](http://img.shields.io/npm/dm/music-metadata.svg)](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600&interval=30)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/Borewit/music-metadata/badge.svg?branch=master)](https://coveralls.io/github/Borewit/music-metadata?branch=master)
6
6
  [![Codacy Badge](https://api.codacy.com/project/badge/Grade/57d731b05c9e41889a2a17cb4b0384d7)](https://app.codacy.com/app/Borewit/music-metadata?utm_source=github.com&utm_medium=referral&utm_content=Borewit/music-metadata&utm_campaign=Badge_Grade_Dashboard)
7
7
  [![CodeQL](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml)
@@ -31,14 +31,14 @@ The distributed JavaScript codebase is compliant with the [ECMAScript 2020 (11th
31
31
  This module requires a [Node.js ≥ 16](https://nodejs.org/en/about/previous-releases) engine.
32
32
  It can also be used in a browser environment when bundled with a module bundler.
33
33
 
34
- ## Sponsor
35
- If you appreciate my work and want to support the development of open-source projects like [music-metadata](https://github.com/Borewit/music-metadata), [file-type](https://github.com/sindresorhus/file-type), and [listFix()](https://github.com/Borewit/listFix), consider becoming a sponsor or making a small contribution.
36
- Your support helps sustain ongoing development and improvements.
37
- [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
34
+ ## Support the Project
35
+ If you find this project useful and would like to support its development, consider sponsoring or contributing:
38
36
 
39
- or
37
+ - [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
40
38
 
41
- <a href="https://www.buymeacoffee.com/borewit" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy me A coffee" height="41" width="174"></a>
39
+ - Buy me a coffee:
40
+
41
+ <a href="https://www.buymeacoffee.com/borewit" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy me A coffee" height="41" width="174"></a>
42
42
 
43
43
  ## Features
44
44
 
@@ -48,6 +48,7 @@ or
48
48
  | ------------- |---------------------------------| -------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------:|
49
49
  | AIFF / AIFF-C | Audio Interchange File Format | [:link:](https://wikipedia.org/wiki/Audio_Interchange_File_Format) | <img src="https://upload.wikimedia.org/wikipedia/commons/8/84/Apple_Computer_Logo_rainbow.svg" width="40" alt="Apple rainbow logo"> |
50
50
  | AAC | ADTS / Advanced Audio Coding | [:link:](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) | <img src="https://svgshare.com/i/UT8.svg" width="40" alt="AAC logo"> |
51
+ | AMR | Adaptive Multi-Rate audio codec | [:link:](https://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec) | <img src="https://foreverhits.files.wordpress.com/2015/05/ape_audio.jpg" width="40" alt="Monkey's Audio logo"> |
51
52
  | APE | Monkey's Audio | [:link:](https://wikipedia.org/wiki/Monkey's_Audio) | <img src="https://foreverhits.files.wordpress.com/2015/05/ape_audio.jpg" width="40" alt="Monkey's Audio logo"> |
52
53
  | ASF | Advanced Systems Format | [:link:](https://wikipedia.org/wiki/Advanced_Systems_Format) | |
53
54
  | BWF | Broadcast Wave Format | [:link:](https://en.wikipedia.org/wiki/Broadcast_Wave_Format) | |
@@ -2,14 +2,18 @@ import { type MediaType } from 'media-typer';
2
2
  import { type INativeMetadataCollector } from './common/MetadataCollector.js';
3
3
  import type { IAudioMetadata, IOptions, ParserType } from './type.js';
4
4
  import type { ITokenizer } from 'strtok3';
5
- export interface ITokenParser {
5
+ export interface IParserLoader {
6
+ /**
7
+ * Returns a list of supported file extensions
8
+ */
9
+ extensions: string[];
10
+ parserType: ParserType;
6
11
  /**
7
- * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
8
- * @param metadata - Output
9
- * @param tokenizer - Input
10
- * @param options - Parsing options
12
+ * Lazy load the parser
11
13
  */
12
- init(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IOptions): ITokenParser;
14
+ load(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IOptions): Promise<ITokenParser>;
15
+ }
16
+ export interface ITokenParser {
13
17
  /**
14
18
  * Parse audio track.
15
19
  * Called after init(...).
@@ -23,18 +27,16 @@ interface IContentType extends MediaType {
23
27
  };
24
28
  }
25
29
  export declare function parseHttpContentType(contentType: string): IContentType;
26
- /**
27
- * Parse metadata from tokenizer
28
- * @param tokenizer - Tokenizer
29
- * @param opts - Options
30
- * @returns Native metadata
31
- */
32
- export declare function parseOnContentType(tokenizer: ITokenizer, opts?: IOptions): Promise<IAudioMetadata>;
33
- export declare function parse(tokenizer: ITokenizer, parserId?: ParserType, opts?: IOptions): Promise<IAudioMetadata>;
34
- /**
35
- * @param filePath - Path, filename or extension to audio file
36
- * @return Parser submodule name
37
- */
38
- export declare function getParserIdForExtension(filePath: string | undefined): ParserType | undefined;
39
- export declare function loadParser(moduleName: ParserType): Promise<ITokenParser>;
30
+ export declare class ParserFactory {
31
+ parsers: IParserLoader[];
32
+ constructor();
33
+ registerParser(parser: IParserLoader): void;
34
+ parse(tokenizer: ITokenizer, parserLoader: IParserLoader | undefined, opts?: IOptions): Promise<IAudioMetadata>;
35
+ /**
36
+ * @param filePath - Path, filename or extension to audio file
37
+ * @return Parser submodule name
38
+ */
39
+ findLoaderForExtension(filePath: string | undefined): IParserLoader | undefined;
40
+ findLoaderForType(moduleName: ParserType | undefined): IParserLoader | undefined;
41
+ }
40
42
  export {};
@@ -3,20 +3,21 @@ import ContentType from 'content-type';
3
3
  import { parse as mimeTypeParse } from 'media-typer';
4
4
  import initDebug from 'debug';
5
5
  import { MetadataCollector } from './common/MetadataCollector.js';
6
- import { AIFFParser } from './aiff/AiffParser.js';
7
- import { APEv2Parser } from './apev2/APEv2Parser.js';
8
- import { AsfParser } from './asf/AsfParser.js';
9
- import { FlacParser } from './flac/FlacParser.js';
10
- import { MP4Parser } from './mp4/MP4Parser.js';
11
- import { MpegParser } from './mpeg/MpegParser.js';
12
- import MusepackParser from './musepack/index.js';
13
- import { OggParser } from './ogg/OggParser.js';
14
- import { WaveParser } from './wav/WaveParser.js';
15
- import { WavPackParser } from './wavpack/WavPackParser.js';
16
- import { DsfParser } from './dsf/DsfParser.js';
17
- import { DsdiffParser } from './dsdiff/DsdiffParser.js';
18
- import { MatroskaParser } from './matroska/MatroskaParser.js';
19
- import { CouldNotDetermineFileTypeError, InternalParserError, UnsupportedFileTypeError } from './ParseError.js';
6
+ import { mpegParserLoader } from './mpeg/MpegLoader.js';
7
+ import { CouldNotDetermineFileTypeError, UnsupportedFileTypeError } from './ParseError.js';
8
+ import { apeParserLoader } from './apev2/Apev2Loader.js';
9
+ import { asfParserLoader } from './asf/AsfLoader.js';
10
+ import { dsdiffParserLoader } from './dsdiff/DsdiffLoader.js';
11
+ import { aiffParserLoader } from './aiff/AiffLoader.js';
12
+ import { dsfParserLoader } from './dsf/DsfLoader.js';
13
+ import { flacParserLoader } from './flac/FlacLoader.js';
14
+ import { matroskaParserLoader } from './matroska/MatroskaLoader.js';
15
+ import { mp4ParserLoader } from './mp4/Mp4Loader.js';
16
+ import { musepackParserLoader } from './musepack/MusepackLoader.js';
17
+ import { oggParserLoader } from './ogg/OggLoader.js';
18
+ import { wavpackParserLoader } from './wavpack/WavPackLoader.js';
19
+ import { riffParserLoader } from './wav/WaveLoader.js';
20
+ import { amrParserLoader } from './amr/AmrLoader.js';
20
21
  const debug = initDebug('music-metadata:parser:factory');
21
22
  export function parseHttpContentType(contentType) {
22
23
  const type = ContentType.parse(contentType);
@@ -28,129 +29,73 @@ export function parseHttpContentType(contentType) {
28
29
  parameters: type.parameters
29
30
  };
30
31
  }
31
- /**
32
- * Parse metadata from tokenizer
33
- * @param tokenizer - Tokenizer
34
- * @param opts - Options
35
- * @returns Native metadata
36
- */
37
- export async function parseOnContentType(tokenizer, opts) {
38
- const { mimeType, path, url } = tokenizer.fileInfo;
39
- // Resolve parser based on MIME-type or file extension
40
- const parserId = getParserIdForMimeType(mimeType) || getParserIdForExtension(path) || getParserIdForExtension(url);
41
- if (!parserId) {
42
- debug(`No parser found for MIME-type / extension: ${mimeType}`);
32
+ export class ParserFactory {
33
+ constructor() {
34
+ this.parsers = [];
35
+ [
36
+ flacParserLoader,
37
+ mpegParserLoader,
38
+ apeParserLoader,
39
+ mp4ParserLoader,
40
+ matroskaParserLoader,
41
+ riffParserLoader,
42
+ oggParserLoader,
43
+ asfParserLoader,
44
+ aiffParserLoader,
45
+ wavpackParserLoader,
46
+ musepackParserLoader,
47
+ dsfParserLoader,
48
+ dsdiffParserLoader,
49
+ amrParserLoader
50
+ ].forEach(parser => this.registerParser(parser));
43
51
  }
44
- return parse(tokenizer, parserId, opts);
45
- }
46
- export async function parse(tokenizer, parserId, opts) {
47
- if (!parserId) {
48
- if (tokenizer.fileInfo.path) {
49
- parserId = getParserIdForExtension(tokenizer.fileInfo.path);
50
- }
51
- if (!parserId) {
52
- // Parser could not be determined on MIME-type or extension
53
- debug('Guess parser on content...');
52
+ registerParser(parser) {
53
+ this.parsers.push(parser);
54
+ }
55
+ async parse(tokenizer, parserLoader, opts) {
56
+ if (!parserLoader) {
54
57
  const buf = new Uint8Array(4100);
55
- await tokenizer.peekBuffer(buf, { mayBeLess: true });
56
- const guessedType = await fileTypeFromBuffer(buf);
57
- if (!guessedType) {
58
- throw new CouldNotDetermineFileTypeError('Failed to determine audio format');
58
+ if (tokenizer.fileInfo.mimeType) {
59
+ parserLoader = this.findLoaderForType(getParserIdForMimeType(tokenizer.fileInfo.mimeType));
60
+ }
61
+ if (!parserLoader && tokenizer.fileInfo.path) {
62
+ parserLoader = this.findLoaderForExtension(tokenizer.fileInfo.path);
59
63
  }
60
- debug(`Guessed file type is mime=${guessedType.mime}, extension=${guessedType.ext}`);
61
- parserId = getParserIdForMimeType(guessedType.mime);
62
- if (!parserId) {
63
- throw new UnsupportedFileTypeError(`Guessed MIME-type not supported: ${guessedType.mime}`);
64
+ if (!parserLoader) {
65
+ // Parser could not be determined on MIME-type or extension
66
+ debug('Guess parser on content...');
67
+ await tokenizer.peekBuffer(buf, { mayBeLess: true });
68
+ const guessedType = await fileTypeFromBuffer(buf);
69
+ if (!guessedType || !guessedType.mime) {
70
+ throw new CouldNotDetermineFileTypeError('Failed to determine audio format');
71
+ }
72
+ debug(`Guessed file type is mime=${guessedType.mime}, extension=${guessedType.ext}`);
73
+ parserLoader = this.findLoaderForType(getParserIdForMimeType(guessedType.mime));
74
+ if (!parserLoader) {
75
+ throw new UnsupportedFileTypeError(`Guessed MIME-type not supported: ${guessedType.mime}`);
76
+ }
64
77
  }
65
78
  }
79
+ // Parser found, execute parser
80
+ debug(`Loading ${parserLoader.parserType} parser...`);
81
+ const metadata = new MetadataCollector(opts);
82
+ const parser = await parserLoader.load(metadata, tokenizer, opts ?? {});
83
+ debug(`Parser ${parserLoader.parserType} loaded`);
84
+ await parser.parse();
85
+ return metadata.toCommonMetadata();
66
86
  }
67
- // Parser found, execute parser
68
- const parser = await loadParser(parserId);
69
- const metadata = new MetadataCollector(opts);
70
- await parser.init(metadata, tokenizer, opts ?? {}).parse();
71
- return metadata.toCommonMetadata();
72
- }
73
- /**
74
- * @param filePath - Path, filename or extension to audio file
75
- * @return Parser submodule name
76
- */
77
- export function getParserIdForExtension(filePath) {
78
- if (!filePath)
79
- return;
80
- const extension = getExtension(filePath).toLocaleLowerCase() || filePath;
81
- switch (extension) {
82
- case '.mp2':
83
- case '.mp3':
84
- case '.m2a':
85
- case '.aac': // Assume it is ADTS-container
86
- return 'mpeg';
87
- case '.ape':
88
- return 'apev2';
89
- case '.mp4':
90
- case '.m4a':
91
- case '.m4b':
92
- case '.m4pa':
93
- case '.m4v':
94
- case '.m4r':
95
- case '.3gp':
96
- return 'mp4';
97
- case '.wma':
98
- case '.wmv':
99
- case '.asf':
100
- return 'asf';
101
- case '.flac':
102
- return 'flac';
103
- case '.ogg':
104
- case '.ogv':
105
- case '.oga':
106
- case '.ogm':
107
- case '.ogx':
108
- case '.opus': // recommended filename extension for Ogg Opus
109
- case '.spx': // recommended filename extension for Ogg Speex
110
- return 'ogg';
111
- case '.aif':
112
- case '.aiff':
113
- case '.aifc':
114
- return 'aiff';
115
- case '.wav':
116
- case '.bwf': // Broadcast Wave Format
117
- return 'riff';
118
- case '.wv':
119
- case '.wvp':
120
- return 'wavpack';
121
- case '.mpc':
122
- return 'musepack';
123
- case '.dsf':
124
- return 'dsf';
125
- case '.dff':
126
- return 'dsdiff';
127
- case '.mka':
128
- case '.mkv':
129
- case '.mk3d':
130
- case '.mks':
131
- case '.webm':
132
- return 'matroska';
87
+ /**
88
+ * @param filePath - Path, filename or extension to audio file
89
+ * @return Parser submodule name
90
+ */
91
+ findLoaderForExtension(filePath) {
92
+ if (!filePath)
93
+ return;
94
+ const extension = getExtension(filePath).toLocaleLowerCase() || filePath;
95
+ return this.parsers.find(parser => parser.extensions.indexOf(extension) !== -1);
133
96
  }
134
- }
135
- export async function loadParser(moduleName) {
136
- switch (moduleName) {
137
- case 'aiff': return new AIFFParser();
138
- case 'adts':
139
- case 'mpeg':
140
- return new MpegParser();
141
- case 'apev2': return new APEv2Parser();
142
- case 'asf': return new AsfParser();
143
- case 'dsf': return new DsfParser();
144
- case 'dsdiff': return new DsdiffParser();
145
- case 'flac': return new FlacParser();
146
- case 'mp4': return new MP4Parser();
147
- case 'musepack': return new MusepackParser();
148
- case 'ogg': return new OggParser();
149
- case 'riff': return new WaveParser();
150
- case 'wavpack': return new WavPackParser();
151
- case 'matroska': return new MatroskaParser();
152
- default:
153
- throw new InternalParserError(`Unknown parser type: ${moduleName}`);
97
+ findLoaderForType(moduleName) {
98
+ return moduleName ? this.parsers.find(parser => parser.parserType === moduleName) : undefined;
154
99
  }
155
100
  }
156
101
  function getExtension(fname) {
@@ -181,7 +126,7 @@ function getParserIdForMimeType(httpContentType) {
181
126
  return 'mpeg';
182
127
  case 'aac':
183
128
  case 'aacp':
184
- return 'adts';
129
+ return 'mpeg'; // adts
185
130
  case 'flac':
186
131
  return 'flac';
187
132
  case 'ape':
@@ -215,6 +160,8 @@ function getParserIdForMimeType(httpContentType) {
215
160
  return 'matroska';
216
161
  case 'dsf':
217
162
  return 'dsf';
163
+ case 'amr':
164
+ return 'amr';
218
165
  }
219
166
  break;
220
167
  case 'video':
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const aiffParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const aiffParserLoader = {
2
+ parserType: 'aiff',
3
+ extensions: ['.aif', 'aiff', 'aifc'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./AiffParser.js')).AIFFParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=AiffLoader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const amrParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const amrParserLoader = {
2
+ parserType: 'amr',
3
+ extensions: ['.amr'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./AmrParser.js')).AmrParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=AmrLoader.js.map
@@ -0,0 +1,7 @@
1
+ import { BasicParser } from '../common/BasicParser.js';
2
+ /**
3
+ * Adaptive Multi-Rate audio codec
4
+ */
5
+ export declare class AmrParser extends BasicParser {
6
+ parse(): Promise<void>;
7
+ }
@@ -0,0 +1,49 @@
1
+ import { BasicParser } from '../common/BasicParser.js';
2
+ import { AnsiStringType } from 'token-types';
3
+ import initDebug from 'debug';
4
+ import { FrameHeader } from './AmrToken.js';
5
+ const debug = initDebug('music-metadata:parser:AMR');
6
+ /**
7
+ * There are 8 varying levels of compression. First byte of the frame specifies CMR
8
+ * (codec mode request), values 0-7 are valid for AMR. Each mode have different frame size.
9
+ * This table reflects that fact.
10
+ */
11
+ const m_block_size = [12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0];
12
+ /**
13
+ * Adaptive Multi-Rate audio codec
14
+ */
15
+ export class AmrParser extends BasicParser {
16
+ async parse() {
17
+ const magicNr = await this.tokenizer.readToken(new AnsiStringType(5));
18
+ if (magicNr !== '#!AMR') {
19
+ throw new Error('Invalid AMR file: invalid MAGIC number');
20
+ }
21
+ this.metadata.setFormat('container', 'AMR');
22
+ this.metadata.setFormat('codec', 'AMR');
23
+ this.metadata.setFormat('sampleRate', 8000);
24
+ this.metadata.setFormat('bitrate', 64000);
25
+ this.metadata.setFormat('numberOfChannels', 1);
26
+ let total_size = 0;
27
+ let frames = 0;
28
+ const assumedFileLength = this.tokenizer.fileInfo?.size ?? Number.MAX_SAFE_INTEGER;
29
+ if (this.options.duration) {
30
+ while (this.tokenizer.position < assumedFileLength) {
31
+ const header = await this.tokenizer.readToken(FrameHeader);
32
+ /* first byte is rate mode. each rate mode has frame of given length. look it up. */
33
+ const size = m_block_size[header.frameType];
34
+ if (size > 0) {
35
+ total_size += size + 1;
36
+ if (total_size > assumedFileLength)
37
+ break;
38
+ await this.tokenizer.ignore(size);
39
+ ++frames;
40
+ }
41
+ else {
42
+ debug(`Found no-data frame, frame-type: ${header.frameType}. Skipping`);
43
+ }
44
+ }
45
+ this.metadata.setFormat('duration', frames * 0.02);
46
+ }
47
+ }
48
+ }
49
+ //# sourceMappingURL=AmrParser.js.map
@@ -0,0 +1,11 @@
1
+ import type { IGetToken } from 'strtok3';
2
+ interface IFrameHeader {
3
+ frameType: number;
4
+ }
5
+ /**
6
+ * ID3v2 header
7
+ * Ref: http://id3.org/id3v2.3.0#ID3v2_header
8
+ * ToDo
9
+ */
10
+ export declare const FrameHeader: IGetToken<IFrameHeader>;
11
+ export {};
@@ -0,0 +1,15 @@
1
+ import { getBitAllignedNumber } from '../common/Util.js';
2
+ /**
3
+ * ID3v2 header
4
+ * Ref: http://id3.org/id3v2.3.0#ID3v2_header
5
+ * ToDo
6
+ */
7
+ export const FrameHeader = {
8
+ len: 1,
9
+ get: (buf, off) => {
10
+ return {
11
+ frameType: getBitAllignedNumber(buf, off, 1, 4)
12
+ };
13
+ }
14
+ };
15
+ //# sourceMappingURL=AmrToken.js.map
@@ -17,8 +17,7 @@ export class APEv2Parser extends BasicParser {
17
17
  this.ape = {};
18
18
  }
19
19
  static tryParseApeHeader(metadata, tokenizer, options) {
20
- const apeParser = new APEv2Parser();
21
- apeParser.init(metadata, tokenizer, options);
20
+ const apeParser = new APEv2Parser(metadata, tokenizer, options);
22
21
  return apeParser.tryParseApeHeader();
23
22
  }
24
23
  /**
@@ -57,8 +56,7 @@ export class APEv2Parser extends BasicParser {
57
56
  if (footer.ID !== preamble)
58
57
  throw new ApeContentError('Unexpected APEv2 Footer ID preamble value');
59
58
  strtok3.fromBuffer(buffer);
60
- const apeParser = new APEv2Parser();
61
- apeParser.init(metadata, strtok3.fromBuffer(buffer), options);
59
+ const apeParser = new APEv2Parser(metadata, strtok3.fromBuffer(buffer), options);
62
60
  return apeParser.parseTags(footer);
63
61
  }
64
62
  /**
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const apeParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const apeParserLoader = {
2
+ parserType: 'apev2',
3
+ extensions: ['.ape'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./APEv2Parser.js')).APEv2Parser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=Apev2Loader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const asfParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const asfParserLoader = {
2
+ parserType: 'asf',
3
+ extensions: ['.asf'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./AsfParser.js')).AsfParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=AsfLoader.js.map
@@ -10,7 +10,13 @@ declare const AsfContentParseError_base: {
10
10
  stack?: string;
11
11
  };
12
12
  captureStackTrace(targetObject: object, constructorOpt?: Function): void;
13
- prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
13
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite /**
14
+ * Specifies the amount of time to buffer data before starting to play the file, in millisecond units.
15
+ * If this value is nonzero, the Play Duration field and all of the payload Presentation Time fields have been offset
16
+ * by this amount. Therefore, player software must subtract the value in the preroll field from the play duration and
17
+ * presentation times to calculate their actual values. It follows that all payload Presentation Time fields need to
18
+ * be at least this value.
19
+ */[]) => any) | undefined;
14
20
  stackTraceLimit: number;
15
21
  };
16
22
  export declare class AsfContentParseError extends AsfContentParseError_base {
@@ -1,17 +1,17 @@
1
1
  import type { ITokenizer } from 'strtok3';
2
2
  import type { ITokenParser } from '../ParserFactory.js';
3
- import type { IOptions, IPrivateOptions } from '../type.js';
3
+ import type { IOptions } from '../type.js';
4
4
  import type { INativeMetadataCollector } from './MetadataCollector.js';
5
5
  export declare abstract class BasicParser implements ITokenParser {
6
- protected metadata: INativeMetadataCollector;
7
- protected tokenizer: ITokenizer;
8
- protected options: IPrivateOptions;
6
+ protected readonly metadata: INativeMetadataCollector;
7
+ protected readonly tokenizer: ITokenizer;
8
+ protected readonly options: IOptions;
9
9
  /**
10
10
  * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
11
11
  * @param {INativeMetadataCollector} metadata Output
12
12
  * @param {ITokenizer} tokenizer Input
13
13
  * @param {IOptions} options Parsing options
14
14
  */
15
- init(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IOptions): ITokenParser;
15
+ constructor(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IOptions);
16
16
  abstract parse(): Promise<void>;
17
17
  }
@@ -1,20 +1,14 @@
1
1
  export class BasicParser {
2
- constructor() {
3
- this.metadata = undefined;
4
- this.tokenizer = undefined;
5
- this.options = undefined;
6
- }
7
2
  /**
8
3
  * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
9
4
  * @param {INativeMetadataCollector} metadata Output
10
5
  * @param {ITokenizer} tokenizer Input
11
6
  * @param {IOptions} options Parsing options
12
7
  */
13
- init(metadata, tokenizer, options) {
8
+ constructor(metadata, tokenizer, options) {
14
9
  this.metadata = metadata;
15
10
  this.tokenizer = tokenizer;
16
11
  this.options = options;
17
- return this;
18
12
  }
19
13
  }
20
14
  //# sourceMappingURL=BasicParser.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/lib/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Primary entry point, Node.js specific entry point is index.ts
2
+ * Primary entry point, Node.js specific entry point is MusepackParser.ts
3
3
  */
4
4
  import { type AnyWebByteStream, type IFileInfo, type ITokenizer } from 'strtok3';
5
5
  import type { IAudioMetadata, INativeTagDict, IOptions, IPicture, IPrivateOptions, IRandomReader, ITag } from './type.js';
package/lib/core.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Primary entry point, Node.js specific entry point is index.ts
2
+ * Primary entry point, Node.js specific entry point is MusepackParser.ts
3
3
  */
4
4
  import { fromWebStream, fromBuffer } from 'strtok3';
5
- import { parseOnContentType } from './ParserFactory.js';
5
+ import { ParserFactory } from './ParserFactory.js';
6
6
  import { RandomUint8ArrayReader } from './common/RandomUint8ArrayReader.js';
7
7
  import { APEv2Parser } from './apev2/APEv2Parser.js';
8
8
  import { hasID3v1Header } from './id3v1/ID3v1Parser.js';
@@ -53,7 +53,8 @@ export async function parseBuffer(uint8Array, fileInfo, options = {}) {
53
53
  * @returns Metadata
54
54
  */
55
55
  export function parseFromTokenizer(tokenizer, options) {
56
- return parseOnContentType(tokenizer, options);
56
+ const parserFactory = new ParserFactory();
57
+ return parserFactory.parse(tokenizer, undefined, options);
57
58
  }
58
59
  /**
59
60
  * Create a dictionary ordered by their tag id (key)
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const dsdiffParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const dsdiffParserLoader = {
2
+ parserType: 'dsdiff',
3
+ extensions: ['.dff'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./DsdiffParser.js')).DsdiffParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=DsdiffLoader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const dsfParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const dsfParserLoader = {
2
+ parserType: 'dsf',
3
+ extensions: ['.dsf'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./DsfParser.js')).DsfParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=DsfLoader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const flacParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const flacParserLoader = {
2
+ parserType: 'flac',
3
+ extensions: ['.flac'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./FlacParser.js')).FlacParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=FlacLoader.js.map
@@ -1,18 +1,7 @@
1
- import type { ITokenizer } from 'strtok3';
2
1
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
3
- import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
4
- import type { IOptions } from '../type.js';
5
- import type { ITokenParser } from '../ParserFactory.js';
6
2
  export declare class FlacParser extends AbstractID3Parser {
7
3
  private vorbisParser;
8
4
  private padding;
9
- /**
10
- * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
11
- * @param {INativeMetadataCollector} metadata Output
12
- * @param {ITokenizer} tokenizer Input
13
- * @param {IOptions} options Parsing options
14
- */
15
- init(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IOptions): ITokenParser;
16
5
  postId3v2Parse(): Promise<void>;
17
6
  private parseDataBlock;
18
7
  /**
@@ -27,19 +27,9 @@ var BlockType;
27
27
  export class FlacParser extends AbstractID3Parser {
28
28
  constructor() {
29
29
  super(...arguments);
30
+ this.vorbisParser = new VorbisParser(this.metadata, this.options);
30
31
  this.padding = 0;
31
32
  }
32
- /**
33
- * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
34
- * @param {INativeMetadataCollector} metadata Output
35
- * @param {ITokenizer} tokenizer Input
36
- * @param {IOptions} options Parsing options
37
- */
38
- init(metadata, tokenizer, options) {
39
- super.init(metadata, tokenizer, options);
40
- this.vorbisParser = new VorbisParser(metadata, options);
41
- return this;
42
- }
43
33
  async postId3v2Parse() {
44
34
  const fourCC = await this.tokenizer.readToken(FourCcToken);
45
35
  if (fourCC.toString() !== 'fLaC') {
@@ -1,11 +1,15 @@
1
+ import type { ITokenizer } from 'strtok3';
1
2
  import { BasicParser } from '../common/BasicParser.js';
2
- import type { IRandomReader } from '../type.js';
3
+ import type { IPrivateOptions, IRandomReader } from '../type.js';
4
+ import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
3
5
  /**
4
6
  * ID3v1 Genre mappings
5
7
  * Ref: https://de.wikipedia.org/wiki/Liste_der_ID3v1-Genres
6
8
  */
7
9
  export declare const Genres: string[];
8
10
  export declare class ID3v1Parser extends BasicParser {
11
+ private apeHeader;
12
+ constructor(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IPrivateOptions);
9
13
  private static getGenre;
10
14
  parse(): Promise<void>;
11
15
  private addTag;
@@ -79,6 +79,10 @@ class Id3v1StringType {
79
79
  }
80
80
  }
81
81
  export class ID3v1Parser extends BasicParser {
82
+ constructor(metadata, tokenizer, options) {
83
+ super(metadata, tokenizer, options);
84
+ this.apeHeader = options.apeHeader;
85
+ }
82
86
  static getGenre(genreIndex) {
83
87
  if (genreIndex < Genres.length) {
84
88
  return Genres[genreIndex];
@@ -90,11 +94,10 @@ export class ID3v1Parser extends BasicParser {
90
94
  debug('Skip checking for ID3v1 because the file-size is unknown');
91
95
  return;
92
96
  }
93
- if (this.options.apeHeader) {
94
- this.tokenizer.ignore(this.options.apeHeader.offset - this.tokenizer.position);
95
- const apeParser = new APEv2Parser();
96
- apeParser.init(this.metadata, this.tokenizer, this.options);
97
- await apeParser.parseTags(this.options.apeHeader.footer);
97
+ if (this.apeHeader) {
98
+ this.tokenizer.ignore(this.apeHeader.offset - this.tokenizer.position);
99
+ const apeParser = new APEv2Parser(this.metadata, this.tokenizer, this.options);
100
+ await apeParser.parseTags(this.apeHeader.footer);
98
101
  }
99
102
  const offset = this.tokenizer.fileInfo.size - Iid3v1Token.len;
100
103
  if (this.tokenizer.position > offset) {
@@ -40,8 +40,8 @@ export class AbstractID3Parser extends BasicParser {
40
40
  this.finalize();
41
41
  }
42
42
  else {
43
- const id3v1parser = new ID3v1Parser();
44
- await id3v1parser.init(this.metadata, this.tokenizer, this.options).parse();
43
+ const id3v1parser = new ID3v1Parser(this.metadata, this.tokenizer, this.options);
44
+ await id3v1parser.parse();
45
45
  this.finalize();
46
46
  }
47
47
  }
package/lib/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { fromFile, fromStream } from 'strtok3';
5
5
  import initDebug from 'debug';
6
6
  import { parseFromTokenizer, scanAppendingHeaders } from './core.js';
7
- import { getParserIdForExtension, parse } from './ParserFactory.js';
7
+ import { ParserFactory } from './ParserFactory.js';
8
8
  import { RandomFileReader } from './common/RandomFileReader.js';
9
9
  export * from './core.js';
10
10
  const debug = initDebug('music-metadata:parser');
@@ -35,11 +35,12 @@ export async function parseFile(filePath, options = {}) {
35
35
  finally {
36
36
  await fileReader.close();
37
37
  }
38
+ const parserFactory = new ParserFactory();
38
39
  try {
39
- const parserName = getParserIdForExtension(filePath);
40
- if (!parserName)
40
+ const parserLoader = parserFactory.findLoaderForExtension(filePath);
41
+ if (!parserLoader)
41
42
  debug(' Parser could not be determined by file extension');
42
- return await parse(fileTokenizer, parserName, options);
43
+ return await parserFactory.parse(fileTokenizer, parserLoader, options);
43
44
  }
44
45
  finally {
45
46
  await fileTokenizer.close();
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const matroskaParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const matroskaParserLoader = {
2
+ parserType: 'matroska',
3
+ extensions: ['.mka', '.mkv', '.mk3d', '.mks', 'webm'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./MatroskaParser.js')).MatroskaParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=MatroskaLoader.js.map
@@ -1,8 +1,4 @@
1
- import type { ITokenizer } from 'strtok3';
2
- import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
3
1
  import { BasicParser } from '../common/BasicParser.js';
4
- import type { IOptions } from '../type.js';
5
- import type { ITokenParser } from '../ParserFactory.js';
6
2
  /**
7
3
  * Extensible Binary Meta Language (EBML) parser
8
4
  * https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Language
@@ -18,13 +14,6 @@ export declare class MatroskaParser extends BasicParser {
18
14
  * Significant performance impact
19
15
  */
20
16
  private flagUseIndexToSkipClusters;
21
- /**
22
- * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
23
- * @param {INativeMetadataCollector} metadata Output
24
- * @param {ITokenizer} tokenizer Input
25
- * @param {IOptions} options Parsing options
26
- */
27
- init(metadata: INativeMetadataCollector, tokenizer: ITokenizer, options: IOptions): ITokenParser;
28
17
  parse(): Promise<void>;
29
18
  private addTag;
30
19
  }
@@ -19,18 +19,7 @@ export class MatroskaParser extends BasicParser {
19
19
  * Use index to skip multiple segment/cluster elements at once.
20
20
  * Significant performance impact
21
21
  */
22
- this.flagUseIndexToSkipClusters = false;
23
- }
24
- /**
25
- * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
26
- * @param {INativeMetadataCollector} metadata Output
27
- * @param {ITokenizer} tokenizer Input
28
- * @param {IOptions} options Parsing options
29
- */
30
- init(metadata, tokenizer, options) {
31
- super.init(metadata, tokenizer, options);
32
- this.flagUseIndexToSkipClusters = options.mkvUseIndex ?? false;
33
- return this;
22
+ this.flagUseIndexToSkipClusters = this.options.mkvUseIndex ?? false;
34
23
  }
35
24
  async parse() {
36
25
  const containerSize = this.tokenizer.fileInfo.size ?? Number.MAX_SAFE_INTEGER;
package/lib/mp4/Atom.js CHANGED
@@ -6,13 +6,13 @@ export class Atom {
6
6
  static async readAtom(tokenizer, dataHandler, parent, remaining) {
7
7
  // Parse atom header
8
8
  const offset = tokenizer.position;
9
- // debug(`Reading next token on offset=${offset}...`); // buf.toString('ascii')
9
+ debug(`Reading next token on offset=${offset}...`); // buf.toString('ascii')
10
10
  const header = await tokenizer.readToken(AtomToken.Header);
11
- const extended = header.length === BigInt(1);
11
+ const extended = header.length === 1n;
12
12
  if (extended) {
13
13
  header.length = await tokenizer.readToken(AtomToken.ExtendedSize);
14
14
  }
15
- const atomBean = new Atom(header, header.length === BigInt(1), parent);
15
+ const atomBean = new Atom(header, extended, parent);
16
16
  const payloadLength = atomBean.getPayloadLength(remaining);
17
17
  debug(`parse atom name=${atomBean.atomPath}, extended=${atomBean.extended}, offset=${offset}, len=${atomBean.header.length}`); // buf.toString('ascii')
18
18
  await atomBean.readData(tokenizer, dataHandler, payloadLength);
@@ -29,13 +29,13 @@ export class Atom {
29
29
  return this.extended ? 16 : 8;
30
30
  }
31
31
  getPayloadLength(remaining) {
32
- return (this.header.length === BigInt(0) ? remaining : Number(this.header.length)) - this.getHeaderLength();
32
+ return (this.header.length === 0n ? remaining : Number(this.header.length)) - this.getHeaderLength();
33
33
  }
34
34
  async readAtoms(tokenizer, dataHandler, size) {
35
35
  while (size > 0) {
36
36
  const atomBean = await Atom.readAtom(tokenizer, dataHandler, this, size);
37
37
  this.children.push(atomBean);
38
- size -= atomBean.header.length === BigInt(0) ? size : Number(atomBean.header.length);
38
+ size -= atomBean.header.length === 0n ? size : Number(atomBean.header.length);
39
39
  }
40
40
  }
41
41
  async readData(tokenizer, dataHandler, remaining) {
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const mp4ParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const mp4ParserLoader = {
2
+ parserType: 'mp4',
3
+ extensions: ['.mp4', '.m4a', '.m4b', '.m4pa', 'm4v', 'm4r', '3gp'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./MP4Parser.js')).MP4Parser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=Mp4Loader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const mpegParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const mpegParserLoader = {
2
+ parserType: 'mpeg',
3
+ extensions: ['.mp2', '.mp3', '.m2a', '.aac', 'aacp'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./MpegParser.js')).MpegParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=MpegLoader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const musepackParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const musepackParserLoader = {
2
+ parserType: 'musepack',
3
+ extensions: ['.mpc'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./MusepackParser.js')).MusepackParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=MusepackLoader.js.map
@@ -1,5 +1,4 @@
1
1
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
2
- declare class MusepackParser extends AbstractID3Parser {
2
+ export declare class MusepackParser extends AbstractID3Parser {
3
3
  postId3v2Parse(): Promise<void>;
4
4
  }
5
- export default MusepackParser;
@@ -5,28 +5,26 @@ import { MpcSv8Parser } from './sv8/MpcSv8Parser.js';
5
5
  import { MpcSv7Parser } from './sv7/MpcSv7Parser.js';
6
6
  import { MusepackContentError } from './MusepackConentError.js';
7
7
  const debug = initDebug('music-metadata:parser:musepack');
8
- class MusepackParser extends AbstractID3Parser {
8
+ export class MusepackParser extends AbstractID3Parser {
9
9
  async postId3v2Parse() {
10
10
  const signature = await this.tokenizer.peekToken(new Token.StringType(3, 'latin1'));
11
11
  let mpcParser;
12
12
  switch (signature) {
13
13
  case 'MP+': {
14
14
  debug('Stream-version 7');
15
- mpcParser = new MpcSv7Parser();
15
+ mpcParser = new MpcSv7Parser(this.metadata, this.tokenizer, this.options);
16
16
  break;
17
17
  }
18
18
  case 'MPC': {
19
19
  debug('Stream-version 8');
20
- mpcParser = new MpcSv8Parser();
20
+ mpcParser = new MpcSv8Parser(this.metadata, this.tokenizer, this.options);
21
21
  break;
22
22
  }
23
23
  default: {
24
24
  throw new MusepackContentError('Invalid signature prefix');
25
25
  }
26
26
  }
27
- mpcParser.init(this.metadata, this.tokenizer, this.options);
28
27
  return mpcParser.parse();
29
28
  }
30
29
  }
31
- export default MusepackParser;
32
- //# sourceMappingURL=index.js.map
30
+ //# sourceMappingURL=MusepackParser.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const oggParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const oggParserLoader = {
2
+ parserType: 'ogg',
3
+ extensions: ['.ogg', '.ogv', '.oga', '.ogm', '.ogx', '.opus', '.spx'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./OggParser.js')).OggParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=OggLoader.js.map
package/lib/type.d.ts CHANGED
@@ -521,7 +521,7 @@ export interface IAudioMetadata extends INativeAudioMetadata {
521
521
  /**
522
522
  * Corresponds with parser module name
523
523
  */
524
- export type ParserType = 'mpeg' | 'apev2' | 'mp4' | 'asf' | 'flac' | 'ogg' | 'aiff' | 'wavpack' | 'riff' | 'musepack' | 'dsf' | 'dsdiff' | 'adts' | 'matroska';
524
+ export type ParserType = 'mpeg' | 'apev2' | 'mp4' | 'asf' | 'flac' | 'ogg' | 'aiff' | 'wavpack' | 'riff' | 'musepack' | 'dsf' | 'dsdiff' | 'adts' | 'matroska' | 'amr';
525
525
  export interface IOptions {
526
526
  /**
527
527
  * default: `false`, if set to `true`, it will parse the whole media file if required to determine the duration.
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const riffParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const riffParserLoader = {
2
+ parserType: 'riff',
3
+ extensions: ['.wav', 'wave', '.bwf'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./WaveParser.js')).WaveParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=WaveLoader.js.map
@@ -0,0 +1,2 @@
1
+ import type { IParserLoader } from '../ParserFactory.js';
2
+ export declare const wavpackParserLoader: IParserLoader;
@@ -0,0 +1,8 @@
1
+ export const wavpackParserLoader = {
2
+ parserType: 'wavpack',
3
+ extensions: ['.wv', '.wvp'],
4
+ async load(metadata, tokenizer, options) {
5
+ return new (await import('./WavPackParser.js')).WavPackParser(metadata, tokenizer, options);
6
+ }
7
+ };
8
+ //# sourceMappingURL=WavPackLoader.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "music-metadata",
3
3
  "description": "Music metadata parser for Node.js, supporting virtual any audio and tag format.",
4
- "version": "10.3.1",
4
+ "version": "10.5.0",
5
5
  "author": {
6
6
  "name": "Borewit",
7
7
  "url": "https://github.com/Borewit"
@@ -105,10 +105,10 @@
105
105
  "dependencies": {
106
106
  "@tokenizer/token": "^0.3.0",
107
107
  "content-type": "^1.0.5",
108
- "debug": "^4.3.4",
109
- "file-type": "^19.4.1",
108
+ "debug": "^4.3.7",
109
+ "file-type": "^19.5.0",
110
110
  "media-typer": "^1.1.0",
111
- "strtok3": "^8.1.0",
111
+ "strtok3": "^9.0.0",
112
112
  "token-types": "^6.0.0",
113
113
  "uint8array-extras": "^1.4.0"
114
114
  },
@@ -120,7 +120,7 @@
120
120
  "@types/debug": "^4.1.12",
121
121
  "@types/media-typer": "^1.1.3",
122
122
  "@types/mocha": "^10.0.7",
123
- "@types/node": "^22.5.1",
123
+ "@types/node": "^22.5.4",
124
124
  "c8": "^10.1.2",
125
125
  "chai": "^5.1.1",
126
126
  "chai-as-promised": "^8.0.0",