music-metadata 10.0.1 → 10.1.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.
- package/LICENSE.txt +9 -9
- package/README.md +520 -520
- package/lib/ParserFactory.d.ts +20 -28
- package/lib/ParserFactory.js +204 -207
- package/lib/aiff/AiffParser.js +20 -18
- package/lib/aiff/AiffToken.d.ts +14 -2
- package/lib/aiff/AiffToken.js +12 -0
- package/lib/apev2/APEv2Parser.d.ts +4 -4
- package/lib/apev2/APEv2Parser.js +12 -10
- package/lib/apev2/APEv2Token.d.ts +2 -4
- package/lib/apev2/APEv2Token.js +0 -3
- package/lib/asf/AsfObject.d.ts +7 -16
- package/lib/asf/AsfObject.js +8 -34
- package/lib/asf/AsfParser.js +17 -10
- package/lib/asf/AsfTagMapper.d.ts +1 -1
- package/lib/asf/AsfTagMapper.js +3 -2
- package/lib/asf/AsfUtil.d.ts +3 -11
- package/lib/asf/AsfUtil.js +29 -30
- package/lib/asf/GUID.js +6 -9
- package/lib/common/BasicParser.d.ts +4 -4
- package/lib/common/BasicParser.js +6 -0
- package/lib/common/CaseInsensitiveTagMap.d.ts +1 -1
- package/lib/common/CombinedTagMapper.d.ts +5 -5
- package/lib/common/CombinedTagMapper.js +1 -1
- package/lib/common/FourCC.d.ts +1 -1
- package/lib/common/GenericTagMapper.d.ts +8 -8
- package/lib/common/GenericTagMapper.js +4 -4
- package/lib/common/GenericTagTypes.d.ts +5 -4
- package/lib/common/GenericTagTypes.js +2 -2
- package/lib/common/MetadataCollector.d.ts +9 -9
- package/lib/common/MetadataCollector.js +24 -17
- package/lib/common/RandomFileReader.d.ts +1 -1
- package/lib/common/RandomFileReader.js +1 -1
- package/lib/common/RandomUint8ArrayReader.d.ts +1 -1
- package/lib/common/Util.d.ts +2 -6
- package/lib/common/Util.js +9 -11
- package/lib/core.d.ts +7 -9
- package/lib/core.js +10 -7
- package/lib/dsdiff/DsdiffParser.js +26 -14
- package/lib/dsdiff/DsdiffToken.d.ts +2 -2
- package/lib/dsdiff/DsdiffToken.js +1 -0
- package/lib/dsf/DsfChunk.js +1 -0
- package/lib/dsf/DsfParser.js +4 -2
- package/lib/flac/FlacParser.d.ts +3 -3
- package/lib/flac/FlacParser.js +9 -12
- package/lib/id3v1/ID3v1Parser.d.ts +1 -1
- package/lib/id3v1/ID3v1Parser.js +7 -4
- package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
- package/lib/id3v2/AbstractID3Parser.js +2 -1
- package/lib/id3v2/FrameParser.d.ts +28 -3
- package/lib/id3v2/FrameParser.js +50 -28
- package/lib/id3v2/ID3v22TagMapper.d.ts +1 -1
- package/lib/id3v2/ID3v24TagMapper.d.ts +2 -1
- package/lib/id3v2/ID3v24TagMapper.js +23 -16
- package/lib/id3v2/ID3v2Parser.d.ts +2 -2
- package/lib/id3v2/ID3v2Parser.js +19 -8
- package/lib/iff/index.js +1 -0
- package/lib/index.d.ts +5 -6
- package/lib/index.js +7 -8
- package/lib/lyrics3/Lyrics3.d.ts +1 -1
- package/lib/lyrics3/Lyrics3.js +1 -1
- package/lib/matroska/MatroskaDtd.d.ts +1 -1
- package/lib/matroska/MatroskaDtd.js +139 -138
- package/lib/matroska/MatroskaParser.d.ts +4 -4
- package/lib/matroska/MatroskaParser.js +12 -12
- package/lib/matroska/types.d.ts +6 -4
- package/lib/mp4/Atom.d.ts +3 -3
- package/lib/mp4/Atom.js +4 -7
- package/lib/mp4/AtomToken.js +2 -1
- package/lib/mp4/MP4Parser.js +29 -20
- package/lib/mp4/MP4TagMapper.d.ts +2 -2
- package/lib/mp4/MP4TagMapper.js +1 -1
- package/lib/mpeg/ExtendedLameHeader.d.ts +4 -4
- package/lib/mpeg/ExtendedLameHeader.js +2 -1
- package/lib/mpeg/MpegParser.d.ts +1 -1
- package/lib/mpeg/MpegParser.js +145 -96
- package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
- package/lib/mpeg/ReplayGainDataFormat.js +1 -0
- package/lib/mpeg/XingTag.d.ts +4 -4
- package/lib/mpeg/XingTag.js +5 -4
- package/lib/musepack/index.js +1 -0
- package/lib/musepack/sv7/BitReader.js +14 -17
- package/lib/musepack/sv7/MpcSv7Parser.js +6 -1
- package/lib/musepack/sv7/StreamVersion7.js +1 -0
- package/lib/musepack/sv8/MpcSv8Parser.js +6 -2
- package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
- package/lib/musepack/sv8/StreamVersion8.js +1 -0
- package/lib/ogg/Ogg.d.ts +1 -1
- package/lib/ogg/Ogg.js +1 -0
- package/lib/ogg/OggParser.d.ts +3 -3
- package/lib/ogg/OggParser.js +25 -16
- package/lib/ogg/opus/Opus.d.ts +1 -1
- package/lib/ogg/opus/Opus.js +1 -0
- package/lib/ogg/opus/OpusParser.d.ts +4 -4
- package/lib/ogg/opus/OpusParser.js +2 -0
- package/lib/ogg/speex/Speex.js +1 -0
- package/lib/ogg/speex/SpeexParser.d.ts +4 -4
- package/lib/ogg/speex/SpeexParser.js +1 -0
- package/lib/ogg/theora/Theora.js +1 -0
- package/lib/ogg/theora/TheoraParser.d.ts +4 -4
- package/lib/ogg/theora/TheoraParser.js +1 -0
- package/lib/ogg/vorbis/Vorbis.d.ts +3 -3
- package/lib/ogg/vorbis/Vorbis.js +22 -11
- package/lib/ogg/vorbis/VorbisDecoder.d.ts +1 -1
- package/lib/ogg/vorbis/VorbisDecoder.js +1 -0
- package/lib/ogg/vorbis/VorbisParser.d.ts +4 -4
- package/lib/ogg/vorbis/VorbisParser.js +3 -2
- package/lib/ogg/vorbis/VorbisTagMapper.d.ts +2 -2
- package/lib/ogg/vorbis/VorbisTagMapper.js +2 -2
- package/lib/riff/RiffChunk.d.ts +3 -3
- package/lib/riff/RiffChunk.js +1 -0
- package/lib/riff/RiffInfoTagMap.d.ts +1 -1
- package/lib/type.d.ts +35 -25
- package/lib/wav/BwfChunk.js +1 -0
- package/lib/wav/WaveChunk.d.ts +1 -1
- package/lib/wav/WaveChunk.js +1 -0
- package/lib/wav/WaveParser.js +27 -17
- package/lib/wavpack/WavPackParser.d.ts +1 -1
- package/lib/wavpack/WavPackParser.js +22 -13
- package/lib/wavpack/WavPackToken.d.ts +13 -17
- package/lib/wavpack/WavPackToken.js +22 -23
- package/package.json +139 -150
package/lib/dsf/DsfParser.js
CHANGED
|
@@ -17,7 +17,7 @@ export class DsfParser extends AbstractID3Parser {
|
|
|
17
17
|
this.metadata.setFormat('lossless', true);
|
|
18
18
|
const dsdChunk = await this.tokenizer.readToken(DsdChunk);
|
|
19
19
|
if (dsdChunk.metadataPointer === BigInt(0)) {
|
|
20
|
-
debug(
|
|
20
|
+
debug("No ID3v2 tag present");
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
23
23
|
debug(`expect ID3v2 at offset=${dsdChunk.metadataPointer}`);
|
|
@@ -32,7 +32,7 @@ export class DsfParser extends AbstractID3Parser {
|
|
|
32
32
|
const chunkHeader = await this.tokenizer.readToken(ChunkHeader);
|
|
33
33
|
debug(`Parsing chunk name=${chunkHeader.id} size=${chunkHeader.size}`);
|
|
34
34
|
switch (chunkHeader.id) {
|
|
35
|
-
case 'fmt ':
|
|
35
|
+
case 'fmt ': {
|
|
36
36
|
const formatChunk = await this.tokenizer.readToken(FormatChunk);
|
|
37
37
|
this.metadata.setFormat('numberOfChannels', formatChunk.channelNum);
|
|
38
38
|
this.metadata.setFormat('sampleRate', formatChunk.samplingFrequency);
|
|
@@ -42,6 +42,7 @@ export class DsfParser extends AbstractID3Parser {
|
|
|
42
42
|
const bitrate = formatChunk.bitsPerSample * formatChunk.samplingFrequency * formatChunk.channelNum;
|
|
43
43
|
this.metadata.setFormat('bitrate', bitrate);
|
|
44
44
|
return; // We got what we want, stop further processing of chunks
|
|
45
|
+
}
|
|
45
46
|
default:
|
|
46
47
|
this.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len);
|
|
47
48
|
break;
|
|
@@ -50,3 +51,4 @@ export class DsfParser extends AbstractID3Parser {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
}
|
|
54
|
+
//# sourceMappingURL=DsfParser.js.map
|
package/lib/flac/FlacParser.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ITokenizer } from 'strtok3';
|
|
2
2
|
import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
|
|
3
|
-
import { INativeMetadataCollector } from '../common/MetadataCollector.js';
|
|
4
|
-
import { IOptions } from '../type.js';
|
|
5
|
-
import { ITokenParser } from '../ParserFactory.js';
|
|
3
|
+
import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
|
|
4
|
+
import type { IOptions } from '../type.js';
|
|
5
|
+
import type { ITokenParser } from '../ParserFactory.js';
|
|
6
6
|
export declare class FlacParser extends AbstractID3Parser {
|
|
7
7
|
private vorbisParser;
|
|
8
8
|
private padding;
|
package/lib/flac/FlacParser.js
CHANGED
|
@@ -45,7 +45,7 @@ export class FlacParser extends AbstractID3Parser {
|
|
|
45
45
|
let blockHeader;
|
|
46
46
|
do {
|
|
47
47
|
// Read block header
|
|
48
|
-
blockHeader = await this.tokenizer.readToken(
|
|
48
|
+
blockHeader = await this.tokenizer.readToken(BlockHeader);
|
|
49
49
|
// Parse block data
|
|
50
50
|
await this.parseDataBlock(blockHeader);
|
|
51
51
|
} while (!blockHeader.lastBlock);
|
|
@@ -74,7 +74,7 @@ export class FlacParser extends AbstractID3Parser {
|
|
|
74
74
|
await this.parsePicture(blockHeader.length);
|
|
75
75
|
return;
|
|
76
76
|
default:
|
|
77
|
-
this.metadata.addWarning(
|
|
77
|
+
this.metadata.addWarning(`Unknown block type: ${blockHeader.type}`);
|
|
78
78
|
}
|
|
79
79
|
// Ignore data block
|
|
80
80
|
return this.tokenizer.ignore(blockHeader.length).then();
|
|
@@ -83,9 +83,9 @@ export class FlacParser extends AbstractID3Parser {
|
|
|
83
83
|
* Parse STREAMINFO
|
|
84
84
|
*/
|
|
85
85
|
async parseBlockStreamInfo(dataLen) {
|
|
86
|
-
if (dataLen !==
|
|
86
|
+
if (dataLen !== BlockStreamInfo.len)
|
|
87
87
|
throw new Error('Unexpected block-stream-info length');
|
|
88
|
-
const streamInfo = await this.tokenizer.readToken(
|
|
88
|
+
const streamInfo = await this.tokenizer.readToken(BlockStreamInfo);
|
|
89
89
|
this.metadata.setFormat('container', 'FLAC');
|
|
90
90
|
this.metadata.setFormat('codec', 'FLAC');
|
|
91
91
|
this.metadata.setFormat('lossless', true);
|
|
@@ -115,15 +115,11 @@ export class FlacParser extends AbstractID3Parser {
|
|
|
115
115
|
if (this.options.skipCovers) {
|
|
116
116
|
return this.tokenizer.ignore(dataLen);
|
|
117
117
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.vorbisParser.addTag('METADATA_BLOCK_PICTURE', picture);
|
|
121
|
-
}
|
|
118
|
+
const picture = await this.tokenizer.readToken(new VorbisPictureToken(dataLen));
|
|
119
|
+
this.vorbisParser.addTag('METADATA_BLOCK_PICTURE', picture);
|
|
122
120
|
}
|
|
123
121
|
}
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
Metadata.BlockHeader = {
|
|
122
|
+
const BlockHeader = {
|
|
127
123
|
len: 4,
|
|
128
124
|
get: (buf, off) => {
|
|
129
125
|
return {
|
|
@@ -137,7 +133,7 @@ Metadata.BlockHeader = {
|
|
|
137
133
|
* METADATA_BLOCK_DATA
|
|
138
134
|
* Ref: https://xiph.org/flac/format.html#metadata_block_streaminfo
|
|
139
135
|
*/
|
|
140
|
-
|
|
136
|
+
const BlockStreamInfo = {
|
|
141
137
|
len: 34,
|
|
142
138
|
get: (buf, off) => {
|
|
143
139
|
return {
|
|
@@ -171,3 +167,4 @@ Metadata.BlockStreamInfo = {
|
|
|
171
167
|
};
|
|
172
168
|
}
|
|
173
169
|
};
|
|
170
|
+
//# sourceMappingURL=FlacParser.js.map
|
package/lib/id3v1/ID3v1Parser.js
CHANGED
|
@@ -66,12 +66,13 @@ const Iid3v1Token = {
|
|
|
66
66
|
} : null;
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
|
-
class Id3v1StringType
|
|
69
|
+
class Id3v1StringType {
|
|
70
70
|
constructor(len) {
|
|
71
|
-
|
|
71
|
+
this.len = len;
|
|
72
|
+
this.stringType = new StringType(len, 'latin1');
|
|
72
73
|
}
|
|
73
74
|
get(buf, off) {
|
|
74
|
-
let value =
|
|
75
|
+
let value = this.stringType.get(buf, off);
|
|
75
76
|
value = util.trimRightNull(value);
|
|
76
77
|
value = value.trim();
|
|
77
78
|
return value.length > 0 ? value : undefined;
|
|
@@ -103,7 +104,8 @@ export class ID3v1Parser extends BasicParser {
|
|
|
103
104
|
const header = await this.tokenizer.readToken(Iid3v1Token, offset);
|
|
104
105
|
if (header) {
|
|
105
106
|
debug('ID3v1 header found at: pos=%s', this.tokenizer.fileInfo.size - Iid3v1Token.len);
|
|
106
|
-
|
|
107
|
+
const props = ['title', 'artist', 'album', 'comment', 'track', 'year'];
|
|
108
|
+
for (const id of props) {
|
|
107
109
|
if (header[id] && header[id] !== '')
|
|
108
110
|
await this.addTag(id, header[id]);
|
|
109
111
|
}
|
|
@@ -127,3 +129,4 @@ export async function hasID3v1Header(reader) {
|
|
|
127
129
|
}
|
|
128
130
|
return false;
|
|
129
131
|
}
|
|
132
|
+
//# sourceMappingURL=ID3v1Parser.js.map
|
|
@@ -22,7 +22,7 @@ export class AbstractID3Parser extends BasicParser {
|
|
|
22
22
|
}
|
|
23
23
|
catch (err) {
|
|
24
24
|
if (err instanceof EndOfStreamError) {
|
|
25
|
-
debug(
|
|
25
|
+
debug("End-of-stream");
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
28
|
throw err;
|
|
@@ -54,3 +54,4 @@ export class AbstractID3Parser extends BasicParser {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
//# sourceMappingURL=AbstractID3Parser.js.map
|
|
@@ -1,5 +1,29 @@
|
|
|
1
|
-
import { ID3v2MajorVersion, ITextEncoding } from './ID3v2Token.js';
|
|
2
|
-
import { IWarningCollector } from '../common/MetadataCollector.js';
|
|
1
|
+
import { type ID3v2MajorVersion, type ITextEncoding } from './ID3v2Token.js';
|
|
2
|
+
import type { IWarningCollector } from '../common/MetadataCollector.js';
|
|
3
|
+
interface ICustomTag {
|
|
4
|
+
owner_identifier: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ICustomDataTag extends ICustomTag {
|
|
7
|
+
data: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
export interface IIdentifierTag extends ICustomTag {
|
|
10
|
+
identifier: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
export interface ITextTag {
|
|
13
|
+
description: string;
|
|
14
|
+
text: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface IPopularimeter {
|
|
17
|
+
email: string;
|
|
18
|
+
rating: number;
|
|
19
|
+
counter: number;
|
|
20
|
+
}
|
|
21
|
+
export interface IGeneralEncapsulatedObject {
|
|
22
|
+
type: string;
|
|
23
|
+
filename: string;
|
|
24
|
+
description: string;
|
|
25
|
+
data: Uint8Array;
|
|
26
|
+
}
|
|
3
27
|
export declare function parseGenre(origVal: string): string[];
|
|
4
28
|
export declare class FrameParser {
|
|
5
29
|
private major;
|
|
@@ -10,7 +34,7 @@ export declare class FrameParser {
|
|
|
10
34
|
* @param warningCollector - Used to collect decode issue
|
|
11
35
|
*/
|
|
12
36
|
constructor(major: ID3v2MajorVersion, warningCollector: IWarningCollector);
|
|
13
|
-
readData(uint8Array: Uint8Array, type: string, includeCovers: boolean):
|
|
37
|
+
readData(uint8Array: Uint8Array, type: string, includeCovers: boolean): unknown;
|
|
14
38
|
protected static readNullTerminatedString(uint8Array: Uint8Array, encoding: ITextEncoding): {
|
|
15
39
|
text: string;
|
|
16
40
|
len: number;
|
|
@@ -33,3 +57,4 @@ export declare class FrameParser {
|
|
|
33
57
|
private static readIdentifierAndData;
|
|
34
58
|
private static getNullTerminatorLength;
|
|
35
59
|
}
|
|
60
|
+
export {};
|
package/lib/id3v2/FrameParser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import initDebug from 'debug';
|
|
2
2
|
import * as Token from 'token-types';
|
|
3
3
|
import * as util from '../common/Util.js';
|
|
4
|
-
import { AttachedPictureType,
|
|
4
|
+
import { AttachedPictureType, SyncTextHeader, TextEncodingToken, TextHeader } from './ID3v2Token.js';
|
|
5
5
|
import { Genres } from '../id3v1/ID3v1Parser.js';
|
|
6
6
|
const debug = initDebug('music-metadata:id3v2:frame-parser');
|
|
7
7
|
const defaultEnc = 'latin1'; // latin1 == iso-8859-1;
|
|
@@ -39,9 +39,11 @@ export function parseGenre(origVal) {
|
|
|
39
39
|
}
|
|
40
40
|
if (word) {
|
|
41
41
|
if (genres.length === 0 && word.match(/^\d*$/)) {
|
|
42
|
-
word =
|
|
42
|
+
word = parseGenreCode(word);
|
|
43
|
+
}
|
|
44
|
+
if (word) {
|
|
45
|
+
genres.push(word);
|
|
43
46
|
}
|
|
44
|
-
genres.push(word);
|
|
45
47
|
}
|
|
46
48
|
return genres;
|
|
47
49
|
}
|
|
@@ -51,7 +53,7 @@ function parseGenreCode(code) {
|
|
|
51
53
|
if (code === 'CR')
|
|
52
54
|
return 'Cover';
|
|
53
55
|
if (code.match(/^\d*$/)) {
|
|
54
|
-
return Genres[code];
|
|
56
|
+
return Genres[Number.parseInt(code)];
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
export class FrameParser {
|
|
@@ -83,20 +85,23 @@ export class FrameParser {
|
|
|
83
85
|
case 'MVIN':
|
|
84
86
|
case 'MVNM':
|
|
85
87
|
case 'PCS':
|
|
86
|
-
case 'PCST':
|
|
88
|
+
case 'PCST': {
|
|
87
89
|
let text;
|
|
88
90
|
try {
|
|
89
91
|
text = util.decodeString(uint8Array.slice(1), encoding).replace(/\x00+$/, '');
|
|
90
92
|
}
|
|
91
93
|
catch (error) {
|
|
92
|
-
|
|
94
|
+
if (error instanceof Error) {
|
|
95
|
+
this.warningCollector.addWarning(`id3v2.${this.major} type=${type} header has invalid string value: ${error.message}`);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
throw error;
|
|
93
99
|
}
|
|
94
100
|
switch (type) {
|
|
95
101
|
case 'TMCL': // Musician credits list
|
|
96
102
|
case 'TIPL': // Involved people list
|
|
97
103
|
case 'IPLS': // Involved people list
|
|
98
|
-
output = this.splitValue(type, text);
|
|
99
|
-
output = FrameParser.functionList(output);
|
|
104
|
+
output = FrameParser.functionList(this.splitValue(type, text));
|
|
100
105
|
break;
|
|
101
106
|
case 'TRK':
|
|
102
107
|
case 'TRCK':
|
|
@@ -126,13 +131,16 @@ export class FrameParser {
|
|
|
126
131
|
output = this.major >= 4 ? this.splitValue(type, text) : [text];
|
|
127
132
|
}
|
|
128
133
|
break;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
}
|
|
135
|
+
case 'TXXX': {
|
|
136
|
+
const idAndData = FrameParser.readIdentifierAndData(uint8Array, offset + 1, length, encoding);
|
|
137
|
+
const textTag = {
|
|
138
|
+
description: idAndData.id,
|
|
139
|
+
text: this.splitValue(type, util.decodeString(idAndData.data, encoding).replace(/\x00+$/, ''))
|
|
134
140
|
};
|
|
141
|
+
output = textTag;
|
|
135
142
|
break;
|
|
143
|
+
}
|
|
136
144
|
case 'PIC':
|
|
137
145
|
case 'APIC':
|
|
138
146
|
if (includeCovers) {
|
|
@@ -150,7 +158,7 @@ export class FrameParser {
|
|
|
150
158
|
offset = fzero + 1;
|
|
151
159
|
break;
|
|
152
160
|
default:
|
|
153
|
-
throw new Error(
|
|
161
|
+
throw new Error(`Warning: unexpected major versionIndex: ${this.major}`);
|
|
154
162
|
}
|
|
155
163
|
pic.format = FrameParser.fixPictureMimeType(pic.format);
|
|
156
164
|
pic.type = AttachedPictureType[uint8Array[offset]];
|
|
@@ -166,7 +174,7 @@ export class FrameParser {
|
|
|
166
174
|
case 'PCNT':
|
|
167
175
|
output = Token.UINT32_BE.get(uint8Array, 0);
|
|
168
176
|
break;
|
|
169
|
-
case 'SYLT':
|
|
177
|
+
case 'SYLT': {
|
|
170
178
|
const syltHeader = SyncTextHeader.get(uint8Array, 0);
|
|
171
179
|
offset += SyncTextHeader.len;
|
|
172
180
|
const result = {
|
|
@@ -195,10 +203,11 @@ export class FrameParser {
|
|
|
195
203
|
}
|
|
196
204
|
output = result;
|
|
197
205
|
break;
|
|
206
|
+
}
|
|
198
207
|
case 'ULT':
|
|
199
208
|
case 'USLT':
|
|
200
209
|
case 'COM':
|
|
201
|
-
case 'COMM':
|
|
210
|
+
case 'COMM': {
|
|
202
211
|
const textHeader = TextHeader.get(uint8Array, offset);
|
|
203
212
|
offset += TextHeader.len;
|
|
204
213
|
const descriptorStr = FrameParser.readNullTerminatedString(uint8Array.subarray(offset), textHeader.encoding);
|
|
@@ -211,15 +220,18 @@ export class FrameParser {
|
|
|
211
220
|
};
|
|
212
221
|
output = comment;
|
|
213
222
|
break;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
}
|
|
224
|
+
case 'UFID': {
|
|
225
|
+
const ufid = FrameParser.readIdentifierAndData(uint8Array, offset, length, defaultEnc);
|
|
226
|
+
output = { owner_identifier: ufid.id, identifier: ufid.data };
|
|
217
227
|
break;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
228
|
+
}
|
|
229
|
+
case 'PRIV': { // private frame
|
|
230
|
+
const priv = FrameParser.readIdentifierAndData(uint8Array, offset, length, defaultEnc);
|
|
231
|
+
output = { owner_identifier: priv.id, data: priv.data };
|
|
221
232
|
break;
|
|
222
|
-
|
|
233
|
+
}
|
|
234
|
+
case 'POPM': { // Popularimeter
|
|
223
235
|
fzero = util.findZero(uint8Array, offset, length, defaultEnc);
|
|
224
236
|
const email = util.decodeString(uint8Array.slice(offset, fzero), defaultEnc);
|
|
225
237
|
offset = fzero + 1;
|
|
@@ -230,6 +242,7 @@ export class FrameParser {
|
|
|
230
242
|
counter: dataLen >= 5 ? Token.UINT32_BE.get(uint8Array, offset + 1) : undefined
|
|
231
243
|
};
|
|
232
244
|
break;
|
|
245
|
+
}
|
|
233
246
|
case 'GEOB': { // General encapsulated object
|
|
234
247
|
fzero = util.findZero(uint8Array, offset + 1, length, encoding);
|
|
235
248
|
const mimeType = util.decodeString(uint8Array.slice(offset + 1, fzero), defaultEnc);
|
|
@@ -239,12 +252,13 @@ export class FrameParser {
|
|
|
239
252
|
offset = fzero + 1;
|
|
240
253
|
fzero = util.findZero(uint8Array, offset, length - offset, encoding);
|
|
241
254
|
const description = util.decodeString(uint8Array.slice(offset, fzero), defaultEnc);
|
|
242
|
-
|
|
255
|
+
const geob = {
|
|
243
256
|
type: mimeType,
|
|
244
257
|
filename,
|
|
245
258
|
description,
|
|
246
259
|
data: uint8Array.slice(offset + 1, length)
|
|
247
260
|
};
|
|
261
|
+
output = geob;
|
|
248
262
|
break;
|
|
249
263
|
}
|
|
250
264
|
// W-Frames:
|
|
@@ -257,6 +271,7 @@ export class FrameParser {
|
|
|
257
271
|
case 'WPAY':
|
|
258
272
|
case 'WPUB':
|
|
259
273
|
// Decode URL
|
|
274
|
+
fzero = util.findZero(uint8Array, offset + 1, length, encoding);
|
|
260
275
|
output = util.decodeString(uint8Array.slice(offset, fzero), defaultEnc);
|
|
261
276
|
break;
|
|
262
277
|
case 'WXXX': {
|
|
@@ -277,15 +292,21 @@ export class FrameParser {
|
|
|
277
292
|
break;
|
|
278
293
|
}
|
|
279
294
|
default:
|
|
280
|
-
debug(
|
|
295
|
+
debug(`Warning: unsupported id3v2-tag-type: ${type}`);
|
|
281
296
|
break;
|
|
282
297
|
}
|
|
283
298
|
return output;
|
|
284
299
|
}
|
|
285
300
|
static readNullTerminatedString(uint8Array, encoding) {
|
|
286
301
|
let offset = encoding.bom ? 2 : 0;
|
|
287
|
-
const
|
|
288
|
-
|
|
302
|
+
const zeroIndex = util.findZero(uint8Array, offset, uint8Array.length, encoding.encoding);
|
|
303
|
+
const txt = uint8Array.slice(offset, zeroIndex);
|
|
304
|
+
if (encoding.encoding === 'utf-16le') {
|
|
305
|
+
offset = zeroIndex + 2;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
offset = zeroIndex + 1;
|
|
309
|
+
}
|
|
289
310
|
return {
|
|
290
311
|
text: util.decodeString(txt, encoding.encoding),
|
|
291
312
|
len: offset
|
|
@@ -309,7 +330,7 @@ export class FrameParser {
|
|
|
309
330
|
const res = {};
|
|
310
331
|
for (let i = 0; i + 1 < entries.length; i += 2) {
|
|
311
332
|
const names = entries[i + 1].split(',');
|
|
312
|
-
res[entries[i]] = res
|
|
333
|
+
res[entries[i]] = res[entries[i]] ? res[entries[i]].concat(names) : names;
|
|
313
334
|
}
|
|
314
335
|
return res;
|
|
315
336
|
}
|
|
@@ -349,3 +370,4 @@ export class FrameParser {
|
|
|
349
370
|
return enc === 'utf-16le' ? 2 : 1;
|
|
350
371
|
}
|
|
351
372
|
}
|
|
373
|
+
//# sourceMappingURL=FrameParser.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
|
|
2
2
|
import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
|
|
3
3
|
import type { IRating, ITag } from '../type.js';
|
|
4
|
+
import type { IPopularimeter } from './FrameParser.js';
|
|
4
5
|
export declare class ID3v24TagMapper extends CaseInsensitiveTagMap {
|
|
5
|
-
static toRating(popm:
|
|
6
|
+
static toRating(popm: IPopularimeter): IRating;
|
|
6
7
|
constructor();
|
|
7
8
|
/**
|
|
8
9
|
* Handle post mapping exceptions / correction
|
|
@@ -156,25 +156,32 @@ export class ID3v24TagMapper extends CaseInsensitiveTagMap {
|
|
|
156
156
|
*/
|
|
157
157
|
postMap(tag, warnings) {
|
|
158
158
|
switch (tag.id) {
|
|
159
|
-
case 'UFID':
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
case 'UFID':
|
|
160
|
+
{
|
|
161
|
+
// decode MusicBrainz Recording Id
|
|
162
|
+
const idTag = tag.value;
|
|
163
|
+
if (idTag.owner_identifier === 'http://musicbrainz.org') {
|
|
164
|
+
tag.id += `:${idTag.owner_identifier}`;
|
|
165
|
+
tag.value = decodeString(idTag.identifier, 'latin1'); // latin1 == iso-8859-1
|
|
166
|
+
}
|
|
163
167
|
}
|
|
164
168
|
break;
|
|
165
169
|
case 'PRIV':
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
170
|
+
{
|
|
171
|
+
const customTag = tag.value;
|
|
172
|
+
switch (customTag.owner_identifier) {
|
|
173
|
+
// decode Windows Media Player
|
|
174
|
+
case 'AverageLevel':
|
|
175
|
+
case 'PeakValue':
|
|
176
|
+
tag.id += `:${customTag.owner_identifier}`;
|
|
177
|
+
tag.value = customTag.data.length === 4 ? UINT32_LE.get(customTag.data, 0) : null;
|
|
178
|
+
if (tag.value === null) {
|
|
179
|
+
warnings.addWarning('Failed to parse PRIV:PeakValue');
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
warnings.addWarning(`Unknown PRIV owner-identifier: ${customTag.data}`);
|
|
184
|
+
}
|
|
178
185
|
}
|
|
179
186
|
break;
|
|
180
187
|
case 'POPM':
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ITokenizer } from 'strtok3';
|
|
2
|
-
import { IOptions } from '../type.js';
|
|
3
|
-
import { INativeMetadataCollector } from '../common/MetadataCollector.js';
|
|
2
|
+
import type { IOptions } from '../type.js';
|
|
3
|
+
import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
|
|
4
4
|
export declare class ID3v2Parser {
|
|
5
5
|
static removeUnsyncBytes(buffer: Uint8Array): Uint8Array;
|
|
6
6
|
private static getFrameHeaderLength;
|
package/lib/id3v2/ID3v2Parser.js
CHANGED
|
@@ -4,6 +4,13 @@ import { FrameParser } from './FrameParser.js';
|
|
|
4
4
|
import { ExtendedHeader, ID3v2Header, UINT32SYNCSAFE } from './ID3v2Token.js';
|
|
5
5
|
const asciiDecoder = new TextDecoder('ascii');
|
|
6
6
|
export class ID3v2Parser {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.tokenizer = undefined;
|
|
9
|
+
this.id3Header = undefined;
|
|
10
|
+
this.metadata = undefined;
|
|
11
|
+
this.headerType = undefined;
|
|
12
|
+
this.options = undefined;
|
|
13
|
+
}
|
|
7
14
|
static removeUnsyncBytes(buffer) {
|
|
8
15
|
let readI = 0;
|
|
9
16
|
let writeI = 0;
|
|
@@ -47,21 +54,22 @@ export class ID3v2Parser {
|
|
|
47
54
|
};
|
|
48
55
|
}
|
|
49
56
|
static readFrameData(uint8Array, frameHeader, majorVer, includeCovers, warningCollector) {
|
|
57
|
+
var _a, _b;
|
|
50
58
|
const frameParser = new FrameParser(majorVer, warningCollector);
|
|
51
59
|
switch (majorVer) {
|
|
52
60
|
case 2:
|
|
53
61
|
return frameParser.readData(uint8Array, frameHeader.id, includeCovers);
|
|
54
62
|
case 3:
|
|
55
63
|
case 4:
|
|
56
|
-
if (frameHeader.flags.format.unsynchronisation) {
|
|
64
|
+
if ((_a = frameHeader.flags) === null || _a === void 0 ? void 0 : _a.format.unsynchronisation) {
|
|
57
65
|
uint8Array = ID3v2Parser.removeUnsyncBytes(uint8Array);
|
|
58
66
|
}
|
|
59
|
-
if (frameHeader.flags.format.data_length_indicator) {
|
|
67
|
+
if ((_b = frameHeader.flags) === null || _b === void 0 ? void 0 : _b.format.data_length_indicator) {
|
|
60
68
|
uint8Array = uint8Array.slice(4, uint8Array.length);
|
|
61
69
|
}
|
|
62
70
|
return frameParser.readData(uint8Array, frameHeader.id, includeCovers);
|
|
63
71
|
default:
|
|
64
|
-
throw new Error(
|
|
72
|
+
throw new Error(`Unexpected majorVer: ${majorVer}`);
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
/**
|
|
@@ -71,7 +79,7 @@ export class ID3v2Parser {
|
|
|
71
79
|
* @returns string e.g. COM:iTunPGAP
|
|
72
80
|
*/
|
|
73
81
|
static makeDescriptionTagName(tag, description) {
|
|
74
|
-
return tag + (description ?
|
|
82
|
+
return tag + (description ? `:${description}` : '');
|
|
75
83
|
}
|
|
76
84
|
async parse(metadata, tokenizer, options) {
|
|
77
85
|
this.tokenizer = tokenizer;
|
|
@@ -82,7 +90,7 @@ export class ID3v2Parser {
|
|
|
82
90
|
throw new Error('expected ID3-header file-identifier \'ID3\' was not found');
|
|
83
91
|
}
|
|
84
92
|
this.id3Header = id3Header;
|
|
85
|
-
this.headerType = (
|
|
93
|
+
this.headerType = (`ID3v2.${id3Header.version.major}`);
|
|
86
94
|
return id3Header.flags.isExtendedHeader ? this.parseExtendedHeader() : this.parseId3Data(id3Header.size);
|
|
87
95
|
}
|
|
88
96
|
async parseExtendedHeader() {
|
|
@@ -125,9 +133,11 @@ export class ID3v2Parser {
|
|
|
125
133
|
this.metadata.addWarning('Illegal ID3v2 tag length');
|
|
126
134
|
break;
|
|
127
135
|
}
|
|
128
|
-
const frameHeaderBytes = data.slice(offset, offset
|
|
136
|
+
const frameHeaderBytes = data.slice(offset, offset + frameHeaderLength);
|
|
137
|
+
offset += frameHeaderLength;
|
|
129
138
|
const frameHeader = this.readFrameHeader(frameHeaderBytes, this.id3Header.version.major);
|
|
130
|
-
const frameDataBytes = data.slice(offset, offset
|
|
139
|
+
const frameDataBytes = data.slice(offset, offset + frameHeader.length);
|
|
140
|
+
offset += frameHeader.length;
|
|
131
141
|
const values = ID3v2Parser.readFrameData(frameDataBytes, frameHeader, this.id3Header.version.major, !this.options.skipCovers, this.metadata);
|
|
132
142
|
if (values) {
|
|
133
143
|
tags.push({ id: frameHeader.id, value: values });
|
|
@@ -159,8 +169,9 @@ export class ID3v2Parser {
|
|
|
159
169
|
}
|
|
160
170
|
break;
|
|
161
171
|
default:
|
|
162
|
-
throw new Error(
|
|
172
|
+
throw new Error(`Unexpected majorVer: ${majorVer}`);
|
|
163
173
|
}
|
|
164
174
|
return header;
|
|
165
175
|
}
|
|
166
176
|
}
|
|
177
|
+
//# sourceMappingURL=ID3v2Parser.js.map
|
package/lib/iff/index.js
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node.js specific entry point.
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { IAudioMetadata, IOptions } from './type.js';
|
|
7
|
-
export
|
|
8
|
-
export { parseFromTokenizer, parseBuffer, parseBlob, parseWebStream, selectCover, orderTags, ratingToStars, IFileInfo } from './core.js';
|
|
4
|
+
import type { Readable } from 'node:stream';
|
|
5
|
+
import { type IFileInfo } from 'strtok3';
|
|
6
|
+
import type { IAudioMetadata, IOptions } from './type.js';
|
|
7
|
+
export * from './core.js';
|
|
9
8
|
/**
|
|
10
9
|
* Parse audio from Node Stream.Readable
|
|
11
10
|
* @param stream - Stream to read the audio track from
|
|
@@ -13,7 +12,7 @@ export { parseFromTokenizer, parseBuffer, parseBlob, parseWebStream, selectCover
|
|
|
13
12
|
* @param options - Parsing options
|
|
14
13
|
* @returns Metadata
|
|
15
14
|
*/
|
|
16
|
-
export declare function parseStream(stream:
|
|
15
|
+
export declare function parseStream(stream: Readable, fileInfo?: IFileInfo | string, options?: IOptions): Promise<IAudioMetadata>;
|
|
17
16
|
/**
|
|
18
17
|
* Parse audio from Node file
|
|
19
18
|
* @param filePath - Media file to read meta-data from
|