music-metadata 7.11.3 → 7.11.7
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 +432 -432
- package/lib/ParserFactory.d.ts +49 -49
- package/lib/ParserFactory.js +251 -251
- package/lib/aiff/AiffParser.d.ts +15 -15
- package/lib/aiff/AiffParser.js +85 -85
- package/lib/aiff/AiffToken.d.ts +22 -22
- package/lib/aiff/AiffToken.js +43 -43
- package/lib/apev2/APEv2Parser.d.ts +30 -30
- package/lib/apev2/APEv2Parser.js +162 -162
- package/lib/apev2/APEv2TagMapper.d.ts +4 -4
- package/lib/apev2/APEv2TagMapper.js +87 -86
- package/lib/apev2/APEv2Token.d.ts +100 -100
- package/lib/apev2/APEv2Token.js +127 -126
- package/lib/asf/AsfObject.d.ts +319 -319
- package/lib/asf/AsfObject.js +384 -384
- package/lib/asf/AsfParser.d.ts +17 -17
- package/lib/asf/AsfParser.js +135 -135
- package/lib/asf/AsfTagMapper.d.ts +7 -7
- package/lib/asf/AsfTagMapper.js +96 -95
- package/lib/asf/AsfUtil.d.ts +13 -13
- package/lib/asf/AsfUtil.js +40 -40
- package/lib/asf/GUID.d.ts +86 -86
- package/lib/asf/GUID.js +123 -123
- package/lib/common/BasicParser.d.ts +17 -17
- package/lib/common/BasicParser.js +18 -18
- package/lib/common/CaseInsensitiveTagMap.d.ts +10 -10
- package/lib/common/CaseInsensitiveTagMap.js +22 -21
- package/lib/common/CombinedTagMapper.d.ts +19 -19
- package/lib/common/CombinedTagMapper.js +52 -51
- package/lib/common/FourCC.d.ts +6 -6
- package/lib/common/FourCC.js +29 -28
- package/lib/common/GenericTagMapper.d.ts +51 -51
- package/lib/common/GenericTagMapper.js +56 -55
- package/lib/common/GenericTagTypes.d.ts +33 -33
- package/lib/common/GenericTagTypes.js +132 -131
- package/lib/common/MetadataCollector.d.ts +76 -76
- package/lib/common/MetadataCollector.js +276 -274
- package/lib/common/RandomFileReader.d.ts +20 -20
- package/lib/common/RandomFileReader.js +37 -37
- package/lib/common/RandomUint8ArrayReader.d.ts +18 -18
- package/lib/common/RandomUint8ArrayReader.js +25 -25
- package/lib/common/Util.d.ts +58 -57
- package/lib/common/Util.js +163 -169
- package/lib/core.d.ts +48 -48
- package/lib/core.js +90 -90
- package/lib/dsdiff/DsdiffParser.d.ts +14 -14
- package/lib/dsdiff/DsdiffParser.js +143 -143
- package/lib/dsdiff/DsdiffToken.d.ts +9 -9
- package/lib/dsdiff/DsdiffToken.js +21 -21
- package/lib/dsf/DsfChunk.d.ts +86 -86
- package/lib/dsf/DsfChunk.js +54 -54
- package/lib/dsf/DsfParser.d.ts +9 -9
- package/lib/dsf/DsfParser.js +56 -56
- package/lib/flac/FlacParser.d.ts +28 -28
- package/lib/flac/FlacParser.js +175 -175
- package/lib/id3v1/ID3v1Parser.d.ts +13 -13
- package/lib/id3v1/ID3v1Parser.js +134 -134
- package/lib/id3v1/ID3v1TagMap.d.ts +4 -4
- package/lib/id3v1/ID3v1TagMap.js +23 -22
- package/lib/id3v2/AbstractID3Parser.d.ts +17 -17
- package/lib/id3v2/AbstractID3Parser.js +60 -60
- package/lib/id3v2/FrameParser.d.ts +32 -32
- package/lib/id3v2/FrameParser.js +329 -323
- package/lib/id3v2/ID3v22TagMapper.d.ts +9 -9
- package/lib/id3v2/ID3v22TagMapper.js +56 -54
- package/lib/id3v2/ID3v24TagMapper.d.ts +14 -14
- package/lib/id3v2/ID3v24TagMapper.js +194 -193
- package/lib/id3v2/ID3v2Parser.d.ts +29 -29
- package/lib/id3v2/ID3v2Parser.js +194 -194
- package/lib/id3v2/ID3v2Token.d.ts +73 -73
- package/lib/id3v2/ID3v2Token.js +106 -106
- package/lib/iff/index.d.ts +33 -33
- package/lib/iff/index.js +19 -19
- package/lib/index.d.ts +45 -34
- package/lib/index.js +74 -64
- package/lib/lyrics3/Lyrics3.d.ts +3 -3
- package/lib/lyrics3/Lyrics3.js +17 -17
- package/lib/matroska/MatroskaDtd.d.ts +8 -8
- package/lib/matroska/MatroskaDtd.js +279 -279
- package/lib/matroska/MatroskaParser.d.ts +37 -37
- package/lib/matroska/MatroskaParser.js +235 -234
- package/lib/matroska/MatroskaTagMapper.d.ts +4 -4
- package/lib/matroska/MatroskaTagMapper.js +36 -35
- package/lib/matroska/types.d.ts +175 -175
- package/lib/matroska/types.js +32 -32
- package/lib/mp4/Atom.d.ts +16 -16
- package/lib/mp4/Atom.js +70 -70
- package/lib/mp4/AtomToken.d.ts +395 -395
- package/lib/mp4/AtomToken.js +406 -406
- package/lib/mp4/MP4Parser.d.ts +30 -30
- package/lib/mp4/MP4Parser.js +511 -511
- package/lib/mp4/MP4TagMapper.d.ts +5 -5
- package/lib/mp4/MP4TagMapper.js +116 -115
- package/lib/mpeg/ExtendedLameHeader.d.ts +27 -27
- package/lib/mpeg/ExtendedLameHeader.js +31 -31
- package/lib/mpeg/MpegParser.d.ts +49 -49
- package/lib/mpeg/MpegParser.js +529 -529
- package/lib/mpeg/ReplayGainDataFormat.d.ts +55 -55
- package/lib/mpeg/ReplayGainDataFormat.js +69 -69
- package/lib/mpeg/XingTag.d.ts +45 -45
- package/lib/mpeg/XingTag.js +69 -69
- package/lib/musepack/index.d.ts +5 -5
- package/lib/musepack/index.js +32 -32
- package/lib/musepack/sv7/BitReader.d.ts +13 -13
- package/lib/musepack/sv7/BitReader.js +54 -54
- package/lib/musepack/sv7/MpcSv7Parser.d.ts +8 -8
- package/lib/musepack/sv7/MpcSv7Parser.js +46 -46
- package/lib/musepack/sv7/StreamVersion7.d.ts +28 -28
- package/lib/musepack/sv7/StreamVersion7.js +41 -41
- package/lib/musepack/sv8/MpcSv8Parser.d.ts +6 -6
- package/lib/musepack/sv8/MpcSv8Parser.js +55 -55
- package/lib/musepack/sv8/StreamVersion8.d.ts +40 -40
- package/lib/musepack/sv8/StreamVersion8.js +80 -80
- package/lib/ogg/Ogg.d.ts +72 -72
- package/lib/ogg/Ogg.js +2 -2
- package/lib/ogg/OggParser.d.ts +23 -23
- package/lib/ogg/OggParser.js +126 -126
- package/lib/ogg/opus/Opus.d.ts +48 -48
- package/lib/ogg/opus/Opus.js +28 -28
- package/lib/ogg/opus/OpusParser.d.ts +25 -25
- package/lib/ogg/opus/OpusParser.js +56 -56
- package/lib/ogg/speex/Speex.d.ts +36 -36
- package/lib/ogg/speex/Speex.js +31 -31
- package/lib/ogg/speex/SpeexParser.d.ts +22 -22
- package/lib/ogg/speex/SpeexParser.js +35 -35
- package/lib/ogg/theora/Theora.d.ts +20 -20
- package/lib/ogg/theora/Theora.js +23 -23
- package/lib/ogg/theora/TheoraParser.d.ts +28 -28
- package/lib/ogg/theora/TheoraParser.js +44 -44
- package/lib/ogg/vorbis/Vorbis.d.ts +79 -79
- package/lib/ogg/vorbis/Vorbis.js +78 -78
- package/lib/ogg/vorbis/VorbisDecoder.d.ts +12 -12
- package/lib/ogg/vorbis/VorbisDecoder.js +32 -32
- package/lib/ogg/vorbis/VorbisParser.d.ts +36 -36
- package/lib/ogg/vorbis/VorbisParser.js +128 -128
- package/lib/ogg/vorbis/VorbisTagMapper.d.ts +7 -7
- package/lib/ogg/vorbis/VorbisTagMapper.js +133 -132
- package/lib/riff/RiffChunk.d.ts +16 -16
- package/lib/riff/RiffChunk.js +32 -32
- package/lib/riff/RiffInfoTagMap.d.ts +10 -10
- package/lib/riff/RiffInfoTagMap.js +38 -37
- package/lib/type.d.ts +599 -599
- package/lib/type.js +14 -13
- package/lib/wav/WaveChunk.d.ts +64 -64
- package/lib/wav/WaveChunk.js +65 -65
- package/lib/wav/WaveParser.d.ts +24 -24
- package/lib/wav/WaveParser.js +144 -144
- package/lib/wavpack/WavPackParser.d.ts +14 -14
- package/lib/wavpack/WavPackParser.js +105 -105
- package/lib/wavpack/WavPackToken.d.ts +64 -64
- package/lib/wavpack/WavPackToken.js +76 -76
- package/package.json +142 -142
package/lib/id3v2/ID3v2Parser.js
CHANGED
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ID3v2Parser = void 0;
|
|
4
|
-
const Token = require("token-types");
|
|
5
|
-
const util = require("../common/Util");
|
|
6
|
-
const FrameParser_1 = require("./FrameParser");
|
|
7
|
-
const ID3v2Token_1 = require("./ID3v2Token");
|
|
8
|
-
class ID3v2Parser {
|
|
9
|
-
static removeUnsyncBytes(buffer) {
|
|
10
|
-
let readI = 0;
|
|
11
|
-
let writeI = 0;
|
|
12
|
-
while (readI < buffer.length - 1) {
|
|
13
|
-
if (readI !== writeI) {
|
|
14
|
-
buffer[writeI] = buffer[readI];
|
|
15
|
-
}
|
|
16
|
-
readI += (buffer[readI] === 0xFF && buffer[readI + 1] === 0) ? 2 : 1;
|
|
17
|
-
writeI++;
|
|
18
|
-
}
|
|
19
|
-
if (readI < buffer.length) {
|
|
20
|
-
buffer[writeI++] = buffer[readI];
|
|
21
|
-
}
|
|
22
|
-
return buffer.slice(0, writeI);
|
|
23
|
-
}
|
|
24
|
-
static getFrameHeaderLength(majorVer) {
|
|
25
|
-
switch (majorVer) {
|
|
26
|
-
case 2:
|
|
27
|
-
return 6;
|
|
28
|
-
case 3:
|
|
29
|
-
case 4:
|
|
30
|
-
return 10;
|
|
31
|
-
default:
|
|
32
|
-
throw new Error('header versionIndex is incorrect');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
static readFrameFlags(b) {
|
|
36
|
-
return {
|
|
37
|
-
status: {
|
|
38
|
-
tag_alter_preservation: util.getBit(b, 0, 6),
|
|
39
|
-
file_alter_preservation: util.getBit(b, 0, 5),
|
|
40
|
-
read_only: util.getBit(b, 0, 4)
|
|
41
|
-
},
|
|
42
|
-
format: {
|
|
43
|
-
grouping_identity: util.getBit(b, 1, 7),
|
|
44
|
-
compression: util.getBit(b, 1, 3),
|
|
45
|
-
encryption: util.getBit(b, 1, 2),
|
|
46
|
-
unsynchronisation: util.getBit(b, 1, 1),
|
|
47
|
-
data_length_indicator: util.getBit(b, 1, 0)
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
static readFrameData(buf, frameHeader, majorVer, includeCovers, warningCollector) {
|
|
52
|
-
const frameParser = new FrameParser_1.FrameParser(majorVer, warningCollector);
|
|
53
|
-
switch (majorVer) {
|
|
54
|
-
case 2:
|
|
55
|
-
return frameParser.readData(buf, frameHeader.id, includeCovers);
|
|
56
|
-
case 3:
|
|
57
|
-
case 4:
|
|
58
|
-
if (frameHeader.flags.format.unsynchronisation) {
|
|
59
|
-
buf = ID3v2Parser.removeUnsyncBytes(buf);
|
|
60
|
-
}
|
|
61
|
-
if (frameHeader.flags.format.data_length_indicator) {
|
|
62
|
-
buf = buf.slice(4, buf.length);
|
|
63
|
-
}
|
|
64
|
-
return frameParser.readData(buf, frameHeader.id, includeCovers);
|
|
65
|
-
default:
|
|
66
|
-
throw new Error('Unexpected majorVer: ' + majorVer);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Create a combined tag key, of tag & description
|
|
71
|
-
* @param {string} tag e.g.: COM
|
|
72
|
-
* @param {string} description e.g. iTunPGAP
|
|
73
|
-
* @returns {string} e.g. COM:iTunPGAP
|
|
74
|
-
*/
|
|
75
|
-
static makeDescriptionTagName(tag, description) {
|
|
76
|
-
return tag + (description ? ':' + description : '');
|
|
77
|
-
}
|
|
78
|
-
async parse(metadata, tokenizer, options) {
|
|
79
|
-
this.tokenizer = tokenizer;
|
|
80
|
-
this.metadata = metadata;
|
|
81
|
-
this.options = options;
|
|
82
|
-
const id3Header = await this.tokenizer.readToken(ID3v2Token_1.ID3v2Header);
|
|
83
|
-
if (id3Header.fileIdentifier !== 'ID3') {
|
|
84
|
-
throw new Error('expected ID3-header file-identifier \'ID3\' was not found');
|
|
85
|
-
}
|
|
86
|
-
this.id3Header = id3Header;
|
|
87
|
-
this.headerType = ('ID3v2.' + id3Header.version.major);
|
|
88
|
-
if (id3Header.flags.isExtendedHeader) {
|
|
89
|
-
return this.parseExtendedHeader();
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
return this.parseId3Data(id3Header.size);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
async parseExtendedHeader() {
|
|
96
|
-
const extendedHeader = await this.tokenizer.readToken(ID3v2Token_1.ExtendedHeader);
|
|
97
|
-
const dataRemaining = extendedHeader.size - ID3v2Token_1.ExtendedHeader.len;
|
|
98
|
-
if (dataRemaining > 0) {
|
|
99
|
-
return this.parseExtendedHeaderData(dataRemaining, extendedHeader.size);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
return this.parseId3Data(this.id3Header.size - extendedHeader.size);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
async parseExtendedHeaderData(dataRemaining, extendedHeaderSize) {
|
|
106
|
-
const buffer = Buffer.alloc(dataRemaining);
|
|
107
|
-
await this.tokenizer.readBuffer(buffer, { length: dataRemaining });
|
|
108
|
-
return this.parseId3Data(this.id3Header.size - extendedHeaderSize);
|
|
109
|
-
}
|
|
110
|
-
async parseId3Data(dataLen) {
|
|
111
|
-
const buffer = Buffer.alloc(dataLen);
|
|
112
|
-
await this.tokenizer.readBuffer(buffer, { length: dataLen });
|
|
113
|
-
for (const tag of this.parseMetadata(buffer)) {
|
|
114
|
-
if (tag.id === 'TXXX') {
|
|
115
|
-
if (tag.value) {
|
|
116
|
-
for (const text of tag.value.text) {
|
|
117
|
-
this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, tag.value.description), text);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
else if (tag.id === 'COM') {
|
|
122
|
-
for (const value of tag.value) {
|
|
123
|
-
this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value.text);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else if (tag.id === 'COMM') {
|
|
127
|
-
for (const value of tag.value) {
|
|
128
|
-
this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
else if (Array.isArray(tag.value)) {
|
|
132
|
-
for (const value of tag.value) {
|
|
133
|
-
this.addTag(tag.id, value);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
this.addTag(tag.id, tag.value);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
addTag(id, value) {
|
|
142
|
-
this.metadata.addTag(this.headerType, id, value);
|
|
143
|
-
}
|
|
144
|
-
parseMetadata(data) {
|
|
145
|
-
let offset = 0;
|
|
146
|
-
const tags = [];
|
|
147
|
-
while (true) {
|
|
148
|
-
if (offset === data.length)
|
|
149
|
-
break;
|
|
150
|
-
const frameHeaderLength = ID3v2Parser.getFrameHeaderLength(this.id3Header.version.major);
|
|
151
|
-
if (offset + frameHeaderLength > data.length) {
|
|
152
|
-
this.metadata.addWarning('Illegal ID3v2 tag length');
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
const frameHeaderBytes = data.slice(offset, offset += frameHeaderLength);
|
|
156
|
-
const frameHeader = this.readFrameHeader(frameHeaderBytes, this.id3Header.version.major);
|
|
157
|
-
const frameDataBytes = data.slice(offset, offset += frameHeader.length);
|
|
158
|
-
const values = ID3v2Parser.readFrameData(frameDataBytes, frameHeader, this.id3Header.version.major, !this.options.skipCovers, this.metadata);
|
|
159
|
-
if (values) {
|
|
160
|
-
tags.push({ id: frameHeader.id, value: values });
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return tags;
|
|
164
|
-
}
|
|
165
|
-
readFrameHeader(v, majorVer) {
|
|
166
|
-
let header;
|
|
167
|
-
switch (majorVer) {
|
|
168
|
-
case 2:
|
|
169
|
-
header = {
|
|
170
|
-
id: v.toString('ascii', 0, 3),
|
|
171
|
-
length: Token.UINT24_BE.get(v, 3)
|
|
172
|
-
};
|
|
173
|
-
if (!header.id.match(/[A-Z0-9]{3}/g)) {
|
|
174
|
-
this.metadata.addWarning(`Invalid ID3v2.${this.id3Header.version.major} frame-header-ID: ${header.id}`);
|
|
175
|
-
}
|
|
176
|
-
break;
|
|
177
|
-
case 3:
|
|
178
|
-
case 4:
|
|
179
|
-
header = {
|
|
180
|
-
id: v.toString('ascii', 0, 4),
|
|
181
|
-
length: (majorVer === 4 ? ID3v2Token_1.UINT32SYNCSAFE : Token.UINT32_BE).get(v, 4),
|
|
182
|
-
flags: ID3v2Parser.readFrameFlags(v.slice(8, 10))
|
|
183
|
-
};
|
|
184
|
-
if (!header.id.match(/[A-Z0-9]{4}/g)) {
|
|
185
|
-
this.metadata.addWarning(`Invalid ID3v2.${this.id3Header.version.major} frame-header-ID: ${header.id}`);
|
|
186
|
-
}
|
|
187
|
-
break;
|
|
188
|
-
default:
|
|
189
|
-
throw new Error('Unexpected majorVer: ' + majorVer);
|
|
190
|
-
}
|
|
191
|
-
return header;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
exports.ID3v2Parser = ID3v2Parser;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ID3v2Parser = void 0;
|
|
4
|
+
const Token = require("token-types");
|
|
5
|
+
const util = require("../common/Util");
|
|
6
|
+
const FrameParser_1 = require("./FrameParser");
|
|
7
|
+
const ID3v2Token_1 = require("./ID3v2Token");
|
|
8
|
+
class ID3v2Parser {
|
|
9
|
+
static removeUnsyncBytes(buffer) {
|
|
10
|
+
let readI = 0;
|
|
11
|
+
let writeI = 0;
|
|
12
|
+
while (readI < buffer.length - 1) {
|
|
13
|
+
if (readI !== writeI) {
|
|
14
|
+
buffer[writeI] = buffer[readI];
|
|
15
|
+
}
|
|
16
|
+
readI += (buffer[readI] === 0xFF && buffer[readI + 1] === 0) ? 2 : 1;
|
|
17
|
+
writeI++;
|
|
18
|
+
}
|
|
19
|
+
if (readI < buffer.length) {
|
|
20
|
+
buffer[writeI++] = buffer[readI];
|
|
21
|
+
}
|
|
22
|
+
return buffer.slice(0, writeI);
|
|
23
|
+
}
|
|
24
|
+
static getFrameHeaderLength(majorVer) {
|
|
25
|
+
switch (majorVer) {
|
|
26
|
+
case 2:
|
|
27
|
+
return 6;
|
|
28
|
+
case 3:
|
|
29
|
+
case 4:
|
|
30
|
+
return 10;
|
|
31
|
+
default:
|
|
32
|
+
throw new Error('header versionIndex is incorrect');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
static readFrameFlags(b) {
|
|
36
|
+
return {
|
|
37
|
+
status: {
|
|
38
|
+
tag_alter_preservation: util.getBit(b, 0, 6),
|
|
39
|
+
file_alter_preservation: util.getBit(b, 0, 5),
|
|
40
|
+
read_only: util.getBit(b, 0, 4)
|
|
41
|
+
},
|
|
42
|
+
format: {
|
|
43
|
+
grouping_identity: util.getBit(b, 1, 7),
|
|
44
|
+
compression: util.getBit(b, 1, 3),
|
|
45
|
+
encryption: util.getBit(b, 1, 2),
|
|
46
|
+
unsynchronisation: util.getBit(b, 1, 1),
|
|
47
|
+
data_length_indicator: util.getBit(b, 1, 0)
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
static readFrameData(buf, frameHeader, majorVer, includeCovers, warningCollector) {
|
|
52
|
+
const frameParser = new FrameParser_1.FrameParser(majorVer, warningCollector);
|
|
53
|
+
switch (majorVer) {
|
|
54
|
+
case 2:
|
|
55
|
+
return frameParser.readData(buf, frameHeader.id, includeCovers);
|
|
56
|
+
case 3:
|
|
57
|
+
case 4:
|
|
58
|
+
if (frameHeader.flags.format.unsynchronisation) {
|
|
59
|
+
buf = ID3v2Parser.removeUnsyncBytes(buf);
|
|
60
|
+
}
|
|
61
|
+
if (frameHeader.flags.format.data_length_indicator) {
|
|
62
|
+
buf = buf.slice(4, buf.length);
|
|
63
|
+
}
|
|
64
|
+
return frameParser.readData(buf, frameHeader.id, includeCovers);
|
|
65
|
+
default:
|
|
66
|
+
throw new Error('Unexpected majorVer: ' + majorVer);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a combined tag key, of tag & description
|
|
71
|
+
* @param {string} tag e.g.: COM
|
|
72
|
+
* @param {string} description e.g. iTunPGAP
|
|
73
|
+
* @returns {string} e.g. COM:iTunPGAP
|
|
74
|
+
*/
|
|
75
|
+
static makeDescriptionTagName(tag, description) {
|
|
76
|
+
return tag + (description ? ':' + description : '');
|
|
77
|
+
}
|
|
78
|
+
async parse(metadata, tokenizer, options) {
|
|
79
|
+
this.tokenizer = tokenizer;
|
|
80
|
+
this.metadata = metadata;
|
|
81
|
+
this.options = options;
|
|
82
|
+
const id3Header = await this.tokenizer.readToken(ID3v2Token_1.ID3v2Header);
|
|
83
|
+
if (id3Header.fileIdentifier !== 'ID3') {
|
|
84
|
+
throw new Error('expected ID3-header file-identifier \'ID3\' was not found');
|
|
85
|
+
}
|
|
86
|
+
this.id3Header = id3Header;
|
|
87
|
+
this.headerType = ('ID3v2.' + id3Header.version.major);
|
|
88
|
+
if (id3Header.flags.isExtendedHeader) {
|
|
89
|
+
return this.parseExtendedHeader();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
return this.parseId3Data(id3Header.size);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async parseExtendedHeader() {
|
|
96
|
+
const extendedHeader = await this.tokenizer.readToken(ID3v2Token_1.ExtendedHeader);
|
|
97
|
+
const dataRemaining = extendedHeader.size - ID3v2Token_1.ExtendedHeader.len;
|
|
98
|
+
if (dataRemaining > 0) {
|
|
99
|
+
return this.parseExtendedHeaderData(dataRemaining, extendedHeader.size);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return this.parseId3Data(this.id3Header.size - extendedHeader.size);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async parseExtendedHeaderData(dataRemaining, extendedHeaderSize) {
|
|
106
|
+
const buffer = Buffer.alloc(dataRemaining);
|
|
107
|
+
await this.tokenizer.readBuffer(buffer, { length: dataRemaining });
|
|
108
|
+
return this.parseId3Data(this.id3Header.size - extendedHeaderSize);
|
|
109
|
+
}
|
|
110
|
+
async parseId3Data(dataLen) {
|
|
111
|
+
const buffer = Buffer.alloc(dataLen);
|
|
112
|
+
await this.tokenizer.readBuffer(buffer, { length: dataLen });
|
|
113
|
+
for (const tag of this.parseMetadata(buffer)) {
|
|
114
|
+
if (tag.id === 'TXXX') {
|
|
115
|
+
if (tag.value) {
|
|
116
|
+
for (const text of tag.value.text) {
|
|
117
|
+
this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, tag.value.description), text);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (tag.id === 'COM') {
|
|
122
|
+
for (const value of tag.value) {
|
|
123
|
+
this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value.text);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else if (tag.id === 'COMM') {
|
|
127
|
+
for (const value of tag.value) {
|
|
128
|
+
this.addTag(ID3v2Parser.makeDescriptionTagName(tag.id, value.description), value);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (Array.isArray(tag.value)) {
|
|
132
|
+
for (const value of tag.value) {
|
|
133
|
+
this.addTag(tag.id, value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.addTag(tag.id, tag.value);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
addTag(id, value) {
|
|
142
|
+
this.metadata.addTag(this.headerType, id, value);
|
|
143
|
+
}
|
|
144
|
+
parseMetadata(data) {
|
|
145
|
+
let offset = 0;
|
|
146
|
+
const tags = [];
|
|
147
|
+
while (true) {
|
|
148
|
+
if (offset === data.length)
|
|
149
|
+
break;
|
|
150
|
+
const frameHeaderLength = ID3v2Parser.getFrameHeaderLength(this.id3Header.version.major);
|
|
151
|
+
if (offset + frameHeaderLength > data.length) {
|
|
152
|
+
this.metadata.addWarning('Illegal ID3v2 tag length');
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
const frameHeaderBytes = data.slice(offset, offset += frameHeaderLength);
|
|
156
|
+
const frameHeader = this.readFrameHeader(frameHeaderBytes, this.id3Header.version.major);
|
|
157
|
+
const frameDataBytes = data.slice(offset, offset += frameHeader.length);
|
|
158
|
+
const values = ID3v2Parser.readFrameData(frameDataBytes, frameHeader, this.id3Header.version.major, !this.options.skipCovers, this.metadata);
|
|
159
|
+
if (values) {
|
|
160
|
+
tags.push({ id: frameHeader.id, value: values });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return tags;
|
|
164
|
+
}
|
|
165
|
+
readFrameHeader(v, majorVer) {
|
|
166
|
+
let header;
|
|
167
|
+
switch (majorVer) {
|
|
168
|
+
case 2:
|
|
169
|
+
header = {
|
|
170
|
+
id: v.toString('ascii', 0, 3),
|
|
171
|
+
length: Token.UINT24_BE.get(v, 3)
|
|
172
|
+
};
|
|
173
|
+
if (!header.id.match(/[A-Z0-9]{3}/g)) {
|
|
174
|
+
this.metadata.addWarning(`Invalid ID3v2.${this.id3Header.version.major} frame-header-ID: ${header.id}`);
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
case 3:
|
|
178
|
+
case 4:
|
|
179
|
+
header = {
|
|
180
|
+
id: v.toString('ascii', 0, 4),
|
|
181
|
+
length: (majorVer === 4 ? ID3v2Token_1.UINT32SYNCSAFE : Token.UINT32_BE).get(v, 4),
|
|
182
|
+
flags: ID3v2Parser.readFrameFlags(v.slice(8, 10))
|
|
183
|
+
};
|
|
184
|
+
if (!header.id.match(/[A-Z0-9]{4}/g)) {
|
|
185
|
+
this.metadata.addWarning(`Invalid ID3v2.${this.id3Header.version.major} frame-header-ID: ${header.id}`);
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
default:
|
|
189
|
+
throw new Error('Unexpected majorVer: ' + majorVer);
|
|
190
|
+
}
|
|
191
|
+
return header;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.ID3v2Parser = ID3v2Parser;
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
/**
|
|
4
|
-
* The picture type according to the ID3v2 APIC frame
|
|
5
|
-
* Ref: http://id3.org/id3v2.3.0#Attached_picture
|
|
6
|
-
*/
|
|
7
|
-
export declare enum AttachedPictureType {
|
|
8
|
-
'Other' = 0,
|
|
9
|
-
"32x32 pixels 'file icon' (PNG only)" = 1,
|
|
10
|
-
'Other file icon' = 2,
|
|
11
|
-
'Cover (front)' = 3,
|
|
12
|
-
'Cover (back)' = 4,
|
|
13
|
-
'Leaflet page' = 5,
|
|
14
|
-
'Media (e.g. label side of CD)' = 6,
|
|
15
|
-
'Lead artist/lead performer/soloist' = 7,
|
|
16
|
-
'Artist/performer' = 8,
|
|
17
|
-
'Conductor' = 9,
|
|
18
|
-
'Band/Orchestra' = 10,
|
|
19
|
-
'Composer' = 11,
|
|
20
|
-
'Lyricist/text writer' = 12,
|
|
21
|
-
'Recording Location' = 13,
|
|
22
|
-
'During recording' = 14,
|
|
23
|
-
'During performance' = 15,
|
|
24
|
-
'Movie/video screen capture' = 16,
|
|
25
|
-
'A bright coloured fish' = 17,
|
|
26
|
-
'Illustration' = 18,
|
|
27
|
-
'Band/artist logotype' = 19,
|
|
28
|
-
'Publisher/Studio logotype' = 20
|
|
29
|
-
}
|
|
30
|
-
export declare type ID3v2MajorVersion = 2 | 3 | 4;
|
|
31
|
-
export interface IExtendedHeader {
|
|
32
|
-
size: number;
|
|
33
|
-
extendedFlags: number;
|
|
34
|
-
sizeOfPadding: number;
|
|
35
|
-
crcDataPresent: boolean;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* 28 bits (representing up to 256MB) integer, the msb is 0 to avoid 'false syncsignals'.
|
|
39
|
-
* 4 * %0xxxxxxx
|
|
40
|
-
*/
|
|
41
|
-
export declare const UINT32SYNCSAFE: {
|
|
42
|
-
get: (buf: Uint8Array, off: number) => number;
|
|
43
|
-
len: number;
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* ID3v2 tag header
|
|
47
|
-
*/
|
|
48
|
-
export interface IID3v2header {
|
|
49
|
-
fileIdentifier: string;
|
|
50
|
-
version: {
|
|
51
|
-
major: ID3v2MajorVersion;
|
|
52
|
-
revision: number;
|
|
53
|
-
};
|
|
54
|
-
flags: {
|
|
55
|
-
unsynchronisation: boolean;
|
|
56
|
-
isExtendedHeader: boolean;
|
|
57
|
-
expIndicator: boolean;
|
|
58
|
-
footer: boolean;
|
|
59
|
-
};
|
|
60
|
-
size: number;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* ID3v2 header
|
|
64
|
-
* Ref: http://id3.org/id3v2.3.0#ID3v2_header
|
|
65
|
-
* ToDo
|
|
66
|
-
*/
|
|
67
|
-
export declare const ID3v2Header: IGetToken<IID3v2header>;
|
|
68
|
-
export declare const ExtendedHeader: IGetToken<IExtendedHeader>;
|
|
69
|
-
export interface ITextEncoding {
|
|
70
|
-
encoding: util.StringEncoding;
|
|
71
|
-
bom?: boolean;
|
|
72
|
-
}
|
|
73
|
-
export declare const TextEncodingToken: IGetToken<ITextEncoding>;
|
|
1
|
+
import { IGetToken } from 'strtok3/lib/core';
|
|
2
|
+
import * as util from '../common/Util';
|
|
3
|
+
/**
|
|
4
|
+
* The picture type according to the ID3v2 APIC frame
|
|
5
|
+
* Ref: http://id3.org/id3v2.3.0#Attached_picture
|
|
6
|
+
*/
|
|
7
|
+
export declare enum AttachedPictureType {
|
|
8
|
+
'Other' = 0,
|
|
9
|
+
"32x32 pixels 'file icon' (PNG only)" = 1,
|
|
10
|
+
'Other file icon' = 2,
|
|
11
|
+
'Cover (front)' = 3,
|
|
12
|
+
'Cover (back)' = 4,
|
|
13
|
+
'Leaflet page' = 5,
|
|
14
|
+
'Media (e.g. label side of CD)' = 6,
|
|
15
|
+
'Lead artist/lead performer/soloist' = 7,
|
|
16
|
+
'Artist/performer' = 8,
|
|
17
|
+
'Conductor' = 9,
|
|
18
|
+
'Band/Orchestra' = 10,
|
|
19
|
+
'Composer' = 11,
|
|
20
|
+
'Lyricist/text writer' = 12,
|
|
21
|
+
'Recording Location' = 13,
|
|
22
|
+
'During recording' = 14,
|
|
23
|
+
'During performance' = 15,
|
|
24
|
+
'Movie/video screen capture' = 16,
|
|
25
|
+
'A bright coloured fish' = 17,
|
|
26
|
+
'Illustration' = 18,
|
|
27
|
+
'Band/artist logotype' = 19,
|
|
28
|
+
'Publisher/Studio logotype' = 20
|
|
29
|
+
}
|
|
30
|
+
export declare type ID3v2MajorVersion = 2 | 3 | 4;
|
|
31
|
+
export interface IExtendedHeader {
|
|
32
|
+
size: number;
|
|
33
|
+
extendedFlags: number;
|
|
34
|
+
sizeOfPadding: number;
|
|
35
|
+
crcDataPresent: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 28 bits (representing up to 256MB) integer, the msb is 0 to avoid 'false syncsignals'.
|
|
39
|
+
* 4 * %0xxxxxxx
|
|
40
|
+
*/
|
|
41
|
+
export declare const UINT32SYNCSAFE: {
|
|
42
|
+
get: (buf: Uint8Array, off: number) => number;
|
|
43
|
+
len: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* ID3v2 tag header
|
|
47
|
+
*/
|
|
48
|
+
export interface IID3v2header {
|
|
49
|
+
fileIdentifier: string;
|
|
50
|
+
version: {
|
|
51
|
+
major: ID3v2MajorVersion;
|
|
52
|
+
revision: number;
|
|
53
|
+
};
|
|
54
|
+
flags: {
|
|
55
|
+
unsynchronisation: boolean;
|
|
56
|
+
isExtendedHeader: boolean;
|
|
57
|
+
expIndicator: boolean;
|
|
58
|
+
footer: boolean;
|
|
59
|
+
};
|
|
60
|
+
size: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* ID3v2 header
|
|
64
|
+
* Ref: http://id3.org/id3v2.3.0#ID3v2_header
|
|
65
|
+
* ToDo
|
|
66
|
+
*/
|
|
67
|
+
export declare const ID3v2Header: IGetToken<IID3v2header>;
|
|
68
|
+
export declare const ExtendedHeader: IGetToken<IExtendedHeader>;
|
|
69
|
+
export interface ITextEncoding {
|
|
70
|
+
encoding: util.StringEncoding;
|
|
71
|
+
bom?: boolean;
|
|
72
|
+
}
|
|
73
|
+
export declare const TextEncodingToken: IGetToken<ITextEncoding>;
|