music-metadata 11.6.1 → 11.7.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 (3) hide show
  1. package/README.md +35 -22
  2. package/lib/core.js +7 -5
  3. package/package.json +7 -6
package/README.md CHANGED
@@ -179,11 +179,11 @@ parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>
179
179
  The following example demonstrates how to use the parseFile function to read metadata from an audio file:
180
180
  ```js
181
181
  import { parseFile } from 'music-metadata';
182
- import { inspect } from 'util';
182
+ import { inspect } from 'node:util';
183
183
 
184
184
  (async () => {
185
185
  try {
186
- const filePath = '../music-metadata/test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3';
186
+ const filePath = 'test/samples/MusicBrainz - Beth Hart - Sinner\'s Prayer [id3v2.3].V2.mp3';
187
187
  const metadata = await parseFile(filePath);
188
188
 
189
189
  // Output the parsed metadata to the console in a readable format
@@ -322,19 +322,24 @@ Here’s an example of how to use the `parseWebStream` function to extract metad
322
322
  import { parseWebStream } from 'music-metadata';
323
323
 
324
324
  (async () => {
325
- try {
326
- // Assuming you have a ReadableStream of an audio file
327
- const response = await fetch('https://example.com/path/to/audio/file.mp3');
328
- const webStream = response.body;
325
+ try {
326
+ // Fetch the audio file
327
+ const response = await fetch('https://github.com/Borewit/test-audio/raw/refs/heads/master/Various%20Artists%20-%202008%20-%20netBloc%20Vol%2013%20-%20Color%20in%20a%20World%20of%20Monochrome%20%5BAAC-40%5D/1.02.%20Solid%20Ground.m4a');
328
+
329
+ // Extract the Content-Length header and convert it to a number
330
+ const contentLength = response.headers.get('Content-Length');
331
+ const size = contentLength ? parseInt(contentLength, 10) : undefined;
329
332
 
330
333
  // Parse the metadata from the web stream
331
- const metadata = await parseWebStream(webStream, 'audio/mpeg');
334
+ const metadata = await parseWebStream(response.body, {
335
+ mimeType: response.headers.get('Content-Type'),
336
+ size // Important to pass the content-length
337
+ });
332
338
 
333
- // Log the parsed metadata
334
339
  console.log(metadata);
335
- } catch (error) {
336
- console.error('Error parsing metadata:', error.message);
337
- }
340
+ } catch (error) {
341
+ console.error('Error parsing metadata:', error.message);
342
+ }
338
343
  })();
339
344
  ```
340
345
 
@@ -591,23 +596,31 @@ Returns a list of supported MIME-types. This may include some MIME-types which a
591
596
  ### `IOptions` Interface
592
597
  - `duration`: `boolean` (default: `false`)
593
598
 
594
- If set to `true`, the parser will analyze the entire media file, if necessary, to determine its duration.
595
- This option ensures accurate duration calculation but may increase processing time for large files.
599
+ When `true`, the parser will read the entire media file _if necessary_ to determine the duration.
600
+ This is only applicable in cases where duration cannot be reliably inferred without full file analysis.
601
+ Note that enabling this option **does not guarantee** that duration will be available,
602
+ only that the parser will attempt to calculate it when possible, even if it requires reading the full file.
603
+
604
+ - `mkvUseIndex`: `boolean` (default: `false`)
605
+
606
+ When `true`, the parser uses the SeekHead index in Matroska (MKV) files to skip segment and cluster elements.
607
+ This experimental feature can improve performance, but:
608
+ - Metadata not listed in the SeekHead may be skipped.
609
+ - If the SeekHead is missing, this option has no effect.
596
610
 
597
611
  - `observer`: `(update: MetadataEvent) => void;`:
598
612
 
599
- A callback function that is invoked whenever there is an update to the common (generic) tag or format properties during parsing.
600
- This allows for real-time updates on metadata changes.
613
+ Callback function triggered when common tags or format properties are updated during parsing.
614
+ Allows real-time monitoring of metadata as it becomes available.
601
615
 
602
616
  - `skipCovers`: `boolean` (default: `false`)
603
-
604
- If set to `true`, the parser will skip the extraction of embedded cover art (images) from the media file.
605
- This can be useful to avoid processing unnecessary data if cover images are not required.
617
+
618
+ When `true`, embedded cover art (images) will not be extracted.
619
+ Useful for reducing memory and processing when cover images are unnecessary.
606
620
 
607
- - `mkvUseIndex`: `boolean` (default: `false`)
608
-
609
- 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.
610
- If the SeekHead element is absent in the Matroska file, this flag has no effect.
621
+ - `skipPostHeaders`: `boolean` (default: `false`)
622
+ When `true`, tag headers located at the end of the file will not be read.
623
+ This is particularly beneficial for streaming input, as it avoids the need to read the entire stream.
611
624
 
612
625
  > [!NOTE]
613
626
  > - 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.
package/lib/core.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Primary entry point, Node.js specific entry point is MusepackParser.ts
3
3
  */
4
- import { fromWebStream, fromBuffer } from 'strtok3';
4
+ import { fromWebStream, fromBuffer, fromBlob } from 'strtok3';
5
5
  import { ParserFactory } from './ParserFactory.js';
6
6
  import { APEv2Parser } from './apev2/APEv2Parser.js';
7
7
  import { hasID3v1Header } from './id3v1/ID3v1Parser.js';
@@ -17,11 +17,13 @@ export * from './ParseError.js';
17
17
  * @returns Metadata
18
18
  */
19
19
  export async function parseBlob(blob, options = {}) {
20
- const fileInfo = { mimeType: blob.type, size: blob.size };
21
- if (blob instanceof File) {
22
- fileInfo.path = blob.name;
20
+ const tokenizer = fromBlob(blob);
21
+ try {
22
+ return await parseFromTokenizer(tokenizer, options);
23
+ }
24
+ finally {
25
+ await tokenizer.close();
23
26
  }
24
- return parseWebStream(blob.stream(), fileInfo, options);
25
27
  }
26
28
  /**
27
29
  * Parse audio from Web Stream.Readable
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "music-metadata",
3
3
  "description": "Music metadata parser for Node.js, supporting virtual any audio and tag format.",
4
- "version": "11.6.1",
4
+ "version": "11.7.0",
5
5
  "author": {
6
6
  "name": "Borewit",
7
7
  "url": "https://github.com/Borewit"
@@ -102,7 +102,8 @@
102
102
  "test-coverage": "c8 yarn run test",
103
103
  "send-codacy": "c8 report --reporter=text-lcov | codacy-coverage",
104
104
  "doc-gen": "yarn node doc-gen/gen.js",
105
- "typecheck": "tsc --project ./lib/tsconfig.json --noEmit && tsc --project ./test/tsconfig.json --noEmit"
105
+ "typecheck": "tsc --project ./lib/tsconfig.json --noEmit && tsc --project ./test/tsconfig.json --noEmit",
106
+ "update-biome": "yarn add -D --exact @biomejs/biome && npx @biomejs/biome migrate --write"
106
107
  },
107
108
  "dependencies": {
108
109
  "@tokenizer/token": "^0.3.0",
@@ -110,21 +111,21 @@
110
111
  "debug": "^4.4.1",
111
112
  "file-type": "^21.0.0",
112
113
  "media-typer": "^1.1.0",
113
- "strtok3": "^10.3.1",
114
+ "strtok3": "^10.3.2",
114
115
  "token-types": "^6.0.3",
115
116
  "uint8array-extras": "^1.4.0"
116
117
  },
117
118
  "devDependencies": {
118
- "@biomejs/biome": "2.0.6",
119
+ "@biomejs/biome": "2.1.1",
119
120
  "@types/chai": "^5.2.2",
120
121
  "@types/chai-as-promised": "^8.0.2",
121
122
  "@types/content-type": "^1.1.9",
122
123
  "@types/debug": "^4.1.12",
123
124
  "@types/media-typer": "^1.1.3",
124
125
  "@types/mocha": "^10.0.10",
125
- "@types/node": "^24.0.4",
126
+ "@types/node": "^24.0.14",
126
127
  "c8": "^10.1.3",
127
- "chai": "^5.2.0",
128
+ "chai": "^5.2.1",
128
129
  "chai-as-promised": "^8.0.1",
129
130
  "del-cli": "^6.0.0",
130
131
  "mime": "^4.0.7",