music-metadata 10.0.1 → 10.2.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/LICENSE.txt +9 -9
- package/README.md +535 -520
- package/lib/ParserFactory.d.ts +20 -28
- package/lib/ParserFactory.js +204 -207
- package/lib/aiff/AiffParser.js +20 -19
- package/lib/aiff/AiffToken.d.ts +14 -2
- package/lib/aiff/AiffToken.js +12 -0
- package/lib/apev2/APEv2Parser.d.ts +4 -4
- package/lib/apev2/APEv2Parser.js +12 -10
- package/lib/apev2/APEv2Token.d.ts +2 -4
- package/lib/apev2/APEv2Token.js +0 -3
- package/lib/asf/AsfObject.d.ts +7 -16
- package/lib/asf/AsfObject.js +8 -34
- package/lib/asf/AsfParser.js +17 -10
- package/lib/asf/AsfTagMapper.d.ts +1 -1
- package/lib/asf/AsfTagMapper.js +3 -2
- package/lib/asf/AsfUtil.d.ts +3 -11
- package/lib/asf/AsfUtil.js +29 -30
- package/lib/asf/GUID.js +6 -9
- package/lib/common/BasicParser.d.ts +4 -4
- package/lib/common/BasicParser.js +6 -0
- package/lib/common/CaseInsensitiveTagMap.d.ts +1 -1
- package/lib/common/CombinedTagMapper.d.ts +5 -5
- package/lib/common/CombinedTagMapper.js +1 -1
- package/lib/common/FourCC.d.ts +1 -1
- package/lib/common/GenericTagMapper.d.ts +8 -8
- package/lib/common/GenericTagMapper.js +4 -4
- package/lib/common/GenericTagTypes.d.ts +5 -4
- package/lib/common/GenericTagTypes.js +2 -2
- package/lib/common/MetadataCollector.d.ts +9 -9
- package/lib/common/MetadataCollector.js +22 -17
- package/lib/common/RandomFileReader.d.ts +1 -1
- package/lib/common/RandomFileReader.js +1 -1
- package/lib/common/RandomUint8ArrayReader.d.ts +1 -1
- package/lib/common/Util.d.ts +2 -6
- package/lib/common/Util.js +9 -11
- package/lib/core.d.ts +7 -9
- package/lib/core.js +10 -7
- package/lib/dsdiff/DsdiffParser.js +26 -14
- package/lib/dsdiff/DsdiffToken.d.ts +2 -2
- package/lib/dsdiff/DsdiffToken.js +1 -0
- package/lib/dsf/DsfChunk.js +1 -0
- package/lib/dsf/DsfParser.js +4 -2
- package/lib/ebml/EbmlIterator.d.ts +52 -0
- package/lib/ebml/EbmlIterator.js +220 -0
- package/lib/ebml/types.d.ts +36 -0
- package/lib/ebml/types.js +10 -0
- package/lib/flac/FlacParser.d.ts +3 -3
- package/lib/flac/FlacParser.js +9 -12
- package/lib/id3v1/ID3v1Parser.d.ts +1 -1
- package/lib/id3v1/ID3v1Parser.js +7 -4
- package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
- package/lib/id3v2/AbstractID3Parser.js +2 -1
- package/lib/id3v2/FrameParser.d.ts +28 -3
- package/lib/id3v2/FrameParser.js +50 -28
- package/lib/id3v2/ID3v22TagMapper.d.ts +1 -1
- package/lib/id3v2/ID3v24TagMapper.d.ts +2 -1
- package/lib/id3v2/ID3v24TagMapper.js +23 -16
- package/lib/id3v2/ID3v2Parser.d.ts +2 -2
- package/lib/id3v2/ID3v2Parser.js +18 -8
- package/lib/iff/index.js +1 -0
- package/lib/index.d.ts +5 -6
- package/lib/index.js +7 -8
- package/lib/lyrics3/Lyrics3.d.ts +1 -1
- package/lib/lyrics3/Lyrics3.js +1 -1
- package/lib/matroska/MatroskaDtd.d.ts +2 -2
- package/lib/matroska/MatroskaDtd.js +247 -239
- package/lib/matroska/MatroskaParser.d.ts +10 -24
- package/lib/matroska/MatroskaParser.js +120 -205
- package/lib/matroska/types.d.ts +12 -46
- package/lib/matroska/types.js +0 -9
- package/lib/mp4/Atom.d.ts +3 -3
- package/lib/mp4/Atom.js +4 -7
- package/lib/mp4/AtomToken.js +2 -1
- package/lib/mp4/MP4Parser.js +29 -20
- package/lib/mp4/MP4TagMapper.d.ts +2 -2
- package/lib/mp4/MP4TagMapper.js +1 -1
- package/lib/mpeg/ExtendedLameHeader.d.ts +4 -4
- package/lib/mpeg/ExtendedLameHeader.js +2 -1
- package/lib/mpeg/MpegParser.d.ts +1 -1
- package/lib/mpeg/MpegParser.js +145 -96
- package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
- package/lib/mpeg/ReplayGainDataFormat.js +1 -0
- package/lib/mpeg/XingTag.d.ts +4 -4
- package/lib/mpeg/XingTag.js +5 -4
- package/lib/musepack/index.js +1 -0
- package/lib/musepack/sv7/BitReader.js +14 -17
- package/lib/musepack/sv7/MpcSv7Parser.js +6 -1
- package/lib/musepack/sv7/StreamVersion7.js +1 -0
- package/lib/musepack/sv8/MpcSv8Parser.js +6 -2
- package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
- package/lib/musepack/sv8/StreamVersion8.js +1 -0
- package/lib/ogg/Ogg.d.ts +1 -1
- package/lib/ogg/Ogg.js +1 -0
- package/lib/ogg/OggParser.d.ts +3 -3
- package/lib/ogg/OggParser.js +25 -16
- package/lib/ogg/opus/Opus.d.ts +1 -1
- package/lib/ogg/opus/Opus.js +1 -0
- package/lib/ogg/opus/OpusParser.d.ts +4 -4
- package/lib/ogg/opus/OpusParser.js +2 -0
- package/lib/ogg/speex/Speex.js +1 -0
- package/lib/ogg/speex/SpeexParser.d.ts +4 -4
- package/lib/ogg/speex/SpeexParser.js +1 -0
- package/lib/ogg/theora/Theora.js +1 -0
- package/lib/ogg/theora/TheoraParser.d.ts +4 -4
- package/lib/ogg/theora/TheoraParser.js +1 -0
- package/lib/ogg/vorbis/Vorbis.d.ts +3 -3
- package/lib/ogg/vorbis/Vorbis.js +22 -11
- package/lib/ogg/vorbis/VorbisDecoder.d.ts +1 -1
- package/lib/ogg/vorbis/VorbisDecoder.js +1 -0
- package/lib/ogg/vorbis/VorbisParser.d.ts +4 -4
- package/lib/ogg/vorbis/VorbisParser.js +3 -2
- package/lib/ogg/vorbis/VorbisTagMapper.d.ts +2 -2
- package/lib/ogg/vorbis/VorbisTagMapper.js +2 -2
- package/lib/riff/RiffChunk.d.ts +3 -3
- package/lib/riff/RiffChunk.js +1 -0
- package/lib/riff/RiffInfoTagMap.d.ts +1 -1
- package/lib/type.d.ts +44 -25
- package/lib/wav/BwfChunk.js +1 -0
- package/lib/wav/WaveChunk.d.ts +1 -1
- package/lib/wav/WaveChunk.js +1 -0
- package/lib/wav/WaveParser.js +27 -17
- package/lib/wavpack/WavPackParser.d.ts +1 -1
- package/lib/wavpack/WavPackParser.js +22 -13
- package/lib/wavpack/WavPackToken.d.ts +13 -17
- package/lib/wavpack/WavPackToken.js +22 -23
- package/package.json +139 -150
package/lib/ParserFactory.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { type MediaType } from 'media-typer';
|
|
2
|
+
import { type INativeMetadataCollector } from './common/MetadataCollector.js';
|
|
3
|
+
import type { IOptions, IAudioMetadata, ParserType } from './type.js';
|
|
3
4
|
import type { ITokenizer } from 'strtok3';
|
|
4
5
|
export interface ITokenParser {
|
|
5
6
|
/**
|
|
@@ -16,33 +17,24 @@ export interface ITokenParser {
|
|
|
16
17
|
*/
|
|
17
18
|
parse(): Promise<void>;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
type: string;
|
|
21
|
-
subtype: string;
|
|
22
|
-
suffix?: string;
|
|
20
|
+
interface IContentType extends MediaType {
|
|
23
21
|
parameters: {
|
|
24
22
|
[id: string]: string;
|
|
25
23
|
};
|
|
26
|
-
};
|
|
27
|
-
export declare class ParserFactory {
|
|
28
|
-
/**
|
|
29
|
-
* Parse metadata from tokenizer
|
|
30
|
-
* @param tokenizer - Tokenizer
|
|
31
|
-
* @param opts - Options
|
|
32
|
-
* @returns Native metadata
|
|
33
|
-
*/
|
|
34
|
-
static parseOnContentType(tokenizer: ITokenizer, opts: IOptions): Promise<IAudioMetadata>;
|
|
35
|
-
static parse(tokenizer: ITokenizer, parserId: ParserType, opts: IOptions): Promise<IAudioMetadata>;
|
|
36
|
-
/**
|
|
37
|
-
* @param filePath - Path, filename or extension to audio file
|
|
38
|
-
* @return Parser sub-module name
|
|
39
|
-
*/
|
|
40
|
-
static getParserIdForExtension(filePath: string): ParserType;
|
|
41
|
-
static loadParser(moduleName: ParserType): Promise<ITokenParser>;
|
|
42
|
-
private static getExtension;
|
|
43
|
-
/**
|
|
44
|
-
* @param httpContentType - HTTP Content-Type, extension, path or filename
|
|
45
|
-
* @returns Parser sub-module name
|
|
46
|
-
*/
|
|
47
|
-
private static getParserIdForMimeType;
|
|
48
24
|
}
|
|
25
|
+
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>;
|
|
40
|
+
export {};
|
package/lib/ParserFactory.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fileTypeFromBuffer } from 'file-type';
|
|
2
2
|
import ContentType from 'content-type';
|
|
3
|
-
import
|
|
3
|
+
import { parse as mimeTypeParse } from 'media-typer';
|
|
4
4
|
import initDebug from 'debug';
|
|
5
5
|
import { MetadataCollector } from './common/MetadataCollector.js';
|
|
6
6
|
import { AIFFParser } from './aiff/AiffParser.js';
|
|
@@ -19,7 +19,7 @@ import { MatroskaParser } from './matroska/MatroskaParser.js';
|
|
|
19
19
|
const debug = initDebug('music-metadata:parser:factory');
|
|
20
20
|
export function parseHttpContentType(contentType) {
|
|
21
21
|
const type = ContentType.parse(contentType);
|
|
22
|
-
const mime =
|
|
22
|
+
const mime = mimeTypeParse(type.type);
|
|
23
23
|
return {
|
|
24
24
|
type: mime.type,
|
|
25
25
|
subtype: mime.subtype,
|
|
@@ -27,221 +27,218 @@ export function parseHttpContentType(contentType) {
|
|
|
27
27
|
parameters: type.parameters
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Parse metadata from tokenizer
|
|
32
|
+
* @param tokenizer - Tokenizer
|
|
33
|
+
* @param opts - Options
|
|
34
|
+
* @returns Native metadata
|
|
35
|
+
*/
|
|
36
|
+
export async function parseOnContentType(tokenizer, opts) {
|
|
37
|
+
const { mimeType, path, url } = tokenizer.fileInfo;
|
|
38
|
+
// Resolve parser based on MIME-type or file extension
|
|
39
|
+
const parserId = getParserIdForMimeType(mimeType) || getParserIdForExtension(path) || getParserIdForExtension(url);
|
|
40
|
+
if (!parserId) {
|
|
41
|
+
debug(`No parser found for MIME-type / extension: ${mimeType}`);
|
|
42
|
+
}
|
|
43
|
+
return parse(tokenizer, parserId, opts);
|
|
36
44
|
}
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const { mimeType, path, url } = await tokenizer.fileInfo;
|
|
46
|
-
// Resolve parser based on MIME-type or file extension
|
|
47
|
-
const parserId = ParserFactory.getParserIdForMimeType(mimeType) || ParserFactory.getParserIdForExtension(path) || ParserFactory.getParserIdForExtension(url);
|
|
48
|
-
if (!parserId) {
|
|
49
|
-
debug('No parser found for MIME-type / extension: ' + mimeType);
|
|
45
|
+
export async function parse(tokenizer, parserId, opts) {
|
|
46
|
+
if (!parserId) {
|
|
47
|
+
// Parser could not be determined on MIME-type or extension
|
|
48
|
+
debug('Guess parser on content...');
|
|
49
|
+
const buf = new Uint8Array(4100);
|
|
50
|
+
await tokenizer.peekBuffer(buf, { mayBeLess: true });
|
|
51
|
+
if (tokenizer.fileInfo.path) {
|
|
52
|
+
parserId = getParserIdForExtension(tokenizer.fileInfo.path);
|
|
50
53
|
}
|
|
51
|
-
return this.parse(tokenizer, parserId, opts);
|
|
52
|
-
}
|
|
53
|
-
static async parse(tokenizer, parserId, opts) {
|
|
54
54
|
if (!parserId) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
await tokenizer.peekBuffer(buf, { mayBeLess: true });
|
|
59
|
-
if (tokenizer.fileInfo.path) {
|
|
60
|
-
parserId = this.getParserIdForExtension(tokenizer.fileInfo.path);
|
|
55
|
+
const guessedType = await fileTypeFromBuffer(buf);
|
|
56
|
+
if (!guessedType) {
|
|
57
|
+
throw new Error('Failed to determine audio format');
|
|
61
58
|
}
|
|
59
|
+
debug(`Guessed file type is mime=${guessedType.mime}, extension=${guessedType.ext}`);
|
|
60
|
+
parserId = getParserIdForMimeType(guessedType.mime);
|
|
62
61
|
if (!parserId) {
|
|
63
|
-
|
|
64
|
-
if (!guessedType) {
|
|
65
|
-
throw new Error('Failed to determine audio format');
|
|
66
|
-
}
|
|
67
|
-
debug(`Guessed file type is mime=${guessedType.mime}, extension=${guessedType.ext}`);
|
|
68
|
-
parserId = ParserFactory.getParserIdForMimeType(guessedType.mime);
|
|
69
|
-
if (!parserId) {
|
|
70
|
-
throw new Error('Guessed MIME-type not supported: ' + guessedType.mime);
|
|
71
|
-
}
|
|
62
|
+
throw new Error(`Guessed MIME-type not supported: ${guessedType.mime}`);
|
|
72
63
|
}
|
|
73
64
|
}
|
|
74
|
-
// Parser found, execute parser
|
|
75
|
-
return parse(tokenizer, parserId, opts);
|
|
76
65
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
66
|
+
// Parser found, execute parser
|
|
67
|
+
const parser = await loadParser(parserId);
|
|
68
|
+
const metadata = new MetadataCollector(opts);
|
|
69
|
+
await parser.init(metadata, tokenizer, opts ?? {}).parse();
|
|
70
|
+
return metadata.toCommonMetadata();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* @param filePath - Path, filename or extension to audio file
|
|
74
|
+
* @return Parser submodule name
|
|
75
|
+
*/
|
|
76
|
+
export function getParserIdForExtension(filePath) {
|
|
77
|
+
if (!filePath)
|
|
78
|
+
return;
|
|
79
|
+
const extension = getExtension(filePath).toLocaleLowerCase() || filePath;
|
|
80
|
+
switch (extension) {
|
|
81
|
+
case '.mp2':
|
|
82
|
+
case '.mp3':
|
|
83
|
+
case '.m2a':
|
|
84
|
+
case '.aac': // Assume it is ADTS-container
|
|
85
|
+
return 'mpeg';
|
|
86
|
+
case '.ape':
|
|
87
|
+
return 'apev2';
|
|
88
|
+
case '.mp4':
|
|
89
|
+
case '.m4a':
|
|
90
|
+
case '.m4b':
|
|
91
|
+
case '.m4pa':
|
|
92
|
+
case '.m4v':
|
|
93
|
+
case '.m4r':
|
|
94
|
+
case '.3gp':
|
|
95
|
+
return 'mp4';
|
|
96
|
+
case '.wma':
|
|
97
|
+
case '.wmv':
|
|
98
|
+
case '.asf':
|
|
99
|
+
return 'asf';
|
|
100
|
+
case '.flac':
|
|
101
|
+
return 'flac';
|
|
102
|
+
case '.ogg':
|
|
103
|
+
case '.ogv':
|
|
104
|
+
case '.oga':
|
|
105
|
+
case '.ogm':
|
|
106
|
+
case '.ogx':
|
|
107
|
+
case '.opus': // recommended filename extension for Ogg Opus
|
|
108
|
+
case '.spx': // recommended filename extension for Ogg Speex
|
|
109
|
+
return 'ogg';
|
|
110
|
+
case '.aif':
|
|
111
|
+
case '.aiff':
|
|
112
|
+
case '.aifc':
|
|
113
|
+
return 'aiff';
|
|
114
|
+
case '.wav':
|
|
115
|
+
case '.bwf': // Broadcast Wave Format
|
|
116
|
+
return 'riff';
|
|
117
|
+
case '.wv':
|
|
118
|
+
case '.wvp':
|
|
119
|
+
return 'wavpack';
|
|
120
|
+
case '.mpc':
|
|
121
|
+
return 'musepack';
|
|
122
|
+
case '.dsf':
|
|
123
|
+
return 'dsf';
|
|
124
|
+
case '.dff':
|
|
125
|
+
return 'dsdiff';
|
|
126
|
+
case '.mka':
|
|
127
|
+
case '.mkv':
|
|
128
|
+
case '.mk3d':
|
|
129
|
+
case '.mks':
|
|
130
|
+
case '.webm':
|
|
131
|
+
return 'matroska';
|
|
138
132
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
133
|
+
}
|
|
134
|
+
export async function loadParser(moduleName) {
|
|
135
|
+
switch (moduleName) {
|
|
136
|
+
case 'aiff': return new AIFFParser();
|
|
137
|
+
case 'adts':
|
|
138
|
+
case 'mpeg':
|
|
139
|
+
return new MpegParser();
|
|
140
|
+
case 'apev2': return new APEv2Parser();
|
|
141
|
+
case 'asf': return new AsfParser();
|
|
142
|
+
case 'dsf': return new DsfParser();
|
|
143
|
+
case 'dsdiff': return new DsdiffParser();
|
|
144
|
+
case 'flac': return new FlacParser();
|
|
145
|
+
case 'mp4': return new MP4Parser();
|
|
146
|
+
case 'musepack': return new MusepackParser();
|
|
147
|
+
case 'ogg': return new OggParser();
|
|
148
|
+
case 'riff': return new WaveParser();
|
|
149
|
+
case 'wavpack': return new WavPackParser();
|
|
150
|
+
case 'matroska': return new MatroskaParser();
|
|
151
|
+
default:
|
|
152
|
+
throw new Error(`Unknown parser type: ${moduleName}`);
|
|
159
153
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
154
|
+
}
|
|
155
|
+
function getExtension(fname) {
|
|
156
|
+
const i = fname.lastIndexOf('.');
|
|
157
|
+
return i === -1 ? '' : fname.slice(i);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* @param httpContentType - HTTP Content-Type, extension, path or filename
|
|
161
|
+
* @returns Parser submodule name
|
|
162
|
+
*/
|
|
163
|
+
function getParserIdForMimeType(httpContentType) {
|
|
164
|
+
let mime;
|
|
165
|
+
if (!httpContentType)
|
|
166
|
+
return;
|
|
167
|
+
try {
|
|
168
|
+
mime = parseHttpContentType(httpContentType);
|
|
163
169
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
break;
|
|
237
|
-
case 'application':
|
|
238
|
-
switch (subType) {
|
|
239
|
-
case 'vnd.ms-asf':
|
|
240
|
-
return 'asf';
|
|
241
|
-
case 'ogg':
|
|
242
|
-
return 'ogg';
|
|
243
|
-
}
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
debug(`Invalid HTTP Content-Type header value: ${httpContentType}`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const subType = mime.subtype.indexOf('x-') === 0 ? mime.subtype.substring(2) : mime.subtype;
|
|
175
|
+
switch (mime.type) {
|
|
176
|
+
case 'audio':
|
|
177
|
+
switch (subType) {
|
|
178
|
+
case 'mp3': // Incorrect MIME-type, Chrome, in Web API File object
|
|
179
|
+
case 'mpeg':
|
|
180
|
+
return 'mpeg';
|
|
181
|
+
case 'aac':
|
|
182
|
+
case 'aacp':
|
|
183
|
+
return 'adts';
|
|
184
|
+
case 'flac':
|
|
185
|
+
return 'flac';
|
|
186
|
+
case 'ape':
|
|
187
|
+
case 'monkeys-audio':
|
|
188
|
+
return 'apev2';
|
|
189
|
+
case 'mp4':
|
|
190
|
+
case 'm4a':
|
|
191
|
+
return 'mp4';
|
|
192
|
+
case 'ogg': // RFC 7845
|
|
193
|
+
case 'opus': // RFC 6716
|
|
194
|
+
case 'speex': // RFC 5574
|
|
195
|
+
return 'ogg';
|
|
196
|
+
case 'ms-wma':
|
|
197
|
+
case 'ms-wmv':
|
|
198
|
+
case 'ms-asf':
|
|
199
|
+
return 'asf';
|
|
200
|
+
case 'aiff':
|
|
201
|
+
case 'aif':
|
|
202
|
+
case 'aifc':
|
|
203
|
+
return 'aiff';
|
|
204
|
+
case 'vnd.wave':
|
|
205
|
+
case 'wav':
|
|
206
|
+
case 'wave':
|
|
207
|
+
return 'riff';
|
|
208
|
+
case 'wavpack':
|
|
209
|
+
return 'wavpack';
|
|
210
|
+
case 'musepack':
|
|
211
|
+
return 'musepack';
|
|
212
|
+
case 'matroska':
|
|
213
|
+
case 'webm':
|
|
214
|
+
return 'matroska';
|
|
215
|
+
case 'dsf':
|
|
216
|
+
return 'dsf';
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
case 'video':
|
|
220
|
+
switch (subType) {
|
|
221
|
+
case 'ms-asf':
|
|
222
|
+
case 'ms-wmv':
|
|
223
|
+
return 'asf';
|
|
224
|
+
case 'm4v':
|
|
225
|
+
case 'mp4':
|
|
226
|
+
return 'mp4';
|
|
227
|
+
case 'ogg':
|
|
228
|
+
return 'ogg';
|
|
229
|
+
case 'matroska':
|
|
230
|
+
case 'webm':
|
|
231
|
+
return 'matroska';
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case 'application':
|
|
235
|
+
switch (subType) {
|
|
236
|
+
case 'vnd.ms-asf':
|
|
237
|
+
return 'asf';
|
|
238
|
+
case 'ogg':
|
|
239
|
+
return 'ogg';
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
246
242
|
}
|
|
247
243
|
}
|
|
244
|
+
//# sourceMappingURL=ParserFactory.js.map
|
package/lib/aiff/AiffParser.js
CHANGED
|
@@ -6,18 +6,8 @@ import { FourCcToken } from '../common/FourCC.js';
|
|
|
6
6
|
import { BasicParser } from '../common/BasicParser.js';
|
|
7
7
|
import * as AiffToken from './AiffToken.js';
|
|
8
8
|
import * as iff from '../iff/index.js';
|
|
9
|
+
import { compressionTypes } from './AiffToken.js';
|
|
9
10
|
const debug = initDebug('music-metadata:parser:aiff');
|
|
10
|
-
const compressionTypes = {
|
|
11
|
-
NONE: 'not compressed PCM Apple Computer',
|
|
12
|
-
sowt: 'PCM (byte swapped)',
|
|
13
|
-
fl32: '32-bit floating point IEEE 32-bit float',
|
|
14
|
-
fl64: '64-bit floating point IEEE 64-bit float Apple Computer',
|
|
15
|
-
alaw: 'ALaw 2:1 8-bit ITU-T G.711 A-law',
|
|
16
|
-
ulaw: 'µLaw 2:1 8-bit ITU-T G.711 µ-law Apple Computer',
|
|
17
|
-
ULAW: 'CCITT G.711 u-law 8-bit ITU-T G.711 µ-law',
|
|
18
|
-
ALAW: 'CCITT G.711 A-law 8-bit ITU-T G.711 A-law',
|
|
19
|
-
FL32: 'Float 32 IEEE 32-bit float '
|
|
20
|
-
};
|
|
21
11
|
/**
|
|
22
12
|
* AIFF - Audio Interchange File Format
|
|
23
13
|
*
|
|
@@ -26,6 +16,10 @@ const compressionTypes = {
|
|
|
26
16
|
* - http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/Docs/AIFF-1.3.pdf
|
|
27
17
|
*/
|
|
28
18
|
export class AIFFParser extends BasicParser {
|
|
19
|
+
constructor() {
|
|
20
|
+
super(...arguments);
|
|
21
|
+
this.isCompressed = null;
|
|
22
|
+
}
|
|
29
23
|
async parse() {
|
|
30
24
|
const header = await this.tokenizer.readToken(iff.Header);
|
|
31
25
|
if (header.chunkID !== 'FORM')
|
|
@@ -41,12 +35,12 @@ export class AIFFParser extends BasicParser {
|
|
|
41
35
|
this.isCompressed = true;
|
|
42
36
|
break;
|
|
43
37
|
default:
|
|
44
|
-
throw Error(
|
|
38
|
+
throw new Error(`Unsupported AIFF type: ${type}`);
|
|
45
39
|
}
|
|
46
40
|
this.metadata.setFormat('lossless', !this.isCompressed);
|
|
47
41
|
try {
|
|
48
42
|
while (!this.tokenizer.fileInfo.size || this.tokenizer.fileInfo.size - this.tokenizer.position >= iff.Header.len) {
|
|
49
|
-
debug(
|
|
43
|
+
debug(`Reading AIFF chunk at offset=${this.tokenizer.position}`);
|
|
50
44
|
const chunkHeader = await this.tokenizer.readToken(iff.Header);
|
|
51
45
|
const nextChunk = 2 * Math.round(chunkHeader.chunkSize / 2);
|
|
52
46
|
const bytesRead = await this.readData(chunkHeader);
|
|
@@ -55,7 +49,7 @@ export class AIFFParser extends BasicParser {
|
|
|
55
49
|
}
|
|
56
50
|
catch (err) {
|
|
57
51
|
if (err instanceof strtok3.EndOfStreamError) {
|
|
58
|
-
debug(
|
|
52
|
+
debug("End-of-stream");
|
|
59
53
|
}
|
|
60
54
|
else {
|
|
61
55
|
throw err;
|
|
@@ -63,22 +57,28 @@ export class AIFFParser extends BasicParser {
|
|
|
63
57
|
}
|
|
64
58
|
}
|
|
65
59
|
async readData(header) {
|
|
66
|
-
var _a;
|
|
67
60
|
switch (header.chunkID) {
|
|
68
|
-
case 'COMM': // The Common Chunk
|
|
61
|
+
case 'COMM': { // The Common Chunk
|
|
62
|
+
if (this.isCompressed === null) {
|
|
63
|
+
throw new Error('Failed to parse AIFF.COMM chunk when compression type is unknown');
|
|
64
|
+
}
|
|
69
65
|
const common = await this.tokenizer.readToken(new AiffToken.Common(header, this.isCompressed));
|
|
70
66
|
this.metadata.setFormat('bitsPerSample', common.sampleSize);
|
|
71
67
|
this.metadata.setFormat('sampleRate', common.sampleRate);
|
|
72
68
|
this.metadata.setFormat('numberOfChannels', common.numChannels);
|
|
73
69
|
this.metadata.setFormat('numberOfSamples', common.numSampleFrames);
|
|
74
70
|
this.metadata.setFormat('duration', common.numSampleFrames / common.sampleRate);
|
|
75
|
-
|
|
71
|
+
if (common.compressionName || common.compressionType) {
|
|
72
|
+
this.metadata.setFormat('codec', common.compressionName ?? compressionTypes[common.compressionType]);
|
|
73
|
+
}
|
|
76
74
|
return header.chunkSize;
|
|
77
|
-
|
|
75
|
+
}
|
|
76
|
+
case 'ID3 ': { // ID3-meta-data
|
|
78
77
|
const id3_data = await this.tokenizer.readToken(new Token.Uint8ArrayType(header.chunkSize));
|
|
79
78
|
const rst = strtok3.fromBuffer(id3_data);
|
|
80
79
|
await new ID3v2Parser().parse(this.metadata, rst, this.options);
|
|
81
80
|
return header.chunkSize;
|
|
81
|
+
}
|
|
82
82
|
case 'SSND': // Sound Data Chunk
|
|
83
83
|
if (this.metadata.format.duration) {
|
|
84
84
|
this.metadata.setFormat('bitrate', 8 * header.chunkSize / this.metadata.format.duration);
|
|
@@ -96,8 +96,9 @@ export class AIFFParser extends BasicParser {
|
|
|
96
96
|
}
|
|
97
97
|
async readTextChunk(header) {
|
|
98
98
|
const value = await this.tokenizer.readToken(new Token.StringType(header.chunkSize, 'ascii'));
|
|
99
|
-
const values = value.split('\0').map(v => v.trim()).filter(v => v
|
|
99
|
+
const values = value.split('\0').map(v => v.trim()).filter(v => v?.length);
|
|
100
100
|
await Promise.all(values.map(v => this.metadata.addTag('AIFF', header.chunkID, v)));
|
|
101
101
|
return header.chunkSize;
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
//# sourceMappingURL=AiffParser.js.map
|
package/lib/aiff/AiffToken.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import * as iff from '../iff/index.js';
|
|
1
|
+
import type * as iff from '../iff/index.js';
|
|
2
2
|
import type { IGetToken } from 'strtok3';
|
|
3
|
+
export declare const compressionTypes: {
|
|
4
|
+
NONE: string;
|
|
5
|
+
sowt: string;
|
|
6
|
+
fl32: string;
|
|
7
|
+
fl64: string;
|
|
8
|
+
alaw: string;
|
|
9
|
+
ulaw: string;
|
|
10
|
+
ULAW: string;
|
|
11
|
+
ALAW: string;
|
|
12
|
+
FL32: string;
|
|
13
|
+
};
|
|
14
|
+
export type CompressionTypeCode = keyof typeof compressionTypes;
|
|
3
15
|
/**
|
|
4
16
|
* The Common Chunk.
|
|
5
17
|
* Describes fundamental parameters of the waveform data such as sample rate, bit resolution, and how many channels of
|
|
@@ -10,7 +22,7 @@ export interface ICommon {
|
|
|
10
22
|
numSampleFrames: number;
|
|
11
23
|
sampleSize: number;
|
|
12
24
|
sampleRate: number;
|
|
13
|
-
compressionType?:
|
|
25
|
+
compressionType?: CompressionTypeCode;
|
|
14
26
|
compressionName?: string;
|
|
15
27
|
}
|
|
16
28
|
export declare class Common implements IGetToken<ICommon> {
|
package/lib/aiff/AiffToken.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import * as Token from 'token-types';
|
|
2
2
|
import { FourCcToken } from '../common/FourCC.js';
|
|
3
|
+
export const compressionTypes = {
|
|
4
|
+
NONE: 'not compressed PCM Apple Computer',
|
|
5
|
+
sowt: 'PCM (byte swapped)',
|
|
6
|
+
fl32: '32-bit floating point IEEE 32-bit float',
|
|
7
|
+
fl64: '64-bit floating point IEEE 64-bit float Apple Computer',
|
|
8
|
+
alaw: 'ALaw 2:1 8-bit ITU-T G.711 A-law',
|
|
9
|
+
ulaw: 'µLaw 2:1 8-bit ITU-T G.711 µ-law Apple Computer',
|
|
10
|
+
ULAW: 'CCITT G.711 u-law 8-bit ITU-T G.711 µ-law',
|
|
11
|
+
ALAW: 'CCITT G.711 A-law 8-bit ITU-T G.711 A-law',
|
|
12
|
+
FL32: 'Float 32 IEEE 32-bit float '
|
|
13
|
+
};
|
|
3
14
|
export class Common {
|
|
4
15
|
constructor(header, isAifc) {
|
|
5
16
|
this.isAifc = isAifc;
|
|
@@ -42,3 +53,4 @@ export class Common {
|
|
|
42
53
|
return res;
|
|
43
54
|
}
|
|
44
55
|
}
|
|
56
|
+
//# sourceMappingURL=AiffToken.js.map
|