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.
Files changed (63) hide show
  1. package/README.md +465 -141
  2. package/lib/ParseError.d.ts +87 -0
  3. package/lib/ParseError.js +39 -0
  4. package/lib/ParserFactory.d.ts +1 -1
  5. package/lib/ParserFactory.js +9 -8
  6. package/lib/aiff/AiffParser.js +6 -7
  7. package/lib/aiff/AiffToken.d.ts +15 -0
  8. package/lib/aiff/AiffToken.js +5 -2
  9. package/lib/apev2/APEv2Parser.d.ts +15 -0
  10. package/lib/apev2/APEv2Parser.js +6 -3
  11. package/lib/asf/AsfObject.d.ts +15 -0
  12. package/lib/asf/AsfObject.js +4 -1
  13. package/lib/asf/AsfParser.js +2 -1
  14. package/lib/common/CombinedTagMapper.js +2 -1
  15. package/lib/common/FourCC.js +3 -2
  16. package/lib/common/MetadataCollector.js +2 -4
  17. package/lib/common/Util.js +3 -2
  18. package/lib/core.d.ts +2 -1
  19. package/lib/core.js +1 -1
  20. package/lib/default.cjs +5 -0
  21. package/lib/dsdiff/DsdiffParser.d.ts +15 -0
  22. package/lib/dsdiff/DsdiffParser.js +6 -3
  23. package/lib/dsf/DsfParser.d.ts +15 -0
  24. package/lib/dsf/DsfParser.js +4 -1
  25. package/lib/ebml/EbmlIterator.d.ts +67 -0
  26. package/lib/ebml/EbmlIterator.js +223 -0
  27. package/lib/ebml/types.d.ts +36 -0
  28. package/lib/ebml/types.js +10 -0
  29. package/lib/flac/FlacParser.js +5 -2
  30. package/lib/id3v2/FrameParser.d.ts +14 -0
  31. package/lib/id3v2/FrameParser.js +7 -1
  32. package/lib/id3v2/ID3v2Parser.js +10 -8
  33. package/lib/matroska/MatroskaDtd.d.ts +2 -2
  34. package/lib/matroska/MatroskaDtd.js +246 -239
  35. package/lib/matroska/MatroskaParser.d.ts +8 -22
  36. package/lib/matroska/MatroskaParser.js +119 -204
  37. package/lib/matroska/types.d.ts +8 -44
  38. package/lib/matroska/types.js +0 -9
  39. package/lib/mp4/AtomToken.d.ts +14 -0
  40. package/lib/mp4/AtomToken.js +6 -3
  41. package/lib/mp4/MP4Parser.js +4 -3
  42. package/lib/mpeg/MpegParser.d.ts +15 -0
  43. package/lib/mpeg/MpegParser.js +7 -4
  44. package/lib/musepack/MusepackConentError.d.ts +15 -0
  45. package/lib/musepack/MusepackConentError.js +4 -0
  46. package/lib/musepack/index.js +4 -3
  47. package/lib/musepack/sv7/MpcSv7Parser.js +2 -1
  48. package/lib/musepack/sv8/MpcSv8Parser.js +3 -2
  49. package/lib/node.cjs +5 -0
  50. package/lib/ogg/OggParser.d.ts +15 -0
  51. package/lib/ogg/OggParser.js +5 -2
  52. package/lib/ogg/opus/Opus.d.ts +15 -0
  53. package/lib/ogg/opus/Opus.js +4 -1
  54. package/lib/ogg/opus/OpusParser.js +2 -1
  55. package/lib/ogg/vorbis/VorbisParser.d.ts +15 -0
  56. package/lib/ogg/vorbis/VorbisParser.js +6 -3
  57. package/lib/type.d.ts +9 -0
  58. package/lib/wav/WaveChunk.d.ts +15 -0
  59. package/lib/wav/WaveChunk.js +5 -2
  60. package/lib/wav/WaveParser.js +3 -2
  61. package/lib/wavpack/WavPackParser.d.ts +15 -0
  62. package/lib/wavpack/WavPackParser.js +6 -3
  63. package/package.json +20 -11
@@ -1,10 +1,8 @@
1
- import { Float32_BE, Float64_BE, StringType, UINT8 } from 'token-types';
2
1
  import initDebug from 'debug';
3
- import { EndOfStreamError } from 'strtok3';
4
2
  import { BasicParser } from '../common/BasicParser.js';
5
- import * as matroskaDtd from './MatroskaDtd.js';
6
- import { DataType, TargetType, TrackType } from './types.js';
7
- import * as Token from 'token-types';
3
+ import { matroskaDtd } from './MatroskaDtd.js';
4
+ import { TargetType, TrackType } from './types.js';
5
+ import { EbmlIterator, ParseAction } from '../ebml/EbmlIterator.js';
8
6
  const debug = initDebug('music-metadata:parser:matroska');
9
7
  /**
10
8
  * Extensible Binary Meta Language (EBML) parser
@@ -15,17 +13,13 @@ const debug = initDebug('music-metadata:parser:matroska');
15
13
  */
16
14
  export class MatroskaParser extends BasicParser {
17
15
  constructor() {
18
- super();
19
- this.padding = 0;
20
- this.parserMap = new Map();
21
- this.ebmlMaxIDLength = 4;
22
- this.ebmlMaxSizeLength = 8;
23
- this.parserMap.set(DataType.uint, e => this.readUint(e));
24
- this.parserMap.set(DataType.string, e => this.readString(e));
25
- this.parserMap.set(DataType.binary, e => this.readBuffer(e));
26
- this.parserMap.set(DataType.uid, async (e) => this.readBuffer(e));
27
- this.parserMap.set(DataType.bool, e => this.readFlag(e));
28
- this.parserMap.set(DataType.float, e => this.readFloat(e));
16
+ super(...arguments);
17
+ this.seekHeadOffset = 0;
18
+ /**
19
+ * Use index to skip multiple segment/cluster elements at once.
20
+ * Significant performance impact
21
+ */
22
+ this.flagUseIndexToSkipClusters = false;
29
23
  }
30
24
  /**
31
25
  * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
@@ -35,206 +29,127 @@ export class MatroskaParser extends BasicParser {
35
29
  */
36
30
  init(metadata, tokenizer, options) {
37
31
  super.init(metadata, tokenizer, options);
32
+ this.flagUseIndexToSkipClusters = options.mkvUseIndex ?? false;
38
33
  return this;
39
34
  }
40
35
  async parse() {
41
- var _a;
42
- const containerSize = (_a = this.tokenizer.fileInfo.size) !== null && _a !== void 0 ? _a : Number.MAX_SAFE_INTEGER;
43
- const matroska = await this.parseContainer(matroskaDtd.elements, containerSize, []);
44
- this.metadata.setFormat('container', `EBML/${matroska.ebml.docType}`);
45
- if (matroska.segment) {
46
- const info = matroska.segment.info;
47
- if (info) {
48
- const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
49
- if (typeof info.duration === 'number') {
50
- const duration = info.duration * timecodeScale / 1000000000;
51
- await this.addTag('segment:title', info.title);
52
- this.metadata.setFormat('duration', Number(duration));
53
- }
54
- }
55
- const audioTracks = matroska.segment.tracks;
56
- if (audioTracks === null || audioTracks === void 0 ? void 0 : audioTracks.entries) {
57
- audioTracks.entries.forEach(entry => {
58
- const stream = {
59
- codecName: entry.codecID.replace('A_', '').replace('V_', ''),
60
- codecSettings: entry.codecSettings,
61
- flagDefault: entry.flagDefault,
62
- flagLacing: entry.flagLacing,
63
- flagEnabled: entry.flagEnabled,
64
- language: entry.language,
65
- name: entry.name,
66
- type: entry.trackType,
67
- audio: entry.audio,
68
- video: entry.video
69
- };
70
- this.metadata.addStreamInfo(stream);
71
- });
72
- const audioTrack = audioTracks.entries
73
- .filter(entry => entry.trackType === TrackType.audio)
74
- .reduce((acc, cur) => {
75
- if (!acc)
76
- return cur;
77
- if (cur.flagDefault && !acc.flagDefault)
78
- return cur;
79
- if (cur.trackNumber < acc.trackNumber)
80
- return cur;
81
- return acc;
82
- }, null);
83
- if (audioTrack) {
84
- this.metadata.setFormat('codec', audioTrack.codecID.replace('A_', ''));
85
- this.metadata.setFormat('sampleRate', audioTrack.audio.samplingFrequency);
86
- this.metadata.setFormat('numberOfChannels', audioTrack.audio.channels);
87
- }
88
- if (matroska.segment.tags) {
89
- await Promise.all(matroska.segment.tags.tag.map(async (tag) => {
90
- const target = tag.target;
91
- const targetType = (target === null || target === void 0 ? void 0 : target.targetTypeValue) ? TargetType[target.targetTypeValue] : ((target === null || target === void 0 ? void 0 : target.targetType) ? target.targetType : 'track');
92
- await Promise.all(tag.simpleTags.map(async (simpleTag) => {
93
- const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
94
- await this.addTag(`${targetType}:${simpleTag.name}`, value);
95
- }));
96
- }));
97
- }
98
- if (matroska.segment.attachments) {
99
- await Promise.all(matroska.segment.attachments.attachedFiles
100
- .filter(file => file.mimeType.startsWith('image/'))
101
- .map(file => this.addTag('picture', {
102
- data: file.data,
103
- format: file.mimeType,
104
- description: file.description,
105
- name: file.name
106
- })));
107
- }
108
- }
109
- }
110
- }
111
- async parseContainer(container, posDone, path) {
112
- const tree = {};
113
- while (this.tokenizer.position < posDone) {
114
- let element;
115
- try {
116
- element = await this.readElement();
117
- }
118
- catch (error) {
119
- if (error instanceof EndOfStreamError) {
120
- break;
121
- }
122
- throw error;
123
- }
124
- const type = container[element.id];
125
- if (type) {
126
- debug(`Element: name=${type.name}, container=${!!type.container}`);
127
- if (type.container) {
128
- const res = await this.parseContainer(type.container, element.len >= 0 ? this.tokenizer.position + element.len : -1, path.concat([type.name]));
129
- if (type.multiple) {
130
- if (!tree[type.name]) {
131
- tree[type.name] = [];
36
+ const containerSize = this.tokenizer.fileInfo.size ?? Number.MAX_SAFE_INTEGER;
37
+ const matroskaIterator = new EbmlIterator(this.tokenizer);
38
+ debug('Initializing DTD end MatroskaIterator');
39
+ await matroskaIterator.iterate(matroskaDtd, containerSize, {
40
+ startNext: (element) => {
41
+ switch (element.id) {
42
+ // case 0x1f43b675: // cluster
43
+ case 0x1c53bb6b: // Cueing Data
44
+ debug(`Skip element: name=${element.name}, id=0x${element.id.toString(16)}`);
45
+ return ParseAction.IgnoreElement;
46
+ case 0x1f43b675: // cluster
47
+ if (this.flagUseIndexToSkipClusters && this.seekHead) {
48
+ const index = this.seekHead.seek.find(index => index.position + this.seekHeadOffset > this.tokenizer.position);
49
+ if (index) {
50
+ // Go to next index position
51
+ const ignoreSize = index.position + this.seekHeadOffset - this.tokenizer.position;
52
+ debug(`Use index to go to next position, ignoring ${ignoreSize} bytes`);
53
+ this.tokenizer.ignore(ignoreSize);
54
+ return ParseAction.SkipElement;
55
+ }
132
56
  }
133
- tree[type.name].push(res);
134
- }
135
- else {
136
- tree[type.name] = res;
137
- }
138
- }
139
- else {
140
- const parser = this.parserMap.get(type.value);
141
- if (typeof parser === 'function') {
142
- tree[type.name] = await parser(element);
143
- }
57
+ return ParseAction.IgnoreElement;
58
+ default:
59
+ return ParseAction.ReadNext;
144
60
  }
145
- }
146
- else {
61
+ },
62
+ elementValue: async (element, value, offset) => {
63
+ debug(`Received: name=${element.name}, value=${value}`);
147
64
  switch (element.id) {
148
- case 0xec: // void
149
- this.padding += element.len;
150
- await this.tokenizer.ignore(element.len);
65
+ case 0x4282: // docType
66
+ this.metadata.setFormat('container', `EBML/${value}`);
67
+ break;
68
+ case 0x114d9b74:
69
+ this.seekHead = value;
70
+ this.seekHeadOffset = offset;
71
+ break;
72
+ case 0x1549a966:
73
+ { // Info (Segment Information)
74
+ const info = value;
75
+ const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
76
+ if (typeof info.duration === 'number') {
77
+ const duration = info.duration * timecodeScale / 1000000000;
78
+ await this.addTag('segment:title', info.title);
79
+ this.metadata.setFormat('duration', Number(duration));
80
+ }
81
+ }
82
+ break;
83
+ case 0x1654ae6b:
84
+ { // tracks
85
+ const audioTracks = value;
86
+ if (audioTracks?.entries) {
87
+ audioTracks.entries.forEach(entry => {
88
+ const stream = {
89
+ codecName: entry.codecID.replace('A_', '').replace('V_', ''),
90
+ codecSettings: entry.codecSettings,
91
+ flagDefault: entry.flagDefault,
92
+ flagLacing: entry.flagLacing,
93
+ flagEnabled: entry.flagEnabled,
94
+ language: entry.language,
95
+ name: entry.name,
96
+ type: entry.trackType,
97
+ audio: entry.audio,
98
+ video: entry.video
99
+ };
100
+ this.metadata.addStreamInfo(stream);
101
+ });
102
+ const audioTrack = audioTracks.entries
103
+ .filter(entry => entry.trackType === TrackType.audio)
104
+ .reduce((acc, cur) => {
105
+ if (!acc)
106
+ return cur;
107
+ if (cur.flagDefault && !acc.flagDefault)
108
+ return cur;
109
+ if (cur.trackNumber < acc.trackNumber)
110
+ return cur;
111
+ return acc;
112
+ }, null);
113
+ if (audioTrack) {
114
+ this.metadata.setFormat('codec', audioTrack.codecID.replace('A_', ''));
115
+ this.metadata.setFormat('sampleRate', audioTrack.audio.samplingFrequency);
116
+ this.metadata.setFormat('numberOfChannels', audioTrack.audio.channels);
117
+ }
118
+ }
119
+ }
120
+ break;
121
+ case 0x1254c367:
122
+ { // tags
123
+ const tags = value;
124
+ await Promise.all(tags.tag.map(async (tag) => {
125
+ const target = tag.target;
126
+ const targetType = target?.targetTypeValue ? TargetType[target.targetTypeValue] : (target?.targetType ? target.targetType : 'track');
127
+ await Promise.all(tag.simpleTags.map(async (simpleTag) => {
128
+ const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
129
+ await this.addTag(`${targetType}:${simpleTag.name}`, value);
130
+ }));
131
+ }));
132
+ }
133
+ break;
134
+ case 0x1941a469:
135
+ { // attachments
136
+ const attachments = value;
137
+ await Promise.all(attachments.attachedFiles
138
+ .filter(file => file.mimeType.startsWith('image/'))
139
+ .map(file => this.addTag('picture', {
140
+ data: file.data,
141
+ format: file.mimeType,
142
+ description: file.description,
143
+ name: file.name
144
+ })));
145
+ }
151
146
  break;
152
- default:
153
- debug(`parseEbml: path=${path.join('/')}, unknown element: id=${element.id.toString(16)}`);
154
- this.padding += element.len;
155
- await this.tokenizer.ignore(element.len);
156
147
  }
157
148
  }
158
- }
159
- return tree;
160
- }
161
- async readVintData(maxLength) {
162
- const msb = await this.tokenizer.peekNumber(UINT8);
163
- let mask = 0x80;
164
- let oc = 1;
165
- // Calculate VINT_WIDTH
166
- while ((msb & mask) === 0) {
167
- if (oc > maxLength) {
168
- throw new Error('VINT value exceeding maximum size');
169
- }
170
- ++oc;
171
- mask >>= 1;
172
- }
173
- const id = new Uint8Array(oc);
174
- await this.tokenizer.readBuffer(id);
175
- return id;
176
- }
177
- async readElement() {
178
- const id = await this.readVintData(this.ebmlMaxIDLength);
179
- const lenField = await this.readVintData(this.ebmlMaxSizeLength);
180
- lenField[0] ^= 0x80 >> (lenField.length - 1);
181
- return {
182
- id: MatroskaParser.readUIntBE(id, id.length),
183
- len: MatroskaParser.readUIntBE(lenField, lenField.length)
184
- };
185
- }
186
- async readFloat(e) {
187
- switch (e.len) {
188
- case 0:
189
- return 0.0;
190
- case 4:
191
- return this.tokenizer.readNumber(Float32_BE);
192
- case 8:
193
- return this.tokenizer.readNumber(Float64_BE);
194
- case 10:
195
- return this.tokenizer.readNumber(Float64_BE);
196
- default:
197
- throw new Error(`Invalid IEEE-754 float length: ${e.len}`);
198
- }
199
- }
200
- async readFlag(e) {
201
- return (await this.readUint(e)) === 1;
202
- }
203
- async readUint(e) {
204
- const buf = await this.readBuffer(e);
205
- return MatroskaParser.readUIntBE(buf, e.len);
206
- }
207
- async readString(e) {
208
- const rawString = await this.tokenizer.readToken(new StringType(e.len, 'utf-8'));
209
- return rawString.replace(/\x00.*$/g, '');
210
- }
211
- async readBuffer(e) {
212
- const buf = new Uint8Array(e.len);
213
- await this.tokenizer.readBuffer(buf);
214
- return buf;
149
+ });
215
150
  }
216
151
  async addTag(tagId, value) {
217
152
  await this.metadata.addTag('matroska', tagId, value);
218
153
  }
219
- static readUIntBE(buf, len) {
220
- return Number(MatroskaParser.readUIntBeAsBigInt(buf, len));
221
- }
222
- /**
223
- * Reeds an unsigned integer from a big endian buffer of length `len`
224
- * @param buf Buffer to decode from
225
- * @param len Number of bytes
226
- * @private
227
- */
228
- static readUIntBeAsBigInt(buf, len) {
229
- const normalizedNumber = new Uint8Array(8);
230
- const cleanNumber = buf.subarray(0, len);
231
- try {
232
- normalizedNumber.set(cleanNumber, 8 - len);
233
- return Token.UINT64_BE.get(normalizedNumber, 0);
234
- }
235
- catch (error) {
236
- return BigInt(-1);
237
- }
238
- }
239
154
  }
240
155
  //# sourceMappingURL=MatroskaParser.js.map
@@ -1,34 +1,10 @@
1
- export interface IHeader {
2
- id: number;
3
- len: number;
4
- }
5
- export declare enum DataType {
6
- 'string' = 0,
7
- uint = 1,
8
- uid = 2,
9
- bool = 3,
10
- binary = 4,
11
- float = 5
12
- }
13
- export interface IElementType<T> {
14
- readonly name: string;
15
- readonly value?: DataType;
16
- readonly container?: IContainerType;
17
- readonly multiple?: boolean;
18
- }
19
- export interface IContainerType {
20
- [id: number]: IElementType<string | number | boolean | Uint8Array>;
21
- }
22
- export interface ITree {
23
- [name: string]: string | number | boolean | Uint8Array | ITree | ITree[];
24
- }
25
- export type ValueType = string | number | Uint8Array | boolean | ITree | ITree[];
26
- export interface ISeekHead {
27
- id?: Uint8Array;
28
- position?: number;
1
+ import type { IEbmlDoc } from '../ebml/types.js';
2
+ export interface ISeek {
3
+ id: Uint8Array;
4
+ position: number;
29
5
  }
30
- export interface IMetaSeekInformation {
31
- seekHeads: ISeekHead[];
6
+ export interface ISeekHead {
7
+ seek: ISeek[];
32
8
  }
33
9
  export interface ISegmentInformation {
34
10
  uid?: Uint8Array;
@@ -151,26 +127,14 @@ export interface IAttachments {
151
127
  attachedFiles: IAttachmedFile[];
152
128
  }
153
129
  export interface IMatroskaSegment {
154
- metaSeekInfo?: IMetaSeekInformation;
155
- seekHeads?: ISeekHead[];
130
+ metaSeekInfo?: ISeekHead;
131
+ seekHeads?: ISeek[];
156
132
  info?: ISegmentInformation;
157
133
  tracks?: ITrackElement;
158
134
  tags?: ITags;
159
135
  cues?: ICuePoint[];
160
136
  attachments?: IAttachments;
161
137
  }
162
- export interface IEbmlElements {
163
- version?: number;
164
- readVersion?: number;
165
- maxIDWidth?: number;
166
- maxSizeWidth?: number;
167
- docType?: string;
168
- docTypeVersion?: number;
169
- docTypeReadVersion?: number;
170
- }
171
- export interface IEbmlDoc {
172
- ebml: IEbmlElements;
173
- }
174
138
  export interface IMatroskaDoc extends IEbmlDoc {
175
139
  segment: IMatroskaSegment;
176
140
  }
@@ -1,12 +1,3 @@
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
1
  export var TargetType;
11
2
  (function (TargetType) {
12
3
  TargetType[TargetType["shot"] = 10] = "shot";
@@ -1,4 +1,18 @@
1
1
  import type { IToken, IGetToken } from 'strtok3';
2
+ declare const Mp4ContentError_base: {
3
+ new (message: string): {
4
+ readonly fileType: string;
5
+ toString(): string;
6
+ name: "UnexpectedFileContentError";
7
+ message: string;
8
+ stack?: string;
9
+ };
10
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
11
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
12
+ stackTraceLimit: number;
13
+ };
14
+ export declare class Mp4ContentError extends Mp4ContentError_base {
15
+ }
2
16
  interface IVersionAndFlags {
3
17
  /**
4
18
  * A 1-byte specification of the version
@@ -1,13 +1,16 @@
1
1
  import * as Token from 'token-types';
2
2
  import initDebug from 'debug';
3
3
  import { FourCcToken } from '../common/FourCC.js';
4
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
4
5
  const debug = initDebug('music-metadata:parser:MP4:atom');
6
+ export class Mp4ContentError extends makeUnexpectedFileContentError('MP4') {
7
+ }
5
8
  export const Header = {
6
9
  len: 8,
7
10
  get: (buf, off) => {
8
11
  const length = Token.UINT32_BE.get(buf, off);
9
12
  if (length < 0)
10
- throw new Error('Invalid atom header length');
13
+ throw new Mp4ContentError('Invalid atom header length');
11
14
  return {
12
15
  length: BigInt(length),
13
16
  name: new Token.StringType(4, 'latin1').get(buf, off + 4)
@@ -66,7 +69,7 @@ export class FixedLengthAtom {
66
69
  constructor(len, expLen, atomId) {
67
70
  this.len = len;
68
71
  if (len < expLen) {
69
- throw new Error(`Atom ${atomId} expected to be ${expLen}, but specifies ${len} bytes long.`);
72
+ throw new Mp4ContentError(`Atom ${atomId} expected to be ${expLen}, but specifies ${len} bytes long.`);
70
73
  }
71
74
  if (len > expLen) {
72
75
  debug(`Warning: atom ${atomId} expected to be ${expLen}, but was actually ${len} bytes long.`);
@@ -381,7 +384,7 @@ function readTokenTable(buf, token, off, remainingLen, numberOfEntries) {
381
384
  if (remainingLen === 0)
382
385
  return [];
383
386
  if (remainingLen !== numberOfEntries * token.len)
384
- throw new Error('mismatch number-of-entries with remaining atom-length');
387
+ throw new Mp4ContentError('mismatch number-of-entries with remaining atom-length');
385
388
  const entries = [];
386
389
  // parse offset-table
387
390
  for (let n = 0; n < numberOfEntries; ++n) {
@@ -6,6 +6,7 @@ import { Atom } from './Atom.js';
6
6
  import * as AtomToken from './AtomToken.js';
7
7
  import { TrackType } from '../type.js';
8
8
  import { uint8ArrayToHex, uint8ArrayToString } from 'uint8array-extras';
9
+ import { Mp4ContentError } from './AtomToken.js';
9
10
  const debug = initDebug('music-metadata:parser:MP4');
10
11
  const tagFormat = 'iTunes';
11
12
  const encoderDict = {
@@ -209,7 +210,7 @@ export class MP4Parser extends BasicParser {
209
210
  const integerType = (signed ? 'INT' : 'UINT') + array.length * 8 + (array.length > 1 ? '_BE' : '');
210
211
  const token = Token[integerType];
211
212
  if (!token) {
212
- throw new Error(`Token for integer type not found: "${integerType}"`);
213
+ throw new Mp4ContentError(`Token for integer type not found: "${integerType}"`);
213
214
  }
214
215
  return Number(token.get(array, 0));
215
216
  }
@@ -361,7 +362,7 @@ export class MP4Parser extends BasicParser {
361
362
  async parseValueAtom(tagKey, metaAtom) {
362
363
  const dataAtom = await this.tokenizer.readToken(new AtomToken.DataAtom(Number(metaAtom.header.length) - AtomToken.Header.len));
363
364
  if (dataAtom.type.set !== 0) {
364
- throw new Error(`Unsupported type-set != 0: ${dataAtom.type.set}`);
365
+ throw new Mp4ContentError(`Unsupported type-set != 0: ${dataAtom.type.set}`);
365
366
  }
366
367
  // Use well-known-type table
367
368
  // Ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35
@@ -464,7 +465,7 @@ export class MP4Parser extends BasicParser {
464
465
  const sampleSize = chapterTrack.sampleSize > 0 ? chapterTrack.sampleSize : chapterTrack.sampleSizeTable[i];
465
466
  len -= nextChunkLen + sampleSize;
466
467
  if (len < 0)
467
- throw new Error('Chapter chunk exceeding token length');
468
+ throw new Mp4ContentError('Chapter chunk exceeding token length');
468
469
  await this.tokenizer.ignore(nextChunkLen);
469
470
  const title = await this.tokenizer.readToken(new AtomToken.ChapterText(sampleSize));
470
471
  debug(`Chapter ${i + 1}: ${title}`);
@@ -1,4 +1,18 @@
1
1
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
2
+ declare const MpegContentError_base: {
3
+ new (message: string): {
4
+ readonly fileType: string;
5
+ toString(): string;
6
+ name: "UnexpectedFileContentError";
7
+ message: string;
8
+ stack?: string;
9
+ };
10
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
11
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
12
+ stackTraceLimit: number;
13
+ };
14
+ export declare class MpegContentError extends MpegContentError_base {
15
+ }
2
16
  export declare class MpegParser extends AbstractID3Parser {
3
17
  private frameCount;
4
18
  private syncFrameCount;
@@ -47,3 +61,4 @@ export declare class MpegParser extends AbstractID3Parser {
47
61
  private skipFrameData;
48
62
  private areAllSame;
49
63
  }
64
+ export {};
@@ -4,7 +4,10 @@ import initDebug from 'debug';
4
4
  import * as common from '../common/Util.js';
5
5
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
6
6
  import { InfoTagHeaderTag, LameEncoderVersion, readXingHeader } from './XingTag.js';
7
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
7
8
  const debug = initDebug('music-metadata:parser:mpeg');
9
+ export class MpegContentError extends makeUnexpectedFileContentError('MPEG') {
10
+ }
8
11
  /**
9
12
  * Cache buffer size used for searching synchronization preabmle
10
13
  */
@@ -140,13 +143,13 @@ class MpegFrameHeader {
140
143
  // Calculate bitrate
141
144
  const bitrateInKbps = this.calcBitrate();
142
145
  if (!bitrateInKbps) {
143
- throw new Error('Cannot determine bit-rate');
146
+ throw new MpegContentError('Cannot determine bit-rate');
144
147
  }
145
148
  this.bitrate = bitrateInKbps * 1000;
146
149
  // Calculate sampling rate
147
150
  this.samplingRate = this.calcSamplingRate();
148
151
  if (this.samplingRate == null) {
149
- throw new Error('Cannot determine sampling-rate');
152
+ throw new MpegContentError('Cannot determine sampling-rate');
150
153
  }
151
154
  }
152
155
  parseAdtsHeader(buf, off) {
@@ -383,7 +386,7 @@ export class MpegParser extends AbstractID3Parser {
383
386
  }
384
387
  const slot_size = header.calcSlotSize();
385
388
  if (slot_size === null) {
386
- throw new Error('invalid slot_size');
389
+ throw new MpegContentError('invalid slot_size');
387
390
  }
388
391
  const samples_per_frame = header.calcSamplesPerFrame();
389
392
  debug(`samples_per_frame=${samples_per_frame}`);
@@ -555,7 +558,7 @@ export class MpegParser extends AbstractID3Parser {
555
558
  }
556
559
  async skipFrameData(frameDataLeft) {
557
560
  if (frameDataLeft < 0)
558
- throw new Error('frame-data-left cannot be negative');
561
+ throw new MpegContentError('frame-data-left cannot be negative');
559
562
  await this.tokenizer.ignore(frameDataLeft);
560
563
  this.countSkipFrameData += frameDataLeft;
561
564
  }
@@ -0,0 +1,15 @@
1
+ declare const MusepackContentError_base: {
2
+ new (message: string): {
3
+ readonly fileType: string;
4
+ toString(): string;
5
+ name: "UnexpectedFileContentError";
6
+ message: string;
7
+ stack?: string;
8
+ };
9
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
10
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
11
+ stackTraceLimit: number;
12
+ };
13
+ export declare class MusepackContentError extends MusepackContentError_base {
14
+ }
15
+ export {};
@@ -0,0 +1,4 @@
1
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
2
+ export class MusepackContentError extends makeUnexpectedFileContentError('Musepack') {
3
+ }
4
+ //# sourceMappingURL=MusepackConentError.js.map
@@ -3,6 +3,7 @@ import * as Token from 'token-types';
3
3
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
4
4
  import { MpcSv8Parser } from './sv8/MpcSv8Parser.js';
5
5
  import { MpcSv7Parser } from './sv7/MpcSv7Parser.js';
6
+ import { MusepackContentError } from './MusepackConentError.js';
6
7
  const debug = initDebug('music-metadata:parser:musepack');
7
8
  class MusepackParser extends AbstractID3Parser {
8
9
  async postId3v2Parse() {
@@ -10,17 +11,17 @@ class MusepackParser extends AbstractID3Parser {
10
11
  let mpcParser;
11
12
  switch (signature) {
12
13
  case 'MP+': {
13
- debug('Musepack stream-version 7');
14
+ debug('Stream-version 7');
14
15
  mpcParser = new MpcSv7Parser();
15
16
  break;
16
17
  }
17
18
  case 'MPC': {
18
- debug('Musepack stream-version 8');
19
+ debug('Stream-version 8');
19
20
  mpcParser = new MpcSv8Parser();
20
21
  break;
21
22
  }
22
23
  default: {
23
- throw new Error('Invalid Musepack signature prefix');
24
+ throw new MusepackContentError('Invalid signature prefix');
24
25
  }
25
26
  }
26
27
  mpcParser.init(this.metadata, this.tokenizer, this.options);