music-metadata 10.1.0 → 10.3.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 +465 -141
- package/lib/ParseError.d.ts +87 -0
- package/lib/ParseError.js +39 -0
- package/lib/ParserFactory.d.ts +1 -1
- package/lib/ParserFactory.js +9 -8
- package/lib/aiff/AiffParser.js +6 -7
- package/lib/aiff/AiffToken.d.ts +15 -0
- package/lib/aiff/AiffToken.js +5 -2
- package/lib/apev2/APEv2Parser.d.ts +15 -0
- package/lib/apev2/APEv2Parser.js +6 -3
- package/lib/asf/AsfObject.d.ts +15 -0
- package/lib/asf/AsfObject.js +4 -1
- package/lib/asf/AsfParser.js +2 -1
- package/lib/common/CombinedTagMapper.js +2 -1
- package/lib/common/FourCC.js +3 -2
- package/lib/common/MetadataCollector.js +2 -4
- package/lib/common/Util.js +3 -2
- package/lib/core.d.ts +2 -1
- package/lib/core.js +1 -1
- package/lib/default.cjs +5 -0
- package/lib/dsdiff/DsdiffParser.d.ts +15 -0
- package/lib/dsdiff/DsdiffParser.js +6 -3
- package/lib/dsf/DsfParser.d.ts +15 -0
- package/lib/dsf/DsfParser.js +4 -1
- package/lib/ebml/EbmlIterator.d.ts +67 -0
- package/lib/ebml/EbmlIterator.js +223 -0
- package/lib/ebml/types.d.ts +36 -0
- package/lib/ebml/types.js +10 -0
- package/lib/flac/FlacParser.js +5 -2
- package/lib/id3v2/FrameParser.d.ts +14 -0
- package/lib/id3v2/FrameParser.js +7 -1
- package/lib/id3v2/ID3v2Parser.js +10 -8
- package/lib/matroska/MatroskaDtd.d.ts +2 -2
- package/lib/matroska/MatroskaDtd.js +246 -239
- package/lib/matroska/MatroskaParser.d.ts +8 -22
- package/lib/matroska/MatroskaParser.js +119 -204
- package/lib/matroska/types.d.ts +8 -44
- package/lib/matroska/types.js +0 -9
- package/lib/mp4/AtomToken.d.ts +14 -0
- package/lib/mp4/AtomToken.js +6 -3
- package/lib/mp4/MP4Parser.js +4 -3
- package/lib/mpeg/MpegParser.d.ts +15 -0
- package/lib/mpeg/MpegParser.js +7 -4
- package/lib/musepack/MusepackConentError.d.ts +15 -0
- package/lib/musepack/MusepackConentError.js +4 -0
- package/lib/musepack/index.js +4 -3
- package/lib/musepack/sv7/MpcSv7Parser.js +2 -1
- package/lib/musepack/sv8/MpcSv8Parser.js +3 -2
- package/lib/node.cjs +5 -0
- package/lib/ogg/OggParser.d.ts +15 -0
- package/lib/ogg/OggParser.js +5 -2
- package/lib/ogg/opus/Opus.d.ts +15 -0
- package/lib/ogg/opus/Opus.js +4 -1
- package/lib/ogg/opus/OpusParser.js +2 -1
- package/lib/ogg/vorbis/VorbisParser.d.ts +15 -0
- package/lib/ogg/vorbis/VorbisParser.js +6 -3
- package/lib/type.d.ts +9 -0
- package/lib/wav/WaveChunk.d.ts +15 -0
- package/lib/wav/WaveChunk.js +5 -2
- package/lib/wav/WaveParser.js +3 -2
- package/lib/wavpack/WavPackParser.d.ts +15 -0
- package/lib/wavpack/WavPackParser.js +6 -3
- package/package.json +20 -11
package/lib/dsf/DsfParser.js
CHANGED
|
@@ -2,7 +2,10 @@ import initDebug from 'debug';
|
|
|
2
2
|
import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
|
|
3
3
|
import { ChunkHeader, DsdChunk, FormatChunk } from './DsfChunk.js';
|
|
4
4
|
import { ID3v2Parser } from "../id3v2/ID3v2Parser.js";
|
|
5
|
+
import { makeUnexpectedFileContentError } from '../ParseError.js';
|
|
5
6
|
const debug = initDebug('music-metadata:parser:DSF');
|
|
7
|
+
export class DsdContentParseError extends makeUnexpectedFileContentError('DSD') {
|
|
8
|
+
}
|
|
6
9
|
/**
|
|
7
10
|
* DSF (dsd stream file) File Parser
|
|
8
11
|
* Ref: https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
|
@@ -12,7 +15,7 @@ export class DsfParser extends AbstractID3Parser {
|
|
|
12
15
|
const p0 = this.tokenizer.position; // mark start position, normally 0
|
|
13
16
|
const chunkHeader = await this.tokenizer.readToken(ChunkHeader);
|
|
14
17
|
if (chunkHeader.id !== 'DSD ')
|
|
15
|
-
throw new
|
|
18
|
+
throw new DsdContentParseError('Invalid chunk signature');
|
|
16
19
|
this.metadata.setFormat('container', 'DSF');
|
|
17
20
|
this.metadata.setFormat('lossless', true);
|
|
18
21
|
const dsdChunk = await this.tokenizer.readToken(DsdChunk);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ITokenizer } from 'strtok3';
|
|
2
|
+
import { type IElementType, type ITree, type ValueType } from './types.js';
|
|
3
|
+
declare const EbmlContentError_base: {
|
|
4
|
+
new (message: string): {
|
|
5
|
+
readonly fileType: string;
|
|
6
|
+
toString(): string;
|
|
7
|
+
name: "UnexpectedFileContentError";
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string;
|
|
10
|
+
};
|
|
11
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
12
|
+
prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
|
|
13
|
+
stackTraceLimit: number;
|
|
14
|
+
};
|
|
15
|
+
export declare class EbmlContentError extends EbmlContentError_base {
|
|
16
|
+
}
|
|
17
|
+
export interface ILinkedElementType extends IElementType {
|
|
18
|
+
id: number;
|
|
19
|
+
parent: ILinkedElementType | undefined;
|
|
20
|
+
readonly container?: {
|
|
21
|
+
[id: number]: ILinkedElementType;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export declare enum ParseAction {
|
|
25
|
+
ReadNext = 0,// Continue reading the next elements
|
|
26
|
+
IgnoreElement = 2,// Ignore (do not read) this element
|
|
27
|
+
SkipSiblings = 3,// Skip all remaining elements at the same level
|
|
28
|
+
TerminateParsing = 4,// Terminate the parsing process
|
|
29
|
+
SkipElement = 5
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @return true, to quit the parser
|
|
33
|
+
*/
|
|
34
|
+
export type IElementListener = {
|
|
35
|
+
startNext: (dtdElement: ILinkedElementType) => ParseAction;
|
|
36
|
+
elementValue: (dtdElement: ILinkedElementType, value: ValueType, offset: number) => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Extensible Binary Meta Language (EBML) iterator
|
|
40
|
+
* https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Language
|
|
41
|
+
* http://matroska.sourceforge.net/technical/specs/rfc/index.html
|
|
42
|
+
*
|
|
43
|
+
* WEBM VP8 AUDIO FILE
|
|
44
|
+
*/
|
|
45
|
+
export declare class EbmlIterator {
|
|
46
|
+
private tokenizer;
|
|
47
|
+
private padding;
|
|
48
|
+
private parserMap;
|
|
49
|
+
private ebmlMaxIDLength;
|
|
50
|
+
private ebmlMaxSizeLength;
|
|
51
|
+
/**
|
|
52
|
+
* @param {ITokenizer} tokenizer Input
|
|
53
|
+
* @param tokenizer
|
|
54
|
+
*/
|
|
55
|
+
constructor(tokenizer: ITokenizer);
|
|
56
|
+
iterate(dtdElement: IElementType, posDone: number, listener: IElementListener): Promise<ITree>;
|
|
57
|
+
private parseContainer;
|
|
58
|
+
private readVintData;
|
|
59
|
+
private readElement;
|
|
60
|
+
private readFloat;
|
|
61
|
+
private readFlag;
|
|
62
|
+
private readUint;
|
|
63
|
+
private readString;
|
|
64
|
+
private readBuffer;
|
|
65
|
+
}
|
|
66
|
+
export declare function getElementPath(element: ILinkedElementType): string;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Float32_BE, Float64_BE, StringType, UINT8 } from 'token-types';
|
|
2
|
+
import initDebug from 'debug';
|
|
3
|
+
import { EndOfStreamError } from 'strtok3';
|
|
4
|
+
import { DataType } from './types.js';
|
|
5
|
+
import * as Token from 'token-types';
|
|
6
|
+
import { makeUnexpectedFileContentError } from '../ParseError.js';
|
|
7
|
+
const debug = initDebug('music-metadata:parser:ebml');
|
|
8
|
+
export class EbmlContentError extends makeUnexpectedFileContentError('EBML') {
|
|
9
|
+
}
|
|
10
|
+
export var ParseAction;
|
|
11
|
+
(function (ParseAction) {
|
|
12
|
+
ParseAction[ParseAction["ReadNext"] = 0] = "ReadNext";
|
|
13
|
+
ParseAction[ParseAction["IgnoreElement"] = 2] = "IgnoreElement";
|
|
14
|
+
ParseAction[ParseAction["SkipSiblings"] = 3] = "SkipSiblings";
|
|
15
|
+
ParseAction[ParseAction["TerminateParsing"] = 4] = "TerminateParsing";
|
|
16
|
+
ParseAction[ParseAction["SkipElement"] = 5] = "SkipElement"; // Consider the element has read, assume position is at the next element
|
|
17
|
+
})(ParseAction || (ParseAction = {}));
|
|
18
|
+
/**
|
|
19
|
+
* Extensible Binary Meta Language (EBML) iterator
|
|
20
|
+
* https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Language
|
|
21
|
+
* http://matroska.sourceforge.net/technical/specs/rfc/index.html
|
|
22
|
+
*
|
|
23
|
+
* WEBM VP8 AUDIO FILE
|
|
24
|
+
*/
|
|
25
|
+
export class EbmlIterator {
|
|
26
|
+
/**
|
|
27
|
+
* @param {ITokenizer} tokenizer Input
|
|
28
|
+
* @param tokenizer
|
|
29
|
+
*/
|
|
30
|
+
constructor(tokenizer) {
|
|
31
|
+
this.tokenizer = tokenizer;
|
|
32
|
+
this.padding = 0;
|
|
33
|
+
this.parserMap = new Map();
|
|
34
|
+
this.ebmlMaxIDLength = 4;
|
|
35
|
+
this.ebmlMaxSizeLength = 8;
|
|
36
|
+
this.parserMap.set(DataType.uint, e => this.readUint(e));
|
|
37
|
+
this.parserMap.set(DataType.string, e => this.readString(e));
|
|
38
|
+
this.parserMap.set(DataType.binary, e => this.readBuffer(e));
|
|
39
|
+
this.parserMap.set(DataType.uid, async (e) => this.readBuffer(e));
|
|
40
|
+
this.parserMap.set(DataType.bool, e => this.readFlag(e));
|
|
41
|
+
this.parserMap.set(DataType.float, e => this.readFloat(e));
|
|
42
|
+
}
|
|
43
|
+
async iterate(dtdElement, posDone, listener) {
|
|
44
|
+
return this.parseContainer(linkParents(dtdElement), posDone, listener);
|
|
45
|
+
}
|
|
46
|
+
async parseContainer(dtdElement, posDone, listener) {
|
|
47
|
+
const tree = {};
|
|
48
|
+
while (this.tokenizer.position < posDone) {
|
|
49
|
+
let element;
|
|
50
|
+
const elementPosition = this.tokenizer.position;
|
|
51
|
+
try {
|
|
52
|
+
element = await this.readElement();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof EndOfStreamError) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
const child = dtdElement.container[element.id];
|
|
61
|
+
if (child) {
|
|
62
|
+
const action = listener.startNext(child);
|
|
63
|
+
switch (action) {
|
|
64
|
+
case ParseAction.ReadNext:
|
|
65
|
+
{
|
|
66
|
+
if (element.id === 0x1F43B675) {
|
|
67
|
+
// Hack to ignore remaining segment, when cluster element received
|
|
68
|
+
// await this.tokenizer.ignore(posDone - this.tokenizer.position);
|
|
69
|
+
// break;
|
|
70
|
+
}
|
|
71
|
+
debug(`Read element: name=${getElementPath(child)}{id=0x${element.id.toString(16)}, container=${!!child.container}} at position=${elementPosition}`);
|
|
72
|
+
if (child.container) {
|
|
73
|
+
const res = await this.parseContainer(child, element.len >= 0 ? this.tokenizer.position + element.len : -1, listener);
|
|
74
|
+
if (child.multiple) {
|
|
75
|
+
if (!tree[child.name]) {
|
|
76
|
+
tree[child.name] = [];
|
|
77
|
+
}
|
|
78
|
+
tree[child.name].push(res);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
tree[child.name] = res;
|
|
82
|
+
}
|
|
83
|
+
await listener.elementValue(child, res, elementPosition);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const parser = this.parserMap.get(child.value);
|
|
87
|
+
if (typeof parser === 'function') {
|
|
88
|
+
const value = await parser(element);
|
|
89
|
+
tree[child.name] = value;
|
|
90
|
+
await listener.elementValue(child, value, elementPosition);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
case ParseAction.SkipElement:
|
|
96
|
+
debug(`Go to next element: name=${getElementPath(child)}, element.id=0x${element.id}, container=${!!child.container} at position=${elementPosition}`);
|
|
97
|
+
break;
|
|
98
|
+
case ParseAction.IgnoreElement:
|
|
99
|
+
debug(`Ignore element: name=${getElementPath(child)}, element.id=0x${element.id}, container=${!!child.container} at position=${elementPosition}`);
|
|
100
|
+
await this.tokenizer.ignore(element.len);
|
|
101
|
+
break;
|
|
102
|
+
case ParseAction.SkipSiblings:
|
|
103
|
+
debug(`Ignore remaining container, at: name=${getElementPath(child)}, element.id=0x${element.id}, container=${!!child.container} at position=${elementPosition}`);
|
|
104
|
+
await this.tokenizer.ignore(posDone - this.tokenizer.position);
|
|
105
|
+
break;
|
|
106
|
+
case ParseAction.TerminateParsing:
|
|
107
|
+
debug(`Terminate parsing at element: name=${getElementPath(child)}, element.id=0x${element.id}, container=${!!child.container} at position=${elementPosition}`);
|
|
108
|
+
return tree;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
switch (element.id) {
|
|
113
|
+
case 0xec: // void
|
|
114
|
+
this.padding += element.len;
|
|
115
|
+
await this.tokenizer.ignore(element.len);
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
debug(`parseEbml: parent=${getElementPath(dtdElement)}, unknown child: id=${element.id.toString(16)} at position=${elementPosition}`);
|
|
119
|
+
this.padding += element.len;
|
|
120
|
+
await this.tokenizer.ignore(element.len);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return tree;
|
|
125
|
+
}
|
|
126
|
+
async readVintData(maxLength) {
|
|
127
|
+
const msb = await this.tokenizer.peekNumber(UINT8);
|
|
128
|
+
let mask = 0x80;
|
|
129
|
+
let oc = 1;
|
|
130
|
+
// Calculate VINT_WIDTH
|
|
131
|
+
while ((msb & mask) === 0) {
|
|
132
|
+
if (oc > maxLength) {
|
|
133
|
+
throw new EbmlContentError('VINT value exceeding maximum size');
|
|
134
|
+
}
|
|
135
|
+
++oc;
|
|
136
|
+
mask >>= 1;
|
|
137
|
+
}
|
|
138
|
+
const id = new Uint8Array(oc);
|
|
139
|
+
await this.tokenizer.readBuffer(id);
|
|
140
|
+
return id;
|
|
141
|
+
}
|
|
142
|
+
async readElement() {
|
|
143
|
+
const id = await this.readVintData(this.ebmlMaxIDLength);
|
|
144
|
+
const lenField = await this.readVintData(this.ebmlMaxSizeLength);
|
|
145
|
+
lenField[0] ^= 0x80 >> (lenField.length - 1);
|
|
146
|
+
return {
|
|
147
|
+
id: readUIntBE(id, id.length),
|
|
148
|
+
len: readUIntBE(lenField, lenField.length)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async readFloat(e) {
|
|
152
|
+
switch (e.len) {
|
|
153
|
+
case 0:
|
|
154
|
+
return 0.0;
|
|
155
|
+
case 4:
|
|
156
|
+
return this.tokenizer.readNumber(Float32_BE);
|
|
157
|
+
case 8:
|
|
158
|
+
return this.tokenizer.readNumber(Float64_BE);
|
|
159
|
+
case 10:
|
|
160
|
+
return this.tokenizer.readNumber(Float64_BE);
|
|
161
|
+
default:
|
|
162
|
+
throw new EbmlContentError(`Invalid IEEE-754 float length: ${e.len}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async readFlag(e) {
|
|
166
|
+
return (await this.readUint(e)) === 1;
|
|
167
|
+
}
|
|
168
|
+
async readUint(e) {
|
|
169
|
+
const buf = await this.readBuffer(e);
|
|
170
|
+
return readUIntBE(buf, e.len);
|
|
171
|
+
}
|
|
172
|
+
async readString(e) {
|
|
173
|
+
const rawString = await this.tokenizer.readToken(new StringType(e.len, 'utf-8'));
|
|
174
|
+
return rawString.replace(/\x00.*$/g, '');
|
|
175
|
+
}
|
|
176
|
+
async readBuffer(e) {
|
|
177
|
+
const buf = new Uint8Array(e.len);
|
|
178
|
+
await this.tokenizer.readBuffer(buf);
|
|
179
|
+
return buf;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function readUIntBE(buf, len) {
|
|
183
|
+
return Number(readUIntBeAsBigInt(buf, len));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Reeds an unsigned integer from a big endian buffer of length `len`
|
|
187
|
+
* @param buf Buffer to decode from
|
|
188
|
+
* @param len Number of bytes
|
|
189
|
+
* @private
|
|
190
|
+
*/
|
|
191
|
+
function readUIntBeAsBigInt(buf, len) {
|
|
192
|
+
const normalizedNumber = new Uint8Array(8);
|
|
193
|
+
const cleanNumber = buf.subarray(0, len);
|
|
194
|
+
try {
|
|
195
|
+
normalizedNumber.set(cleanNumber, 8 - len);
|
|
196
|
+
return Token.UINT64_BE.get(normalizedNumber, 0);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
return BigInt(-1);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function linkParents(element) {
|
|
203
|
+
if (element.container) {
|
|
204
|
+
Object.keys(element.container)
|
|
205
|
+
.map(id => {
|
|
206
|
+
const child = element.container[id];
|
|
207
|
+
child.id = Number.parseInt(id);
|
|
208
|
+
return child;
|
|
209
|
+
}).forEach(child => {
|
|
210
|
+
child.parent = element;
|
|
211
|
+
linkParents(child);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return element;
|
|
215
|
+
}
|
|
216
|
+
export function getElementPath(element) {
|
|
217
|
+
let path = '';
|
|
218
|
+
if (element.parent && element.parent.name !== 'dtd') {
|
|
219
|
+
path += `${getElementPath(element.parent)}/`;
|
|
220
|
+
}
|
|
221
|
+
return path + element.name;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=EbmlIterator.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface ITree {
|
|
2
|
+
[name: string]: string | number | boolean | Uint8Array | ITree | ITree[];
|
|
3
|
+
}
|
|
4
|
+
export declare enum DataType {
|
|
5
|
+
'string' = 0,
|
|
6
|
+
uint = 1,
|
|
7
|
+
uid = 2,
|
|
8
|
+
bool = 3,
|
|
9
|
+
binary = 4,
|
|
10
|
+
float = 5
|
|
11
|
+
}
|
|
12
|
+
export type ValueType = string | number | Uint8Array | boolean | ITree | ITree[];
|
|
13
|
+
export interface IHeader {
|
|
14
|
+
id: number;
|
|
15
|
+
len: number;
|
|
16
|
+
}
|
|
17
|
+
export interface IEbmlElements {
|
|
18
|
+
version?: number;
|
|
19
|
+
readVersion?: number;
|
|
20
|
+
maxIDWidth?: number;
|
|
21
|
+
maxSizeWidth?: number;
|
|
22
|
+
docType?: string;
|
|
23
|
+
docTypeVersion?: number;
|
|
24
|
+
docTypeReadVersion?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface IElementType {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly value?: DataType;
|
|
29
|
+
readonly container?: {
|
|
30
|
+
[id: number]: IElementType;
|
|
31
|
+
};
|
|
32
|
+
readonly multiple?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface IEbmlDoc {
|
|
35
|
+
ebml: IEbmlElements;
|
|
36
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export var DataType;
|
|
2
|
+
(function (DataType) {
|
|
3
|
+
DataType[DataType["string"] = 0] = "string";
|
|
4
|
+
DataType[DataType["uint"] = 1] = "uint";
|
|
5
|
+
DataType[DataType["uid"] = 2] = "uid";
|
|
6
|
+
DataType[DataType["bool"] = 3] = "bool";
|
|
7
|
+
DataType[DataType["binary"] = 4] = "binary";
|
|
8
|
+
DataType[DataType["float"] = 5] = "float";
|
|
9
|
+
})(DataType || (DataType = {}));
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
package/lib/flac/FlacParser.js
CHANGED
|
@@ -6,7 +6,10 @@ import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
|
|
|
6
6
|
import { FourCcToken } from '../common/FourCC.js';
|
|
7
7
|
import { VorbisParser } from '../ogg/vorbis/VorbisParser.js';
|
|
8
8
|
import { VorbisDecoder } from '../ogg/vorbis/VorbisDecoder.js';
|
|
9
|
+
import { makeUnexpectedFileContentError } from '../ParseError.js';
|
|
9
10
|
const debug = initDebug('music-metadata:parser:FLAC');
|
|
11
|
+
class FlacContentError extends makeUnexpectedFileContentError('FLAC') {
|
|
12
|
+
}
|
|
10
13
|
/**
|
|
11
14
|
* FLAC supports up to 128 kinds of metadata blocks; currently the following are defined:
|
|
12
15
|
* ref: https://xiph.org/flac/format.html#metadata_block
|
|
@@ -40,7 +43,7 @@ export class FlacParser extends AbstractID3Parser {
|
|
|
40
43
|
async postId3v2Parse() {
|
|
41
44
|
const fourCC = await this.tokenizer.readToken(FourCcToken);
|
|
42
45
|
if (fourCC.toString() !== 'fLaC') {
|
|
43
|
-
throw new
|
|
46
|
+
throw new FlacContentError('Invalid FLAC preamble');
|
|
44
47
|
}
|
|
45
48
|
let blockHeader;
|
|
46
49
|
do {
|
|
@@ -84,7 +87,7 @@ export class FlacParser extends AbstractID3Parser {
|
|
|
84
87
|
*/
|
|
85
88
|
async parseBlockStreamInfo(dataLen) {
|
|
86
89
|
if (dataLen !== BlockStreamInfo.len)
|
|
87
|
-
throw new
|
|
90
|
+
throw new FlacContentError('Unexpected block-stream-info length');
|
|
88
91
|
const streamInfo = await this.tokenizer.readToken(BlockStreamInfo);
|
|
89
92
|
this.metadata.setFormat('container', 'FLAC');
|
|
90
93
|
this.metadata.setFormat('codec', 'FLAC');
|
|
@@ -57,4 +57,18 @@ export declare class FrameParser {
|
|
|
57
57
|
private static readIdentifierAndData;
|
|
58
58
|
private static getNullTerminatorLength;
|
|
59
59
|
}
|
|
60
|
+
declare const Id3v2ContentError_base: {
|
|
61
|
+
new (message: string): {
|
|
62
|
+
readonly fileType: string;
|
|
63
|
+
toString(): string;
|
|
64
|
+
name: "UnexpectedFileContentError";
|
|
65
|
+
message: string;
|
|
66
|
+
stack?: string;
|
|
67
|
+
};
|
|
68
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
69
|
+
prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
|
|
70
|
+
stackTraceLimit: number;
|
|
71
|
+
};
|
|
72
|
+
export declare class Id3v2ContentError extends Id3v2ContentError_base {
|
|
73
|
+
}
|
|
60
74
|
export {};
|
package/lib/id3v2/FrameParser.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as Token from 'token-types';
|
|
|
3
3
|
import * as util from '../common/Util.js';
|
|
4
4
|
import { AttachedPictureType, SyncTextHeader, TextEncodingToken, TextHeader } from './ID3v2Token.js';
|
|
5
5
|
import { Genres } from '../id3v1/ID3v1Parser.js';
|
|
6
|
+
import { makeUnexpectedFileContentError } from '../ParseError.js';
|
|
6
7
|
const debug = initDebug('music-metadata:id3v2:frame-parser');
|
|
7
8
|
const defaultEnc = 'latin1'; // latin1 == iso-8859-1;
|
|
8
9
|
export function parseGenre(origVal) {
|
|
@@ -158,7 +159,7 @@ export class FrameParser {
|
|
|
158
159
|
offset = fzero + 1;
|
|
159
160
|
break;
|
|
160
161
|
default:
|
|
161
|
-
throw
|
|
162
|
+
throw makeUnexpectedMajorVersionError(this.major);
|
|
162
163
|
}
|
|
163
164
|
pic.format = FrameParser.fixPictureMimeType(pic.format);
|
|
164
165
|
pic.type = AttachedPictureType[uint8Array[offset]];
|
|
@@ -370,4 +371,9 @@ export class FrameParser {
|
|
|
370
371
|
return enc === 'utf-16le' ? 2 : 1;
|
|
371
372
|
}
|
|
372
373
|
}
|
|
374
|
+
export class Id3v2ContentError extends makeUnexpectedFileContentError('id3v2') {
|
|
375
|
+
}
|
|
376
|
+
function makeUnexpectedMajorVersionError(majorVer) {
|
|
377
|
+
throw new Id3v2ContentError(`Unexpected majorVer: ${majorVer}`);
|
|
378
|
+
}
|
|
373
379
|
//# sourceMappingURL=FrameParser.js.map
|
package/lib/id3v2/ID3v2Parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Token from 'token-types';
|
|
2
2
|
import * as util from '../common/Util.js';
|
|
3
|
-
import { FrameParser } from './FrameParser.js';
|
|
3
|
+
import { FrameParser, Id3v2ContentError } from './FrameParser.js';
|
|
4
4
|
import { ExtendedHeader, ID3v2Header, UINT32SYNCSAFE } from './ID3v2Token.js';
|
|
5
5
|
const asciiDecoder = new TextDecoder('ascii');
|
|
6
6
|
export class ID3v2Parser {
|
|
@@ -34,7 +34,7 @@ export class ID3v2Parser {
|
|
|
34
34
|
case 4:
|
|
35
35
|
return 10;
|
|
36
36
|
default:
|
|
37
|
-
throw
|
|
37
|
+
throw makeUnexpectedMajorVersionError(majorVer);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
static readFrameFlags(b) {
|
|
@@ -54,22 +54,21 @@ export class ID3v2Parser {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
static readFrameData(uint8Array, frameHeader, majorVer, includeCovers, warningCollector) {
|
|
57
|
-
var _a, _b;
|
|
58
57
|
const frameParser = new FrameParser(majorVer, warningCollector);
|
|
59
58
|
switch (majorVer) {
|
|
60
59
|
case 2:
|
|
61
60
|
return frameParser.readData(uint8Array, frameHeader.id, includeCovers);
|
|
62
61
|
case 3:
|
|
63
62
|
case 4:
|
|
64
|
-
if (
|
|
63
|
+
if (frameHeader.flags?.format.unsynchronisation) {
|
|
65
64
|
uint8Array = ID3v2Parser.removeUnsyncBytes(uint8Array);
|
|
66
65
|
}
|
|
67
|
-
if (
|
|
66
|
+
if (frameHeader.flags?.format.data_length_indicator) {
|
|
68
67
|
uint8Array = uint8Array.slice(4, uint8Array.length);
|
|
69
68
|
}
|
|
70
69
|
return frameParser.readData(uint8Array, frameHeader.id, includeCovers);
|
|
71
70
|
default:
|
|
72
|
-
throw
|
|
71
|
+
throw makeUnexpectedMajorVersionError(majorVer);
|
|
73
72
|
}
|
|
74
73
|
}
|
|
75
74
|
/**
|
|
@@ -87,7 +86,7 @@ export class ID3v2Parser {
|
|
|
87
86
|
this.options = options;
|
|
88
87
|
const id3Header = await this.tokenizer.readToken(ID3v2Header);
|
|
89
88
|
if (id3Header.fileIdentifier !== 'ID3') {
|
|
90
|
-
throw new
|
|
89
|
+
throw new Id3v2ContentError('expected ID3-header file-identifier \'ID3\' was not found');
|
|
91
90
|
}
|
|
92
91
|
this.id3Header = id3Header;
|
|
93
92
|
this.headerType = (`ID3v2.${id3Header.version.major}`);
|
|
@@ -169,9 +168,12 @@ export class ID3v2Parser {
|
|
|
169
168
|
}
|
|
170
169
|
break;
|
|
171
170
|
default:
|
|
172
|
-
throw
|
|
171
|
+
throw makeUnexpectedMajorVersionError(majorVer);
|
|
173
172
|
}
|
|
174
173
|
return header;
|
|
175
174
|
}
|
|
176
175
|
}
|
|
176
|
+
function makeUnexpectedMajorVersionError(majorVer) {
|
|
177
|
+
throw new Id3v2ContentError(`Unexpected majorVer: ${majorVer}`);
|
|
178
|
+
}
|
|
177
179
|
//# sourceMappingURL=ID3v2Parser.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type IElementType } from '../ebml/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Elements of document type description
|
|
4
4
|
* Derived from https://github.com/tungol/EBML/blob/master/doctypes/matroska.dtd
|
|
5
5
|
* Extended with:
|
|
6
6
|
* - https://www.matroska.org/technical/specs/index.html
|
|
7
7
|
*/
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const matroskaDtd: IElementType;
|