music-metadata 8.3.0 → 9.0.1

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 (96) hide show
  1. package/LICENSE.txt +9 -9
  2. package/README.md +519 -488
  3. package/lib/ParserFactory.d.ts +1 -1
  4. package/lib/ParserFactory.js +1 -2
  5. package/lib/aiff/AiffParser.js +3 -4
  6. package/lib/aiff/AiffToken.d.ts +2 -4
  7. package/lib/aiff/AiffToken.js +7 -7
  8. package/lib/apev2/APEv2Parser.d.ts +1 -1
  9. package/lib/apev2/APEv2Parser.js +10 -12
  10. package/lib/apev2/APEv2Token.d.ts +1 -1
  11. package/lib/asf/AsfObject.d.ts +15 -17
  12. package/lib/asf/AsfObject.js +51 -45
  13. package/lib/asf/AsfParser.js +6 -8
  14. package/lib/asf/AsfUtil.d.ts +1 -3
  15. package/lib/asf/AsfUtil.js +4 -5
  16. package/lib/asf/GUID.d.ts +4 -5
  17. package/lib/asf/GUID.js +14 -11
  18. package/lib/common/BasicParser.d.ts +1 -1
  19. package/lib/common/FourCC.d.ts +1 -1
  20. package/lib/common/FourCC.js +5 -3
  21. package/lib/common/MetadataCollector.d.ts +3 -3
  22. package/lib/common/MetadataCollector.js +7 -8
  23. package/lib/common/RandomFileReader.d.ts +2 -3
  24. package/lib/common/RandomFileReader.js +1 -1
  25. package/lib/common/Util.d.ts +1 -1
  26. package/lib/common/Util.js +4 -3
  27. package/lib/core.d.ts +16 -8
  28. package/lib/core.js +19 -5
  29. package/lib/dsdiff/DsdiffParser.js +1 -1
  30. package/lib/dsdiff/DsdiffToken.d.ts +1 -1
  31. package/lib/dsf/DsfChunk.d.ts +1 -1
  32. package/lib/flac/FlacParser.d.ts +1 -1
  33. package/lib/flac/FlacParser.js +6 -4
  34. package/lib/id3v1/ID3v1Parser.js +7 -7
  35. package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
  36. package/lib/id3v2/AbstractID3Parser.js +1 -1
  37. package/lib/id3v2/FrameParser.js +3 -3
  38. package/lib/id3v2/ID3v24TagMapper.d.ts +2 -3
  39. package/lib/id3v2/ID3v24TagMapper.js +4 -4
  40. package/lib/id3v2/ID3v2Parser.d.ts +1 -1
  41. package/lib/id3v2/ID3v2Parser.js +10 -17
  42. package/lib/id3v2/ID3v2Token.d.ts +1 -1
  43. package/lib/id3v2/ID3v2Token.js +2 -2
  44. package/lib/iff/index.d.ts +1 -1
  45. package/lib/index.d.ts +1 -2
  46. package/lib/index.js +1 -1
  47. package/lib/lyrics3/Lyrics3.js +4 -4
  48. package/lib/matroska/MatroskaParser.d.ts +9 -2
  49. package/lib/matroska/MatroskaParser.js +44 -39
  50. package/lib/matroska/types.d.ts +13 -14
  51. package/lib/mp4/Atom.d.ts +1 -1
  52. package/lib/mp4/AtomToken.d.ts +11 -11
  53. package/lib/mp4/AtomToken.js +3 -2
  54. package/lib/mp4/MP4Parser.js +20 -19
  55. package/lib/mpeg/ExtendedLameHeader.d.ts +1 -1
  56. package/lib/mpeg/MpegParser.js +4 -4
  57. package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
  58. package/lib/mpeg/XingTag.d.ts +2 -3
  59. package/lib/mpeg/XingTag.js +1 -1
  60. package/lib/musepack/index.js +1 -1
  61. package/lib/musepack/sv7/BitReader.d.ts +1 -1
  62. package/lib/musepack/sv7/StreamVersion7.d.ts +1 -1
  63. package/lib/musepack/sv7/StreamVersion7.js +1 -1
  64. package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
  65. package/lib/musepack/sv8/StreamVersion8.js +1 -1
  66. package/lib/ogg/Ogg.d.ts +2 -2
  67. package/lib/ogg/OggParser.d.ts +1 -1
  68. package/lib/ogg/OggParser.js +5 -5
  69. package/lib/ogg/opus/Opus.d.ts +1 -1
  70. package/lib/ogg/opus/Opus.js +6 -6
  71. package/lib/ogg/opus/OpusParser.d.ts +4 -5
  72. package/lib/ogg/opus/OpusParser.js +3 -3
  73. package/lib/ogg/speex/Speex.d.ts +1 -1
  74. package/lib/ogg/speex/Speex.js +13 -13
  75. package/lib/ogg/speex/SpeexParser.d.ts +3 -4
  76. package/lib/ogg/speex/SpeexParser.js +1 -1
  77. package/lib/ogg/theora/Theora.d.ts +1 -1
  78. package/lib/ogg/theora/Theora.js +6 -6
  79. package/lib/ogg/theora/TheoraParser.d.ts +4 -5
  80. package/lib/ogg/theora/TheoraParser.js +4 -4
  81. package/lib/ogg/vorbis/Vorbis.d.ts +3 -4
  82. package/lib/ogg/vorbis/Vorbis.js +11 -12
  83. package/lib/ogg/vorbis/VorbisDecoder.js +1 -1
  84. package/lib/ogg/vorbis/VorbisParser.d.ts +8 -8
  85. package/lib/ogg/vorbis/VorbisParser.js +21 -12
  86. package/lib/riff/RiffChunk.d.ts +1 -1
  87. package/lib/riff/RiffChunk.js +2 -2
  88. package/lib/type.d.ts +9 -11
  89. package/lib/wav/BwfChunk.d.ts +1 -1
  90. package/lib/wav/WaveChunk.d.ts +3 -4
  91. package/lib/wav/WaveChunk.js +8 -7
  92. package/lib/wav/WaveParser.js +2 -2
  93. package/lib/wavpack/WavPackParser.d.ts +1 -0
  94. package/lib/wavpack/WavPackParser.js +5 -3
  95. package/lib/wavpack/WavPackToken.d.ts +1 -1
  96. package/package.json +141 -140
package/lib/asf/GUID.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { hexToUint8Array, uint8ArrayToHex } from 'uint8array-extras';
1
2
  /**
2
3
  * Ref:
3
4
  * - https://tools.ietf.org/html/draft-fleischman-asf-01, Appendix A: ASF GUIDs
@@ -23,11 +24,12 @@ class GUID {
23
24
  * @returns GUID as dashed hexadecimal representation
24
25
  */
25
26
  static decode(objectId, offset = 0) {
26
- const guid = objectId.readUInt32LE(offset).toString(16) + "-" +
27
- objectId.readUInt16LE(offset + 4).toString(16) + "-" +
28
- objectId.readUInt16LE(offset + 6).toString(16) + "-" +
29
- objectId.readUInt16BE(offset + 8).toString(16) + "-" +
30
- objectId.slice(offset + 10, offset + 16).toString('hex');
27
+ const view = new DataView(objectId.buffer, offset);
28
+ const guid = view.getUint32(0, true).toString(16) + "-" +
29
+ view.getUint16(4, true).toString(16) + "-" +
30
+ view.getUint16(6, true).toString(16) + "-" +
31
+ view.getUint16(8).toString(16) + "-" +
32
+ uint8ArrayToHex(objectId.slice(offset + 10, offset + 16));
31
33
  return guid.toUpperCase();
32
34
  }
33
35
  /**
@@ -51,12 +53,13 @@ class GUID {
51
53
  * @returns Encoded Binary GUID
52
54
  */
53
55
  static encode(str) {
54
- const bin = Buffer.alloc(16);
55
- bin.writeUInt32LE(parseInt(str.slice(0, 8), 16), 0);
56
- bin.writeUInt16LE(parseInt(str.slice(9, 13), 16), 4);
57
- bin.writeUInt16LE(parseInt(str.slice(14, 18), 16), 6);
58
- Buffer.from(str.slice(19, 23), "hex").copy(bin, 8);
59
- Buffer.from(str.slice(24), "hex").copy(bin, 10);
56
+ const bin = new Uint8Array(16);
57
+ const view = new DataView(bin.buffer);
58
+ view.setUint32(0, parseInt(str.slice(0, 8), 16), true);
59
+ view.setUint16(4, parseInt(str.slice(9, 13), 16), true);
60
+ view.setUint16(6, parseInt(str.slice(14, 18), 16), true);
61
+ bin.set(hexToUint8Array(str.slice(19, 23)), 8);
62
+ bin.set(hexToUint8Array(str.slice(24)), 10);
60
63
  return bin;
61
64
  }
62
65
  constructor(str) {
@@ -1,4 +1,4 @@
1
- import { ITokenizer } from 'strtok3/core';
1
+ import type { ITokenizer } from 'strtok3';
2
2
  import { ITokenParser } from '../ParserFactory.js';
3
3
  import { IOptions, IPrivateOptions } from '../type.js';
4
4
  import { INativeMetadataCollector } from './MetadataCollector.js';
@@ -1,4 +1,4 @@
1
- import { IToken } from 'strtok3/core';
1
+ import { IToken } from 'strtok3';
2
2
  /**
3
3
  * Token for read FourCC
4
4
  * Ref: https://en.wikipedia.org/wiki/FourCC
@@ -1,3 +1,4 @@
1
+ import { stringToUint8Array, uint8ArrayToString } from 'uint8array-extras';
1
2
  import * as util from './Util.js';
2
3
  const validFourCC = /^[\x21-\x7e©][\x20-\x7e\x00()]{3}/;
3
4
  /**
@@ -7,17 +8,18 @@ const validFourCC = /^[\x21-\x7e©][\x20-\x7e\x00()]{3}/;
7
8
  export const FourCcToken = {
8
9
  len: 4,
9
10
  get: (buf, off) => {
10
- const id = buf.toString('binary', off, off + FourCcToken.len);
11
+ const id = uint8ArrayToString(buf.slice(off, off + FourCcToken.len), 'latin1');
11
12
  if (!id.match(validFourCC)) {
12
13
  throw new Error(`FourCC contains invalid characters: ${util.a2hex(id)} "${id}"`);
13
14
  }
14
15
  return id;
15
16
  },
16
17
  put: (buffer, offset, id) => {
17
- const str = Buffer.from(id, 'binary');
18
+ const str = stringToUint8Array(id);
18
19
  if (str.length !== 4)
19
20
  throw new Error('Invalid length');
20
- return str.copy(buffer, offset);
21
+ buffer.set(str, offset);
22
+ return offset + 4;
21
23
  }
22
24
  };
23
25
  //# sourceMappingURL=FourCC.js.map
@@ -22,7 +22,7 @@ export interface INativeMetadataCollector extends IWarningCollector {
22
22
  */
23
23
  hasAny(): boolean;
24
24
  setFormat(key: FormatId, value: any): void;
25
- addTag(tagType: TagType, tagId: string, value: any): void;
25
+ addTag(tagType: TagType, tagId: string, value: any): Promise<void>;
26
26
  addStreamInfo(streamInfo: ITrackInfo): void;
27
27
  }
28
28
  /**
@@ -51,9 +51,9 @@ export declare class MetadataCollector implements INativeMetadataCollector {
51
51
  hasAny(): boolean;
52
52
  addStreamInfo(streamInfo: ITrackInfo): void;
53
53
  setFormat(key: FormatId, value: any): void;
54
- addTag(tagType: TagType, tagId: string, value: any): void;
54
+ addTag(tagType: TagType, tagId: string, value: any): Promise<void>;
55
55
  addWarning(warning: string): void;
56
- postMap(tagType: TagType | 'artificial', tag: IGenericTag): any;
56
+ postMap(tagType: TagType | 'artificial', tag: IGenericTag): Promise<void>;
57
57
  /**
58
58
  * Convert native tags to common tags
59
59
  * @returns {IAudioMetadata} Native + common tags
@@ -60,19 +60,19 @@ export class MetadataCollector {
60
60
  this.opts.observer({ metadata: this, tag: { type: 'format', id: key, value } });
61
61
  }
62
62
  }
63
- addTag(tagType, tagId, value) {
63
+ async addTag(tagType, tagId, value) {
64
64
  debug(`tag ${tagType}.${tagId} = ${value}`);
65
65
  if (!this.native[tagType]) {
66
66
  this.format.tagTypes.push(tagType);
67
67
  this.native[tagType] = [];
68
68
  }
69
69
  this.native[tagType].push({ id: tagId, value });
70
- this.toCommon(tagType, tagId, value);
70
+ await this.toCommon(tagType, tagId, value);
71
71
  }
72
72
  addWarning(warning) {
73
73
  this.quality.warnings.push({ message: warning });
74
74
  }
75
- postMap(tagType, tag) {
75
+ async postMap(tagType, tag) {
76
76
  // Common tag (alias) found
77
77
  // check if we need to do something special with common tag
78
78
  // if the event has been aliased then we need to clean it before
@@ -100,13 +100,12 @@ export class MetadataCollector {
100
100
  }
101
101
  break;
102
102
  case 'picture':
103
- this.postFixPicture(tag.value).then(picture => {
103
+ return this.postFixPicture(tag.value).then(picture => {
104
104
  if (picture !== null) {
105
105
  tag.value = picture;
106
106
  this.setGenericTag(tagType, tag);
107
107
  }
108
108
  });
109
- return;
110
109
  case 'totaltracks':
111
110
  this.common.track.of = CommonTagMapper.toIntOrNull(tag.value);
112
111
  return;
@@ -194,7 +193,7 @@ export class MetadataCollector {
194
193
  async postFixPicture(picture) {
195
194
  if (picture.data && picture.data.length > 0) {
196
195
  if (!picture.format) {
197
- const fileType = await fileTypeFromBuffer(picture.data);
196
+ const fileType = await fileTypeFromBuffer(Uint8Array.from(picture.data)); // ToDO: remove Buffer
198
197
  if (fileType) {
199
198
  picture.format = fileType.mime;
200
199
  }
@@ -215,11 +214,11 @@ export class MetadataCollector {
215
214
  /**
216
215
  * Convert native tag to common tags
217
216
  */
218
- toCommon(tagType, tagId, value) {
217
+ async toCommon(tagType, tagId, value) {
219
218
  const tag = { id: tagId, value };
220
219
  const genericTag = this.tagMapper.mapTag(tagType, tag, this);
221
220
  if (genericTag) {
222
- this.postMap(tagType, genericTag);
221
+ await this.postMap(tagType, genericTag);
223
222
  }
224
223
  }
225
224
  /**
@@ -1,4 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import { IRandomReader } from '../type.js';
3
2
  /**
4
3
  * Provides abstract file access via the IRandomRead interface
@@ -10,13 +9,13 @@ export declare class RandomFileReader implements IRandomReader {
10
9
  private constructor();
11
10
  /**
12
11
  * Read from a given position of an abstracted file or buffer.
13
- * @param buffer {Buffer} is the buffer that the data will be written to.
12
+ * @param buffer {Uint8Array} is the buffer that the data will be written to.
14
13
  * @param offset {number} is the offset in the buffer to start writing at.
15
14
  * @param length {number}is an integer specifying the number of bytes to read.
16
15
  * @param position {number} is an argument specifying where to begin reading from in the file.
17
16
  * @return {Promise<number>} bytes read
18
17
  */
19
- randomRead(buffer: Buffer, offset: number, length: number, position: number): Promise<number>;
18
+ randomRead(buffer: Uint8Array, offset: number, length: number, position: number): Promise<number>;
20
19
  close(): Promise<void>;
21
20
  static init(filePath: string, fileSize: number): Promise<RandomFileReader>;
22
21
  }
@@ -10,7 +10,7 @@ export class RandomFileReader {
10
10
  }
11
11
  /**
12
12
  * Read from a given position of an abstracted file or buffer.
13
- * @param buffer {Buffer} is the buffer that the data will be written to.
13
+ * @param buffer {Uint8Array} is the buffer that the data will be written to.
14
14
  * @param offset {number} is the offset in the buffer to start writing at.
15
15
  * @param length {number}is an integer specifying the number of bytes to read.
16
16
  * @param position {number} is an argument specifying where to begin reading from in the file.
@@ -1,5 +1,5 @@
1
1
  import { IRatio } from '../type.js';
2
- export type StringEncoding = 'ascii' | 'utf8' | 'utf16le' | 'ucs2' | 'base64url' | 'latin1' | 'hex';
2
+ export type StringEncoding = 'ascii' | 'utf8' | 'utf-16le' | 'ucs2' | 'base64url' | 'latin1' | 'hex';
3
3
  export interface ITextEncoding {
4
4
  encoding: StringEncoding;
5
5
  bom?: boolean;
@@ -1,3 +1,4 @@
1
+ import { StringType } from 'token-types';
1
2
  export function getBit(buf, off, bit) {
2
3
  return (buf[off] & (1 << bit)) !== 0;
3
4
  }
@@ -11,7 +12,7 @@ export function getBit(buf, off, bit) {
11
12
  */
12
13
  export function findZero(uint8Array, start, end, encoding) {
13
14
  let i = start;
14
- if (encoding === 'utf16le') {
15
+ if (encoding === 'utf-16le') {
15
16
  while (uint8Array[i] !== 0 || uint8Array[i + 1] !== 0) {
16
17
  if (i >= end)
17
18
  return end;
@@ -52,13 +53,13 @@ export function decodeString(uint8Array, encoding) {
52
53
  if (uint8Array[0] === 0xFF && uint8Array[1] === 0xFE) { // little endian
53
54
  return decodeString(uint8Array.subarray(2), encoding);
54
55
  }
55
- else if (encoding === 'utf16le' && uint8Array[0] === 0xFE && uint8Array[1] === 0xFF) {
56
+ else if (encoding === 'utf-16le' && uint8Array[0] === 0xFE && uint8Array[1] === 0xFF) {
56
57
  // BOM, indicating big endian decoding
57
58
  if ((uint8Array.length & 1) !== 0)
58
59
  throw new Error('Expected even number of octets for 16-bit unicode string');
59
60
  return decodeString(swapBytes(uint8Array), encoding);
60
61
  }
61
- return Buffer.from(uint8Array).toString(encoding);
62
+ return new StringType(uint8Array.length, encoding).get(uint8Array, 0);
62
63
  }
63
64
  export function stripNulls(str) {
64
65
  str = str.replace(/^\x00+/g, '');
package/lib/core.d.ts CHANGED
@@ -1,16 +1,24 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import { Readable } from 'stream';
3
- import * as strtok3 from 'strtok3/core';
4
- import { IAudioMetadata, INativeTagDict, IOptions, IPicture, IPrivateOptions, IRandomReader, ITag } from './type.js';
5
- export { IFileInfo } from 'strtok3/core';
1
+ import * as strtok3 from 'strtok3';
2
+ import type { IAudioMetadata, INativeTagDict, IOptions, IPicture, IPrivateOptions, IRandomReader, ITag } from './type.js';
3
+ import type { ReadableStream as NodeReadableStream } from 'node:stream/web';
4
+ export { IFileInfo } from 'strtok3';
5
+ export type AnyWebStream<G> = NodeReadableStream<G> | ReadableStream<G>;
6
6
  /**
7
- * Parse audio from Node Stream.Readable
8
- * @param stream - Stream to read the audio track from
7
+ * Parse Web API File
8
+ * Requires Blob to be able to stream using a ReadableStreamBYOBReader, only available since Node.js ≥ 20
9
+ * @param blob - Blob to parse
10
+ * @param options - Parsing options
11
+ * @returns Metadata
12
+ */
13
+ export declare function parseBlob(blob: Blob, options?: IOptions): Promise<IAudioMetadata>;
14
+ /**
15
+ * Parse audio from Web Stream.Readable
16
+ * @param webStream - WebStream to read the audio track from
9
17
  * @param options - Parsing options
10
18
  * @param fileInfo - File information object or MIME-type string
11
19
  * @returns Metadata
12
20
  */
13
- export declare function parseStream(stream: Readable, fileInfo?: strtok3.IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>;
21
+ export declare function parseWebStream(webStream: AnyWebStream<Uint8Array>, fileInfo?: strtok3.IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>;
14
22
  /**
15
23
  * Parse audio from Node Buffer
16
24
  * @param uint8Array - Uint8Array holding audio data
package/lib/core.js CHANGED
@@ -1,18 +1,32 @@
1
- import * as strtok3 from 'strtok3/core';
1
+ import * as strtok3 from 'strtok3';
2
2
  import { ParserFactory } from './ParserFactory.js';
3
3
  import { RandomUint8ArrayReader } from './common/RandomUint8ArrayReader.js';
4
4
  import { APEv2Parser } from './apev2/APEv2Parser.js';
5
5
  import { hasID3v1Header } from './id3v1/ID3v1Parser.js';
6
6
  import { getLyricsHeaderLength } from './lyrics3/Lyrics3.js';
7
7
  /**
8
- * Parse audio from Node Stream.Readable
9
- * @param stream - Stream to read the audio track from
8
+ * Parse Web API File
9
+ * Requires Blob to be able to stream using a ReadableStreamBYOBReader, only available since Node.js ≥ 20
10
+ * @param blob - Blob to parse
11
+ * @param options - Parsing options
12
+ * @returns Metadata
13
+ */
14
+ export async function parseBlob(blob, options = {}) {
15
+ const fileInfo = { mimeType: blob.type, size: blob.size };
16
+ if (blob instanceof File) {
17
+ fileInfo.path = blob.name;
18
+ }
19
+ return parseWebStream(blob.stream(), fileInfo, options);
20
+ }
21
+ /**
22
+ * Parse audio from Web Stream.Readable
23
+ * @param webStream - WebStream to read the audio track from
10
24
  * @param options - Parsing options
11
25
  * @param fileInfo - File information object or MIME-type string
12
26
  * @returns Metadata
13
27
  */
14
- export function parseStream(stream, fileInfo, options = {}) {
15
- return parseFromTokenizer(strtok3.fromStream(stream, typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo), options);
28
+ export function parseWebStream(webStream, fileInfo, options = {}) {
29
+ return parseFromTokenizer(strtok3.fromWebStream(webStream, typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo), options);
16
30
  }
17
31
  /**
18
32
  * Parse audio from Node Buffer
@@ -1,6 +1,6 @@
1
1
  import * as Token from 'token-types';
2
2
  import initDebug from 'debug';
3
- import * as strtok3 from 'strtok3/core';
3
+ import * as strtok3 from 'strtok3';
4
4
  import { FourCcToken } from '../common/FourCC.js';
5
5
  import { BasicParser } from '../common/BasicParser.js';
6
6
  import { ID3v2Parser } from '../id3v2/ID3v2Parser.js';
@@ -1,4 +1,4 @@
1
- import { IGetToken } from 'strtok3/core';
1
+ import type { IGetToken } from 'strtok3';
2
2
  import { IChunkHeader64 } from '../iff/index.js';
3
3
  export { IChunkHeader64 } from '../iff/index.js';
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { IGetToken } from 'strtok3/core';
1
+ import type { IGetToken } from 'strtok3';
2
2
  /**
3
3
  * Common interface for the common chunk DSD header
4
4
  */
@@ -1,4 +1,4 @@
1
- import { ITokenizer } from 'strtok3/core';
1
+ import type { ITokenizer } from 'strtok3';
2
2
  import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
3
3
  import { INativeMetadataCollector } from '../common/MetadataCollector.js';
4
4
  import { IOptions } from '../type.js';
@@ -54,7 +54,7 @@ export class FlacParser extends AbstractID3Parser {
54
54
  this.metadata.setFormat('bitrate', 8 * dataSize / this.metadata.format.duration);
55
55
  }
56
56
  }
57
- parseDataBlock(blockHeader) {
57
+ async parseDataBlock(blockHeader) {
58
58
  debug(`blockHeader type=${blockHeader.type}, length=${blockHeader.length}`);
59
59
  switch (blockHeader.type) {
60
60
  case BlockType.STREAMINFO:
@@ -71,7 +71,8 @@ export class FlacParser extends AbstractID3Parser {
71
71
  case BlockType.CUESHEET:
72
72
  break;
73
73
  case BlockType.PICTURE:
74
- return this.parsePicture(blockHeader.length).then();
74
+ await this.parsePicture(blockHeader.length);
75
+ return;
75
76
  default:
76
77
  this.metadata.addWarning('Unknown block type: ' + blockHeader.type);
77
78
  }
@@ -104,10 +105,11 @@ export class FlacParser extends AbstractID3Parser {
104
105
  const decoder = new VorbisDecoder(data, 0);
105
106
  decoder.readStringUtf8(); // vendor (skip)
106
107
  const commentListLength = decoder.readInt32();
108
+ const tags = new Array(commentListLength);
107
109
  for (let i = 0; i < commentListLength; i++) {
108
- const tag = decoder.parseUserComment();
109
- this.vorbisParser.addTag(tag.key, tag.value);
110
+ tags[i] = decoder.parseUserComment();
110
111
  }
112
+ await Promise.all(tags.map(tag => this.vorbisParser.addTag(tag.key, tag.value)));
111
113
  }
112
114
  async parsePicture(dataLen) {
113
115
  if (this.options.skipCovers) {
@@ -68,7 +68,7 @@ const Iid3v1Token = {
68
68
  };
69
69
  class Id3v1StringType extends StringType {
70
70
  constructor(len) {
71
- super(len, 'binary');
71
+ super(len, 'latin1');
72
72
  }
73
73
  get(buf, off) {
74
74
  let value = super.get(buf, off);
@@ -105,25 +105,25 @@ export class ID3v1Parser extends BasicParser {
105
105
  debug('ID3v1 header found at: pos=%s', this.tokenizer.fileInfo.size - Iid3v1Token.len);
106
106
  for (const id of ['title', 'artist', 'album', 'comment', 'track', 'year']) {
107
107
  if (header[id] && header[id] !== '')
108
- this.addTag(id, header[id]);
108
+ await this.addTag(id, header[id]);
109
109
  }
110
110
  const genre = ID3v1Parser.getGenre(header.genre);
111
111
  if (genre)
112
- this.addTag('genre', genre);
112
+ await this.addTag('genre', genre);
113
113
  }
114
114
  else {
115
115
  debug('ID3v1 header not found at: pos=%s', this.tokenizer.fileInfo.size - Iid3v1Token.len);
116
116
  }
117
117
  }
118
- addTag(id, value) {
119
- this.metadata.addTag('ID3v1', id, value);
118
+ async addTag(id, value) {
119
+ await this.metadata.addTag('ID3v1', id, value);
120
120
  }
121
121
  }
122
122
  export async function hasID3v1Header(reader) {
123
123
  if (reader.fileSize >= 128) {
124
- const tag = Buffer.alloc(3);
124
+ const tag = new Uint8Array(3);
125
125
  await reader.randomRead(tag, 0, tag.length, reader.fileSize - 128);
126
- return tag.toString('binary') === 'TAG';
126
+ return new TextDecoder('latin1').decode(tag) === 'TAG';
127
127
  }
128
128
  return false;
129
129
  }
@@ -1,4 +1,4 @@
1
- import { ITokenizer } from 'strtok3/core';
1
+ import { ITokenizer } from 'strtok3';
2
2
  import { BasicParser } from '../common/BasicParser.js';
3
3
  /**
4
4
  * Abstract parser which tries take ID3v2 and ID3v1 headers.
@@ -1,4 +1,4 @@
1
- import { EndOfStreamError } from 'strtok3/core';
1
+ import { EndOfStreamError } from 'strtok3';
2
2
  import initDebug from 'debug';
3
3
  import { ID3v2Header } from './ID3v2Token.js';
4
4
  import { ID3v2Parser } from './ID3v2Parser.js';
@@ -159,7 +159,7 @@ export class FrameParser {
159
159
  fzero = util.findZero(uint8Array, offset, length, encoding);
160
160
  pic.description = util.decodeString(uint8Array.slice(offset, fzero), encoding);
161
161
  offset = fzero + nullTerminatorLength;
162
- pic.data = Buffer.from(uint8Array.slice(offset, length));
162
+ pic.data = uint8Array.slice(offset, length);
163
163
  output = pic;
164
164
  }
165
165
  break;
@@ -246,7 +246,7 @@ export class FrameParser {
246
246
  // Decode URL
247
247
  fzero = util.findZero(uint8Array, offset + 1, length, encoding);
248
248
  const description = util.decodeString(uint8Array.slice(offset + 1, fzero), encoding);
249
- offset = fzero + (encoding === 'utf16le' ? 2 : 1);
249
+ offset = fzero + (encoding === 'utf-16le' ? 2 : 1);
250
250
  output = { description, url: util.decodeString(uint8Array.slice(offset, length), defaultEnc) };
251
251
  break;
252
252
  }
@@ -320,6 +320,6 @@ export class FrameParser {
320
320
  return { id, data: uint8Array.slice(offset, length) };
321
321
  }
322
322
  static getNullTerminatorLength(enc) {
323
- return enc === 'utf16le' ? 2 : 1;
323
+ return enc === 'utf-16le' ? 2 : 1;
324
324
  }
325
325
  }
@@ -1,6 +1,6 @@
1
1
  import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
2
- import { INativeMetadataCollector } from '../common/MetadataCollector.js';
3
- import { IRating, ITag } from '../type.js';
2
+ import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
3
+ import type { IRating, ITag } from '../type.js';
4
4
  export declare class ID3v24TagMapper extends CaseInsensitiveTagMap {
5
5
  static toRating(popm: any): IRating;
6
6
  constructor();
@@ -8,7 +8,6 @@ export declare class ID3v24TagMapper extends CaseInsensitiveTagMap {
8
8
  * Handle post mapping exceptions / correction
9
9
  * @param tag to post map
10
10
  * @param warnings Wil be used to register (collect) warnings
11
- * @return Common value e.g. "Buena Vista Social Club"
12
11
  */
13
12
  protected postMap(tag: ITag, warnings: INativeMetadataCollector): void;
14
13
  }
@@ -1,6 +1,7 @@
1
+ import { UINT32_LE } from 'token-types';
1
2
  import { CommonTagMapper } from '../common/GenericTagMapper.js';
2
3
  import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
3
- import * as util from '../common/Util.js';
4
+ import { decodeString } from '../common/Util.js';
4
5
  /**
5
6
  * ID3v2.3/ID3v2.4 tag mappings
6
7
  */
@@ -152,7 +153,6 @@ export class ID3v24TagMapper extends CaseInsensitiveTagMap {
152
153
  * Handle post mapping exceptions / correction
153
154
  * @param tag to post map
154
155
  * @param warnings Wil be used to register (collect) warnings
155
- * @return Common value e.g. "Buena Vista Social Club"
156
156
  */
157
157
  postMap(tag, warnings) {
158
158
  var _a, _b;
@@ -160,7 +160,7 @@ export class ID3v24TagMapper extends CaseInsensitiveTagMap {
160
160
  case 'UFID': // decode MusicBrainz Recording Id
161
161
  if (tag.value.owner_identifier === 'http://musicbrainz.org') {
162
162
  tag.id += ':' + tag.value.owner_identifier;
163
- tag.value = util.decodeString(tag.value.identifier, 'latin1'); // latin1 == iso-8859-1
163
+ tag.value = decodeString(tag.value.identifier, 'latin1'); // latin1 == iso-8859-1
164
164
  }
165
165
  break;
166
166
  case 'PRIV':
@@ -169,7 +169,7 @@ export class ID3v24TagMapper extends CaseInsensitiveTagMap {
169
169
  case 'AverageLevel':
170
170
  case 'PeakValue':
171
171
  tag.id += ':' + tag.value.owner_identifier;
172
- tag.value = tag.value.data.length === 4 ? tag.value.data.readUInt32LE(0) : null;
172
+ tag.value = tag.value.data.length === 4 ? UINT32_LE.get(tag.value.data, 0) : null;
173
173
  if (tag.value === null) {
174
174
  warnings.addWarning(`Failed to parse PRIV:PeakValue`);
175
175
  }
@@ -1,4 +1,4 @@
1
- import { ITokenizer } from 'strtok3/core';
1
+ import type { ITokenizer } from 'strtok3';
2
2
  import { IOptions } from '../type.js';
3
3
  import { INativeMetadataCollector } from '../common/MetadataCollector.js';
4
4
  export declare class ID3v2Parser {
@@ -2,6 +2,7 @@ import * as Token from 'token-types';
2
2
  import * as util from '../common/Util.js';
3
3
  import { FrameParser } from './FrameParser.js';
4
4
  import { ExtendedHeader, ID3v2Header, UINT32SYNCSAFE } from './ID3v2Token.js';
5
+ const asciiDecoder = new TextDecoder('ascii');
5
6
  export class ID3v2Parser {
6
7
  static removeUnsyncBytes(buffer) {
7
8
  let readI = 0;
@@ -98,33 +99,25 @@ export class ID3v2Parser {
98
99
  for (const tag of this.parseMetadata(uint8Array)) {
99
100
  if (tag.id === 'TXXX') {
100
101
  if (tag.value) {
101
- for (const text of tag.value.text) {
102
- this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, tag.value.description), text);
103
- }
102
+ await Promise.all(tag.value.text.map(text => this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, tag.value.description), text)));
104
103
  }
105
104
  }
106
105
  else if (tag.id === 'COM') {
107
- for (const value of tag.value) {
108
- this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value.text);
109
- }
106
+ await Promise.all(tag.value.map(value => this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value.text)));
110
107
  }
111
108
  else if (tag.id === 'COMM') {
112
- for (const value of tag.value) {
113
- this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value);
114
- }
109
+ await Promise.all(tag.value.map(value => this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value)));
115
110
  }
116
111
  else if (Array.isArray(tag.value)) {
117
- for (const value of tag.value) {
118
- this.addTag(tag.id, value);
119
- }
112
+ await Promise.all(tag.value.map(value => this.addTag(tag.id, value)));
120
113
  }
121
114
  else {
122
- this.addTag(tag.id, tag.value);
115
+ await this.addTag(tag.id, tag.value);
123
116
  }
124
117
  }
125
118
  }
126
- addTag(id, value) {
127
- this.metadata.addTag(this.headerType, id, value);
119
+ async addTag(id, value) {
120
+ await this.metadata.addTag(this.headerType, id, value);
128
121
  }
129
122
  parseMetadata(data) {
130
123
  let offset = 0;
@@ -152,7 +145,7 @@ export class ID3v2Parser {
152
145
  switch (majorVer) {
153
146
  case 2:
154
147
  header = {
155
- id: Buffer.from(uint8Array.slice(0, 3)).toString('ascii'),
148
+ id: asciiDecoder.decode(uint8Array.slice(0, 3)),
156
149
  length: Token.UINT24_BE.get(uint8Array, 3)
157
150
  };
158
151
  if (!header.id.match(/[A-Z0-9]{3}/g)) {
@@ -162,7 +155,7 @@ export class ID3v2Parser {
162
155
  case 3:
163
156
  case 4:
164
157
  header = {
165
- id: Buffer.from(uint8Array.slice(0, 4)).toString('ascii'),
158
+ id: asciiDecoder.decode(uint8Array.slice(0, 4)),
166
159
  length: (majorVer === 4 ? UINT32SYNCSAFE : Token.UINT32_BE).get(uint8Array, 4),
167
160
  flags: ID3v2Parser.readFrameFlags(uint8Array.slice(8, 10))
168
161
  };
@@ -1,4 +1,4 @@
1
- import { IGetToken } from 'strtok3/core';
1
+ import type { IGetToken } from 'strtok3';
2
2
  import * as util from '../common/Util.js';
3
3
  /**
4
4
  * The picture type according to the ID3v2 APIC frame
@@ -91,9 +91,9 @@ export const TextEncodingToken = {
91
91
  case 0x00:
92
92
  return { encoding: 'latin1' }; // binary
93
93
  case 0x01:
94
- return { encoding: 'utf16le', bom: true };
94
+ return { encoding: 'utf-16le', bom: true };
95
95
  case 0x02:
96
- return { encoding: 'utf16le', bom: false };
96
+ return { encoding: 'utf-16le', bom: false };
97
97
  case 0x03:
98
98
  return { encoding: 'utf8', bom: false };
99
99
  default:
@@ -1,4 +1,4 @@
1
- import { IGetToken } from 'strtok3/core';
1
+ import type { IGetToken } from 'strtok3';
2
2
  /**
3
3
  * "EA IFF 85" Standard for Interchange Format Files
4
4
  * Ref: http://www.martinreddy.net/gfx/2d/IFF.txt
package/lib/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import * as Stream from 'stream';
3
2
  import * as strtok3 from 'strtok3';
4
3
  import { IAudioMetadata, IOptions } from './type.js';
5
4
  export { IAudioMetadata, IOptions, ITag, INativeTagDict, ICommonTagsResult, IFormat, IPicture, IRatio, IChapter } from './type.js';
6
- export { parseFromTokenizer, parseBuffer, selectCover, orderTags, ratingToStars, IFileInfo } from './core.js';
5
+ export { parseFromTokenizer, parseBuffer, parseBlob, selectCover, orderTags, ratingToStars, IFileInfo } from './core.js';
7
6
  /**
8
7
  * Parse audio from Node Stream.Readable
9
8
  * @param stream - Stream to read the audio track from
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ import initDebug from 'debug';
3
3
  import { parseFromTokenizer, scanAppendingHeaders } from './core.js';
4
4
  import { ParserFactory } from './ParserFactory.js';
5
5
  import { RandomFileReader } from './common/RandomFileReader.js';
6
- export { parseFromTokenizer, parseBuffer, selectCover, orderTags, ratingToStars } from './core.js';
6
+ export { parseFromTokenizer, parseBuffer, parseBlob, selectCover, orderTags, ratingToStars } from './core.js';
7
7
  const debug = initDebug('music-metadata:parser');
8
8
  /**
9
9
  * Parse audio from Node Stream.Readable