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
@@ -0,0 +1,87 @@
1
+ export type UnionOfParseErrors = CouldNotDetermineFileTypeError | UnsupportedFileTypeError | UnexpectedFileContentError | FieldDecodingError | InternalParserError;
2
+ export declare const makeParseError: <Name extends string>(name: Name) => {
3
+ new (message: string): {
4
+ name: Name;
5
+ message: string;
6
+ stack?: string;
7
+ };
8
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
9
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
10
+ stackTraceLimit: number;
11
+ };
12
+ declare const CouldNotDetermineFileTypeError_base: {
13
+ new (message: string): {
14
+ name: "CouldNotDetermineFileTypeError";
15
+ message: string;
16
+ stack?: string;
17
+ };
18
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
19
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
20
+ stackTraceLimit: number;
21
+ };
22
+ export declare class CouldNotDetermineFileTypeError extends CouldNotDetermineFileTypeError_base {
23
+ }
24
+ declare const UnsupportedFileTypeError_base: {
25
+ new (message: string): {
26
+ name: "UnsupportedFileTypeError";
27
+ message: string;
28
+ stack?: string;
29
+ };
30
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
31
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
32
+ stackTraceLimit: number;
33
+ };
34
+ export declare class UnsupportedFileTypeError extends UnsupportedFileTypeError_base {
35
+ }
36
+ declare const UnexpectedFileContentError_base: {
37
+ new (message: string): {
38
+ name: "UnexpectedFileContentError";
39
+ message: string;
40
+ stack?: string;
41
+ };
42
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
43
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
44
+ stackTraceLimit: number;
45
+ };
46
+ declare class UnexpectedFileContentError extends UnexpectedFileContentError_base {
47
+ readonly fileType: string;
48
+ constructor(fileType: string, message: string);
49
+ toString(): string;
50
+ }
51
+ declare const FieldDecodingError_base: {
52
+ new (message: string): {
53
+ name: "FieldDecodingError";
54
+ message: string;
55
+ stack?: string;
56
+ };
57
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
58
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
59
+ stackTraceLimit: number;
60
+ };
61
+ export declare class FieldDecodingError extends FieldDecodingError_base {
62
+ }
63
+ declare const InternalParserError_base: {
64
+ new (message: string): {
65
+ name: "InternalParserError";
66
+ message: string;
67
+ stack?: string;
68
+ };
69
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
70
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
71
+ stackTraceLimit: number;
72
+ };
73
+ export declare class InternalParserError extends InternalParserError_base {
74
+ }
75
+ export declare const makeUnexpectedFileContentError: <FileType extends string>(fileType: FileType) => {
76
+ new (message: string): {
77
+ readonly fileType: string;
78
+ toString(): string;
79
+ name: "UnexpectedFileContentError";
80
+ message: string;
81
+ stack?: string;
82
+ };
83
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
84
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
85
+ stackTraceLimit: number;
86
+ };
87
+ export {};
@@ -0,0 +1,39 @@
1
+ export const makeParseError = (name) => {
2
+ return class ParseError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = name;
6
+ }
7
+ };
8
+ };
9
+ // Concrete error class representing a file type determination failure.
10
+ export class CouldNotDetermineFileTypeError extends makeParseError('CouldNotDetermineFileTypeError') {
11
+ }
12
+ // Concrete error class representing an unsupported file type.
13
+ export class UnsupportedFileTypeError extends makeParseError('UnsupportedFileTypeError') {
14
+ }
15
+ // Concrete error class representing unexpected file content.
16
+ class UnexpectedFileContentError extends makeParseError('UnexpectedFileContentError') {
17
+ constructor(fileType, message) {
18
+ super(message);
19
+ this.fileType = fileType;
20
+ }
21
+ // Override toString to include file type information.
22
+ toString() {
23
+ return `${this.name} (FileType: ${this.fileType}): ${this.message}`;
24
+ }
25
+ }
26
+ // Concrete error class representing a field decoding error.
27
+ export class FieldDecodingError extends makeParseError('FieldDecodingError') {
28
+ }
29
+ export class InternalParserError extends makeParseError('InternalParserError') {
30
+ }
31
+ // Factory function to create a specific type of UnexpectedFileContentError.
32
+ export const makeUnexpectedFileContentError = (fileType) => {
33
+ return class extends UnexpectedFileContentError {
34
+ constructor(message) {
35
+ super(fileType, message);
36
+ }
37
+ };
38
+ };
39
+ //# sourceMappingURL=ParseError.js.map
@@ -1,6 +1,6 @@
1
1
  import { type MediaType } from 'media-typer';
2
2
  import { type INativeMetadataCollector } from './common/MetadataCollector.js';
3
- import type { IOptions, IAudioMetadata, ParserType } from './type.js';
3
+ import type { IAudioMetadata, IOptions, ParserType } from './type.js';
4
4
  import type { ITokenizer } from 'strtok3';
5
5
  export interface ITokenParser {
6
6
  /**
@@ -16,6 +16,7 @@ import { WavPackParser } from './wavpack/WavPackParser.js';
16
16
  import { DsfParser } from './dsf/DsfParser.js';
17
17
  import { DsdiffParser } from './dsdiff/DsdiffParser.js';
18
18
  import { MatroskaParser } from './matroska/MatroskaParser.js';
19
+ import { CouldNotDetermineFileTypeError, InternalParserError, UnsupportedFileTypeError } from './ParseError.js';
19
20
  const debug = initDebug('music-metadata:parser:factory');
20
21
  export function parseHttpContentType(contentType) {
21
22
  const type = ContentType.parse(contentType);
@@ -44,29 +45,29 @@ export async function parseOnContentType(tokenizer, opts) {
44
45
  }
45
46
  export async function parse(tokenizer, parserId, opts) {
46
47
  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
48
  if (tokenizer.fileInfo.path) {
52
49
  parserId = getParserIdForExtension(tokenizer.fileInfo.path);
53
50
  }
54
51
  if (!parserId) {
52
+ // Parser could not be determined on MIME-type or extension
53
+ debug('Guess parser on content...');
54
+ const buf = new Uint8Array(4100);
55
+ await tokenizer.peekBuffer(buf, { mayBeLess: true });
55
56
  const guessedType = await fileTypeFromBuffer(buf);
56
57
  if (!guessedType) {
57
- throw new Error('Failed to determine audio format');
58
+ throw new CouldNotDetermineFileTypeError('Failed to determine audio format');
58
59
  }
59
60
  debug(`Guessed file type is mime=${guessedType.mime}, extension=${guessedType.ext}`);
60
61
  parserId = getParserIdForMimeType(guessedType.mime);
61
62
  if (!parserId) {
62
- throw new Error(`Guessed MIME-type not supported: ${guessedType.mime}`);
63
+ throw new UnsupportedFileTypeError(`Guessed MIME-type not supported: ${guessedType.mime}`);
63
64
  }
64
65
  }
65
66
  }
66
67
  // Parser found, execute parser
67
68
  const parser = await loadParser(parserId);
68
69
  const metadata = new MetadataCollector(opts);
69
- await parser.init(metadata, tokenizer, opts !== null && opts !== void 0 ? opts : {}).parse();
70
+ await parser.init(metadata, tokenizer, opts ?? {}).parse();
70
71
  return metadata.toCommonMetadata();
71
72
  }
72
73
  /**
@@ -149,7 +150,7 @@ export async function loadParser(moduleName) {
149
150
  case 'wavpack': return new WavPackParser();
150
151
  case 'matroska': return new MatroskaParser();
151
152
  default:
152
- throw new Error(`Unknown parser type: ${moduleName}`);
153
+ throw new InternalParserError(`Unknown parser type: ${moduleName}`);
153
154
  }
154
155
  }
155
156
  function getExtension(fname) {
@@ -5,8 +5,8 @@ import { ID3v2Parser } from '../id3v2/ID3v2Parser.js';
5
5
  import { FourCcToken } from '../common/FourCC.js';
6
6
  import { BasicParser } from '../common/BasicParser.js';
7
7
  import * as AiffToken from './AiffToken.js';
8
+ import { AiffContentError, compressionTypes } from './AiffToken.js';
8
9
  import * as iff from '../iff/index.js';
9
- import { compressionTypes } from './AiffToken.js';
10
10
  const debug = initDebug('music-metadata:parser:aiff');
11
11
  /**
12
12
  * AIFF - Audio Interchange File Format
@@ -23,7 +23,7 @@ export class AIFFParser extends BasicParser {
23
23
  async parse() {
24
24
  const header = await this.tokenizer.readToken(iff.Header);
25
25
  if (header.chunkID !== 'FORM')
26
- throw new Error('Invalid Chunk-ID, expected \'FORM\''); // Not AIFF format
26
+ throw new AiffContentError('Invalid Chunk-ID, expected \'FORM\''); // Not AIFF format
27
27
  const type = await this.tokenizer.readToken(FourCcToken);
28
28
  switch (type) {
29
29
  case 'AIFF':
@@ -35,7 +35,7 @@ export class AIFFParser extends BasicParser {
35
35
  this.isCompressed = true;
36
36
  break;
37
37
  default:
38
- throw new Error(`Unsupported AIFF type: ${type}`);
38
+ throw new AiffContentError(`Unsupported AIFF type: ${type}`);
39
39
  }
40
40
  this.metadata.setFormat('lossless', !this.isCompressed);
41
41
  try {
@@ -57,11 +57,10 @@ export class AIFFParser extends BasicParser {
57
57
  }
58
58
  }
59
59
  async readData(header) {
60
- var _a;
61
60
  switch (header.chunkID) {
62
61
  case 'COMM': { // The Common Chunk
63
62
  if (this.isCompressed === null) {
64
- throw new Error('Failed to parse AIFF.COMM chunk when compression type is unknown');
63
+ throw new AiffContentError('Failed to parse AIFF.COMM chunk when compression type is unknown');
65
64
  }
66
65
  const common = await this.tokenizer.readToken(new AiffToken.Common(header, this.isCompressed));
67
66
  this.metadata.setFormat('bitsPerSample', common.sampleSize);
@@ -70,7 +69,7 @@ export class AIFFParser extends BasicParser {
70
69
  this.metadata.setFormat('numberOfSamples', common.numSampleFrames);
71
70
  this.metadata.setFormat('duration', common.numSampleFrames / common.sampleRate);
72
71
  if (common.compressionName || common.compressionType) {
73
- this.metadata.setFormat('codec', (_a = common.compressionName) !== null && _a !== void 0 ? _a : compressionTypes[common.compressionType]);
72
+ this.metadata.setFormat('codec', common.compressionName ?? compressionTypes[common.compressionType]);
74
73
  }
75
74
  return header.chunkSize;
76
75
  }
@@ -97,7 +96,7 @@ export class AIFFParser extends BasicParser {
97
96
  }
98
97
  async readTextChunk(header) {
99
98
  const value = await this.tokenizer.readToken(new Token.StringType(header.chunkSize, 'ascii'));
100
- const values = value.split('\0').map(v => v.trim()).filter(v => v === null || v === void 0 ? void 0 : v.length);
99
+ const values = value.split('\0').map(v => v.trim()).filter(v => v?.length);
101
100
  await Promise.all(values.map(v => this.metadata.addTag('AIFF', header.chunkID, v)));
102
101
  return header.chunkSize;
103
102
  }
@@ -12,6 +12,20 @@ export declare const compressionTypes: {
12
12
  FL32: string;
13
13
  };
14
14
  export type CompressionTypeCode = keyof typeof compressionTypes;
15
+ declare const AiffContentError_base: {
16
+ new (message: string): {
17
+ readonly fileType: string;
18
+ toString(): string;
19
+ name: "UnexpectedFileContentError";
20
+ message: string;
21
+ stack?: string;
22
+ };
23
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
24
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
25
+ stackTraceLimit: number;
26
+ };
27
+ export declare class AiffContentError extends AiffContentError_base {
28
+ }
15
29
  /**
16
30
  * The Common Chunk.
17
31
  * Describes fundamental parameters of the waveform data such as sample rate, bit resolution, and how many channels of
@@ -31,3 +45,4 @@ export declare class Common implements IGetToken<ICommon> {
31
45
  constructor(header: iff.IChunkHeader, isAifc: boolean);
32
46
  get(buf: Uint8Array, off: number): ICommon;
33
47
  }
48
+ export {};
@@ -1,5 +1,6 @@
1
1
  import * as Token from 'token-types';
2
2
  import { FourCcToken } from '../common/FourCC.js';
3
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
3
4
  export const compressionTypes = {
4
5
  NONE: 'not compressed PCM Apple Computer',
5
6
  sowt: 'PCM (byte swapped)',
@@ -11,12 +12,14 @@ export const compressionTypes = {
11
12
  ALAW: 'CCITT G.711 A-law 8-bit ITU-T G.711 A-law',
12
13
  FL32: 'Float 32 IEEE 32-bit float '
13
14
  };
15
+ export class AiffContentError extends makeUnexpectedFileContentError('AIFF') {
16
+ }
14
17
  export class Common {
15
18
  constructor(header, isAifc) {
16
19
  this.isAifc = isAifc;
17
20
  const minimumChunkSize = isAifc ? 22 : 18;
18
21
  if (header.chunkSize < minimumChunkSize)
19
- throw new Error(`COMMON CHUNK size should always be at least ${minimumChunkSize}`);
22
+ throw new AiffContentError(`COMMON CHUNK size should always be at least ${minimumChunkSize}`);
20
23
  this.len = header.chunkSize;
21
24
  }
22
25
  get(buf, off) {
@@ -39,7 +42,7 @@ export class Common {
39
42
  res.compressionName = new Token.StringType(strLen, 'latin1').get(buf, off + 23);
40
43
  }
41
44
  else {
42
- throw new Error('Illegal pstring length');
45
+ throw new AiffContentError('Illegal pstring length');
43
46
  }
44
47
  }
45
48
  else {
@@ -3,6 +3,20 @@ import type { IOptions, IRandomReader, IApeHeader } from '../type.js';
3
3
  import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
4
4
  import { BasicParser } from '../common/BasicParser.js';
5
5
  import { type IFooter, type IHeader } from './APEv2Token.js';
6
+ declare const ApeContentError_base: {
7
+ new (message: string): {
8
+ readonly fileType: string;
9
+ toString(): string;
10
+ name: "UnexpectedFileContentError";
11
+ message: string;
12
+ stack?: string;
13
+ };
14
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
15
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
16
+ stackTraceLimit: number;
17
+ };
18
+ export declare class ApeContentError extends ApeContentError_base {
19
+ }
6
20
  export declare class APEv2Parser extends BasicParser {
7
21
  static tryParseApeHeader(metadata: INativeMetadataCollector, tokenizer: strtok3.ITokenizer, options: IOptions): Promise<void>;
8
22
  /**
@@ -28,3 +42,4 @@ export declare class APEv2Parser extends BasicParser {
28
42
  private parseDescriptorExpansion;
29
43
  private parseHeader;
30
44
  }
45
+ export {};
@@ -5,9 +5,12 @@ import { uint8ArrayToString } from 'uint8array-extras';
5
5
  import * as util from '../common/Util.js';
6
6
  import { BasicParser } from '../common/BasicParser.js';
7
7
  import { DataType, DescriptorParser, Header, TagFooter, TagItemHeader } from './APEv2Token.js';
8
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
8
9
  const debug = initDebug('music-metadata:parser:APEv2');
9
10
  const tagFormat = 'APEv2';
10
11
  const preamble = 'APETAGEX';
12
+ export class ApeContentError extends makeUnexpectedFileContentError('APEv2') {
13
+ }
11
14
  export class APEv2Parser extends BasicParser {
12
15
  constructor() {
13
16
  super(...arguments);
@@ -52,7 +55,7 @@ export class APEv2Parser extends BasicParser {
52
55
  static parseTagFooter(metadata, buffer, options) {
53
56
  const footer = TagFooter.get(buffer, buffer.length - TagFooter.len);
54
57
  if (footer.ID !== preamble)
55
- throw new Error('Unexpected APEv2 Footer ID preamble value.');
58
+ throw new ApeContentError('Unexpected APEv2 Footer ID preamble value');
56
59
  strtok3.fromBuffer(buffer);
57
60
  const apeParser = new APEv2Parser();
58
61
  apeParser.init(metadata, strtok3.fromBuffer(buffer), options);
@@ -83,7 +86,7 @@ export class APEv2Parser extends BasicParser {
83
86
  async parse() {
84
87
  const descriptor = await this.tokenizer.readToken(DescriptorParser);
85
88
  if (descriptor.ID !== 'MAC ')
86
- throw new Error('Unexpected descriptor ID');
89
+ throw new ApeContentError('Unexpected descriptor ID');
87
90
  this.ape.descriptor = descriptor;
88
91
  const lenExp = descriptor.descriptorBytes - DescriptorParser.len;
89
92
  const header = await (lenExp > 0 ? this.parseDescriptorExpansion(lenExp) : this.parseHeader());
@@ -156,7 +159,7 @@ export class APEv2Parser extends BasicParser {
156
159
  this.metadata.setFormat('numberOfChannels', header.channel);
157
160
  this.metadata.setFormat('duration', APEv2Parser.calculateDuration(header));
158
161
  if (!this.ape.descriptor) {
159
- throw new Error('Missing APE descriptor');
162
+ throw new ApeContentError('Missing APE descriptor');
160
163
  }
161
164
  return {
162
165
  forwardBytes: this.ape.descriptor.seekTableBytes + this.ape.descriptor.headerDataBytes +
@@ -1,6 +1,20 @@
1
1
  import type { IGetToken, ITokenizer } from 'strtok3';
2
2
  import type { AnyTagValue, IPicture, ITag } from '../type.js';
3
3
  import GUID from './GUID.js';
4
+ declare const AsfContentParseError_base: {
5
+ new (message: string): {
6
+ readonly fileType: string;
7
+ toString(): string;
8
+ name: "UnexpectedFileContentError";
9
+ message: string;
10
+ stack?: string;
11
+ };
12
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
13
+ prepareStackTrace?: ((err: Error, stackTraces: NodeJS.CallSite[]) => any) | undefined;
14
+ stackTraceLimit: number;
15
+ };
16
+ export declare class AsfContentParseError extends AsfContentParseError_base {
17
+ }
4
18
  /**
5
19
  * Data Type: Specifies the type of information being stored. The following values are recognized.
6
20
  */
@@ -307,3 +321,4 @@ export declare class WmPictureToken implements IGetToken<IWmPicture> {
307
321
  constructor(len: number);
308
322
  get(buffer: Uint8Array, offset: number): IWmPicture;
309
323
  }
324
+ export {};
@@ -4,6 +4,9 @@ import * as util from '../common/Util.js';
4
4
  import GUID from './GUID.js';
5
5
  import { getParserForAttr, parseUnicodeAttr } from './AsfUtil.js';
6
6
  import { AttachedPictureType } from '../id3v2/ID3v2Token.js';
7
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
8
+ export class AsfContentParseError extends makeUnexpectedFileContentError('ASF') {
9
+ }
7
10
  /**
8
11
  * Data Type: Specifies the type of information being stored. The following values are recognized.
9
12
  */
@@ -73,7 +76,7 @@ export class State {
73
76
  else {
74
77
  const parseAttr = getParserForAttr(valueType);
75
78
  if (!parseAttr) {
76
- throw new Error(`unexpected value headerType: ${valueType}`);
79
+ throw new AsfContentParseError(`unexpected value headerType: ${valueType}`);
77
80
  }
78
81
  tags.push({ id: name, value: parseAttr(data) });
79
82
  }
@@ -3,6 +3,7 @@ import { TrackType } from '../type.js';
3
3
  import GUID from './GUID.js';
4
4
  import * as AsfObject from './AsfObject.js';
5
5
  import { BasicParser } from '../common/BasicParser.js';
6
+ import { AsfContentParseError } from './AsfObject.js';
6
7
  const debug = initDebug('music-metadata:parser:ASF');
7
8
  const headerType = 'asf';
8
9
  /**
@@ -19,7 +20,7 @@ export class AsfParser extends BasicParser {
19
20
  async parse() {
20
21
  const header = await this.tokenizer.readToken(AsfObject.TopLevelHeaderObjectToken);
21
22
  if (!header.objectId.equals(GUID.HeaderObject)) {
22
- throw new Error(`expected asf header; but was not found; got: ${header.objectId.str}`);
23
+ throw new AsfContentParseError(`expected asf header; but was not found; got: ${header.objectId.str}`);
23
24
  }
24
25
  try {
25
26
  await this.parseObjectHeader(header.numberOfHeaderObjects);
@@ -8,6 +8,7 @@ import { VorbisTagMapper } from '../ogg/vorbis/VorbisTagMapper.js';
8
8
  import { RiffInfoTagMapper } from '../riff/RiffInfoTagMap.js';
9
9
  import { MatroskaTagMapper } from '../matroska/MatroskaTagMapper.js';
10
10
  import { AiffTagMapper } from '../aiff/AiffTagMap.js';
11
+ import { InternalParserError } from '../ParseError.js';
11
12
  export class CombinedTagMapper {
12
13
  constructor() {
13
14
  this.tagMappers = {};
@@ -39,7 +40,7 @@ export class CombinedTagMapper {
39
40
  if (tagMapper) {
40
41
  return this.tagMappers[tagType].mapGenericTag(tag, warnings);
41
42
  }
42
- throw new Error(`No generic tag mapper defined for tag-format: ${tagType}`);
43
+ throw new InternalParserError(`No generic tag mapper defined for tag-format: ${tagType}`);
43
44
  }
44
45
  registerTagMapper(genericTagMapper) {
45
46
  for (const tagType of genericTagMapper.tagTypes) {
@@ -1,5 +1,6 @@
1
1
  import { stringToUint8Array, uint8ArrayToString } from 'uint8array-extras';
2
2
  import * as util from './Util.js';
3
+ import { InternalParserError, FieldDecodingError } from '../ParseError.js';
3
4
  const validFourCC = /^[\x21-\x7e©][\x20-\x7e\x00()]{3}/;
4
5
  /**
5
6
  * Token for read FourCC
@@ -10,14 +11,14 @@ export const FourCcToken = {
10
11
  get: (buf, off) => {
11
12
  const id = uint8ArrayToString(buf.slice(off, off + FourCcToken.len), 'latin1');
12
13
  if (!id.match(validFourCC)) {
13
- throw new Error(`FourCC contains invalid characters: ${util.a2hex(id)} "${id}"`);
14
+ throw new FieldDecodingError(`FourCC contains invalid characters: ${util.a2hex(id)} "${id}"`);
14
15
  }
15
16
  return id;
16
17
  },
17
18
  put: (buffer, offset, id) => {
18
19
  const str = stringToUint8Array(id);
19
20
  if (str.length !== 4)
20
- throw new Error('Invalid length');
21
+ throw new InternalParserError('Invalid length');
21
22
  buffer.set(str, offset);
22
23
  return offset + 4;
23
24
  }
@@ -54,10 +54,9 @@ export class MetadataCollector {
54
54
  this.format.trackInfo.push(streamInfo);
55
55
  }
56
56
  setFormat(key, value) {
57
- var _a;
58
57
  debug(`format: ${key} = ${value}`);
59
58
  this.format[key] = value; // as any to override readonly
60
- if ((_a = this.opts) === null || _a === void 0 ? void 0 : _a.observer) {
59
+ if (this.opts?.observer) {
61
60
  this.opts.observer({ metadata: this, tag: { type: 'format', id: key, value } });
62
61
  }
63
62
  }
@@ -239,7 +238,6 @@ export class MetadataCollector {
239
238
  * Set generic tag
240
239
  */
241
240
  setGenericTag(tagType, tag) {
242
- var _a;
243
241
  debug(`common.${tag.id} = ${tag.value}`);
244
242
  const prio0 = this.commonOrigin[tag.id] || 1000;
245
243
  const prio1 = this.originPriority[tagType];
@@ -270,7 +268,7 @@ export class MetadataCollector {
270
268
  return debug(`Ignore native tag (list): ${tagType}.${tag.id} = ${tag.value}`);
271
269
  }
272
270
  }
273
- if ((_a = this.opts) === null || _a === void 0 ? void 0 : _a.observer) {
271
+ if (this.opts?.observer) {
274
272
  this.opts.observer({ metadata: this, tag: { type: 'common', id: tag.id, value: tag.value } });
275
273
  }
276
274
  // ToDo: trigger metadata event
@@ -1,4 +1,5 @@
1
1
  import { StringType } from 'token-types';
2
+ import { FieldDecodingError } from '../ParseError.js';
2
3
  export function getBit(buf, off, bit) {
3
4
  return (buf[off] & (1 << bit)) !== 0;
4
5
  }
@@ -34,7 +35,7 @@ export function trimRightNull(x) {
34
35
  function swapBytes(uint8Array) {
35
36
  const l = uint8Array.length;
36
37
  if ((l & 1) !== 0)
37
- throw new Error('Buffer length must be even');
38
+ throw new FieldDecodingError('Buffer length must be even');
38
39
  for (let i = 0; i < l; i += 2) {
39
40
  const a = uint8Array[i];
40
41
  uint8Array[i] = uint8Array[i + 1];
@@ -54,7 +55,7 @@ export function decodeString(uint8Array, encoding) {
54
55
  if (encoding === 'utf-16le' && uint8Array[0] === 0xFE && uint8Array[1] === 0xFF) {
55
56
  // BOM, indicating big endian decoding
56
57
  if ((uint8Array.length & 1) !== 0)
57
- throw new Error('Expected even number of octets for 16-bit unicode string');
58
+ throw new FieldDecodingError('Expected even number of octets for 16-bit unicode string');
58
59
  return decodeString(swapBytes(uint8Array), encoding);
59
60
  }
60
61
  return new StringType(uint8Array.length, encoding).get(uint8Array, 0);
package/lib/core.d.ts CHANGED
@@ -5,6 +5,7 @@ import { type AnyWebByteStream, type IFileInfo, type ITokenizer } from 'strtok3'
5
5
  import type { IAudioMetadata, INativeTagDict, IOptions, IPicture, IPrivateOptions, IRandomReader, ITag } from './type.js';
6
6
  export type { IFileInfo } from 'strtok3';
7
7
  export { type IAudioMetadata, type IOptions, type ITag, type INativeTagDict, type ICommonTagsResult, type IFormat, type IPicture, type IRatio, type IChapter, type ILyricsTag, LyricsContentType, TimestampFormat, IMetadataEventTag, IMetadataEvent } from './type.js';
8
+ export type * from './ParseError.js';
8
9
  /**
9
10
  * Parse Web API File
10
11
  * Requires Blob to be able to stream using a ReadableStreamBYOBReader, only available since Node.js ≥ 20
@@ -22,7 +23,7 @@ export declare function parseBlob(blob: Blob, options?: IOptions): Promise<IAudi
22
23
  */
23
24
  export declare function parseWebStream(webStream: AnyWebByteStream, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>;
24
25
  /**
25
- * Parse audio from Node Buffer
26
+ * Parse audio from memory
26
27
  * @param uint8Array - Uint8Array holding audio data
27
28
  * @param fileInfo - File information object or MIME-type string
28
29
  * @param options - Parsing options
package/lib/core.js CHANGED
@@ -33,7 +33,7 @@ export function parseWebStream(webStream, fileInfo, options = {}) {
33
33
  return parseFromTokenizer(fromWebStream(webStream, { fileInfo: typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo }), options);
34
34
  }
35
35
  /**
36
- * Parse audio from Node Buffer
36
+ * Parse audio from memory
37
37
  * @param uint8Array - Uint8Array holding audio data
38
38
  * @param fileInfo - File information object or MIME-type string
39
39
  * @param options - Parsing options
@@ -0,0 +1,5 @@
1
+ // CommonJS core (default) entry point
2
+ "use strict";
3
+ module.exports = {
4
+ loadMusicMetadata: () => import('./core.js'),
5
+ };
@@ -1,4 +1,18 @@
1
1
  import { BasicParser } from '../common/BasicParser.js';
2
+ declare const DsdiffContentParseError_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 DsdiffContentParseError extends DsdiffContentParseError_base {
15
+ }
2
16
  /**
3
17
  * DSDIFF - Direct Stream Digital Interchange File Format (Phillips)
4
18
  *
@@ -12,3 +26,4 @@ export declare class DsdiffParser extends BasicParser {
12
26
  private handleSoundPropertyChunks;
13
27
  private handleChannelChunks;
14
28
  }
29
+ export {};
@@ -5,7 +5,10 @@ import { FourCcToken } from '../common/FourCC.js';
5
5
  import { BasicParser } from '../common/BasicParser.js';
6
6
  import { ID3v2Parser } from '../id3v2/ID3v2Parser.js';
7
7
  import { ChunkHeader64 } from './DsdiffToken.js';
8
+ import { makeUnexpectedFileContentError } from '../ParseError.js';
8
9
  const debug = initDebug('music-metadata:parser:aiff');
10
+ export class DsdiffContentParseError extends makeUnexpectedFileContentError('DSDIFF') {
11
+ }
9
12
  /**
10
13
  * DSDIFF - Direct Stream Digital Interchange File Format (Phillips)
11
14
  *
@@ -16,7 +19,7 @@ export class DsdiffParser extends BasicParser {
16
19
  async parse() {
17
20
  const header = await this.tokenizer.readToken(ChunkHeader64);
18
21
  if (header.chunkID !== 'FRM8')
19
- throw new Error('Unexpected chunk-ID');
22
+ throw new DsdiffContentParseError('Unexpected chunk-ID');
20
23
  const type = (await this.tokenizer.readToken(FourCcToken)).trim();
21
24
  switch (type) {
22
25
  case 'DSD':
@@ -24,7 +27,7 @@ export class DsdiffParser extends BasicParser {
24
27
  this.metadata.setFormat('lossless', true);
25
28
  return this.readFmt8Chunks(header.chunkSize - BigInt(FourCcToken.len));
26
29
  default:
27
- throw new Error(`Unsupported DSDIFF type: ${type}`);
30
+ throw new DsdiffContentParseError(`Unsupported DSDIFF type: ${type}`);
28
31
  }
29
32
  }
30
33
  async readFmt8Chunks(remainingSize) {
@@ -48,7 +51,7 @@ export class DsdiffParser extends BasicParser {
48
51
  case 'PROP': { // 3.2 PROPERTY CHUNK
49
52
  const propType = await this.tokenizer.readToken(FourCcToken);
50
53
  if (propType !== 'SND ')
51
- throw new Error('Unexpected PROP-chunk ID');
54
+ throw new DsdiffContentParseError('Unexpected PROP-chunk ID');
52
55
  await this.handleSoundPropertyChunks(header.chunkSize - BigInt(FourCcToken.len));
53
56
  break;
54
57
  }
@@ -1,4 +1,18 @@
1
1
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
2
+ declare const DsdContentParseError_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 DsdContentParseError extends DsdContentParseError_base {
15
+ }
2
16
  /**
3
17
  * DSF (dsd stream file) File Parser
4
18
  * Ref: https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
@@ -7,3 +21,4 @@ export declare class DsfParser extends AbstractID3Parser {
7
21
  postId3v2Parse(): Promise<void>;
8
22
  private parseChunks;
9
23
  }
24
+ export {};