music-metadata 12.2.0 → 12.2.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 +6 -0
- package/lib/dsdiff/DsdiffLoader.js +1 -1
- package/lib/ebml/EbmlIterator.d.ts +1 -0
- package/lib/ebml/EbmlIterator.js +30 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +6 -1
- package/lib/mp4/MP4Parser.js +7 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -177,6 +177,8 @@ parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>
|
|
|
177
177
|
|
|
178
178
|
- This function is **Node.js-only** and relies on Node.js-specific APIs to access the filesystem.
|
|
179
179
|
|
|
180
|
+
- The path must resolve to a regular file. Symlinks to regular files are supported. Directories, FIFOs (named pipes), and other non-regular filesystem objects are rejected with `NotRegularFileError` from `strtok3`. FIFOs are rejected without waiting for a writer. To parse streamed audio, use [`parseStream()`](#parsestream-function).
|
|
181
|
+
|
|
180
182
|
- For browser environments, consider using the [parseBlob](#parseblob-function) to parse [File object](https://developer.mozilla.org/en-US/docs/Web/API/File) objects.
|
|
181
183
|
|
|
182
184
|
##### Example:
|
|
@@ -631,6 +633,10 @@ Returns a list of supported MIME-types. This may include some MIME-types which a
|
|
|
631
633
|
- Metadata not listed in the SeekHead may be skipped.
|
|
632
634
|
- If the SeekHead is missing, this option has no effect.
|
|
633
635
|
|
|
636
|
+
For large MKV files on network shares, use `parseFile(filePath, { mkvUseIndex: true })`
|
|
637
|
+
when the file's SeekHead index is reliable. The default traversal reads every cluster header,
|
|
638
|
+
so network latency can dominate even though the audio/video payloads are skipped.
|
|
639
|
+
|
|
634
640
|
- `observer`: `(update: IMetadataEvent) => void`:
|
|
635
641
|
|
|
636
642
|
Callback function triggered when common tags or format properties are updated during parsing.
|
package/lib/ebml/EbmlIterator.js
CHANGED
|
@@ -2,7 +2,7 @@ import initDebug from 'debug';
|
|
|
2
2
|
import { EndOfStreamError } from 'strtok3';
|
|
3
3
|
import * as Token from 'token-types';
|
|
4
4
|
import { Float32_BE, Float64_BE, StringType, UINT8 } from 'token-types';
|
|
5
|
-
import { readBuffer } from '../common/Allocation.js';
|
|
5
|
+
import { getMaxMemoryAllocation, readBuffer } from '../common/Allocation.js';
|
|
6
6
|
import { makeUnexpectedFileContentError } from '../ParseError.js';
|
|
7
7
|
import { DataType } from './types.js';
|
|
8
8
|
const debug = initDebug('music-metadata:parser:ebml');
|
|
@@ -28,6 +28,7 @@ export class EbmlIterator {
|
|
|
28
28
|
ebmlMaxIDLength = 4;
|
|
29
29
|
ebmlMaxSizeLength = 8;
|
|
30
30
|
tokenizer;
|
|
31
|
+
elementHeader;
|
|
31
32
|
/**
|
|
32
33
|
* @param {ITokenizer} tokenizer Input
|
|
33
34
|
* @param tokenizer
|
|
@@ -35,6 +36,7 @@ export class EbmlIterator {
|
|
|
35
36
|
constructor(tokenizer, options = {}) {
|
|
36
37
|
this.options = options;
|
|
37
38
|
this.tokenizer = tokenizer;
|
|
39
|
+
this.elementHeader = new Uint8Array(Math.min(12, getMaxMemoryAllocation(options)));
|
|
38
40
|
this.parserMap.set(DataType.uint, e => this.readUint(e));
|
|
39
41
|
this.parserMap.set(DataType.string, e => this.readString(e));
|
|
40
42
|
this.parserMap.set(DataType.binary, e => this.readBuffer(e));
|
|
@@ -158,6 +160,33 @@ export class EbmlIterator {
|
|
|
158
160
|
return id;
|
|
159
161
|
}
|
|
160
162
|
async readElement() {
|
|
163
|
+
if (this.tokenizer.supportsRandomAccess() && this.elementHeader.length === 12) {
|
|
164
|
+
// RFC 8794 (July 2020), sections 4-6: an ID and size occupy at most 4 + 8 bytes.
|
|
165
|
+
// Peek both VINTs together to avoid four separate file/network reads per element.
|
|
166
|
+
// https://www.rfc-editor.org/rfc/rfc8794.html#section-4
|
|
167
|
+
const available = await this.tokenizer.peekBuffer(this.elementHeader, { mayBeLess: true });
|
|
168
|
+
const width = (offset, maximum) => {
|
|
169
|
+
if (offset >= available) {
|
|
170
|
+
throw new EndOfStreamError();
|
|
171
|
+
}
|
|
172
|
+
for (let length = 1; length <= maximum; ++length) {
|
|
173
|
+
if ((this.elementHeader[offset] & (0x80 >> (length - 1))) !== 0) {
|
|
174
|
+
if (offset + length > available) {
|
|
175
|
+
throw new EndOfStreamError();
|
|
176
|
+
}
|
|
177
|
+
return length;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
throw new EbmlContentError('VINT value exceeding maximum size');
|
|
181
|
+
};
|
|
182
|
+
const idLength = width(0, this.ebmlMaxIDLength);
|
|
183
|
+
const sizeLength = width(idLength, this.ebmlMaxSizeLength);
|
|
184
|
+
const id = readUIntBE(this.elementHeader.subarray(0, idLength), idLength);
|
|
185
|
+
const lenField = this.elementHeader.subarray(idLength, idLength + sizeLength);
|
|
186
|
+
lenField[0] ^= 0x80 >> (sizeLength - 1);
|
|
187
|
+
this.tokenizer.setPosition(this.tokenizer.position + idLength + sizeLength);
|
|
188
|
+
return { id, len: readUIntBE(lenField, sizeLength) };
|
|
189
|
+
}
|
|
161
190
|
const id = await this.readVintData(this.ebmlMaxIDLength);
|
|
162
191
|
const lenField = await this.readVintData(this.ebmlMaxSizeLength);
|
|
163
192
|
lenField[0] ^= 0x80 >> (lenField.length - 1);
|
package/lib/index.d.ts
CHANGED
|
@@ -15,8 +15,10 @@ export * from './core.js';
|
|
|
15
15
|
export declare function parseStream(stream: Readable, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>;
|
|
16
16
|
/**
|
|
17
17
|
* Parse audio from Node file
|
|
18
|
+
* Requires a regular file; symlinks to regular files are supported.
|
|
18
19
|
* @param filePath - Media file to read meta-data from
|
|
19
20
|
* @param options - Parsing options
|
|
20
21
|
* @returns Metadata
|
|
22
|
+
* @throws {NotRegularFileError} If the opened filesystem object is not a regular file.
|
|
21
23
|
*/
|
|
22
24
|
export declare function parseFile(filePath: string, options?: IOptions): Promise<IAudioMetadata>;
|
package/lib/index.js
CHANGED
|
@@ -26,13 +26,18 @@ export async function parseStream(stream, fileInfo, options = {}) {
|
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Parse audio from Node file
|
|
29
|
+
* Requires a regular file; symlinks to regular files are supported.
|
|
29
30
|
* @param filePath - Media file to read meta-data from
|
|
30
31
|
* @param options - Parsing options
|
|
31
32
|
* @returns Metadata
|
|
33
|
+
* @throws {NotRegularFileError} If the opened filesystem object is not a regular file.
|
|
32
34
|
*/
|
|
33
35
|
export async function parseFile(filePath, options = {}) {
|
|
34
36
|
debug(`parseFile: ${filePath}`);
|
|
35
|
-
const fileTokenizer = await fromFile(filePath,
|
|
37
|
+
const fileTokenizer = await fromFile(filePath, {
|
|
38
|
+
...getTokenizerOptions(options),
|
|
39
|
+
regularFileOnly: true
|
|
40
|
+
});
|
|
36
41
|
const parserFactory = new ParserFactory();
|
|
37
42
|
try {
|
|
38
43
|
const parserLoader = parserFactory.findLoaderForExtension(filePath);
|
package/lib/mp4/MP4Parser.js
CHANGED
|
@@ -626,7 +626,13 @@ export class MP4Parser extends BasicParser {
|
|
|
626
626
|
case 'tkhd': // TrackHeaderBox
|
|
627
627
|
track.header = await this.readToken(new AtomToken.TrackHeaderAtom(payLoadLength));
|
|
628
628
|
break;
|
|
629
|
-
case 'hdlr': //
|
|
629
|
+
case 'hdlr': // Media handler; metadata and data handlers do not identify the track type.
|
|
630
|
+
// QuickTime File Format, Media atom: Handler reference atom ('hdlr').
|
|
631
|
+
// https://developer.apple.com/documentation/quicktime-file-format/media_atom/handler_reference_atom
|
|
632
|
+
if (child.parent?.header.name !== 'mdia' || child.parent.parent !== trakBox) {
|
|
633
|
+
await this.tokenizer.ignore(payLoadLength);
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
630
636
|
track.handler = await this.readToken(new AtomToken.HandlerBox(payLoadLength));
|
|
631
637
|
if (track.handler.handlerType === 'audi' || track.handler.handlerType === 'soun') {
|
|
632
638
|
this.hasAudioTrack = true;
|
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": "12.2.
|
|
4
|
+
"version": "12.2.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Borewit",
|
|
7
7
|
"url": "https://github.com/Borewit"
|
|
@@ -122,9 +122,9 @@
|
|
|
122
122
|
"@tokenizer/token": "^0.3.0",
|
|
123
123
|
"content-type": "^3.1.1",
|
|
124
124
|
"debug": "^4.4.3",
|
|
125
|
-
"file-type": "^22.
|
|
125
|
+
"file-type": "^22.2.0",
|
|
126
126
|
"media-typer": "^2.0.0",
|
|
127
|
-
"strtok3": "^10.
|
|
127
|
+
"strtok3": "^10.5.0",
|
|
128
128
|
"token-types": "^6.1.2",
|
|
129
129
|
"uint8array-extras": "^1.6.0",
|
|
130
130
|
"win-guid": "^0.2.1"
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"@types/debug": "^4.1.13",
|
|
137
137
|
"@types/media-typer": "^1.1.3",
|
|
138
138
|
"@types/mocha": "^10.0.10",
|
|
139
|
-
"@types/node": "^26.6.
|
|
139
|
+
"@types/node": "^26.6.5",
|
|
140
140
|
"c8": "^12.0.0",
|
|
141
141
|
"chai": "^6.3.0",
|
|
142
142
|
"chai-as-promised": "^8.0.2",
|