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.
- package/README.md +8 -7
- package/lib/ParserFactory.d.ts +22 -20
- package/lib/ParserFactory.js +78 -131
- package/lib/aiff/AiffLoader.d.ts +2 -0
- package/lib/aiff/AiffLoader.js +8 -0
- package/lib/amr/AmrLoader.d.ts +2 -0
- package/lib/amr/AmrLoader.js +8 -0
- package/lib/amr/AmrParser.d.ts +7 -0
- package/lib/amr/AmrParser.js +49 -0
- package/lib/amr/AmrToken.d.ts +11 -0
- package/lib/amr/AmrToken.js +15 -0
- package/lib/apev2/APEv2Parser.js +2 -4
- package/lib/apev2/Apev2Loader.d.ts +2 -0
- package/lib/apev2/Apev2Loader.js +8 -0
- package/lib/asf/AsfLoader.d.ts +2 -0
- package/lib/asf/AsfLoader.js +8 -0
- package/lib/asf/AsfObject.d.ts +7 -1
- package/lib/common/BasicParser.d.ts +5 -5
- package/lib/common/BasicParser.js +1 -7
- package/lib/common/ParserLoader.d.ts +1 -0
- package/lib/common/ParserLoader.js +1 -0
- package/lib/core.d.ts +1 -1
- package/lib/core.js +4 -3
- package/lib/dsdiff/DsdiffLoader.d.ts +2 -0
- package/lib/dsdiff/DsdiffLoader.js +8 -0
- package/lib/dsf/DsfLoader.d.ts +2 -0
- package/lib/dsf/DsfLoader.js +8 -0
- package/lib/flac/FlacLoader.d.ts +2 -0
- package/lib/flac/FlacLoader.js +8 -0
- package/lib/flac/FlacParser.d.ts +0 -11
- package/lib/flac/FlacParser.js +1 -11
- package/lib/id3v1/ID3v1Parser.d.ts +5 -1
- package/lib/id3v1/ID3v1Parser.js +8 -5
- package/lib/id3v2/AbstractID3Parser.js +2 -2
- package/lib/index.js +5 -4
- package/lib/matroska/MatroskaLoader.d.ts +2 -0
- package/lib/matroska/MatroskaLoader.js +8 -0
- package/lib/matroska/MatroskaParser.d.ts +0 -11
- package/lib/matroska/MatroskaParser.js +1 -12
- package/lib/mp4/Atom.js +5 -5
- package/lib/mp4/Mp4Loader.d.ts +2 -0
- package/lib/mp4/Mp4Loader.js +8 -0
- package/lib/mpeg/MpegLoader.d.ts +2 -0
- package/lib/mpeg/MpegLoader.js +8 -0
- package/lib/musepack/MusepackLoader.d.ts +2 -0
- package/lib/musepack/MusepackLoader.js +8 -0
- package/lib/musepack/{index.d.ts → MusepackParser.d.ts} +1 -2
- package/lib/musepack/{index.js → MusepackParser.js} +4 -6
- package/lib/ogg/OggLoader.d.ts +2 -0
- package/lib/ogg/OggLoader.js +8 -0
- package/lib/type.d.ts +1 -1
- package/lib/wav/WaveLoader.d.ts +2 -0
- package/lib/wav/WaveLoader.js +8 -0
- package/lib/wavpack/WavPackLoader.d.ts +2 -0
- package/lib/wavpack/WavPackLoader.js +8 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://github.com/Borewit/music-metadata/actions?query=branch%3Amaster)
|
|
2
2
|
[](https://ci.appveyor.com/project/Borewit/music-metadata/branch/master)
|
|
3
3
|
[](https://npmjs.org/package/music-metadata)
|
|
4
|
-
[](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600)
|
|
4
|
+
[](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600&interval=30)
|
|
5
5
|
[](https://coveralls.io/github/Borewit/music-metadata?branch=master)
|
|
6
6
|
[](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
|
[](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
|
-
##
|
|
35
|
-
If you
|
|
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
|
-
|
|
37
|
+
- [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
|
|
40
38
|
|
|
41
|
-
|
|
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) | |
|
package/lib/ParserFactory.d.ts
CHANGED
|
@@ -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
|
|
5
|
+
export interface IParserLoader {
|
|
6
|
+
/**
|
|
7
|
+
* Returns a list of supported file extensions
|
|
8
|
+
*/
|
|
9
|
+
extensions: string[];
|
|
10
|
+
parserType: ParserType;
|
|
6
11
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param metadata - Output
|
|
9
|
-
* @param tokenizer - Input
|
|
10
|
-
* @param options - Parsing options
|
|
12
|
+
* Lazy load the parser
|
|
11
13
|
*/
|
|
12
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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 {};
|
package/lib/ParserFactory.js
CHANGED
|
@@ -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 {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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 '
|
|
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,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,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,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
|
package/lib/apev2/APEv2Parser.js
CHANGED
|
@@ -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,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
|
package/lib/asf/AsfObject.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
2
|
+
* Primary entry point, Node.js specific entry point is MusepackParser.ts
|
|
3
3
|
*/
|
|
4
4
|
import { fromWebStream, fromBuffer } from 'strtok3';
|
|
5
|
-
import {
|
|
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
|
-
|
|
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,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
|
package/lib/flac/FlacParser.d.ts
CHANGED
|
@@ -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
|
/**
|
package/lib/flac/FlacParser.js
CHANGED
|
@@ -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;
|
package/lib/id3v1/ID3v1Parser.js
CHANGED
|
@@ -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.
|
|
94
|
-
this.tokenizer.ignore(this.
|
|
95
|
-
const apeParser = new APEv2Parser();
|
|
96
|
-
apeParser.
|
|
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.
|
|
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 {
|
|
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
|
|
40
|
-
if (!
|
|
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,
|
|
43
|
+
return await parserFactory.parse(fileTokenizer, parserLoader, options);
|
|
43
44
|
}
|
|
44
45
|
finally {
|
|
45
46
|
await fileTokenizer.close();
|
|
@@ -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
|
-
|
|
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 ===
|
|
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,
|
|
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 ===
|
|
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 ===
|
|
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,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,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,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
|
|
@@ -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
|
-
|
|
32
|
-
//# sourceMappingURL=index.js.map
|
|
30
|
+
//# sourceMappingURL=MusepackParser.js.map
|
|
@@ -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,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,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.
|
|
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.
|
|
109
|
-
"file-type": "^19.
|
|
108
|
+
"debug": "^4.3.7",
|
|
109
|
+
"file-type": "^19.5.0",
|
|
110
110
|
"media-typer": "^1.1.0",
|
|
111
|
-
"strtok3": "^
|
|
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.
|
|
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",
|