music-metadata 10.4.0 → 10.5.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.
package/README.md CHANGED
@@ -1,844 +1,845 @@
1
- [![Node.js CI](https://github.com/Borewit/music-metadata/actions/workflows/nodejs-ci.yml/badge.svg?branch=master)](https://github.com/Borewit/music-metadata/actions?query=branch%3Amaster)
2
- [![Build status](https://ci.appveyor.com/api/projects/status/tgtqynlon8t99qq5/branch/master?svg=true)](https://ci.appveyor.com/project/Borewit/music-metadata/branch/master)
3
- [![NPM version](https://img.shields.io/npm/v/music-metadata.svg)](https://npmjs.org/package/music-metadata)
4
- [![npm downloads](http://img.shields.io/npm/dm/music-metadata.svg)](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600&interval=30)
5
- [![Coverage Status](https://coveralls.io/repos/github/Borewit/music-metadata/badge.svg?branch=master)](https://coveralls.io/github/Borewit/music-metadata?branch=master)
6
- [![Codacy Badge](https://api.codacy.com/project/badge/Grade/57d731b05c9e41889a2a17cb4b0384d7)](https://app.codacy.com/app/Borewit/music-metadata?utm_source=github.com&utm_medium=referral&utm_content=Borewit/music-metadata&utm_campaign=Badge_Grade_Dashboard)
7
- [![CodeQL](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml)
8
- [![DeepScan grade](https://deepscan.io/api/teams/5165/projects/6938/branches/61821/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=5165&pid=6938&bid=61821)
9
- [![Known Vulnerabilities](https://snyk.io/test/github/Borewit/music-metadata/badge.svg?targetFile=package.json)](https://snyk.io/test/github/Borewit/music-metadata?targetFile=package.json)
10
- [![Discord](https://img.shields.io/discord/460524735235883049.svg)](https://discord.gg/KyBr6sb)
11
-
12
- # music-metadata
13
-
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.
22
-
23
- ## Compatibility
24
-
25
- Module: version 8 migrated from [CommonJS](https://en.wikipedia.org/wiki/CommonJS) to [pure ECMAScript Module (ESM)](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
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
- ## Support the Project
35
- If you find this project useful and would like to support its development, consider sponsoring or contributing:
36
-
37
- - [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
38
-
39
- - Buy me a coffee:
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>
42
-
43
- ## Features
44
-
45
- ### Support for audio file types
46
-
47
- | Audio format | Description | Wiki | |
48
- | ------------- |---------------------------------| -------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------:|
49
- | AIFF / AIFF-C | Audio Interchange File Format | [:link:](https://wikipedia.org/wiki/Audio_Interchange_File_Format) | <img src="https://upload.wikimedia.org/wikipedia/commons/8/84/Apple_Computer_Logo_rainbow.svg" width="40" alt="Apple rainbow logo"> |
50
- | AAC | ADTS / Advanced Audio Coding | [:link:](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) | <img src="https://svgshare.com/i/UT8.svg" width="40" alt="AAC logo"> |
51
- | APE | Monkey's Audio | [:link:](https://wikipedia.org/wiki/Monkey's_Audio) | <img src="https://foreverhits.files.wordpress.com/2015/05/ape_audio.jpg" width="40" alt="Monkey's Audio logo"> |
52
- | ASF | Advanced Systems Format | [:link:](https://wikipedia.org/wiki/Advanced_Systems_Format) | |
53
- | BWF | Broadcast Wave Format | [:link:](https://en.wikipedia.org/wiki/Broadcast_Wave_Format) | |
54
- | DSDIFF | Philips DSDIFF | [:link:](https://wikipedia.org/wiki/Direct_Stream_Digital) | <img src="https://upload.wikimedia.org/wikipedia/commons/b/bc/DSDlogo.svg" width="80" alt="DSD logo"> |
55
- | DSF | Sony's DSD Stream File | [:link:](https://wikipedia.org/wiki/Direct_Stream_Digital) | <img src="https://upload.wikimedia.org/wikipedia/commons/b/bc/DSDlogo.svg" width="80" alt="DSD logo"> |
56
- | FLAC | Free Lossless Audio Codec | [:link:](https://wikipedia.org/wiki/FLAC) | <img src="https://upload.wikimedia.org/wikipedia/commons/a/a2/FLAC_logo_vector.svg" width="80" alt="FLAC logo"> |
57
- | MP2 | MPEG-1 Audio Layer II | [:link:](https://wikipedia.org/wiki/MPEG-1_Audio_Layer_II) | |
58
- | Matroska | Matroska (EBML), mka, mkv | [:link:](https://wikipedia.org/wiki/Matroska) | <img src="https://upload.wikimedia.org/wikipedia/commons/1/1a/Matroska_2010.svg" width="80" alt="Matroska logo"> |
59
- | MP3 | MPEG-1 / MPEG-2 Audio Layer III | [:link:](https://wikipedia.org/wiki/MP3) | <img src="https://upload.wikimedia.org/wikipedia/commons/e/ea/Mp3.svg" width="80" alt="MP3 logo"> |
60
- | MPC | Musepack SV7 | [:link:](https://wikipedia.org/wiki/Musepack) | <img src="https://www.musepack.net/pictures/musepack_logo.png" width="80" alt="musepack logo"> |
61
- | MPEG 4 | mp4, m4a, m4v | [:link:](https://wikipedia.org/wiki/MPEG-4) | <img src="https://svgshare.com/i/UU3.svg" width="80" alt="mpeg 4 logo"> |
62
- | Ogg | Open container format | [:link:](https://en.wikipedia.org/wiki/Ogg) | <img src="https://upload.wikimedia.org/wikipedia/commons/a/a1/Ogg_Logo.svg" width="80" alt="Ogg logo"> |
63
- | Opus | | [:link:](https://wikipedia.org/wiki/Opus_(audio_format)) | <img src="https://upload.wikimedia.org/wikipedia/commons/0/02/Opus_logo2.svg" width="80" alt="Opus logo"> |
64
- | Speex | | [:link:](https://wikipedia.org/wiki/Speex) | <img src="https://upload.wikimedia.org/wikipedia/commons/b/b5/Speex_logo_2006.svg" width="80" alt="Speex logo"> |
65
- | Theora | | [:link:](https://en.wikipedia.org/wiki/Theora) | <img src="https://upload.wikimedia.org/wikipedia/commons/5/57/Theora_logo_2007.svg" width="70" alt="Theora logo"> |
66
- | Vorbis | Vorbis audio compression | [:link:](https://wikipedia.org/wiki/Ogg_Vorbis) | <img src="https://upload.wikimedia.org/wikipedia/commons/8/8d/Xiph.Org_logo_square.svg" width="70" alt="Vorbis logo"> |
67
- | WAV | RIFF WAVE | [:link:](https://wikipedia.org/wiki/WAV) | |
68
- | WebM | webm | [:link:](https://wikipedia.org/wiki/WebM) | <img src="https://upload.wikimedia.org/wikipedia/commons/3/34/WebM_logo.svg" width="80" alt="Matroska logo"> |
69
- | WV | WavPack | [:link:](https://wikipedia.org/wiki/WavPack) | <img src="http://www.wavpack.com/wavpacklogo.svg" width="60" alt="WavPack logo"> |
70
- | WMA | Windows Media Audio | [:link:](https://wikipedia.org/wiki/Windows_Media_Audio) | <img src="https://upload.wikimedia.org/wikipedia/commons/7/76/Windows_Media_Player_simplified_logo.svg" width="40" alt="Windows Media logo"> |
71
-
72
- ### Supported tag headers
73
-
74
- Following tag header formats are supported:
75
- - [APE](https://wikipedia.org/wiki/APE_tag)
76
- - [ASF](https://wikipedia.org/wiki/Advanced_Systems_Format)
77
- - EXIF 2.3
78
- - [ID3](https://wikipedia.org/wiki/ID3): ID3v1, ID3v1.1, ID3v2.2, [ID3v2.3](http://id3.org/id3v2.3.0) & [ID3v2.4](http://id3.org/id3v2.4.0-frames)
79
- - [iTunes](https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata)
80
- - [RIFF](https://wikipedia.org/wiki/Resource_Interchange_File_Format)/INFO
81
- - [Vorbis comment](https://wikipedia.org/wiki/Vorbis_comment)
82
- - [AIFF](https://wikipedia.org/wiki/Audio_Interchange_File_Format)
83
-
84
- It allows many tags to be accessed in audio format, and tag format independent way.
85
-
86
- Support for [MusicBrainz](https://musicbrainz.org/) tags as written by [Picard](https://picard.musicbrainz.org/).
87
- [ReplayGain](https://wiki.hydrogenaud.io/index.php?title=ReplayGain) tags are supported.
88
-
89
- ### Audio format & encoding details
90
-
91
- Support for encoding / format details:
92
- - [Bit rate](https://wikipedia.org/wiki/Bit_rate)
93
- - [Audio bit depth](https://wikipedia.org/wiki/Audio_bit_depth)
94
- - Duration
95
- - Encoding profile (e.g. [CBR](https://en.wikipedia.org/wiki/Constant_bitrate), V0, V2)
96
-
97
-
98
- ## Online demo's
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/)
100
- - [<img src="https://cdn.sanity.io/images/3do82whm/next/ba8c847f13a5fa39d88f8bc9b7846b7886531b18-2500x2500.svg" width="40"> Webamp](https://webamp.org/)
101
- - Expected to be released soon: [Overtone](https://overtone.pro/) by [Johannes Schickling](https://github.com/schickling)
102
-
103
- ## Usage
104
-
105
- ### Installation
106
- Install using [npm](http://npmjs.org/):
107
-
108
- ```bash
109
- npm install music-metadata
110
- ```
111
- or using [yarn](https://yarnpkg.com/):
112
- ```bash
113
- yarn add music-metadata
114
- ```
115
-
116
- ## API Documentation
117
-
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).
123
-
124
- **Cross-platform** functions available to read an audio file or stream:
125
-
126
- There are multiple ways to parse (read) audio tracks:
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).
131
-
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.
134
-
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.
138
-
139
- #### `parseFile` function
140
-
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.
143
-
144
- ##### Syntax
145
- ```ts
146
- parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>
147
- ```
148
-
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:
178
- ```js
179
- import { parseFile } from 'music-metadata';
180
- import { inspect } from 'util';
181
-
182
- (async () => {
183
- try {
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
188
- console.log(inspect(metadata, { showHidden: false, depth: null }));
189
- } catch (error) {
190
- console.error('Error parsing metadata:', error.message);
191
- }
192
- })();
193
- ```
194
-
195
- #### `parseStream` function
196
-
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.
199
-
200
- ##### Syntax:
201
- ```ts
202
- parseStream(stream: Readable, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>
203
- ```
204
-
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:
245
- ```js
246
- import { parseStream } from 'music-metadata';
247
- import { createReadStream } from 'fs';
248
-
249
- (async () => {
250
- try {
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
258
- console.log(metadata);
259
- } catch (error) {
260
- console.error('Error parsing metadata:', error.message);
261
- }
262
- })();
263
-
264
- ```
265
-
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.
275
-
276
-
277
- ##### Syntax
278
- ```ts
279
- parseWebStream(webStream: ReadableStream<Uint8Array>, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>
280
- ```
281
-
282
- ##### Parameters
283
-
284
- - `webStream`: `ReadableStream<Uint8Array>`
285
-
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:
318
-
319
- ```js
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;
327
-
328
- // Parse the metadata from the web stream
329
- const metadata = await parseWebStream(webStream, 'audio/mpeg');
330
-
331
- // Log the parsed metadata
332
- console.log(metadata);
333
- } catch (error) {
334
- console.error('Error parsing metadata:', error.message);
335
- }
336
- })();
337
- ```
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
-
371
- ```js
372
- import { parseBlob } from 'music-metadata';
373
-
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
- })();
385
- ```
386
-
387
- #### `parseBuffer` function
388
-
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.
391
-
392
- ##### Syntax
393
- ```ts
394
- parseBuffer(buffer: Uint8Array, fileInfo?: IFileInfo | string, opts?: IOptions = {}): Promise<IAudioMetadata>
395
- ```
396
-
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
-
420
- ```js
421
- import { parseBuffer } from 'music-metadata';
422
- import fs from 'fs';
423
-
424
- (async () => {
425
- const buffer = fs.readFileSync('path/to/audio/file.mp3');
426
-
427
- try {
428
- const metadata = await parseBuffer(buffer, { mimeType: 'audio/mpeg' });
429
- console.log(metadata);
430
- } catch (error) {
431
- console.error('Error parsing metadata:', error.message);
432
- }
433
- })();
434
- ```
435
-
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.
440
-
441
- This also enables special read modules like:
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).
443
-
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
538
-
539
- Utility to Converts the native tags to a dictionary index on the tag identifier
540
-
541
- ```ts
542
- orderTags(nativeTags: ITag[]): [tagId: string]: any[]
543
- ```
544
-
545
- ```js
546
- import { parseFile, orderTags } from 'music-metadata';
547
- import { inspect } from 'util';
548
-
549
- (async () => {
550
- try {
551
- const metadata = await parseFile('../test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3');
552
- const orderedTags = orderTags(metadata.native['ID3v2.3']);
553
- console.log(inspect(orderedTags, { showHidden: false, depth: null }));
554
- } catch (error) {
555
- console.error(error.message);
556
- }
557
- })();
558
- ```
559
-
560
- #### `ratingToStars` function
561
-
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.
563
-
564
- ```ts
565
- ratingToStars(rating: number): number
566
- ```
567
- #### `selectCover` function
568
-
569
- Select cover image based on image type field, otherwise the first picture in file.
570
-
571
- ```ts
572
- export function selectCover(pictures?: IPicture[]): IPicture | null
573
- ```
574
-
575
- ```js
576
- import { parseFile, selectCover } from 'music-metadata';
577
-
578
- (async () => {
579
- const {common} = await parseFile(filePath);
580
- const cover = selectCover(common.picture); // pick the cover image
581
- }
582
- )();
583
- ```
584
-
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.
590
-
591
- - `observer`: `(update: MetadataEvent) => void;`:
592
-
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
612
-
613
- If the returned promise resolves, the metadata (TypeScript `IAudioMetadata` interface) contains:
614
- - [`metadata.format`](#metadataformat) Audio format information
615
- - [`metadata.common`](#metadatacommon) Is a generic (abstract) way of reading metadata information.
616
- - [`metadata.trackInfo`](#metadatatrackInfo) Is a generic (abstract) way of reading metadata information.
617
- - `metadata.native` List of native (original) tags found in the parsed audio file.
618
-
619
- #### `metadata.format`
620
-
621
- The questionmark `?` indicates the property is optional.
622
-
623
- Audio format information. Defined in the TypeScript `IFormat` interface:
624
- - `format.container?: string` Audio encoding format. e.g.: 'flac'
625
- - `format.codec?` Name of the codec (algorithm used for the audio compression)
626
- - `format.codecProfile?: string` Codec profile / settings
627
- - `format.tagTypes?: TagType[]` List of tagging formats found in parsed audio file
628
- - `format.duration?: number` Duration in seconds
629
- - `format.bitrate?: number` Number bits per second of encoded audio file
630
- - `format.sampleRate?: number` Sampling rate in Samples per second (S/s)
631
- - `format.bitsPerSample?: number` Audio bit depth
632
- - `format.lossless?: boolean` True if lossless, false for lossy encoding
633
- - `format.numberOfChannels?: number` Number of audio channels
634
- - `format.creationTime?: Date` Track creation time
635
- - `format.modificationTime?: Date` Track modification / tag update time
636
- - `format.trackGain?: number` Track gain in dB
637
- - `format.albumGain?: number` Album gain in dB
638
-
639
- #### `metadata.trackInfo`
640
-
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,
642
-
643
- `metadata.trackInfo` is either `undefined` or has an **array** of [trackInfo](#trackinfo)
644
-
645
- ##### trackInfo
646
-
647
- Audio format information. Defined in the TypeScript `IFormat` interface:
648
- - `trackInfo.type?: TrackType` Track type
649
- - `trackInfo.codecName?: string` Codec name
650
- - `trackInfo.codecSettings?: string` Codec settings
651
- - `trackInfo.flagEnabled?: boolean` Set if the track is usable, default: `true`
652
- - `trackInfo.flagDefault?: boolean` Set if that track (audio, video or subs) SHOULD be active if no language found matches the user preference.
653
- - `trackInfo.flagLacing?: boolean` Set if the track **may** contain blocks using lacing
654
- - `trackInfo.name?: string` A human-readable track name.
655
- - `trackInfo.language?: string` Specifies the language of the track
656
- - `trackInfo.audio?: IAudioTrack`, see [`trackInfo.audioTrack`](#trackinfoaudiotrack)
657
- - `trackInfo.video?: IVideoTrack`, see [`trackInfo.videoTrack`](#trackinfovideotrack)
658
-
659
- ##### `trackInfo.audioTrack`
660
-
661
- - `audioTrack.samplingFrequency?: number`
662
- - `audioTrack.outputSamplingFrequency?: number`
663
- - `audioTrack.channels?: number`
664
- - `audioTrack.channelPositions?: Buffer`
665
- - `audioTrack.bitDepth?: number`
666
-
667
- ##### `trackInfo.videoTrack`
668
-
669
- - `videoTrack.flagInterlaced?: boolean`
670
- - `videoTrack.stereoMode?: number`
671
- - `videoTrack.pixelWidth?: number`
672
- - `videoTrack.pixelHeight?: number`
673
- - `videoTrack.displayWidth?: number`
674
- - `videoTrack.displayHeight?: number`
675
- - `videoTrack.displayUnit?: number`
676
- - `videoTrack.aspectRatioType?: number`
677
- - `videoTrack.colourSpace?: Buffer`
678
- - `videoTrack.gammaValue?: number`
679
-
680
- #### `metadata.common`
681
-
682
- [Common tag documentation](doc/common_metadata.md) is automatically generated.
683
-
684
- ## Examples
685
-
686
- In order to read the duration of a stream (with the exception of file streams), in some cases you should pass the size of the file in bytes.
687
- ```js
688
- import { parseStream } from 'music-metadata';
689
- import { inspect } from 'util';
690
-
691
- (async () => {
692
- const metadata = await parseStream(someReadStream, {mimeType: 'audio/mpeg', size: 26838}, {duration: true});
693
- console.log(inspect(metadata, {showHidden: false, depth: null}));
694
- someReadStream.close();
695
- }
696
- )();
697
- ```
698
-
699
- ### Access cover art
700
-
701
- Via `metadata.common.picture` you can access an array of cover art if present.
702
- Each picture has this interface:
703
-
704
- ```ts
705
- /**
706
- * Attached picture, typically used for cover art
707
- */
708
- export interface IPicture {
709
- /**
710
- * Image mime type
711
- */
712
- format: string;
713
- /**
714
- * Image data
715
- */
716
- data: Buffer;
717
- /**
718
- * Optional description
719
- */
720
- description?: string;
721
- /**
722
- * Picture type
723
- */
724
- type?: string;
725
- }
726
- ```
727
-
728
- To assign `img` HTML-object you can do something like:
729
- ```js
730
- import {uint8ArrayToBase64} from 'uint8array-extras';
731
-
732
- img.src = `data:${picture.format};base64,${uint8ArrayToBase64(picture.data)}`;
733
- ```
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.
787
-
788
- ## Frequently Asked Questions
789
-
790
- 1. How can I traverse (a long) list of files?
791
-
792
- What is important that file parsing should be done in a sequential manner.
793
- In a plain loop, due to the asynchronous character (like most JavaScript functions), it would cause all the files to run in parallel which is will cause your application to hang in no time.
794
- There are multiple ways of achieving this:
795
-
796
- 1. Using recursion
797
-
798
- ```js
799
- import { parseFile } from 'music-metadata';
800
-
801
- function parseFiles(audioFiles) {
802
-
803
- const audioFile = audioFiles.shift();
804
-
805
- if (audioFile) {
806
- return parseFile(audioFile).then(metadata => {
807
- // Do great things with the metadata
808
- return parseFiles(audioFiles); // process rest of the files AFTER we are finished
809
- })
810
- }
811
- }
812
-
813
- ```
814
-
815
- 1. Use async/await
816
-
817
- Use [async/await](https://javascript.info/async-await)
818
-
819
- ```js
820
- import { parseFile } from 'music-metadata';
821
-
822
- // it is required to declare the function 'async' to allow the use of await
823
- async function parseFiles(audioFiles) {
824
-
825
- for (const audioFile of audioFiles) {
826
-
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
- ```
833
-
834
- ## Licence
835
-
836
- The MIT License (MIT)
837
-
838
- Copyright © 2024 Borewit
839
-
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:
841
-
842
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
843
-
844
- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ [![Node.js CI](https://github.com/Borewit/music-metadata/actions/workflows/nodejs-ci.yml/badge.svg?branch=master)](https://github.com/Borewit/music-metadata/actions?query=branch%3Amaster)
2
+ [![Build status](https://ci.appveyor.com/api/projects/status/tgtqynlon8t99qq5/branch/master?svg=true)](https://ci.appveyor.com/project/Borewit/music-metadata/branch/master)
3
+ [![NPM version](https://img.shields.io/npm/v/music-metadata.svg)](https://npmjs.org/package/music-metadata)
4
+ [![npm downloads](http://img.shields.io/npm/dm/music-metadata.svg)](https://npmcharts.com/compare/music-metadata,jsmediatags,musicmetadata,node-id3,mp3-parser,id3-parser,wav-file-info?start=600&interval=30)
5
+ [![Coverage Status](https://coveralls.io/repos/github/Borewit/music-metadata/badge.svg?branch=master)](https://coveralls.io/github/Borewit/music-metadata?branch=master)
6
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/57d731b05c9e41889a2a17cb4b0384d7)](https://app.codacy.com/app/Borewit/music-metadata?utm_source=github.com&utm_medium=referral&utm_content=Borewit/music-metadata&utm_campaign=Badge_Grade_Dashboard)
7
+ [![CodeQL](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Borewit/music-metadata/actions/workflows/codeql-analysis.yml)
8
+ [![DeepScan grade](https://deepscan.io/api/teams/5165/projects/6938/branches/61821/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=5165&pid=6938&bid=61821)
9
+ [![Known Vulnerabilities](https://snyk.io/test/github/Borewit/music-metadata/badge.svg?targetFile=package.json)](https://snyk.io/test/github/Borewit/music-metadata?targetFile=package.json)
10
+ [![Discord](https://img.shields.io/discord/460524735235883049.svg)](https://discord.gg/KyBr6sb)
11
+
12
+ # music-metadata
13
+
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.
22
+
23
+ ## Compatibility
24
+
25
+ Module: version 8 migrated from [CommonJS](https://en.wikipedia.org/wiki/CommonJS) to [pure ECMAScript Module (ESM)](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
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
+ ## Support the Project
35
+ If you find this project useful and would like to support its development, consider sponsoring or contributing:
36
+
37
+ - [Become a sponsor to Borewit](https://github.com/sponsors/Borewit)
38
+
39
+ - Buy me a coffee:
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>
42
+
43
+ ## Features
44
+
45
+ ### Support for audio file types
46
+
47
+ | Audio format | Description | Wiki | |
48
+ | ------------- |---------------------------------| -------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------:|
49
+ | AIFF / AIFF-C | Audio Interchange File Format | [:link:](https://wikipedia.org/wiki/Audio_Interchange_File_Format) | <img src="https://upload.wikimedia.org/wikipedia/commons/8/84/Apple_Computer_Logo_rainbow.svg" width="40" alt="Apple rainbow logo"> |
50
+ | AAC | ADTS / Advanced Audio Coding | [:link:](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) | <img src="https://svgshare.com/i/UT8.svg" width="40" alt="AAC logo"> |
51
+ | AMR | Adaptive Multi-Rate audio codec | [:link:](https://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec) | <img src="https://foreverhits.files.wordpress.com/2015/05/ape_audio.jpg" width="40" alt="Monkey's Audio logo"> |
52
+ | APE | Monkey's Audio | [:link:](https://wikipedia.org/wiki/Monkey's_Audio) | <img src="https://foreverhits.files.wordpress.com/2015/05/ape_audio.jpg" width="40" alt="Monkey's Audio logo"> |
53
+ | ASF | Advanced Systems Format | [:link:](https://wikipedia.org/wiki/Advanced_Systems_Format) | |
54
+ | BWF | Broadcast Wave Format | [:link:](https://en.wikipedia.org/wiki/Broadcast_Wave_Format) | |
55
+ | DSDIFF | Philips DSDIFF | [:link:](https://wikipedia.org/wiki/Direct_Stream_Digital) | <img src="https://upload.wikimedia.org/wikipedia/commons/b/bc/DSDlogo.svg" width="80" alt="DSD logo"> |
56
+ | DSF | Sony's DSD Stream File | [:link:](https://wikipedia.org/wiki/Direct_Stream_Digital) | <img src="https://upload.wikimedia.org/wikipedia/commons/b/bc/DSDlogo.svg" width="80" alt="DSD logo"> |
57
+ | FLAC | Free Lossless Audio Codec | [:link:](https://wikipedia.org/wiki/FLAC) | <img src="https://upload.wikimedia.org/wikipedia/commons/a/a2/FLAC_logo_vector.svg" width="80" alt="FLAC logo"> |
58
+ | MP2 | MPEG-1 Audio Layer II | [:link:](https://wikipedia.org/wiki/MPEG-1_Audio_Layer_II) | |
59
+ | Matroska | Matroska (EBML), mka, mkv | [:link:](https://wikipedia.org/wiki/Matroska) | <img src="https://upload.wikimedia.org/wikipedia/commons/1/1a/Matroska_2010.svg" width="80" alt="Matroska logo"> |
60
+ | MP3 | MPEG-1 / MPEG-2 Audio Layer III | [:link:](https://wikipedia.org/wiki/MP3) | <img src="https://upload.wikimedia.org/wikipedia/commons/e/ea/Mp3.svg" width="80" alt="MP3 logo"> |
61
+ | MPC | Musepack SV7 | [:link:](https://wikipedia.org/wiki/Musepack) | <img src="https://www.musepack.net/pictures/musepack_logo.png" width="80" alt="musepack logo"> |
62
+ | MPEG 4 | mp4, m4a, m4v | [:link:](https://wikipedia.org/wiki/MPEG-4) | <img src="https://svgshare.com/i/UU3.svg" width="80" alt="mpeg 4 logo"> |
63
+ | Ogg | Open container format | [:link:](https://en.wikipedia.org/wiki/Ogg) | <img src="https://upload.wikimedia.org/wikipedia/commons/a/a1/Ogg_Logo.svg" width="80" alt="Ogg logo"> |
64
+ | Opus | | [:link:](https://wikipedia.org/wiki/Opus_(audio_format)) | <img src="https://upload.wikimedia.org/wikipedia/commons/0/02/Opus_logo2.svg" width="80" alt="Opus logo"> |
65
+ | Speex | | [:link:](https://wikipedia.org/wiki/Speex) | <img src="https://upload.wikimedia.org/wikipedia/commons/b/b5/Speex_logo_2006.svg" width="80" alt="Speex logo"> |
66
+ | Theora | | [:link:](https://en.wikipedia.org/wiki/Theora) | <img src="https://upload.wikimedia.org/wikipedia/commons/5/57/Theora_logo_2007.svg" width="70" alt="Theora logo"> |
67
+ | Vorbis | Vorbis audio compression | [:link:](https://wikipedia.org/wiki/Ogg_Vorbis) | <img src="https://upload.wikimedia.org/wikipedia/commons/8/8d/Xiph.Org_logo_square.svg" width="70" alt="Vorbis logo"> |
68
+ | WAV | RIFF WAVE | [:link:](https://wikipedia.org/wiki/WAV) | |
69
+ | WebM | webm | [:link:](https://wikipedia.org/wiki/WebM) | <img src="https://upload.wikimedia.org/wikipedia/commons/3/34/WebM_logo.svg" width="80" alt="Matroska logo"> |
70
+ | WV | WavPack | [:link:](https://wikipedia.org/wiki/WavPack) | <img src="http://www.wavpack.com/wavpacklogo.svg" width="60" alt="WavPack logo"> |
71
+ | WMA | Windows Media Audio | [:link:](https://wikipedia.org/wiki/Windows_Media_Audio) | <img src="https://upload.wikimedia.org/wikipedia/commons/7/76/Windows_Media_Player_simplified_logo.svg" width="40" alt="Windows Media logo"> |
72
+
73
+ ### Supported tag headers
74
+
75
+ Following tag header formats are supported:
76
+ - [APE](https://wikipedia.org/wiki/APE_tag)
77
+ - [ASF](https://wikipedia.org/wiki/Advanced_Systems_Format)
78
+ - EXIF 2.3
79
+ - [ID3](https://wikipedia.org/wiki/ID3): ID3v1, ID3v1.1, ID3v2.2, [ID3v2.3](http://id3.org/id3v2.3.0) & [ID3v2.4](http://id3.org/id3v2.4.0-frames)
80
+ - [iTunes](https://github.com/sergiomb2/libmp4v2/wiki/iTunesMetadata)
81
+ - [RIFF](https://wikipedia.org/wiki/Resource_Interchange_File_Format)/INFO
82
+ - [Vorbis comment](https://wikipedia.org/wiki/Vorbis_comment)
83
+ - [AIFF](https://wikipedia.org/wiki/Audio_Interchange_File_Format)
84
+
85
+ It allows many tags to be accessed in audio format, and tag format independent way.
86
+
87
+ Support for [MusicBrainz](https://musicbrainz.org/) tags as written by [Picard](https://picard.musicbrainz.org/).
88
+ [ReplayGain](https://wiki.hydrogenaud.io/index.php?title=ReplayGain) tags are supported.
89
+
90
+ ### Audio format & encoding details
91
+
92
+ Support for encoding / format details:
93
+ - [Bit rate](https://wikipedia.org/wiki/Bit_rate)
94
+ - [Audio bit depth](https://wikipedia.org/wiki/Audio_bit_depth)
95
+ - Duration
96
+ - Encoding profile (e.g. [CBR](https://en.wikipedia.org/wiki/Constant_bitrate), V0, V2)
97
+
98
+
99
+ ## Online demo's
100
+ - [<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/)
101
+ - [<img src="https://cdn.sanity.io/images/3do82whm/next/ba8c847f13a5fa39d88f8bc9b7846b7886531b18-2500x2500.svg" width="40"> Webamp](https://webamp.org/)
102
+ - Expected to be released soon: [Overtone](https://overtone.pro/) by [Johannes Schickling](https://github.com/schickling)
103
+
104
+ ## Usage
105
+
106
+ ### Installation
107
+ Install using [npm](http://npmjs.org/):
108
+
109
+ ```bash
110
+ npm install music-metadata
111
+ ```
112
+ or using [yarn](https://yarnpkg.com/):
113
+ ```bash
114
+ yarn add music-metadata
115
+ ```
116
+
117
+ ## API Documentation
118
+
119
+ ### Overview
120
+
121
+ **Node.js specific** functions to read an audio file or stream:
122
+ 1. **File Parsing**: Parse audio files directly from the filesystem using the [parseFile function](#parsefile-function)
123
+ 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).
124
+
125
+ **Cross-platform** functions available to read an audio file or stream:
126
+
127
+ There are multiple ways to parse (read) audio tracks:
128
+ 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).
129
+ 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).
130
+ 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).
131
+ 1. **Tokenizer Parsing:** Use a custom or third-party [strtok3](https://github.com/Borewit/strtok3) `ITokenizer` to parse using the [parseFromTokenizer function](#parsefromtokenizer-function).
132
+
133
+ > [!NOTE]
134
+ > Direct file access in Node.js is generally faster because it can 'jump' to various parts of the file without reading intermediate data.
135
+
136
+ ### Node.js specific function
137
+ These functions are tailored for Node.js environments and leverage Node.js-specific APIs,
138
+ making them incompatible with browser-based JavaScript engines.
139
+
140
+ #### `parseFile` function
141
+
142
+ The `parseFile` function is intended for extracting metadata from audio files on the local filesystem in a Node.js environment.
143
+ It reads the specified file, parses its audio metadata, and returns a promise that resolves with this information.
144
+
145
+ ##### Syntax
146
+ ```ts
147
+ parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>
148
+ ```
149
+
150
+ ##### Parameters
151
+
152
+ - `filePath`: `string`
153
+
154
+ The path to the media file from which metadata should be extracted.
155
+ This should be a valid path to an audio file on the local filesystem.
156
+
157
+ - `options`: [IOptions](#ioptions-interface) (optional)
158
+
159
+ An optional configuration object that allows customization of the parsing process.
160
+ These options can include whether to calculate the file's duration, skip embedded cover art,
161
+ or other parsing behaviors.
162
+
163
+ ##### Returns
164
+
165
+ - `Promise<IAudioMetadata>`:
166
+
167
+ A promise that resolves to an IAudioMetadata object containing metadata about the audio file.
168
+ The metadata includes details such as the file format, codec, duration, bit rate, and any embedded tags like album, artist, or track information.
169
+
170
+ ##### Usage Notes
171
+
172
+ - This function is **Node.js-only** and relies on Node.js-specific APIs to access the filesystem.
173
+
174
+ - For browser environments, consider using the [parseBlob](#parseblob-function) to parse [File object](https://developer.mozilla.org/en-US/docs/Web/API/File) objects.
175
+
176
+ ##### Example:
177
+
178
+ The following example demonstrates how to use the parseFile function to read metadata from an audio file:
179
+ ```js
180
+ import { parseFile } from 'music-metadata';
181
+ import { inspect } from 'util';
182
+
183
+ (async () => {
184
+ try {
185
+ const filePath = '../music-metadata/test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3';
186
+ const metadata = await parseFile(filePath);
187
+
188
+ // Output the parsed metadata to the console in a readable format
189
+ console.log(inspect(metadata, { showHidden: false, depth: null }));
190
+ } catch (error) {
191
+ console.error('Error parsing metadata:', error.message);
192
+ }
193
+ })();
194
+ ```
195
+
196
+ #### `parseStream` function
197
+
198
+ 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.
199
+ 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.
200
+
201
+ ##### Syntax:
202
+ ```ts
203
+ parseStream(stream: Readable, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>
204
+ ```
205
+
206
+ ##### Parameters:
207
+
208
+ - `stream`: `Readable`:
209
+
210
+ The Node.js [Readable](https://nodejs.org/api/stream.html#class-streamreadable) stream from which the audio data is read.
211
+ This stream should provide the raw audio data to be analyzed.
212
+
213
+ - `fileInfo`: `IFileInfo` (optional)
214
+
215
+ An object containing file-related information or a string representing the MIME-type of the audio stream.
216
+ The fileInfo parameter can help the parser to correctly identify the audio format and may include:
217
+
218
+ - `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`).
219
+
220
+ If provided, it is assumed the streamed file content is to be the MIME-type.
221
+ If not provided, the parser will attempt to determine the format based on the content of the stream.
222
+
223
+ - `size`: The total size of the audio stream in bytes (useful for streams with a known length).
224
+
225
+ - `path`: A string representing the file path or filename, which can also assist in determining the format.
226
+
227
+ - `options`: `IOptions` (optional)
228
+
229
+ An optional object containing additional parsing options.
230
+ These options allow you to customize the parsing process,
231
+ such as whether to calculate the duration or skip cover art extraction.
232
+
233
+ ##### Returns
234
+
235
+ - `Promise<IAudioMetadata>`:
236
+
237
+ A promise that resolves to an `IAudioMetadata` object containing detailed metadata about the audio stream.
238
+ This metadata includes information about the format, codec, duration, bitrate, and any embedded tags such as artist, album, or track information.
239
+
240
+ ##### Usage Notes
241
+ - This function is only available in Node.js environments, as it relies on the [Node.js stream API](https://nodejs.org/api/stream.html).
242
+
243
+ ##### Example:
244
+
245
+ The following example demonstrates how to use the `parseStream` function to read metadata from an audio stream:
246
+ ```js
247
+ import { parseStream } from 'music-metadata';
248
+ import { createReadStream } from 'fs';
249
+
250
+ (async () => {
251
+ try {
252
+ // Create a readable stream from a file
253
+ const audioStream = createReadStream('path/to/audio/file.mp3');
254
+
255
+ // Parse the metadata from the stream
256
+ const metadata = await parseStream(audioStream, { mimeType: 'audio/mpeg'});
257
+
258
+ // Log the parsed metadata
259
+ console.log(metadata);
260
+ } catch (error) {
261
+ console.error('Error parsing metadata:', error.message);
262
+ }
263
+ })();
264
+
265
+ ```
266
+
267
+ ### Cross-platform functions
268
+ These functions are designed to be cross-platform,
269
+ meaning it can be used in both Node.js and web browsers.
270
+
271
+ #### `parseWebStream` function
272
+
273
+ The parseWebStream function is used to extract metadata from an audio track provided as a web-compatible ReadableStream.
274
+ This function is ideal for applications running in web environments, such as browsers,
275
+ where audio data is streamed over the network or read from other web-based sources.
276
+
277
+
278
+ ##### Syntax
279
+ ```ts
280
+ parseWebStream(webStream: ReadableStream<Uint8Array>, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>
281
+ ```
282
+
283
+ ##### Parameters
284
+
285
+ - `webStream`: `ReadableStream<Uint8Array>`
286
+
287
+ A [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) that provides the audio data to be parsed.
288
+ This stream should emit Uint8Array chunks, representing the raw audio data.
289
+
290
+ - `fileInfo`: `IFileInfo` (optional)
291
+
292
+ An object containing file-related information or a string representing the MIME-type of the audio stream.
293
+ The fileInfo parameter can help the parser to correctly identify the audio format and may include:
294
+
295
+ - `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`).
296
+
297
+ If provided, it is assumed the streamed file content is to be the MIME-type.
298
+ If not provided, the parser will attempt to determine the format based on the content of the stream.
299
+
300
+ - `size`: The total size of the audio stream in bytes (useful for streams with a known length).
301
+
302
+ - `path`: A string representing the file path or filename, which can also assist in determining the format.
303
+
304
+ - `options`: `IOptions` (optional)
305
+
306
+ An optional object containing additional parsing options.
307
+ These options allow you to customize the parsing process,
308
+ such as whether to calculate the duration or skip cover art extraction.
309
+
310
+ ##### Returns
311
+
312
+ - `Promise<IAudioMetadata>`:
313
+
314
+ A promise that resolves to an `IAudioMetadata` object containing detailed metadata about the audio stream.
315
+ This metadata includes information about the format, codec, duration, bitrate, and any embedded tags such as artist, album, or track information.
316
+
317
+ ##### Example
318
+ Here’s an example of how to use the `parseWebStream` function to extract metadata from an audio stream in a web application:
319
+
320
+ ```js
321
+ import { parseWebStream } from 'music-metadata';
322
+
323
+ (async () => {
324
+ try {
325
+ // Assuming you have a ReadableStream of an audio file
326
+ const response = await fetch('https://example.com/path/to/audio/file.mp3');
327
+ const webStream = response.body;
328
+
329
+ // Parse the metadata from the web stream
330
+ const metadata = await parseWebStream(webStream, 'audio/mpeg');
331
+
332
+ // Log the parsed metadata
333
+ console.log(metadata);
334
+ } catch (error) {
335
+ console.error('Error parsing metadata:', error.message);
336
+ }
337
+ })();
338
+ ```
339
+
340
+ 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.
341
+ The `response.body` provides a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) that is then passed to `parseWebStream`.
342
+
343
+ #### `parseBlob` function
344
+
345
+ Parses metadata from an audio file represented as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
346
+ This function is suitable for use in environments that support the ReadableStreamBYOBReader, which is **available in Node.js 20** and above.
347
+
348
+ ##### Syntax
349
+ ```ts
350
+ parseBlob(blob: Blob, options?: IOptions = {}): Promise<IAudioMetadata>
351
+ ```
352
+
353
+ ##### Parameters
354
+
355
+ - `blob`: [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
356
+
357
+ The Blob object containing the audio data to be parsed.
358
+ 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.
359
+
360
+ - `options`: [IOptions](#ioptions-interface) (optional)
361
+
362
+ An optional configuration object that specifies parsing options.
363
+
364
+ ##### Returns
365
+
366
+ - `Promise<IAudioMetadata>`:
367
+
368
+ A promise that resolves to the metadata of the audio file.
369
+
370
+ ##### Example
371
+
372
+ ```js
373
+ import { parseBlob } from 'music-metadata';
374
+
375
+ (async () => {
376
+ const fileInput = document.querySelector('input[type="file"]');
377
+ const file = fileInput.files[0];
378
+
379
+ try {
380
+ const metadata = await parseBlob(file);
381
+ console.log(metadata);
382
+ } catch (error) {
383
+ console.error('Error parsing metadata:', error.message);
384
+ }
385
+ })();
386
+ ```
387
+
388
+ #### `parseBuffer` function
389
+
390
+ 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).
391
+ This function is particularly useful when you already have audio data in memory.
392
+
393
+ ##### Syntax
394
+ ```ts
395
+ parseBuffer(buffer: Uint8Array, fileInfo?: IFileInfo | string, opts?: IOptions = {}): Promise<IAudioMetadata>
396
+ ```
397
+
398
+ ##### Parameters
399
+ - `uint8Array`: [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
400
+
401
+ A [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) containing the audio data to be parsed.
402
+
403
+ - `fileInfo`: `IFileInfo` | `string` (optional)
404
+
405
+ An object containing file information such as mimeType and size.
406
+ Alternatively, you can pass a MIME-type string directly.
407
+ This helps the parser understand the format of the audio data.
408
+
409
+ - `options`: [IOptions](#ioptions-interface) (optional)
410
+
411
+ An optional configuration object that specifies parsing options.
412
+
413
+ ##### Returns
414
+ - `Promise<IAudioMetadata>`:
415
+
416
+ A promise that resolves to the metadata of the audio file.
417
+
418
+
419
+ ##### Example
420
+
421
+ ```js
422
+ import { parseBuffer } from 'music-metadata';
423
+ import fs from 'fs';
424
+
425
+ (async () => {
426
+ const buffer = fs.readFileSync('path/to/audio/file.mp3');
427
+
428
+ try {
429
+ const metadata = await parseBuffer(buffer, { mimeType: 'audio/mpeg' });
430
+ console.log(metadata);
431
+ } catch (error) {
432
+ console.error('Error parsing metadata:', error.message);
433
+ }
434
+ })();
435
+ ```
436
+
437
+ #### `parseFromTokenizer` function
438
+ Parses metadata from an audio source that implements the [strtok3](https://github.com/Borewit/strtok3) ITokenizer interface.
439
+ This is a low-level function that provides flexibility for advanced use cases,
440
+ such as parsing metadata from streaming audio or custom data sources.
441
+
442
+ This also enables special read modules like:
443
+ - [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).
444
+
445
+ ##### Syntax
446
+ ```ts
447
+ parseFromTokenizer(tokenizer: ITokenizer, options?: IOptions): Promise<IAudioMetadata>
448
+ ```
449
+
450
+ ##### Parameters
451
+ - `tokenizer: ITokenizer`
452
+
453
+ An instance of an ITokenizer that provides access to the audio data.
454
+ The tokenizer abstracts the reading process, enabling support for various types of sources, including streams, buffers, or custom data readers.
455
+
456
+ - `options`: [IOptions](#ioptions-interface) (optional)
457
+
458
+ An optional configuration object that specifies parsing options.
459
+
460
+ ##### Returns
461
+ - `Promise<IAudioMetadata>`:
462
+
463
+ A promise that resolves to the metadata of the audio source, including information like the title, artist, album, and more.
464
+
465
+
466
+ ##### Example
467
+ ````js
468
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
469
+ import { S3Client } from '@aws-sdk/client-s3';
470
+ import { makeTokenizer } from '@tokenizer/s3';
471
+ import { parseFromTokenizer as mmParseFromTokenizer } from 'music-metadata';
472
+
473
+ // Configure the S3 client
474
+ const s3 = new S3Client({
475
+ region: 'eu-west-2',
476
+ credentials: fromNodeProviderChain(),
477
+ });
478
+
479
+ // Helper function to create a tokenizer for S3 objects
480
+ async function makeS3TestDataTokenizer(key, options) {
481
+ return await makeTokenizer(s3, {
482
+ Bucket: 'music-metadata',
483
+ Key: key,
484
+ }, options);
485
+ }
486
+
487
+ // Function to read and log metadata from an S3 object
488
+ async function readMetadata() {
489
+ try {
490
+ // Create a tokenizer for the specified S3 object
491
+ const tokenizer = await makeS3TestDataTokenizer('path/to/audio/file.mp3', { disableChunked: false });
492
+
493
+ // Parse the metadata from the tokenizer
494
+ const metadata = await mmParseFromTokenizer(tokenizer);
495
+
496
+ // Log the retrieved metadata
497
+ console.log(metadata);
498
+ } catch (error) {
499
+ console.error('Error parsing metadata:', error.message);
500
+ }
501
+ }
502
+
503
+ // Execute the metadata reading function
504
+ readMetadata();
505
+ ````
506
+ ##### Additional Resources
507
+ - [strtok3](https://github.com/Borewit/strtok3) - Learn more about the `ITokenizer` interface and how to implement it for various use cases.
508
+ - [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.
509
+ - [@tokenizer/s3](https://github.com/Borewit/tokenizer-s3) - Example of `ITokenizer` implementation.
510
+
511
+ ### Handling Parse Errors
512
+
513
+ `music-metadata` provides a robust and extensible error handling system with custom error classes that inherit from the standard JavaScript `Error`.
514
+ All possible parsing errors are part of a union type `UnionOfParseErrors`, ensuring that every error scenario is accounted for in your code.
515
+
516
+ #### Union of Parse Errors
517
+
518
+ All parsing errors extend from the base class `ParseError` and are included in the `UnionOfParseErrors` type:
519
+ ```ts
520
+ export type UnionOfParseErrors =
521
+ | CouldNotDetermineFileTypeError
522
+ | UnsupportedFileTypeError
523
+ | UnexpectedFileContentError
524
+ | FieldDecodingError
525
+ | InternalParserError;
526
+ ```
527
+
528
+ #### Error Types
529
+
530
+ - `CouldNotDetermineFileTypeError`: Raised when the file type cannot be determined.
531
+ - `UnsupportedFileTypeError`: Raised when an unsupported file type is encountered.
532
+ - `UnexpectedFileContentError`: Raised when the file content does not match the expected format.
533
+ - `FieldDecodingError`: Raised when a specific field in the file cannot be decoded.
534
+ - `InternalParserError`: Raised for internal parser errors.
535
+
536
+ ### Other functions
537
+
538
+ #### `orderTags` function
539
+
540
+ Utility to Converts the native tags to a dictionary index on the tag identifier
541
+
542
+ ```ts
543
+ orderTags(nativeTags: ITag[]): [tagId: string]: any[]
544
+ ```
545
+
546
+ ```js
547
+ import { parseFile, orderTags } from 'music-metadata';
548
+ import { inspect } from 'util';
549
+
550
+ (async () => {
551
+ try {
552
+ const metadata = await parseFile('../test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3');
553
+ const orderedTags = orderTags(metadata.native['ID3v2.3']);
554
+ console.log(inspect(orderedTags, { showHidden: false, depth: null }));
555
+ } catch (error) {
556
+ console.error(error.message);
557
+ }
558
+ })();
559
+ ```
560
+
561
+ #### `ratingToStars` function
562
+
563
+ 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.
564
+
565
+ ```ts
566
+ ratingToStars(rating: number): number
567
+ ```
568
+ #### `selectCover` function
569
+
570
+ Select cover image based on image type field, otherwise the first picture in file.
571
+
572
+ ```ts
573
+ export function selectCover(pictures?: IPicture[]): IPicture | null
574
+ ```
575
+
576
+ ```js
577
+ import { parseFile, selectCover } from 'music-metadata';
578
+
579
+ (async () => {
580
+ const {common} = await parseFile(filePath);
581
+ const cover = selectCover(common.picture); // pick the cover image
582
+ }
583
+ )();
584
+ ```
585
+
586
+ ### `IOptions` Interface
587
+ - `duration`: `boolean` (default: `false`)
588
+
589
+ If set to `true`, the parser will analyze the entire media file, if necessary, to determine its duration.
590
+ This option ensures accurate duration calculation but may increase processing time for large files.
591
+
592
+ - `observer`: `(update: MetadataEvent) => void;`:
593
+
594
+ A callback function that is invoked whenever there is an update to the common (generic) tag or format properties during parsing.
595
+ This allows for real-time updates on metadata changes.
596
+
597
+ - `skipCovers`: `boolean` (default: `false`)
598
+
599
+ If set to `true`, the parser will skip the extraction of embedded cover art (images) from the media file.
600
+ This can be useful to avoid processing unnecessary data if cover images are not required.
601
+
602
+ - `mkvUseIndex`: `boolean` (default: `false`)
603
+
604
+ 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.
605
+ If the SeekHead element is absent in the Matroska file, this flag has no effect.
606
+
607
+ > [!NOTE]
608
+ > - 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.
609
+ > - Using `mkvUseIndex` can improve performance in Matroska files, but be aware of potential side effects, such as missing metadata due to skipped elements.
610
+
611
+
612
+ ### `IAudioMetadata` interface
613
+
614
+ If the returned promise resolves, the metadata (TypeScript `IAudioMetadata` interface) contains:
615
+ - [`metadata.format`](#metadataformat) Audio format information
616
+ - [`metadata.common`](#metadatacommon) Is a generic (abstract) way of reading metadata information.
617
+ - [`metadata.trackInfo`](#metadatatrackInfo) Is a generic (abstract) way of reading metadata information.
618
+ - `metadata.native` List of native (original) tags found in the parsed audio file.
619
+
620
+ #### `metadata.format`
621
+
622
+ The questionmark `?` indicates the property is optional.
623
+
624
+ Audio format information. Defined in the TypeScript `IFormat` interface:
625
+ - `format.container?: string` Audio encoding format. e.g.: 'flac'
626
+ - `format.codec?` Name of the codec (algorithm used for the audio compression)
627
+ - `format.codecProfile?: string` Codec profile / settings
628
+ - `format.tagTypes?: TagType[]` List of tagging formats found in parsed audio file
629
+ - `format.duration?: number` Duration in seconds
630
+ - `format.bitrate?: number` Number bits per second of encoded audio file
631
+ - `format.sampleRate?: number` Sampling rate in Samples per second (S/s)
632
+ - `format.bitsPerSample?: number` Audio bit depth
633
+ - `format.lossless?: boolean` True if lossless, false for lossy encoding
634
+ - `format.numberOfChannels?: number` Number of audio channels
635
+ - `format.creationTime?: Date` Track creation time
636
+ - `format.modificationTime?: Date` Track modification / tag update time
637
+ - `format.trackGain?: number` Track gain in dB
638
+ - `format.albumGain?: number` Album gain in dB
639
+
640
+ #### `metadata.trackInfo`
641
+
642
+ 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,
643
+
644
+ `metadata.trackInfo` is either `undefined` or has an **array** of [trackInfo](#trackinfo)
645
+
646
+ ##### trackInfo
647
+
648
+ Audio format information. Defined in the TypeScript `IFormat` interface:
649
+ - `trackInfo.type?: TrackType` Track type
650
+ - `trackInfo.codecName?: string` Codec name
651
+ - `trackInfo.codecSettings?: string` Codec settings
652
+ - `trackInfo.flagEnabled?: boolean` Set if the track is usable, default: `true`
653
+ - `trackInfo.flagDefault?: boolean` Set if that track (audio, video or subs) SHOULD be active if no language found matches the user preference.
654
+ - `trackInfo.flagLacing?: boolean` Set if the track **may** contain blocks using lacing
655
+ - `trackInfo.name?: string` A human-readable track name.
656
+ - `trackInfo.language?: string` Specifies the language of the track
657
+ - `trackInfo.audio?: IAudioTrack`, see [`trackInfo.audioTrack`](#trackinfoaudiotrack)
658
+ - `trackInfo.video?: IVideoTrack`, see [`trackInfo.videoTrack`](#trackinfovideotrack)
659
+
660
+ ##### `trackInfo.audioTrack`
661
+
662
+ - `audioTrack.samplingFrequency?: number`
663
+ - `audioTrack.outputSamplingFrequency?: number`
664
+ - `audioTrack.channels?: number`
665
+ - `audioTrack.channelPositions?: Buffer`
666
+ - `audioTrack.bitDepth?: number`
667
+
668
+ ##### `trackInfo.videoTrack`
669
+
670
+ - `videoTrack.flagInterlaced?: boolean`
671
+ - `videoTrack.stereoMode?: number`
672
+ - `videoTrack.pixelWidth?: number`
673
+ - `videoTrack.pixelHeight?: number`
674
+ - `videoTrack.displayWidth?: number`
675
+ - `videoTrack.displayHeight?: number`
676
+ - `videoTrack.displayUnit?: number`
677
+ - `videoTrack.aspectRatioType?: number`
678
+ - `videoTrack.colourSpace?: Buffer`
679
+ - `videoTrack.gammaValue?: number`
680
+
681
+ #### `metadata.common`
682
+
683
+ [Common tag documentation](doc/common_metadata.md) is automatically generated.
684
+
685
+ ## Examples
686
+
687
+ In order to read the duration of a stream (with the exception of file streams), in some cases you should pass the size of the file in bytes.
688
+ ```js
689
+ import { parseStream } from 'music-metadata';
690
+ import { inspect } from 'util';
691
+
692
+ (async () => {
693
+ const metadata = await parseStream(someReadStream, {mimeType: 'audio/mpeg', size: 26838}, {duration: true});
694
+ console.log(inspect(metadata, {showHidden: false, depth: null}));
695
+ someReadStream.close();
696
+ }
697
+ )();
698
+ ```
699
+
700
+ ### Access cover art
701
+
702
+ Via `metadata.common.picture` you can access an array of cover art if present.
703
+ Each picture has this interface:
704
+
705
+ ```ts
706
+ /**
707
+ * Attached picture, typically used for cover art
708
+ */
709
+ export interface IPicture {
710
+ /**
711
+ * Image mime type
712
+ */
713
+ format: string;
714
+ /**
715
+ * Image data
716
+ */
717
+ data: Buffer;
718
+ /**
719
+ * Optional description
720
+ */
721
+ description?: string;
722
+ /**
723
+ * Picture type
724
+ */
725
+ type?: string;
726
+ }
727
+ ```
728
+
729
+ To assign `img` HTML-object you can do something like:
730
+ ```js
731
+ import {uint8ArrayToBase64} from 'uint8array-extras';
732
+
733
+ img.src = `data:${picture.format};base64,${uint8ArrayToBase64(picture.data)}`;
734
+ ```
735
+
736
+ ## Dependencies
737
+
738
+ Dependency diagram:
739
+ ```mermaid
740
+ graph TD;
741
+ MMN("music-metadata (Node.js entry point)")-->MMP
742
+ MMN-->FTN
743
+ MMP("music-metadata (primary entry point)")-->S(strtok3)
744
+ MMP-->TY(token-types)
745
+ MMP-->FTP
746
+ MMP-->UAE
747
+ FTN("file-type (Node.js entry point)")-->FTP
748
+ FTP("file-type (primary entry point)")-->S
749
+ S(strtok3)-->P(peek-readable)
750
+ S(strtok3)-->TO("@tokenizer/token")
751
+ TY(token-types)-->TO
752
+ TY-->IE("ieee754")
753
+ FTP-->TY
754
+ NS("node:stream")
755
+ FTN-->NS
756
+ FTP-->UAE(uint8array-extras)
757
+ style NS fill:#F88,stroke:#A44
758
+ style IE fill:#CCC,stroke:#888
759
+ style FTN fill:#FAA,stroke:#A44
760
+ style MMN fill:#FAA,stroke:#A44
761
+ ```
762
+
763
+ Dependency list:
764
+ - [tokenizer-token](https://github.com/Borewit/tokenizer-token)
765
+ - [strtok3](https://github.com/Borewit/strtok3)
766
+ - [token-types](https://github.com/Borewit/token-types)
767
+ - [file-type](https://github.com/sindresorhus/file-type)
768
+ - [@tokenizer-token](https://github.com/Borewit/tokenizer-token)
769
+ - [peek-readable](https://github.com/Borewit/peek-readable)
770
+
771
+ ## CommonJS backward compatibility
772
+
773
+ For legacy CommonJS projects needing to load the `music-metadata` ESM module, you can use the `loadMusicMetadata` function:
774
+ ```js
775
+ const { loadMusicMetadata } = require('music-metadata');
776
+
777
+ (async () => {
778
+ // Dynamically loads the ESM module in a CommonJS project
779
+ const mm = await loadMusicMetadata();
780
+
781
+ const metadata = await mm.parseFile('/path/to/your/file');
782
+ })();
783
+
784
+ ```
785
+
786
+ > [!NOTE]
787
+ > The `loadMusicMetadata` function is experimental.
788
+
789
+ ## Frequently Asked Questions
790
+
791
+ 1. How can I traverse (a long) list of files?
792
+
793
+ What is important that file parsing should be done in a sequential manner.
794
+ In a plain loop, due to the asynchronous character (like most JavaScript functions), it would cause all the files to run in parallel which is will cause your application to hang in no time.
795
+ There are multiple ways of achieving this:
796
+
797
+ 1. Using recursion
798
+
799
+ ```js
800
+ import { parseFile } from 'music-metadata';
801
+
802
+ function parseFiles(audioFiles) {
803
+
804
+ const audioFile = audioFiles.shift();
805
+
806
+ if (audioFile) {
807
+ return parseFile(audioFile).then(metadata => {
808
+ // Do great things with the metadata
809
+ return parseFiles(audioFiles); // process rest of the files AFTER we are finished
810
+ })
811
+ }
812
+ }
813
+
814
+ ```
815
+
816
+ 1. Use async/await
817
+
818
+ Use [async/await](https://javascript.info/async-await)
819
+
820
+ ```js
821
+ import { parseFile } from 'music-metadata';
822
+
823
+ // it is required to declare the function 'async' to allow the use of await
824
+ async function parseFiles(audioFiles) {
825
+
826
+ for (const audioFile of audioFiles) {
827
+
828
+ // await will ensure the metadata parsing is completed before we move on to the next file
829
+ const metadata = await parseFile(audioFile);
830
+ // Do great things with the metadata
831
+ }
832
+ }
833
+ ```
834
+
835
+ ## Licence
836
+
837
+ The MIT License (MIT)
838
+
839
+ Copyright © 2024 Borewit
840
+
841
+ 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:
842
+
843
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
844
+
845
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.