music-metadata 7.11.7 → 7.11.8
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 +433 -432
- package/lib/ParserFactory.d.ts +48 -49
- package/lib/ParserFactory.js +251 -251
- package/lib/aiff/AiffParser.d.ts +14 -15
- package/lib/aiff/AiffParser.js +84 -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 +161 -162
- package/lib/apev2/APEv2TagMapper.d.ts +4 -4
- package/lib/apev2/APEv2TagMapper.js +86 -86
- package/lib/apev2/APEv2Token.d.ts +100 -100
- package/lib/apev2/APEv2Token.js +126 -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 +95 -95
- package/lib/asf/AsfUtil.d.ts +13 -13
- package/lib/asf/AsfUtil.js +40 -40
- package/lib/asf/GUID.d.ts +84 -86
- package/lib/asf/GUID.js +121 -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 +21 -21
- package/lib/common/CombinedTagMapper.d.ts +19 -19
- package/lib/common/CombinedTagMapper.js +51 -51
- package/lib/common/FourCC.d.ts +6 -6
- package/lib/common/FourCC.js +28 -28
- package/lib/common/GenericTagMapper.d.ts +51 -51
- package/lib/common/GenericTagMapper.js +55 -55
- package/lib/common/GenericTagTypes.d.ts +33 -33
- package/lib/common/GenericTagTypes.js +131 -131
- package/lib/common/MetadataCollector.d.ts +76 -76
- package/lib/common/MetadataCollector.js +275 -275
- 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 -58
- package/lib/common/Util.js +157 -162
- 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 +22 -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 -329
- package/lib/id3v2/ID3v22TagMapper.d.ts +9 -9
- package/lib/id3v2/ID3v22TagMapper.js +55 -55
- package/lib/id3v2/ID3v24TagMapper.d.ts +14 -14
- package/lib/id3v2/ID3v24TagMapper.js +193 -193
- package/lib/id3v2/ID3v2Parser.d.ts +29 -29
- package/lib/id3v2/ID3v2Parser.js +184 -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 -45
- package/lib/index.js +74 -74
- 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 -235
- package/lib/matroska/MatroskaTagMapper.d.ts +4 -4
- package/lib/matroska/MatroskaTagMapper.js +35 -35
- package/lib/matroska/types.d.ts +175 -175
- package/lib/matroska/types.js +33 -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 +115 -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 +524 -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 +69 -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 +132 -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 +37 -37
- package/lib/type.d.ts +592 -599
- package/lib/type.js +5 -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 +99 -105
- package/lib/wavpack/WavPackToken.d.ts +64 -64
- package/lib/wavpack/WavPackToken.js +76 -76
- package/package.json +150 -142
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MatroskaParser = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const BasicParser_1 = require("../common/BasicParser");
|
|
7
|
-
const types_1 = require("./types");
|
|
8
|
-
const matroskaDtd = require("./MatroskaDtd");
|
|
9
|
-
const debug =
|
|
10
|
-
/**
|
|
11
|
-
* Extensible Binary Meta Language (EBML) parser
|
|
12
|
-
* https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Language
|
|
13
|
-
* http://matroska.sourceforge.net/technical/specs/rfc/index.html
|
|
14
|
-
*
|
|
15
|
-
* WEBM VP8 AUDIO FILE
|
|
16
|
-
*/
|
|
17
|
-
class MatroskaParser extends BasicParser_1.BasicParser {
|
|
18
|
-
constructor() {
|
|
19
|
-
super();
|
|
20
|
-
this.padding = 0;
|
|
21
|
-
this.parserMap = new Map();
|
|
22
|
-
this.ebmlMaxIDLength = 4;
|
|
23
|
-
this.ebmlMaxSizeLength = 8;
|
|
24
|
-
this.parserMap.set(types_1.DataType.uint, e => this.readUint(e));
|
|
25
|
-
this.parserMap.set(types_1.DataType.string, e => this.readString(e));
|
|
26
|
-
this.parserMap.set(types_1.DataType.binary, e => this.readBuffer(e));
|
|
27
|
-
this.parserMap.set(types_1.DataType.uid, async (e) => await this.readUint(e) === 1);
|
|
28
|
-
this.parserMap.set(types_1.DataType.bool, e => this.readFlag(e));
|
|
29
|
-
this.parserMap.set(types_1.DataType.float, e => this.readFloat(e));
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
|
|
33
|
-
* @param {INativeMetadataCollector} metadata Output
|
|
34
|
-
* @param {ITokenizer} tokenizer Input
|
|
35
|
-
* @param {IOptions} options Parsing options
|
|
36
|
-
*/
|
|
37
|
-
init(metadata, tokenizer, options) {
|
|
38
|
-
super.init(metadata, tokenizer, options);
|
|
39
|
-
return this;
|
|
40
|
-
}
|
|
41
|
-
async parse() {
|
|
42
|
-
const matroska = await this.parseContainer(matroskaDtd.elements, this.tokenizer.fileInfo.size, []);
|
|
43
|
-
this.metadata.setFormat('container', `EBML/${matroska.ebml.docType}`);
|
|
44
|
-
if (matroska.segment) {
|
|
45
|
-
const info = matroska.segment.info;
|
|
46
|
-
if (info) {
|
|
47
|
-
const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
|
|
48
|
-
const duration = info.duration * timecodeScale / 1000000000;
|
|
49
|
-
this.addTag('segment:title', info.title);
|
|
50
|
-
this.metadata.setFormat('duration', duration);
|
|
51
|
-
}
|
|
52
|
-
const audioTracks = matroska.segment.tracks;
|
|
53
|
-
if (audioTracks && audioTracks.entries) {
|
|
54
|
-
audioTracks.entries.forEach(entry => {
|
|
55
|
-
const stream = {
|
|
56
|
-
codecName: entry.codecID.replace('A_', '').replace('V_', ''),
|
|
57
|
-
codecSettings: entry.codecSettings,
|
|
58
|
-
flagDefault: entry.flagDefault,
|
|
59
|
-
flagLacing: entry.flagLacing,
|
|
60
|
-
flagEnabled: entry.flagEnabled,
|
|
61
|
-
language: entry.language,
|
|
62
|
-
name: entry.name,
|
|
63
|
-
type: entry.trackType,
|
|
64
|
-
audio: entry.audio,
|
|
65
|
-
video: entry.video
|
|
66
|
-
};
|
|
67
|
-
this.metadata.addStreamInfo(stream);
|
|
68
|
-
});
|
|
69
|
-
const audioTrack = audioTracks.entries
|
|
70
|
-
.filter(entry => {
|
|
71
|
-
return entry.trackType === types_1.TrackType.audio.valueOf();
|
|
72
|
-
})
|
|
73
|
-
.reduce((acc, cur) => {
|
|
74
|
-
if (!acc) {
|
|
75
|
-
return cur;
|
|
76
|
-
}
|
|
77
|
-
if (!acc.flagDefault && cur.flagDefault) {
|
|
78
|
-
return cur;
|
|
79
|
-
}
|
|
80
|
-
if (cur.trackNumber && cur.trackNumber < acc.trackNumber) {
|
|
81
|
-
return cur;
|
|
82
|
-
}
|
|
83
|
-
return acc;
|
|
84
|
-
}, null);
|
|
85
|
-
if (audioTrack) {
|
|
86
|
-
this.metadata.setFormat('codec', audioTrack.codecID.replace('A_', ''));
|
|
87
|
-
this.metadata.setFormat('sampleRate', audioTrack.audio.samplingFrequency);
|
|
88
|
-
this.metadata.setFormat('numberOfChannels', audioTrack.audio.channels);
|
|
89
|
-
}
|
|
90
|
-
if (matroska.segment.tags) {
|
|
91
|
-
matroska.segment.tags.tag.forEach(tag => {
|
|
92
|
-
const target = tag.target;
|
|
93
|
-
const targetType = target.targetTypeValue ? types_1.TargetType[target.targetTypeValue] : (target.targetType ? target.targetType : 'track');
|
|
94
|
-
tag.simpleTags.forEach(simpleTag => {
|
|
95
|
-
const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
|
|
96
|
-
this.addTag(`${targetType}:${simpleTag.name}`, value);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
if (matroska.segment.attachments) {
|
|
101
|
-
matroska.segment.attachments.attachedFiles
|
|
102
|
-
.filter(file => file.mimeType.startsWith('image/'))
|
|
103
|
-
.map(file => {
|
|
104
|
-
return {
|
|
105
|
-
data: file.data,
|
|
106
|
-
format: file.mimeType,
|
|
107
|
-
description: file.description,
|
|
108
|
-
name: file.name
|
|
109
|
-
};
|
|
110
|
-
}).forEach(picture => {
|
|
111
|
-
this.addTag('picture', picture);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
async parseContainer(container, posDone, path) {
|
|
118
|
-
const tree = {};
|
|
119
|
-
while (this.tokenizer.position < posDone) {
|
|
120
|
-
let element;
|
|
121
|
-
try {
|
|
122
|
-
element = await this.readElement();
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
if (error.message === 'End-Of-Stream') {
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
const type = container[element.id];
|
|
131
|
-
if (type) {
|
|
132
|
-
debug(`Element: name=${type.name}, container=${!!type.container}`);
|
|
133
|
-
if (type.container) {
|
|
134
|
-
const res = await this.parseContainer(type.container, element.len >= 0 ? this.tokenizer.position + element.len : -1, path.concat([type.name]));
|
|
135
|
-
if (type.multiple) {
|
|
136
|
-
if (!tree[type.name]) {
|
|
137
|
-
tree[type.name] = [];
|
|
138
|
-
}
|
|
139
|
-
tree[type.name].push(res);
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
tree[type.name] = res;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
tree[type.name] = await this.parserMap.get(type.value)(element);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
switch (element.id) {
|
|
151
|
-
case 0xec: // void
|
|
152
|
-
this.padding += element.len;
|
|
153
|
-
await this.tokenizer.ignore(element.len);
|
|
154
|
-
break;
|
|
155
|
-
default:
|
|
156
|
-
debug(`parseEbml: path=${path.join('/')}, unknown element: id=${element.id.toString(16)}`);
|
|
157
|
-
this.padding += element.len;
|
|
158
|
-
await this.tokenizer.ignore(element.len);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return tree;
|
|
163
|
-
}
|
|
164
|
-
async readVintData(maxLength) {
|
|
165
|
-
const msb = await this.tokenizer.peekNumber(
|
|
166
|
-
let mask = 0x80;
|
|
167
|
-
let oc = 1;
|
|
168
|
-
// Calculate VINT_WIDTH
|
|
169
|
-
while ((msb & mask) === 0) {
|
|
170
|
-
if (oc > maxLength) {
|
|
171
|
-
throw new Error('VINT value exceeding maximum size');
|
|
172
|
-
}
|
|
173
|
-
++oc;
|
|
174
|
-
mask >>= 1;
|
|
175
|
-
}
|
|
176
|
-
const id = Buffer.alloc(oc);
|
|
177
|
-
await this.tokenizer.readBuffer(id);
|
|
178
|
-
return id;
|
|
179
|
-
}
|
|
180
|
-
async readElement() {
|
|
181
|
-
const id = await this.readVintData(this.ebmlMaxIDLength);
|
|
182
|
-
const lenField = await this.readVintData(this.ebmlMaxSizeLength);
|
|
183
|
-
lenField[0] ^= 0x80 >> (lenField.length - 1);
|
|
184
|
-
const nrLen = Math.min(6, lenField.length); // JavaScript can max read 6 bytes integer
|
|
185
|
-
return {
|
|
186
|
-
id: id.readUIntBE(0, id.length),
|
|
187
|
-
len: lenField.readUIntBE(lenField.length - nrLen, nrLen)
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
isMaxValue(vintData) {
|
|
191
|
-
if (vintData.length === this.ebmlMaxSizeLength) {
|
|
192
|
-
for (let n = 1; n < this.ebmlMaxSizeLength; ++n) {
|
|
193
|
-
if (vintData[n] !== 0xff)
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
async readFloat(e) {
|
|
201
|
-
switch (e.len) {
|
|
202
|
-
case 0:
|
|
203
|
-
return 0.0;
|
|
204
|
-
case 4:
|
|
205
|
-
return this.tokenizer.readNumber(
|
|
206
|
-
case 8:
|
|
207
|
-
return this.tokenizer.readNumber(
|
|
208
|
-
case 10:
|
|
209
|
-
return this.tokenizer.readNumber(
|
|
210
|
-
default:
|
|
211
|
-
throw new Error(`Invalid IEEE-754 float length: ${e.len}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
async readFlag(e) {
|
|
215
|
-
return (await this.readUint(e)) === 1;
|
|
216
|
-
}
|
|
217
|
-
async readUint(e) {
|
|
218
|
-
const buf = await this.readBuffer(e);
|
|
219
|
-
const nrLen = Math.min(6, e.len); // JavaScript can max read 6 bytes integer
|
|
220
|
-
return buf.readUIntBE(e.len - nrLen, nrLen);
|
|
221
|
-
}
|
|
222
|
-
async readString(e) {
|
|
223
|
-
const rawString = await this.tokenizer.readToken(new
|
|
224
|
-
return rawString.replace(/\00.*$/g, '');
|
|
225
|
-
}
|
|
226
|
-
async readBuffer(e) {
|
|
227
|
-
const buf = Buffer.alloc(e.len);
|
|
228
|
-
await this.tokenizer.readBuffer(buf);
|
|
229
|
-
return buf;
|
|
230
|
-
}
|
|
231
|
-
addTag(tagId, value) {
|
|
232
|
-
this.metadata.addTag('matroska', tagId, value);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
exports.MatroskaParser = MatroskaParser;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MatroskaParser = void 0;
|
|
4
|
+
const token_types_1 = require("token-types");
|
|
5
|
+
const initDebug = require("debug");
|
|
6
|
+
const BasicParser_1 = require("../common/BasicParser");
|
|
7
|
+
const types_1 = require("./types");
|
|
8
|
+
const matroskaDtd = require("./MatroskaDtd");
|
|
9
|
+
const debug = initDebug('music-metadata:parser:matroska');
|
|
10
|
+
/**
|
|
11
|
+
* Extensible Binary Meta Language (EBML) parser
|
|
12
|
+
* https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Language
|
|
13
|
+
* http://matroska.sourceforge.net/technical/specs/rfc/index.html
|
|
14
|
+
*
|
|
15
|
+
* WEBM VP8 AUDIO FILE
|
|
16
|
+
*/
|
|
17
|
+
class MatroskaParser extends BasicParser_1.BasicParser {
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
this.padding = 0;
|
|
21
|
+
this.parserMap = new Map();
|
|
22
|
+
this.ebmlMaxIDLength = 4;
|
|
23
|
+
this.ebmlMaxSizeLength = 8;
|
|
24
|
+
this.parserMap.set(types_1.DataType.uint, e => this.readUint(e));
|
|
25
|
+
this.parserMap.set(types_1.DataType.string, e => this.readString(e));
|
|
26
|
+
this.parserMap.set(types_1.DataType.binary, e => this.readBuffer(e));
|
|
27
|
+
this.parserMap.set(types_1.DataType.uid, async (e) => await this.readUint(e) === 1);
|
|
28
|
+
this.parserMap.set(types_1.DataType.bool, e => this.readFlag(e));
|
|
29
|
+
this.parserMap.set(types_1.DataType.float, e => this.readFloat(e));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
|
|
33
|
+
* @param {INativeMetadataCollector} metadata Output
|
|
34
|
+
* @param {ITokenizer} tokenizer Input
|
|
35
|
+
* @param {IOptions} options Parsing options
|
|
36
|
+
*/
|
|
37
|
+
init(metadata, tokenizer, options) {
|
|
38
|
+
super.init(metadata, tokenizer, options);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
async parse() {
|
|
42
|
+
const matroska = await this.parseContainer(matroskaDtd.elements, this.tokenizer.fileInfo.size, []);
|
|
43
|
+
this.metadata.setFormat('container', `EBML/${matroska.ebml.docType}`);
|
|
44
|
+
if (matroska.segment) {
|
|
45
|
+
const info = matroska.segment.info;
|
|
46
|
+
if (info) {
|
|
47
|
+
const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
|
|
48
|
+
const duration = info.duration * timecodeScale / 1000000000;
|
|
49
|
+
this.addTag('segment:title', info.title);
|
|
50
|
+
this.metadata.setFormat('duration', duration);
|
|
51
|
+
}
|
|
52
|
+
const audioTracks = matroska.segment.tracks;
|
|
53
|
+
if (audioTracks && audioTracks.entries) {
|
|
54
|
+
audioTracks.entries.forEach(entry => {
|
|
55
|
+
const stream = {
|
|
56
|
+
codecName: entry.codecID.replace('A_', '').replace('V_', ''),
|
|
57
|
+
codecSettings: entry.codecSettings,
|
|
58
|
+
flagDefault: entry.flagDefault,
|
|
59
|
+
flagLacing: entry.flagLacing,
|
|
60
|
+
flagEnabled: entry.flagEnabled,
|
|
61
|
+
language: entry.language,
|
|
62
|
+
name: entry.name,
|
|
63
|
+
type: entry.trackType,
|
|
64
|
+
audio: entry.audio,
|
|
65
|
+
video: entry.video
|
|
66
|
+
};
|
|
67
|
+
this.metadata.addStreamInfo(stream);
|
|
68
|
+
});
|
|
69
|
+
const audioTrack = audioTracks.entries
|
|
70
|
+
.filter(entry => {
|
|
71
|
+
return entry.trackType === types_1.TrackType.audio.valueOf();
|
|
72
|
+
})
|
|
73
|
+
.reduce((acc, cur) => {
|
|
74
|
+
if (!acc) {
|
|
75
|
+
return cur;
|
|
76
|
+
}
|
|
77
|
+
if (!acc.flagDefault && cur.flagDefault) {
|
|
78
|
+
return cur;
|
|
79
|
+
}
|
|
80
|
+
if (cur.trackNumber && cur.trackNumber < acc.trackNumber) {
|
|
81
|
+
return cur;
|
|
82
|
+
}
|
|
83
|
+
return acc;
|
|
84
|
+
}, null);
|
|
85
|
+
if (audioTrack) {
|
|
86
|
+
this.metadata.setFormat('codec', audioTrack.codecID.replace('A_', ''));
|
|
87
|
+
this.metadata.setFormat('sampleRate', audioTrack.audio.samplingFrequency);
|
|
88
|
+
this.metadata.setFormat('numberOfChannels', audioTrack.audio.channels);
|
|
89
|
+
}
|
|
90
|
+
if (matroska.segment.tags) {
|
|
91
|
+
matroska.segment.tags.tag.forEach(tag => {
|
|
92
|
+
const target = tag.target;
|
|
93
|
+
const targetType = (target === null || target === void 0 ? void 0 : target.targetTypeValue) ? types_1.TargetType[target.targetTypeValue] : ((target === null || target === void 0 ? void 0 : target.targetType) ? target.targetType : 'track');
|
|
94
|
+
tag.simpleTags.forEach(simpleTag => {
|
|
95
|
+
const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
|
|
96
|
+
this.addTag(`${targetType}:${simpleTag.name}`, value);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (matroska.segment.attachments) {
|
|
101
|
+
matroska.segment.attachments.attachedFiles
|
|
102
|
+
.filter(file => file.mimeType.startsWith('image/'))
|
|
103
|
+
.map(file => {
|
|
104
|
+
return {
|
|
105
|
+
data: file.data,
|
|
106
|
+
format: file.mimeType,
|
|
107
|
+
description: file.description,
|
|
108
|
+
name: file.name
|
|
109
|
+
};
|
|
110
|
+
}).forEach(picture => {
|
|
111
|
+
this.addTag('picture', picture);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async parseContainer(container, posDone, path) {
|
|
118
|
+
const tree = {};
|
|
119
|
+
while (this.tokenizer.position < posDone) {
|
|
120
|
+
let element;
|
|
121
|
+
try {
|
|
122
|
+
element = await this.readElement();
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error.message === 'End-Of-Stream') {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
const type = container[element.id];
|
|
131
|
+
if (type) {
|
|
132
|
+
debug(`Element: name=${type.name}, container=${!!type.container}`);
|
|
133
|
+
if (type.container) {
|
|
134
|
+
const res = await this.parseContainer(type.container, element.len >= 0 ? this.tokenizer.position + element.len : -1, path.concat([type.name]));
|
|
135
|
+
if (type.multiple) {
|
|
136
|
+
if (!tree[type.name]) {
|
|
137
|
+
tree[type.name] = [];
|
|
138
|
+
}
|
|
139
|
+
tree[type.name].push(res);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
tree[type.name] = res;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
tree[type.name] = await this.parserMap.get(type.value)(element);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
switch (element.id) {
|
|
151
|
+
case 0xec: // void
|
|
152
|
+
this.padding += element.len;
|
|
153
|
+
await this.tokenizer.ignore(element.len);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
debug(`parseEbml: path=${path.join('/')}, unknown element: id=${element.id.toString(16)}`);
|
|
157
|
+
this.padding += element.len;
|
|
158
|
+
await this.tokenizer.ignore(element.len);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return tree;
|
|
163
|
+
}
|
|
164
|
+
async readVintData(maxLength) {
|
|
165
|
+
const msb = await this.tokenizer.peekNumber(token_types_1.UINT8);
|
|
166
|
+
let mask = 0x80;
|
|
167
|
+
let oc = 1;
|
|
168
|
+
// Calculate VINT_WIDTH
|
|
169
|
+
while ((msb & mask) === 0) {
|
|
170
|
+
if (oc > maxLength) {
|
|
171
|
+
throw new Error('VINT value exceeding maximum size');
|
|
172
|
+
}
|
|
173
|
+
++oc;
|
|
174
|
+
mask >>= 1;
|
|
175
|
+
}
|
|
176
|
+
const id = Buffer.alloc(oc);
|
|
177
|
+
await this.tokenizer.readBuffer(id);
|
|
178
|
+
return id;
|
|
179
|
+
}
|
|
180
|
+
async readElement() {
|
|
181
|
+
const id = await this.readVintData(this.ebmlMaxIDLength);
|
|
182
|
+
const lenField = await this.readVintData(this.ebmlMaxSizeLength);
|
|
183
|
+
lenField[0] ^= 0x80 >> (lenField.length - 1);
|
|
184
|
+
const nrLen = Math.min(6, lenField.length); // JavaScript can max read 6 bytes integer
|
|
185
|
+
return {
|
|
186
|
+
id: id.readUIntBE(0, id.length),
|
|
187
|
+
len: lenField.readUIntBE(lenField.length - nrLen, nrLen)
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
isMaxValue(vintData) {
|
|
191
|
+
if (vintData.length === this.ebmlMaxSizeLength) {
|
|
192
|
+
for (let n = 1; n < this.ebmlMaxSizeLength; ++n) {
|
|
193
|
+
if (vintData[n] !== 0xff)
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
async readFloat(e) {
|
|
201
|
+
switch (e.len) {
|
|
202
|
+
case 0:
|
|
203
|
+
return 0.0;
|
|
204
|
+
case 4:
|
|
205
|
+
return this.tokenizer.readNumber(token_types_1.Float32_BE);
|
|
206
|
+
case 8:
|
|
207
|
+
return this.tokenizer.readNumber(token_types_1.Float64_BE);
|
|
208
|
+
case 10:
|
|
209
|
+
return this.tokenizer.readNumber(token_types_1.Float64_BE);
|
|
210
|
+
default:
|
|
211
|
+
throw new Error(`Invalid IEEE-754 float length: ${e.len}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async readFlag(e) {
|
|
215
|
+
return (await this.readUint(e)) === 1;
|
|
216
|
+
}
|
|
217
|
+
async readUint(e) {
|
|
218
|
+
const buf = await this.readBuffer(e);
|
|
219
|
+
const nrLen = Math.min(6, e.len); // JavaScript can max read 6 bytes integer
|
|
220
|
+
return buf.readUIntBE(e.len - nrLen, nrLen);
|
|
221
|
+
}
|
|
222
|
+
async readString(e) {
|
|
223
|
+
const rawString = await this.tokenizer.readToken(new token_types_1.StringType(e.len, 'utf-8'));
|
|
224
|
+
return rawString.replace(/\00.*$/g, '');
|
|
225
|
+
}
|
|
226
|
+
async readBuffer(e) {
|
|
227
|
+
const buf = Buffer.alloc(e.len);
|
|
228
|
+
await this.tokenizer.readBuffer(buf);
|
|
229
|
+
return buf;
|
|
230
|
+
}
|
|
231
|
+
addTag(tagId, value) {
|
|
232
|
+
this.metadata.addTag('matroska', tagId, value);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
exports.MatroskaParser = MatroskaParser;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap';
|
|
2
|
-
export declare class MatroskaTagMapper extends CaseInsensitiveTagMap {
|
|
3
|
-
constructor();
|
|
4
|
-
}
|
|
1
|
+
import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap';
|
|
2
|
+
export declare class MatroskaTagMapper extends CaseInsensitiveTagMap {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MatroskaTagMapper = void 0;
|
|
4
|
-
const CaseInsensitiveTagMap_1 = require("../common/CaseInsensitiveTagMap");
|
|
5
|
-
/**
|
|
6
|
-
* EBML Tag map
|
|
7
|
-
*/
|
|
8
|
-
const ebmlTagMap = {
|
|
9
|
-
'segment:title': 'title',
|
|
10
|
-
'album:ARTIST': 'albumartist',
|
|
11
|
-
'album:ARTISTSORT': 'albumartistsort',
|
|
12
|
-
'album:TITLE': 'album',
|
|
13
|
-
'album:DATE_RECORDED': 'originaldate',
|
|
14
|
-
'album:PART_NUMBER': 'disk',
|
|
15
|
-
'album:TOTAL_PARTS': 'totaltracks',
|
|
16
|
-
'track:ARTIST': 'artist',
|
|
17
|
-
'track:ARTISTSORT': 'artistsort',
|
|
18
|
-
'track:TITLE': 'title',
|
|
19
|
-
'track:PART_NUMBER': 'track',
|
|
20
|
-
'track:MUSICBRAINZ_TRACKID': 'musicbrainz_recordingid',
|
|
21
|
-
'track:MUSICBRAINZ_ALBUMID': 'musicbrainz_albumid',
|
|
22
|
-
'track:MUSICBRAINZ_ARTISTID': 'musicbrainz_artistid',
|
|
23
|
-
'track:PUBLISHER': 'label',
|
|
24
|
-
'track:GENRE': 'genre',
|
|
25
|
-
'track:ENCODER': 'encodedby',
|
|
26
|
-
'track:ENCODER_OPTIONS': 'encodersettings',
|
|
27
|
-
'edition:TOTAL_PARTS': 'totaldiscs',
|
|
28
|
-
picture: 'picture'
|
|
29
|
-
};
|
|
30
|
-
class MatroskaTagMapper extends CaseInsensitiveTagMap_1.CaseInsensitiveTagMap {
|
|
31
|
-
constructor() {
|
|
32
|
-
super(['matroska'], ebmlTagMap);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
exports.MatroskaTagMapper = MatroskaTagMapper;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MatroskaTagMapper = void 0;
|
|
4
|
+
const CaseInsensitiveTagMap_1 = require("../common/CaseInsensitiveTagMap");
|
|
5
|
+
/**
|
|
6
|
+
* EBML Tag map
|
|
7
|
+
*/
|
|
8
|
+
const ebmlTagMap = {
|
|
9
|
+
'segment:title': 'title',
|
|
10
|
+
'album:ARTIST': 'albumartist',
|
|
11
|
+
'album:ARTISTSORT': 'albumartistsort',
|
|
12
|
+
'album:TITLE': 'album',
|
|
13
|
+
'album:DATE_RECORDED': 'originaldate',
|
|
14
|
+
'album:PART_NUMBER': 'disk',
|
|
15
|
+
'album:TOTAL_PARTS': 'totaltracks',
|
|
16
|
+
'track:ARTIST': 'artist',
|
|
17
|
+
'track:ARTISTSORT': 'artistsort',
|
|
18
|
+
'track:TITLE': 'title',
|
|
19
|
+
'track:PART_NUMBER': 'track',
|
|
20
|
+
'track:MUSICBRAINZ_TRACKID': 'musicbrainz_recordingid',
|
|
21
|
+
'track:MUSICBRAINZ_ALBUMID': 'musicbrainz_albumid',
|
|
22
|
+
'track:MUSICBRAINZ_ARTISTID': 'musicbrainz_artistid',
|
|
23
|
+
'track:PUBLISHER': 'label',
|
|
24
|
+
'track:GENRE': 'genre',
|
|
25
|
+
'track:ENCODER': 'encodedby',
|
|
26
|
+
'track:ENCODER_OPTIONS': 'encodersettings',
|
|
27
|
+
'edition:TOTAL_PARTS': 'totaldiscs',
|
|
28
|
+
picture: 'picture'
|
|
29
|
+
};
|
|
30
|
+
class MatroskaTagMapper extends CaseInsensitiveTagMap_1.CaseInsensitiveTagMap {
|
|
31
|
+
constructor() {
|
|
32
|
+
super(['matroska'], ebmlTagMap);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.MatroskaTagMapper = MatroskaTagMapper;
|
|
36
36
|
//# sourceMappingURL=MatroskaTagMapper.js.map
|