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