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
package/README.md CHANGED
@@ -11,15 +11,34 @@
11
11
 
12
12
  # music-metadata
13
13
 
14
- Stream and file based music metadata parser for [node.js](https://nodejs.org/) and browser projects.
15
- Supports any common audio and tagging format.
14
+ Key features:
15
+ - **Comprehensive Format Support**: Supports popular audio formats like MP3, MP4, FLAC, Ogg, WAV, AIFF, and more.
16
+ - **Extensive Metadata Extraction**: Extracts detailed metadata, including ID3v1, ID3v2, APE, Vorbis, and iTunes/MP4 tags.
17
+ - **Streaming Support**: Efficiently handles large audio files by reading metadata from streams, making it suitable for server-side and browser-based applications.
18
+ - **Promise-Based API**: Provides a modern, promise-based API for easy integration into asynchronous workflows.
19
+ - **Cross-Platform**: Works in both [Node.js](https://nodejs.org/) and browser environments with the help of bundlers like [Webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/introduction/).
20
+
21
+ The [`music-metadata`](https://github.com/Borewit/music-metadata) module is ideal for developers working on media applications, music players, or any project that requires access to detailed audio file metadata.
16
22
 
17
23
  ## Compatibility
18
24
 
19
25
  Module: version 8 migrated from [CommonJS](https://en.wikipedia.org/wiki/CommonJS) to [pure ECMAScript Module (ESM)](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
20
- JavaScript is compliant with [ECMAScript 2019 (ES10)](https://en.wikipedia.org/wiki/ECMAScript#10th_Edition_%E2%80%93_ECMAScript_2019).
21
- Requires Node.js ≥ 16 engine.
22
- Primarily designed for [Node.js](https://nodejs.org/), but has also been designed for browser compatibility.
26
+ The distributed JavaScript codebase is compliant with the [ECMAScript 2020 (11th Edition)](https://en.wikipedia.org/wiki/ECMAScript_version_history#11th_Edition_%E2%80%93_ECMAScript_2020) standard.
27
+
28
+ > [!NOTE]
29
+ > See also [CommonJS Backward Compatibility](#commonjs-backward-compatibility)
30
+
31
+ This module requires a [Node.js ≥ 16](https://nodejs.org/en/about/previous-releases) engine.
32
+ It can also be used in a browser environment when bundled with a module bundler.
33
+
34
+ ## Sponsor
35
+ If you appreciate my work and want to support the development of open-source projects like [music-metadata](https://github.com/Borewit/music-metadata), [file-type](https://github.com/sindresorhus/file-type), and [listFix()](https://github.com/Borewit/listFix), consider becoming a sponsor or making a small contribution.
36
+ Your support helps sustain ongoing development and improvements.
37
+ [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
38
+
39
+ or
40
+
41
+ <a href="https://www.buymeacoffee.com/borewit" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy me A coffee" height="41" width="174"></a>
23
42
 
24
43
  ## Features
25
44
 
@@ -79,48 +98,7 @@ Support for encoding / format details:
79
98
  ## Online demo's
80
99
  - [<img src="https://raw.githubusercontent.com/Borewit/audio-tag-analyzer/master/src/assets/icon/audio-tag-analyzer.svg" width="40">Audio Tag Analyzer](https://audio-tag-analyzer.netlify.app/)
81
100
  - [<img src="https://cdn.sanity.io/images/3do82whm/next/ba8c847f13a5fa39d88f8bc9b7846b7886531b18-2500x2500.svg" width="40"> Webamp](https://webamp.org/)
82
-
83
-
84
- ### Sponsor
85
- [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
86
-
87
- <a href="https://www.buymeacoffee.com/borewit" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy me A coffee" height="41" width="174"></a>
88
-
89
- ## Dependencies
90
-
91
- Dependency diagram:
92
- ```mermaid
93
- graph TD;
94
- MMN("music-metadata (Node.js entry point)")-->MMP
95
- MMN-->FTN
96
- MMP("music-metadata (primary entry point)")-->S(strtok3)
97
- MMP-->TY(token-types)
98
- MMP-->FTP
99
- MMP-->UAE
100
- FTN("file-type (Node.js entry point)")-->FTP
101
- FTP("file-type (primary entry point)")-->S
102
- S(strtok3)-->P(peek-readable)
103
- S(strtok3)-->TO("@tokenizer/token")
104
- TY(token-types)-->TO
105
- TY-->IE("ieee754")
106
- FTP-->TY
107
- NS("node:stream")
108
- FTN-->NS
109
- FTP-->UAE(uint8array-extras)
110
- style NS fill:#F88,stroke:#A44
111
- style IE fill:#CCC,stroke:#888
112
- style FTN fill:#FAA,stroke:#A44
113
- style MMN fill:#FAA,stroke:#A44
114
- ```
115
-
116
- Dependency list:
117
- - [tokenizer-token](https://github.com/Borewit/tokenizer-token)
118
- - [strtok3](https://github.com/Borewit/strtok3)
119
- - [token-types](https://github.com/Borewit/token-types)
120
- - [file-type](https://github.com/sindresorhus/file-type)
121
- - [@tokenizer-token](https://github.com/Borewit/tokenizer-token)
122
- - [peek-readable](https://github.com/Borewit/peek-readable)
123
- - [readable-web-to-node-stream](https://github.com/Borewit/readable-web-to-node-stream)
101
+ - Expected to be released soon: [Overtone](https://overtone.pro/) by [Johannes Schickling](https://github.com/schickling)
124
102
 
125
103
  ## Usage
126
104
 
@@ -135,146 +113,428 @@ or using [yarn](https://yarnpkg.com/):
135
113
  yarn add music-metadata
136
114
  ```
137
115
 
138
- ### Import music-metadata
116
+ ## API Documentation
139
117
 
140
- Import music-metadata:
141
- ```JavaScript
142
- import { parseFile } from 'music-metadata';
143
- ```
144
- Import the methods you need, like `parseFile` in this example.
118
+ ### Overview
119
+
120
+ **Node.js specific** functions to read an audio file or stream:
121
+ 1. **File Parsing**: Parse audio files directly from the filesystem using the [parseFile function](#parsefile-function)
122
+ 1. **Stream Parsing**: Parse audio metadata from a Node.js [Readable stream](https://nodejs.org/api/stream.html#class-streamreadable) using the [parseStream function](#parsewebstream-function).
145
123
 
146
- ### Module Functions
124
+ **Cross-platform** functions available to read an audio file or stream:
147
125
 
148
126
  There are multiple ways to parse (read) audio tracks:
149
- 1. In Node.js, audio (music) files can be parsed using direct file access using the [parseFile function](#parsefile-function)
150
- 1. By parsing from a Web stream using the [parseStream function](#parsewebstream-function).
151
- 1. By parsing a (Web API) [Blob](https://developer.mozilla.org/docs/Web/API/Blob) or a [File](https://developer.mozilla.org/docs/Web/API/File) using the [parseBlob function](#parseblob-function).
152
- 1. From a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) using the [parseBuffer function](#parsebuffer-function).
153
- 1. Via your own, or a third-party [strtok3](https://github.com/Borewit/strtok3) ITokenizer using the [parseFromTokenizer function](#parsefromtokenizer-function).
127
+ 1. **Web Stream Parsing**: Parse audio data from a web-compatible [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) using the [parseWebStream function](#parsewebstream-function).
128
+ 1. **Blob Parsing**: Parse audio metadata from a (Web API) [Blob](https://developer.mozilla.org/docs/Web/API/Blob) or [File](https://developer.mozilla.org/docs/Web/API/File) using the [parseBlob function](#parseblob-function).
129
+ 1. **Buffer Parsing**: Parse audio metadata from a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) or [Buffer](https://nodejs.org/api/buffer.html) using the [parseBuffer function](#parsebuffer-function).
130
+ 1. **Tokenizer Parsing:** Use a custom or third-party [strtok3](https://github.com/Borewit/strtok3) `ITokenizer` to parse using the [parseFromTokenizer function](#parsefromtokenizer-function).
154
131
 
155
- Direct file access tends to be a little faster, because it can 'jump' to various parts in the file without being obliged to read intermediate data.
132
+ > [!NOTE]
133
+ > Direct file access in Node.js is generally faster because it can 'jump' to various parts of the file without reading intermediate data.
156
134
 
157
- #### parseFile function
135
+ ### Node.js specific function
136
+ These functions are tailored for Node.js environments and leverage Node.js-specific APIs,
137
+ making them incompatible with browser-based JavaScript engines.
158
138
 
159
- This method can only be used if the JavaScript engine is Node.js.
160
- To read from a [File](https://developer.mozilla.org/docs/Web/API/File),
161
- please see [fileTypeFromBlob(blob)](#parseblob-function).
139
+ #### `parseFile` function
162
140
 
163
- Parses the specified file (`filePath`) and returns a promise with the metadata result (`IAudioMetadata`).
141
+ The `parseFile` function is intended for extracting metadata from audio files on the local filesystem in a Node.js environment.
142
+ It reads the specified file, parses its audio metadata, and returns a promise that resolves with this information.
164
143
 
144
+ ##### Syntax
165
145
  ```ts
166
- parseFile(filePath: string, opts: IOptions = {}): Promise<IAudioMetadata>`
146
+ parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>
167
147
  ```
168
148
 
169
- Example:
149
+ ##### Parameters
150
+
151
+ - `filePath`: `string`
152
+
153
+ The path to the media file from which metadata should be extracted.
154
+ This should be a valid path to an audio file on the local filesystem.
155
+
156
+ - `options`: [IOptions](#ioptions-interface) (optional)
157
+
158
+ An optional configuration object that allows customization of the parsing process.
159
+ These options can include whether to calculate the file's duration, skip embedded cover art,
160
+ or other parsing behaviors.
161
+
162
+ ##### Returns
163
+
164
+ - `Promise<IAudioMetadata>`:
165
+
166
+ A promise that resolves to an IAudioMetadata object containing metadata about the audio file.
167
+ The metadata includes details such as the file format, codec, duration, bit rate, and any embedded tags like album, artist, or track information.
168
+
169
+ ##### Usage Notes
170
+
171
+ - This function is **Node.js-only** and relies on Node.js-specific APIs to access the filesystem.
172
+
173
+ - For browser environments, consider using the [parseBlob](#parseblob-function) to parse [File object](https://developer.mozilla.org/en-US/docs/Web/API/File) objects.
174
+
175
+ ##### Example:
176
+
177
+ The following example demonstrates how to use the parseFile function to read metadata from an audio file:
170
178
  ```js
171
179
  import { parseFile } from 'music-metadata';
172
180
  import { inspect } from 'util';
173
181
 
174
182
  (async () => {
175
183
  try {
176
- const metadata = await parseFile('../music-metadata/test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3');
184
+ const filePath = '../music-metadata/test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3';
185
+ const metadata = await parseFile(filePath);
186
+
187
+ // Output the parsed metadata to the console in a readable format
177
188
  console.log(inspect(metadata, { showHidden: false, depth: null }));
178
189
  } catch (error) {
179
- console.error(error.message);
190
+ console.error('Error parsing metadata:', error.message);
180
191
  }
181
192
  })();
182
193
  ```
183
194
 
184
- #### parseStream function
185
-
186
- _Only available using a Node.js JavaScript engines._
195
+ #### `parseStream` function
187
196
 
188
- Parses the provided audio stream for metadata.
189
- The stream should be of type [Node.js Readable](https://nodejs.org/api/stream.html#class-streamreadable).
190
- It is recommended to provide the corresponding [MIME-type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types).
191
- An extension (e.g.: `.mp3`), filename or path will also work.
192
- If the MIME-type or filename (via `fileInfo.path`) is not provided, or not understood, music-metadata will try to derive the type from the content.
197
+ The parseStream function is used to parse metadata from an audio track provided as a Node.js [`Readable`](https://nodejs.org/api/stream.html#class-streamreadable) stream.
198
+ This is particularly useful for processing audio data that is being streamed or piped from another source, such as a web server or file system.
193
199
 
200
+ ##### Syntax:
194
201
  ```ts
195
- parseStream(stream: Stream.Readable, fileInfo?: IFileInfo | string, opts?: IOptions = {}): Promise<IAudioMetadata>`
202
+ parseStream(stream: Readable, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>
196
203
  ```
197
204
 
198
- Example:
205
+ ##### Parameters:
206
+
207
+ - `stream`: `Readable`:
208
+
209
+ The Node.js [Readable](https://nodejs.org/api/stream.html#class-streamreadable) stream from which the audio data is read.
210
+ This stream should provide the raw audio data to be analyzed.
211
+
212
+ - `fileInfo`: `IFileInfo` (optional)
213
+
214
+ An object containing file-related information or a string representing the MIME-type of the audio stream.
215
+ The fileInfo parameter can help the parser to correctly identify the audio format and may include:
216
+
217
+ - `mimeType`: A string representing the [MIME-type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) (e.g., `audio/mpeg`).
218
+
219
+ If provided, it is assumed the streamed file content is to be the MIME-type.
220
+ If not provided, the parser will attempt to determine the format based on the content of the stream.
221
+
222
+ - `size`: The total size of the audio stream in bytes (useful for streams with a known length).
223
+
224
+ - `path`: A string representing the file path or filename, which can also assist in determining the format.
225
+
226
+ - `options`: `IOptions` (optional)
227
+
228
+ An optional object containing additional parsing options.
229
+ These options allow you to customize the parsing process,
230
+ such as whether to calculate the duration or skip cover art extraction.
231
+
232
+ ##### Returns
233
+
234
+ - `Promise<IAudioMetadata>`:
235
+
236
+ A promise that resolves to an `IAudioMetadata` object containing detailed metadata about the audio stream.
237
+ This metadata includes information about the format, codec, duration, bitrate, and any embedded tags such as artist, album, or track information.
238
+
239
+ ##### Usage Notes
240
+ - This function is only available in Node.js environments, as it relies on the [Node.js stream API](https://nodejs.org/api/stream.html).
241
+
242
+ ##### Example:
243
+
244
+ The following example demonstrates how to use the `parseStream` function to read metadata from an audio stream:
199
245
  ```js
200
246
  import { parseStream } from 'music-metadata';
247
+ import { createReadStream } from 'fs';
201
248
 
202
249
  (async () => {
203
250
  try {
204
- const metadata = await parseStream(someReadStream, {mimeType: 'audio/mpeg', size: 26838});
251
+ // Create a readable stream from a file
252
+ const audioStream = createReadStream('path/to/audio/file.mp3');
253
+
254
+ // Parse the metadata from the stream
255
+ const metadata = await parseStream(audioStream, { mimeType: 'audio/mpeg'});
256
+
257
+ // Log the parsed metadata
205
258
  console.log(metadata);
206
259
  } catch (error) {
207
- console.error(error.message);
260
+ console.error('Error parsing metadata:', error.message);
208
261
  }
209
262
  })();
263
+
210
264
  ```
211
265
 
212
- #### parseWebStream function
266
+ ### Cross-platform functions
267
+ These functions are designed to be cross-platform,
268
+ meaning it can be used in both Node.js and web browsers.
269
+
270
+ #### `parseWebStream` function
271
+
272
+ The parseWebStream function is used to extract metadata from an audio track provided as a web-compatible ReadableStream.
273
+ This function is ideal for applications running in web environments, such as browsers,
274
+ where audio data is streamed over the network or read from other web-based sources.
213
275
 
214
- Parses the provided audio web stream for metadata.
215
- The stream should be of type [ReadableStream<Uint8Array>](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream).
216
- It is recommended to provide the corresponding [MIME-type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types).
217
- An extension (e.g.: `.mp3`), filename or path will also work.
218
- If the MIME-type or filename (via `fileInfo.path`) is not provided, or not understood, music-metadata will try to derive the type from the content.
219
276
 
277
+ ##### Syntax
220
278
  ```ts
221
- parseWebStream(stream: ReadableStream<Uint8Array>, fileInfo?: IFileInfo | string, opts?: IOptions = {}): Promise<IAudioMetadata>`
279
+ parseWebStream(webStream: ReadableStream<Uint8Array>, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>
222
280
  ```
223
281
 
224
- #### parseBlob function
282
+ ##### Parameters
225
283
 
226
- _This method can only be used if with a Node.js JavaScript engine._
284
+ - `webStream`: `ReadableStream<Uint8Array>`
227
285
 
228
- Parse an audio file from a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or [File](https://developer.mozilla.org/en-US/docs/Web/API/File).
286
+ A [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) that provides the audio data to be parsed.
287
+ This stream should emit Uint8Array chunks, representing the raw audio data.
288
+
289
+ - `fileInfo`: `IFileInfo` (optional)
290
+
291
+ An object containing file-related information or a string representing the MIME-type of the audio stream.
292
+ The fileInfo parameter can help the parser to correctly identify the audio format and may include:
293
+
294
+ - `mimeType`: A string representing the [MIME-type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) (e.g., `audio/mpeg`).
295
+
296
+ If provided, it is assumed the streamed file content is to be the MIME-type.
297
+ If not provided, the parser will attempt to determine the format based on the content of the stream.
298
+
299
+ - `size`: The total size of the audio stream in bytes (useful for streams with a known length).
300
+
301
+ - `path`: A string representing the file path or filename, which can also assist in determining the format.
302
+
303
+ - `options`: `IOptions` (optional)
304
+
305
+ An optional object containing additional parsing options.
306
+ These options allow you to customize the parsing process,
307
+ such as whether to calculate the duration or skip cover art extraction.
308
+
309
+ ##### Returns
310
+
311
+ - `Promise<IAudioMetadata>`:
312
+
313
+ A promise that resolves to an `IAudioMetadata` object containing detailed metadata about the audio stream.
314
+ This metadata includes information about the format, codec, duration, bitrate, and any embedded tags such as artist, album, or track information.
315
+
316
+ ##### Example
317
+ Here’s an example of how to use the `parseWebStream` function to extract metadata from an audio stream in a web application:
229
318
 
230
319
  ```js
231
- const musicMetadata = require('music-metadata');
320
+ import { parseWebStream } from 'music-metadata';
321
+
322
+ (async () => {
323
+ try {
324
+ // Assuming you have a ReadableStream of an audio file
325
+ const response = await fetch('https://example.com/path/to/audio/file.mp3');
326
+ const webStream = response.body;
232
327
 
233
- let blob;
328
+ // Parse the metadata from the web stream
329
+ const metadata = await parseWebStream(webStream, 'audio/mpeg');
234
330
 
235
- musicMetadata.parseBlob(blob).then(metadata => {
236
- // metadata has all the metadata found in the blob or file
237
- });
331
+ // Log the parsed metadata
332
+ console.log(metadata);
333
+ } catch (error) {
334
+ console.error('Error parsing metadata:', error.message);
335
+ }
336
+ })();
238
337
  ```
239
- Or with async/await if you prefer:
338
+
339
+ The example uses the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to retrieve an audio file from a URL.
340
+ The `response.body` provides a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) that is then passed to `parseWebStream`.
341
+
342
+ #### `parseBlob` function
343
+
344
+ Parses metadata from an audio file represented as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
345
+ This function is suitable for use in environments that support the ReadableStreamBYOBReader, which is **available in Node.js 20** and above.
346
+
347
+ ##### Syntax
348
+ ```ts
349
+ parseBlob(blob: Blob, options?: IOptions = {}): Promise<IAudioMetadata>
350
+ ```
351
+
352
+ ##### Parameters
353
+
354
+ - `blob`: [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
355
+
356
+ The Blob object containing the audio data to be parsed.
357
+ This can be a file or any binary data. If the Blob is an instance of File, its name will be used as the file path in the metadata.
358
+
359
+ - `options`: [IOptions](#ioptions-interface) (optional)
360
+
361
+ An optional configuration object that specifies parsing options.
362
+
363
+ ##### Returns
364
+
365
+ - `Promise<IAudioMetadata>`:
366
+
367
+ A promise that resolves to the metadata of the audio file.
368
+
369
+ ##### Example
370
+
240
371
  ```js
241
- (async () => {
242
- let blob; // File or Blob
372
+ import { parseBlob } from 'music-metadata';
243
373
 
244
- const metadata = await musicMetadata.parseBlob(blob);
245
- // metadata has all the metadata found in the blob or file
246
- });
374
+ (async () => {
375
+ const fileInput = document.querySelector('input[type="file"]');
376
+ const file = fileInput.files[0];
377
+
378
+ try {
379
+ const metadata = await parseBlob(file);
380
+ console.log(metadata);
381
+ } catch (error) {
382
+ console.error('Error parsing metadata:', error.message);
383
+ }
384
+ })();
247
385
  ```
248
386
 
249
- #### parseBuffer function
387
+ #### `parseBuffer` function
250
388
 
251
- Parse metadata from an audio file, where the audio file is held in a [Buffer](https://nodejs.org/api/buffer.html).
389
+ Parses metadata from an audio file where the audio data is held in a [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) or [Buffer](https://nodejs.org/api/buffer.html).
390
+ This function is particularly useful when you already have audio data in memory.
252
391
 
392
+ ##### Syntax
253
393
  ```ts
254
394
  parseBuffer(buffer: Uint8Array, fileInfo?: IFileInfo | string, opts?: IOptions = {}): Promise<IAudioMetadata>
255
395
  ```
256
396
 
257
- Example:
397
+ ##### Parameters
398
+ - `uint8Array`: [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
399
+
400
+ A [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) containing the audio data to be parsed.
401
+
402
+ - `fileInfo`: `IFileInfo` | `string` (optional)
403
+
404
+ An object containing file information such as mimeType and size.
405
+ Alternatively, you can pass a MIME-type string directly.
406
+ This helps the parser understand the format of the audio data.
407
+
408
+ - `options`: [IOptions](#ioptions-interface) (optional)
409
+
410
+ An optional configuration object that specifies parsing options.
411
+
412
+ ##### Returns
413
+ - `Promise<IAudioMetadata>`:
414
+
415
+ A promise that resolves to the metadata of the audio file.
416
+
417
+
418
+ ##### Example
419
+
258
420
  ```js
259
421
  import { parseBuffer } from 'music-metadata';
422
+ import fs from 'fs';
260
423
 
261
424
  (async () => {
425
+ const buffer = fs.readFileSync('path/to/audio/file.mp3');
426
+
262
427
  try {
263
- const metadata = await parseBuffer(someBuffer, 'audio/mpeg');
428
+ const metadata = await parseBuffer(buffer, { mimeType: 'audio/mpeg' });
264
429
  console.log(metadata);
265
430
  } catch (error) {
266
- console.error(error.message);
431
+ console.error('Error parsing metadata:', error.message);
267
432
  }
268
433
  })();
269
434
  ```
270
435
 
271
- #### parseFromTokenizer function
272
- This is a low level function, reading from a [strtok3](https://github.com/Borewit/strtok3) ITokenizer interface.
436
+ #### `parseFromTokenizer` function
437
+ Parses metadata from an audio source that implements the [strtok3](https://github.com/Borewit/strtok3) ITokenizer interface.
438
+ This is a low-level function that provides flexibility for advanced use cases,
439
+ such as parsing metadata from streaming audio or custom data sources.
273
440
 
274
441
  This also enables special read modules like:
275
442
  - [streaming-http-token-reader](https://github.com/Borewit/streaming-http-token-reader) for chunked HTTP(S) reading, using [HTTP range requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests).
276
443
 
277
- #### orderTags function
444
+ ##### Syntax
445
+ ```ts
446
+ parseFromTokenizer(tokenizer: ITokenizer, options?: IOptions): Promise<IAudioMetadata>
447
+ ```
448
+
449
+ ##### Parameters
450
+ - `tokenizer: ITokenizer`
451
+
452
+ An instance of an ITokenizer that provides access to the audio data.
453
+ The tokenizer abstracts the reading process, enabling support for various types of sources, including streams, buffers, or custom data readers.
454
+
455
+ - `options`: [IOptions](#ioptions-interface) (optional)
456
+
457
+ An optional configuration object that specifies parsing options.
458
+
459
+ ##### Returns
460
+ - `Promise<IAudioMetadata>`:
461
+
462
+ A promise that resolves to the metadata of the audio source, including information like the title, artist, album, and more.
463
+
464
+
465
+ ##### Example
466
+ ````js
467
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
468
+ import { S3Client } from '@aws-sdk/client-s3';
469
+ import { makeTokenizer } from '@tokenizer/s3';
470
+ import { parseFromTokenizer as mmParseFromTokenizer } from 'music-metadata';
471
+
472
+ // Configure the S3 client
473
+ const s3 = new S3Client({
474
+ region: 'eu-west-2',
475
+ credentials: fromNodeProviderChain(),
476
+ });
477
+
478
+ // Helper function to create a tokenizer for S3 objects
479
+ async function makeS3TestDataTokenizer(key, options) {
480
+ return await makeTokenizer(s3, {
481
+ Bucket: 'music-metadata',
482
+ Key: key,
483
+ }, options);
484
+ }
485
+
486
+ // Function to read and log metadata from an S3 object
487
+ async function readMetadata() {
488
+ try {
489
+ // Create a tokenizer for the specified S3 object
490
+ const tokenizer = await makeS3TestDataTokenizer('path/to/audio/file.mp3', { disableChunked: false });
491
+
492
+ // Parse the metadata from the tokenizer
493
+ const metadata = await mmParseFromTokenizer(tokenizer);
494
+
495
+ // Log the retrieved metadata
496
+ console.log(metadata);
497
+ } catch (error) {
498
+ console.error('Error parsing metadata:', error.message);
499
+ }
500
+ }
501
+
502
+ // Execute the metadata reading function
503
+ readMetadata();
504
+ ````
505
+ ##### Additional Resources
506
+ - [strtok3](https://github.com/Borewit/strtok3) - Learn more about the `ITokenizer` interface and how to implement it for various use cases.
507
+ - [AWS SDK for JavaScript](https://aws.amazon.com/sdk-for-javascript/) - Documentation on using the AWS SDK to interact with S3 and other AWS services.
508
+ - [@tokenizer/s3](https://github.com/Borewit/tokenizer-s3) - Example of `ITokenizer` implementation.
509
+
510
+ ### Handling Parse Errors
511
+
512
+ `music-metadata` provides a robust and extensible error handling system with custom error classes that inherit from the standard JavaScript `Error`.
513
+ All possible parsing errors are part of a union type `UnionOfParseErrors`, ensuring that every error scenario is accounted for in your code.
514
+
515
+ #### Union of Parse Errors
516
+
517
+ All parsing errors extend from the base class `ParseError` and are included in the `UnionOfParseErrors` type:
518
+ ```ts
519
+ export type UnionOfParseErrors =
520
+ | CouldNotDetermineFileTypeError
521
+ | UnsupportedFileTypeError
522
+ | UnexpectedFileContentError
523
+ | FieldDecodingError
524
+ | InternalParserError;
525
+ ```
526
+
527
+ #### Error Types
528
+
529
+ - `CouldNotDetermineFileTypeError`: Raised when the file type cannot be determined.
530
+ - `UnsupportedFileTypeError`: Raised when an unsupported file type is encountered.
531
+ - `UnexpectedFileContentError`: Raised when the file content does not match the expected format.
532
+ - `FieldDecodingError`: Raised when a specific field in the file cannot be decoded.
533
+ - `InternalParserError`: Raised for internal parser errors.
534
+
535
+ ### Other functions
536
+
537
+ #### `orderTags` function
278
538
 
279
539
  Utility to Converts the native tags to a dictionary index on the tag identifier
280
540
 
@@ -297,14 +557,14 @@ import { inspect } from 'util';
297
557
  })();
298
558
  ```
299
559
 
300
- #### ratingToStars function
560
+ #### `ratingToStars` function
301
561
 
302
562
  Can be used to convert the normalized rating value to the 0..5 stars, where 0 an undefined rating, 1 the star the lowest rating and 5 the highest rating.
303
563
 
304
564
  ```ts
305
565
  ratingToStars(rating: number): number
306
566
  ```
307
- #### selectCover function
567
+ #### `selectCover` function
308
568
 
309
569
  Select cover image based on image type field, otherwise the first picture in file.
310
570
 
@@ -322,17 +582,33 @@ import { parseFile, selectCover } from 'music-metadata';
322
582
  )();
323
583
  ```
324
584
 
325
- ### Options
326
- - `duration`: default: `false`, if set to `true`, it will parse the whole media file if required to determine the duration.
327
- - `observer: (update: MetadataEvent) => void;`: Will be called after each change to `common` (generic) tag, or `format` properties.
328
- - `skipCovers`: default: `false`, if set to `true`, it will not return embedded cover-art (images).
329
- - `skipPostHeaders? boolean` default: `false`, if set to `true`, it will not search all the entire track for additional headers. Only recommenced to use in combination with streams.
330
- - `includeChapters` default: `false`, if set to `true`, it will parse chapters (currently only MP4 files). _experimental functionality_
585
+ ### `IOptions` Interface
586
+ - `duration`: `boolean` (default: `false`)
587
+
588
+ If set to `true`, the parser will analyze the entire media file, if necessary, to determine its duration.
589
+ This option ensures accurate duration calculation but may increase processing time for large files.
331
590
 
332
- Although in most cases duration is included, in some cases it requires `music-metadata` parsing the entire file.
333
- To enforce parsing the entire file if needed you should set `duration` to `true`.
591
+ - `observer`: `(update: MetadataEvent) => void;`:
334
592
 
335
- ### Metadata result
593
+ A callback function that is invoked whenever there is an update to the common (generic) tag or format properties during parsing.
594
+ This allows for real-time updates on metadata changes.
595
+
596
+ - `skipCovers`: `boolean` (default: `false`)
597
+
598
+ If set to `true`, the parser will skip the extraction of embedded cover art (images) from the media file.
599
+ This can be useful to avoid processing unnecessary data if cover images are not required.
600
+
601
+ - `mkvUseIndex`: `boolean` (default: `false`)
602
+
603
+ If set to true, the parser will use the SeekHead element index to skip segment/cluster elements in Matroska-based files. This is an experimental feature and can significantly impact performance. It may also result in some metadata being skipped if it is not indexed.
604
+ If the SeekHead element is absent in the Matroska file, this flag has no effect.
605
+
606
+ > [!NOTE]
607
+ > - The `duration` option is typically included in most cases, but setting it to true ensures that the entire file is parsed if necessary to get an accurate duration.
608
+ > - Using `mkvUseIndex` can improve performance in Matroska files, but be aware of potential side effects, such as missing metadata due to skipped elements.
609
+
610
+
611
+ ### `IAudioMetadata` interface
336
612
 
337
613
  If the returned promise resolves, the metadata (TypeScript `IAudioMetadata` interface) contains:
338
614
  - [`metadata.format`](#metadataformat) Audio format information
@@ -362,7 +638,7 @@ Audio format information. Defined in the TypeScript `IFormat` interface:
362
638
 
363
639
  #### `metadata.trackInfo`
364
640
 
365
- To support advanced containers like [Matroska](https://wikipedia.org/wiki/Matroska) or [MPEG-4](https://en.wikipedia.org/wiki/MPEG-4), which may contain multiple audio and video tracks, the **experimental*- `metadata.trackInfo` has been added,
641
+ To support advanced containers like [Matroska](https://wikipedia.org/wiki/Matroska) or [MPEG-4](https://en.wikipedia.org/wiki/MPEG-4), which may contain multiple audio and video tracks, the **experimental**- `metadata.trackInfo` has been added,
366
642
 
367
643
  `metadata.trackInfo` is either `undefined` or has an **array** of [trackInfo](#trackinfo)
368
644
 
@@ -456,6 +732,59 @@ import {uint8ArrayToBase64} from 'uint8array-extras';
456
732
  img.src = `data:${picture.format};base64,${uint8ArrayToBase64(picture.data)}`;
457
733
  ```
458
734
 
735
+ ## Dependencies
736
+
737
+ Dependency diagram:
738
+ ```mermaid
739
+ graph TD;
740
+ MMN("music-metadata (Node.js entry point)")-->MMP
741
+ MMN-->FTN
742
+ MMP("music-metadata (primary entry point)")-->S(strtok3)
743
+ MMP-->TY(token-types)
744
+ MMP-->FTP
745
+ MMP-->UAE
746
+ FTN("file-type (Node.js entry point)")-->FTP
747
+ FTP("file-type (primary entry point)")-->S
748
+ S(strtok3)-->P(peek-readable)
749
+ S(strtok3)-->TO("@tokenizer/token")
750
+ TY(token-types)-->TO
751
+ TY-->IE("ieee754")
752
+ FTP-->TY
753
+ NS("node:stream")
754
+ FTN-->NS
755
+ FTP-->UAE(uint8array-extras)
756
+ style NS fill:#F88,stroke:#A44
757
+ style IE fill:#CCC,stroke:#888
758
+ style FTN fill:#FAA,stroke:#A44
759
+ style MMN fill:#FAA,stroke:#A44
760
+ ```
761
+
762
+ Dependency list:
763
+ - [tokenizer-token](https://github.com/Borewit/tokenizer-token)
764
+ - [strtok3](https://github.com/Borewit/strtok3)
765
+ - [token-types](https://github.com/Borewit/token-types)
766
+ - [file-type](https://github.com/sindresorhus/file-type)
767
+ - [@tokenizer-token](https://github.com/Borewit/tokenizer-token)
768
+ - [peek-readable](https://github.com/Borewit/peek-readable)
769
+
770
+ ## CommonJS Backward compatibility
771
+
772
+ For legacy CommonJS projects needing to load the `music-metadata` ESM module, you can use the `loadMusicMetadata` function:
773
+ ```js
774
+ const { loadMusicMetadata } = require('music-metadata');
775
+
776
+ (async () => {
777
+ // Dynamically loads the ESM module in a CommonJS project
778
+ const mm = await loadMusicMetadata();
779
+
780
+ const metadata = await mm.parseFile('/path/to/your/file');
781
+ })();
782
+
783
+ ```
784
+
785
+ > [!NOTE]
786
+ > The `loadMusicMetadata` function is experimental and is not currently covered by any TypeScript typings.
787
+
459
788
  ## Frequently Asked Questions
460
789
 
461
790
  1. How can I traverse (a long) list of files?
@@ -483,35 +812,30 @@ img.src = `data:${picture.format};base64,${uint8ArrayToBase64(picture.data)}`;
483
812
 
484
813
  ```
485
814
 
486
- 2. Use async/await
815
+ 1. Use async/await
487
816
 
488
- Use [async/await](https://javascript.info/async-await)
817
+ Use [async/await](https://javascript.info/async-await)
489
818
 
490
- ```js
491
- import { parseFile } from 'music-metadata';
492
-
493
- // it is required to declare the function 'async' to allow the use of await
494
- async function parseFiles(audioFiles) {
819
+ ```js
820
+ import { parseFile } from 'music-metadata';
495
821
 
496
- for (const audioFile of audioFiles) {
497
-
498
- // await will ensure the metadata parsing is completed before we move on to the next file
499
- const metadata = await parseFile(audioFile);
500
- // Do great things with the metadata
501
- }
502
- }
503
- ```
822
+ // it is required to declare the function 'async' to allow the use of await
823
+ async function parseFiles(audioFiles) {
504
824
 
505
- 3. Use a specialized module to traverse files
825
+ for (const audioFile of audioFiles) {
506
826
 
507
- There are specialized modules to traversing (walking) files and directory,
508
- like [walk](https://www.npmjs.com/package/walk).
827
+ // await will ensure the metadata parsing is completed before we move on to the next file
828
+ const metadata = await parseFile(audioFile);
829
+ // Do great things with the metadata
830
+ }
831
+ }
832
+ ```
509
833
 
510
834
  ## Licence
511
835
 
512
836
  The MIT License (MIT)
513
837
 
514
- Copyright © 2022 Borewit
838
+ Copyright © 2024 Borewit
515
839
 
516
840
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
517
841