music-metadata 7.11.7 → 7.12.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.
Files changed (155) hide show
  1. package/LICENSE.txt +9 -0
  2. package/README.md +434 -432
  3. package/lib/ParserFactory.d.ts +48 -49
  4. package/lib/ParserFactory.js +252 -251
  5. package/lib/aiff/AiffParser.d.ts +14 -15
  6. package/lib/aiff/AiffParser.js +84 -85
  7. package/lib/aiff/AiffToken.d.ts +22 -22
  8. package/lib/aiff/AiffToken.js +43 -43
  9. package/lib/apev2/APEv2Parser.d.ts +30 -30
  10. package/lib/apev2/APEv2Parser.js +161 -162
  11. package/lib/apev2/APEv2TagMapper.d.ts +4 -4
  12. package/lib/apev2/APEv2TagMapper.js +86 -86
  13. package/lib/apev2/APEv2Token.d.ts +100 -100
  14. package/lib/apev2/APEv2Token.js +126 -126
  15. package/lib/asf/AsfObject.d.ts +319 -319
  16. package/lib/asf/AsfObject.js +384 -384
  17. package/lib/asf/AsfParser.d.ts +17 -17
  18. package/lib/asf/AsfParser.js +135 -135
  19. package/lib/asf/AsfTagMapper.d.ts +7 -7
  20. package/lib/asf/AsfTagMapper.js +95 -95
  21. package/lib/asf/AsfUtil.d.ts +13 -13
  22. package/lib/asf/AsfUtil.js +40 -40
  23. package/lib/asf/GUID.d.ts +84 -86
  24. package/lib/asf/GUID.js +121 -123
  25. package/lib/common/BasicParser.d.ts +17 -17
  26. package/lib/common/BasicParser.js +18 -18
  27. package/lib/common/CaseInsensitiveTagMap.d.ts +10 -10
  28. package/lib/common/CaseInsensitiveTagMap.js +21 -21
  29. package/lib/common/CombinedTagMapper.d.ts +19 -19
  30. package/lib/common/CombinedTagMapper.js +51 -51
  31. package/lib/common/FourCC.d.ts +6 -6
  32. package/lib/common/FourCC.js +28 -28
  33. package/lib/common/GenericTagMapper.d.ts +51 -51
  34. package/lib/common/GenericTagMapper.js +55 -55
  35. package/lib/common/GenericTagTypes.d.ts +33 -33
  36. package/lib/common/GenericTagTypes.js +131 -131
  37. package/lib/common/MetadataCollector.d.ts +76 -76
  38. package/lib/common/MetadataCollector.js +275 -275
  39. package/lib/common/RandomFileReader.d.ts +22 -20
  40. package/lib/common/RandomFileReader.js +34 -37
  41. package/lib/common/RandomUint8ArrayReader.d.ts +18 -18
  42. package/lib/common/RandomUint8ArrayReader.js +25 -25
  43. package/lib/common/Util.d.ts +57 -58
  44. package/lib/common/Util.js +157 -162
  45. package/lib/core.d.ts +48 -48
  46. package/lib/core.js +90 -90
  47. package/lib/dsdiff/DsdiffParser.d.ts +14 -14
  48. package/lib/dsdiff/DsdiffParser.js +143 -143
  49. package/lib/dsdiff/DsdiffToken.d.ts +9 -9
  50. package/lib/dsdiff/DsdiffToken.js +21 -21
  51. package/lib/dsf/DsfChunk.d.ts +86 -86
  52. package/lib/dsf/DsfChunk.js +54 -54
  53. package/lib/dsf/DsfParser.d.ts +9 -9
  54. package/lib/dsf/DsfParser.js +56 -56
  55. package/lib/flac/FlacParser.d.ts +28 -28
  56. package/lib/flac/FlacParser.js +175 -175
  57. package/lib/id3v1/ID3v1Parser.d.ts +13 -13
  58. package/lib/id3v1/ID3v1Parser.js +134 -134
  59. package/lib/id3v1/ID3v1TagMap.d.ts +4 -4
  60. package/lib/id3v1/ID3v1TagMap.js +22 -22
  61. package/lib/id3v2/AbstractID3Parser.d.ts +17 -17
  62. package/lib/id3v2/AbstractID3Parser.js +60 -60
  63. package/lib/id3v2/FrameParser.d.ts +32 -32
  64. package/lib/id3v2/FrameParser.js +329 -329
  65. package/lib/id3v2/ID3v22TagMapper.d.ts +9 -9
  66. package/lib/id3v2/ID3v22TagMapper.js +55 -55
  67. package/lib/id3v2/ID3v24TagMapper.d.ts +14 -14
  68. package/lib/id3v2/ID3v24TagMapper.js +193 -193
  69. package/lib/id3v2/ID3v2Parser.d.ts +29 -29
  70. package/lib/id3v2/ID3v2Parser.js +184 -194
  71. package/lib/id3v2/ID3v2Token.d.ts +73 -73
  72. package/lib/id3v2/ID3v2Token.js +106 -106
  73. package/lib/iff/index.d.ts +33 -33
  74. package/lib/iff/index.js +19 -19
  75. package/lib/index.d.ts +45 -45
  76. package/lib/index.js +74 -74
  77. package/lib/lyrics3/Lyrics3.d.ts +3 -3
  78. package/lib/lyrics3/Lyrics3.js +17 -17
  79. package/lib/matroska/MatroskaDtd.d.ts +8 -8
  80. package/lib/matroska/MatroskaDtd.js +279 -279
  81. package/lib/matroska/MatroskaParser.d.ts +37 -37
  82. package/lib/matroska/MatroskaParser.js +235 -235
  83. package/lib/matroska/MatroskaTagMapper.d.ts +4 -4
  84. package/lib/matroska/MatroskaTagMapper.js +35 -35
  85. package/lib/matroska/types.d.ts +175 -175
  86. package/lib/matroska/types.js +33 -32
  87. package/lib/mp4/Atom.d.ts +16 -16
  88. package/lib/mp4/Atom.js +70 -70
  89. package/lib/mp4/AtomToken.d.ts +395 -395
  90. package/lib/mp4/AtomToken.js +406 -406
  91. package/lib/mp4/MP4Parser.d.ts +30 -30
  92. package/lib/mp4/MP4Parser.js +511 -511
  93. package/lib/mp4/MP4TagMapper.d.ts +5 -5
  94. package/lib/mp4/MP4TagMapper.js +115 -115
  95. package/lib/mpeg/ExtendedLameHeader.d.ts +27 -27
  96. package/lib/mpeg/ExtendedLameHeader.js +31 -31
  97. package/lib/mpeg/MpegParser.d.ts +49 -49
  98. package/lib/mpeg/MpegParser.js +524 -529
  99. package/lib/mpeg/ReplayGainDataFormat.d.ts +55 -55
  100. package/lib/mpeg/ReplayGainDataFormat.js +69 -69
  101. package/lib/mpeg/XingTag.d.ts +45 -45
  102. package/lib/mpeg/XingTag.js +69 -69
  103. package/lib/musepack/index.d.ts +5 -5
  104. package/lib/musepack/index.js +32 -32
  105. package/lib/musepack/sv7/BitReader.d.ts +13 -13
  106. package/lib/musepack/sv7/BitReader.js +54 -54
  107. package/lib/musepack/sv7/MpcSv7Parser.d.ts +8 -8
  108. package/lib/musepack/sv7/MpcSv7Parser.js +46 -46
  109. package/lib/musepack/sv7/StreamVersion7.d.ts +28 -28
  110. package/lib/musepack/sv7/StreamVersion7.js +41 -41
  111. package/lib/musepack/sv8/MpcSv8Parser.d.ts +6 -6
  112. package/lib/musepack/sv8/MpcSv8Parser.js +55 -55
  113. package/lib/musepack/sv8/StreamVersion8.d.ts +40 -40
  114. package/lib/musepack/sv8/StreamVersion8.js +80 -80
  115. package/lib/ogg/Ogg.d.ts +72 -72
  116. package/lib/ogg/Ogg.js +2 -2
  117. package/lib/ogg/OggParser.d.ts +23 -23
  118. package/lib/ogg/OggParser.js +126 -126
  119. package/lib/ogg/opus/Opus.d.ts +48 -48
  120. package/lib/ogg/opus/Opus.js +28 -28
  121. package/lib/ogg/opus/OpusParser.d.ts +25 -25
  122. package/lib/ogg/opus/OpusParser.js +56 -56
  123. package/lib/ogg/speex/Speex.d.ts +36 -36
  124. package/lib/ogg/speex/Speex.js +31 -31
  125. package/lib/ogg/speex/SpeexParser.d.ts +22 -22
  126. package/lib/ogg/speex/SpeexParser.js +35 -35
  127. package/lib/ogg/theora/Theora.d.ts +20 -20
  128. package/lib/ogg/theora/Theora.js +23 -23
  129. package/lib/ogg/theora/TheoraParser.d.ts +28 -28
  130. package/lib/ogg/theora/TheoraParser.js +44 -44
  131. package/lib/ogg/vorbis/Vorbis.d.ts +69 -79
  132. package/lib/ogg/vorbis/Vorbis.js +78 -78
  133. package/lib/ogg/vorbis/VorbisDecoder.d.ts +12 -12
  134. package/lib/ogg/vorbis/VorbisDecoder.js +32 -32
  135. package/lib/ogg/vorbis/VorbisParser.d.ts +36 -36
  136. package/lib/ogg/vorbis/VorbisParser.js +128 -128
  137. package/lib/ogg/vorbis/VorbisTagMapper.d.ts +7 -7
  138. package/lib/ogg/vorbis/VorbisTagMapper.js +132 -132
  139. package/lib/riff/RiffChunk.d.ts +16 -16
  140. package/lib/riff/RiffChunk.js +32 -32
  141. package/lib/riff/RiffInfoTagMap.d.ts +10 -10
  142. package/lib/riff/RiffInfoTagMap.js +37 -37
  143. package/lib/type.d.ts +592 -599
  144. package/lib/type.js +5 -13
  145. package/lib/wav/BwfChunk.d.ts +17 -0
  146. package/lib/wav/BwfChunk.js +28 -0
  147. package/lib/wav/WaveChunk.d.ts +64 -64
  148. package/lib/wav/WaveChunk.js +65 -65
  149. package/lib/wav/WaveParser.d.ts +24 -24
  150. package/lib/wav/WaveParser.js +156 -144
  151. package/lib/wavpack/WavPackParser.d.ts +14 -14
  152. package/lib/wavpack/WavPackParser.js +99 -105
  153. package/lib/wavpack/WavPackToken.d.ts +64 -64
  154. package/lib/wavpack/WavPackToken.js +76 -76
  155. package/package.json +150 -142
@@ -1,329 +1,329 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FrameParser = exports.parseGenre = void 0;
4
- const initDebug = require("debug");
5
- const Token = require("token-types");
6
- const util = require("../common/Util");
7
- const ID3v2Token_1 = require("./ID3v2Token");
8
- const ID3v1Parser_1 = require("../id3v1/ID3v1Parser");
9
- const debug = initDebug('music-metadata:id3v2:frame-parser');
10
- const defaultEnc = 'latin1'; // latin1 == iso-8859-1;
11
- function parseGenre(origVal) {
12
- // match everything inside parentheses
13
- const genres = [];
14
- let code;
15
- let word = '';
16
- for (const c of origVal) {
17
- if (typeof code === 'string') {
18
- if (c === '(' && code === '') {
19
- word += '(';
20
- code = undefined;
21
- }
22
- else if (c === ')') {
23
- if (word !== '') {
24
- genres.push(word);
25
- word = '';
26
- }
27
- const genre = parseGenreCode(code);
28
- if (genre) {
29
- genres.push(genre);
30
- }
31
- code = undefined;
32
- }
33
- else
34
- code += c;
35
- }
36
- else if (c === '(') {
37
- code = '';
38
- }
39
- else {
40
- word += c;
41
- }
42
- }
43
- if (word) {
44
- if (genres.length === 0 && word.match(/^\d*$/)) {
45
- word = ID3v1Parser_1.Genres[word];
46
- }
47
- genres.push(word);
48
- }
49
- return genres;
50
- }
51
- exports.parseGenre = parseGenre;
52
- function parseGenreCode(code) {
53
- if (code === 'RX')
54
- return 'Remix';
55
- if (code === 'CR')
56
- return 'Cover';
57
- if (code.match(/^\d*$/)) {
58
- return ID3v1Parser_1.Genres[code];
59
- }
60
- }
61
- class FrameParser {
62
- /**
63
- * Create id3v2 frame parser
64
- * @param major - Major version, e.g. (4) for id3v2.4
65
- * @param warningCollector - Used to collect decode issue
66
- */
67
- constructor(major, warningCollector) {
68
- this.major = major;
69
- this.warningCollector = warningCollector;
70
- }
71
- readData(b, type, includeCovers) {
72
- if (b.length === 0) {
73
- this.warningCollector.addWarning(`id3v2.${this.major} header has empty tag type=${type}`);
74
- return;
75
- }
76
- const { encoding, bom } = ID3v2Token_1.TextEncodingToken.get(b, 0);
77
- const length = b.length;
78
- let offset = 0;
79
- let output = []; // ToDo
80
- const nullTerminatorLength = FrameParser.getNullTerminatorLength(encoding);
81
- let fzero;
82
- const out = {};
83
- debug(`Parsing tag type=${type}, encoding=${encoding}, bom=${bom}`);
84
- switch (type !== 'TXXX' && type[0] === 'T' ? 'T*' : type) {
85
- case 'T*': // 4.2.1. Text information frames - details
86
- case 'IPLS': // v2.3: Involved people list
87
- case 'MVIN':
88
- case 'MVNM':
89
- case 'PCS':
90
- case 'PCST':
91
- let text;
92
- try {
93
- text = util.decodeString(b.subarray(1), encoding).replace(/\x00+$/, '');
94
- }
95
- catch (error) {
96
- this.warningCollector.addWarning(`id3v2.${this.major} type=${type} header has invalid string value: ${error.message}`);
97
- }
98
- switch (type) {
99
- case 'TMCL': // Musician credits list
100
- case 'TIPL': // Involved people list
101
- case 'IPLS': // Involved people list
102
- output = this.splitValue(type, text);
103
- output = FrameParser.functionList(output);
104
- break;
105
- case 'TRK':
106
- case 'TRCK':
107
- case 'TPOS':
108
- output = text;
109
- break;
110
- case 'TCOM':
111
- case 'TEXT':
112
- case 'TOLY':
113
- case 'TOPE':
114
- case 'TPE1':
115
- case 'TSRC':
116
- // id3v2.3 defines that TCOM, TEXT, TOLY, TOPE & TPE1 values are separated by /
117
- output = this.splitValue(type, text);
118
- break;
119
- case 'TCO':
120
- case 'TCON':
121
- output = this.splitValue(type, text).map(v => parseGenre(v)).reduce((acc, val) => acc.concat(val), []);
122
- break;
123
- case 'PCS':
124
- case 'PCST':
125
- // TODO: Why `default` not results `1` but `''`?
126
- output = this.major >= 4 ? this.splitValue(type, text) : [text];
127
- output = (Array.isArray(output) && output[0] === '') ? 1 : 0;
128
- break;
129
- default:
130
- output = this.major >= 4 ? this.splitValue(type, text) : [text];
131
- }
132
- break;
133
- case 'TXXX':
134
- output = FrameParser.readIdentifierAndData(b, offset + 1, length, encoding);
135
- output = {
136
- description: output.id,
137
- text: this.splitValue(type, util.decodeString(output.data, encoding).replace(/\x00+$/, ''))
138
- };
139
- break;
140
- case 'PIC':
141
- case 'APIC':
142
- if (includeCovers) {
143
- const pic = {};
144
- offset += 1;
145
- switch (this.major) {
146
- case 2:
147
- pic.format = util.decodeString(b.slice(offset, offset + 3), 'latin1'); // 'latin1'; // latin1 == iso-8859-1;
148
- offset += 3;
149
- break;
150
- case 3:
151
- case 4:
152
- fzero = util.findZero(b, offset, length, defaultEnc);
153
- pic.format = util.decodeString(b.slice(offset, fzero), defaultEnc);
154
- offset = fzero + 1;
155
- break;
156
- default:
157
- throw new Error('Warning: unexpected major versionIndex: ' + this.major);
158
- }
159
- pic.format = FrameParser.fixPictureMimeType(pic.format);
160
- pic.type = ID3v2Token_1.AttachedPictureType[b[offset]];
161
- offset += 1;
162
- fzero = util.findZero(b, offset, length, encoding);
163
- pic.description = util.decodeString(b.slice(offset, fzero), encoding);
164
- offset = fzero + nullTerminatorLength;
165
- pic.data = Buffer.from(b.slice(offset, length));
166
- output = pic;
167
- }
168
- break;
169
- case 'CNT':
170
- case 'PCNT':
171
- output = Token.UINT32_BE.get(b, 0);
172
- break;
173
- case 'SYLT':
174
- // skip text encoding (1 byte),
175
- // language (3 bytes),
176
- // time stamp format (1 byte),
177
- // content tagTypes (1 byte),
178
- // content descriptor (1 byte)
179
- offset += 7;
180
- output = [];
181
- while (offset < length) {
182
- const txt = b.slice(offset, offset = util.findZero(b, offset, length, encoding));
183
- offset += 5; // push offset forward one + 4 byte timestamp
184
- output.push(util.decodeString(txt, encoding));
185
- }
186
- break;
187
- case 'ULT':
188
- case 'USLT':
189
- case 'COM':
190
- case 'COMM':
191
- offset += 1;
192
- out.language = util.decodeString(b.slice(offset, offset + 3), defaultEnc);
193
- offset += 3;
194
- fzero = util.findZero(b, offset, length, encoding);
195
- out.description = util.decodeString(b.slice(offset, fzero), encoding);
196
- offset = fzero + nullTerminatorLength;
197
- out.text = util.decodeString(b.slice(offset, length), encoding).replace(/\x00+$/, '');
198
- output = [out];
199
- break;
200
- case 'UFID':
201
- output = FrameParser.readIdentifierAndData(b, offset, length, defaultEnc);
202
- output = { owner_identifier: output.id, identifier: output.data };
203
- break;
204
- case 'PRIV': // private frame
205
- output = FrameParser.readIdentifierAndData(b, offset, length, defaultEnc);
206
- output = { owner_identifier: output.id, data: output.data };
207
- break;
208
- case 'POPM': // Popularimeter
209
- fzero = util.findZero(b, offset, length, defaultEnc);
210
- const email = util.decodeString(b.slice(offset, fzero), defaultEnc);
211
- offset = fzero + 1;
212
- const dataLen = length - offset;
213
- output = {
214
- email,
215
- rating: b.readUInt8(offset),
216
- counter: dataLen >= 5 ? b.readUInt32BE(offset + 1) : undefined
217
- };
218
- break;
219
- case 'GEOB': { // General encapsulated object
220
- fzero = util.findZero(b, offset + 1, length, encoding);
221
- const mimeType = util.decodeString(b.slice(offset + 1, fzero), defaultEnc);
222
- offset = fzero + 1;
223
- fzero = util.findZero(b, offset, length - offset, encoding);
224
- const filename = util.decodeString(b.slice(offset, fzero), defaultEnc);
225
- offset = fzero + 1;
226
- fzero = util.findZero(b, offset, length - offset, encoding);
227
- const description = util.decodeString(b.slice(offset, fzero), defaultEnc);
228
- output = {
229
- type: mimeType,
230
- filename,
231
- description,
232
- data: b.slice(offset + 1, length)
233
- };
234
- break;
235
- }
236
- // W-Frames:
237
- case 'WCOM':
238
- case 'WCOP':
239
- case 'WOAF':
240
- case 'WOAR':
241
- case 'WOAS':
242
- case 'WORS':
243
- case 'WPAY':
244
- case 'WPUB':
245
- // Decode URL
246
- output = util.decodeString(b.slice(offset, fzero), defaultEnc);
247
- break;
248
- case 'WXXX': {
249
- // Decode URL
250
- fzero = util.findZero(b, offset + 1, length, encoding);
251
- const description = util.decodeString(b.slice(offset + 1, fzero), encoding);
252
- offset = fzero + (encoding === 'utf16le' ? 2 : 1);
253
- output = { description, url: util.decodeString(b.slice(offset, length), defaultEnc) };
254
- break;
255
- }
256
- case 'WFD':
257
- case 'WFED':
258
- output = util.decodeString(b.slice(offset + 1, util.findZero(b, offset + 1, length, encoding)), encoding);
259
- break;
260
- case 'MCDI': {
261
- // Music CD identifier
262
- output = b.slice(0, length);
263
- break;
264
- }
265
- default:
266
- debug('Warning: unsupported id3v2-tag-type: ' + type);
267
- break;
268
- }
269
- return output;
270
- }
271
- static fixPictureMimeType(pictureType) {
272
- pictureType = pictureType.toLocaleLowerCase();
273
- switch (pictureType) {
274
- case 'jpg':
275
- return 'image/jpeg';
276
- case 'png':
277
- return 'image/png';
278
- }
279
- return pictureType;
280
- }
281
- /**
282
- * Converts TMCL (Musician credits list) or TIPL (Involved people list)
283
- * @param entries
284
- */
285
- static functionList(entries) {
286
- const res = {};
287
- for (let i = 0; i + 1 < entries.length; i += 2) {
288
- const names = entries[i + 1].split(',');
289
- res[entries[i]] = res.hasOwnProperty(entries[i]) ? res[entries[i]].concat(names) : names;
290
- }
291
- return res;
292
- }
293
- /**
294
- * id3v2.4 defines that multiple T* values are separated by 0x00
295
- * id3v2.3 defines that TCOM, TEXT, TOLY, TOPE & TPE1 values are separated by /
296
- * @param tag - Tag name
297
- * @param text - Concatenated tag value
298
- * @returns Split tag value
299
- */
300
- splitValue(tag, text) {
301
- let values;
302
- if (this.major < 4) {
303
- values = text.split(/\x00/g);
304
- if (values.length > 1) {
305
- this.warningCollector.addWarning(`ID3v2.${this.major} ${tag} uses non standard null-separator.`);
306
- }
307
- else {
308
- values = text.split(/\//g);
309
- }
310
- }
311
- else {
312
- values = text.split(/\x00/g);
313
- }
314
- return FrameParser.trimArray(values);
315
- }
316
- static trimArray(values) {
317
- return values.map(value => value.replace(/\x00+$/, '').trim());
318
- }
319
- static readIdentifierAndData(b, offset, length, encoding) {
320
- const fzero = util.findZero(b, offset, length, encoding);
321
- const id = util.decodeString(b.slice(offset, fzero), encoding);
322
- offset = fzero + FrameParser.getNullTerminatorLength(encoding);
323
- return { id, data: b.slice(offset, length) };
324
- }
325
- static getNullTerminatorLength(enc) {
326
- return enc === 'utf16le' ? 2 : 1;
327
- }
328
- }
329
- exports.FrameParser = FrameParser;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FrameParser = exports.parseGenre = void 0;
4
+ const initDebug = require("debug");
5
+ const Token = require("token-types");
6
+ const util = require("../common/Util");
7
+ const ID3v2Token_1 = require("./ID3v2Token");
8
+ const ID3v1Parser_1 = require("../id3v1/ID3v1Parser");
9
+ const debug = initDebug('music-metadata:id3v2:frame-parser');
10
+ const defaultEnc = 'latin1'; // latin1 == iso-8859-1;
11
+ function parseGenre(origVal) {
12
+ // match everything inside parentheses
13
+ const genres = [];
14
+ let code;
15
+ let word = '';
16
+ for (const c of origVal) {
17
+ if (typeof code === 'string') {
18
+ if (c === '(' && code === '') {
19
+ word += '(';
20
+ code = undefined;
21
+ }
22
+ else if (c === ')') {
23
+ if (word !== '') {
24
+ genres.push(word);
25
+ word = '';
26
+ }
27
+ const genre = parseGenreCode(code);
28
+ if (genre) {
29
+ genres.push(genre);
30
+ }
31
+ code = undefined;
32
+ }
33
+ else
34
+ code += c;
35
+ }
36
+ else if (c === '(') {
37
+ code = '';
38
+ }
39
+ else {
40
+ word += c;
41
+ }
42
+ }
43
+ if (word) {
44
+ if (genres.length === 0 && word.match(/^\d*$/)) {
45
+ word = ID3v1Parser_1.Genres[word];
46
+ }
47
+ genres.push(word);
48
+ }
49
+ return genres;
50
+ }
51
+ exports.parseGenre = parseGenre;
52
+ function parseGenreCode(code) {
53
+ if (code === 'RX')
54
+ return 'Remix';
55
+ if (code === 'CR')
56
+ return 'Cover';
57
+ if (code.match(/^\d*$/)) {
58
+ return ID3v1Parser_1.Genres[code];
59
+ }
60
+ }
61
+ class FrameParser {
62
+ /**
63
+ * Create id3v2 frame parser
64
+ * @param major - Major version, e.g. (4) for id3v2.4
65
+ * @param warningCollector - Used to collect decode issue
66
+ */
67
+ constructor(major, warningCollector) {
68
+ this.major = major;
69
+ this.warningCollector = warningCollector;
70
+ }
71
+ readData(b, type, includeCovers) {
72
+ if (b.length === 0) {
73
+ this.warningCollector.addWarning(`id3v2.${this.major} header has empty tag type=${type}`);
74
+ return;
75
+ }
76
+ const { encoding, bom } = ID3v2Token_1.TextEncodingToken.get(b, 0);
77
+ const length = b.length;
78
+ let offset = 0;
79
+ let output = []; // ToDo
80
+ const nullTerminatorLength = FrameParser.getNullTerminatorLength(encoding);
81
+ let fzero;
82
+ const out = {};
83
+ debug(`Parsing tag type=${type}, encoding=${encoding}, bom=${bom}`);
84
+ switch (type !== 'TXXX' && type[0] === 'T' ? 'T*' : type) {
85
+ case 'T*': // 4.2.1. Text information frames - details
86
+ case 'IPLS': // v2.3: Involved people list
87
+ case 'MVIN':
88
+ case 'MVNM':
89
+ case 'PCS':
90
+ case 'PCST':
91
+ let text;
92
+ try {
93
+ text = util.decodeString(b.subarray(1), encoding).replace(/\x00+$/, '');
94
+ }
95
+ catch (error) {
96
+ this.warningCollector.addWarning(`id3v2.${this.major} type=${type} header has invalid string value: ${error.message}`);
97
+ }
98
+ switch (type) {
99
+ case 'TMCL': // Musician credits list
100
+ case 'TIPL': // Involved people list
101
+ case 'IPLS': // Involved people list
102
+ output = this.splitValue(type, text);
103
+ output = FrameParser.functionList(output);
104
+ break;
105
+ case 'TRK':
106
+ case 'TRCK':
107
+ case 'TPOS':
108
+ output = text;
109
+ break;
110
+ case 'TCOM':
111
+ case 'TEXT':
112
+ case 'TOLY':
113
+ case 'TOPE':
114
+ case 'TPE1':
115
+ case 'TSRC':
116
+ // id3v2.3 defines that TCOM, TEXT, TOLY, TOPE & TPE1 values are separated by /
117
+ output = this.splitValue(type, text);
118
+ break;
119
+ case 'TCO':
120
+ case 'TCON':
121
+ output = this.splitValue(type, text).map(v => parseGenre(v)).reduce((acc, val) => acc.concat(val), []);
122
+ break;
123
+ case 'PCS':
124
+ case 'PCST':
125
+ // TODO: Why `default` not results `1` but `''`?
126
+ output = this.major >= 4 ? this.splitValue(type, text) : [text];
127
+ output = (Array.isArray(output) && output[0] === '') ? 1 : 0;
128
+ break;
129
+ default:
130
+ output = this.major >= 4 ? this.splitValue(type, text) : [text];
131
+ }
132
+ break;
133
+ case 'TXXX':
134
+ output = FrameParser.readIdentifierAndData(b, offset + 1, length, encoding);
135
+ output = {
136
+ description: output.id,
137
+ text: this.splitValue(type, util.decodeString(output.data, encoding).replace(/\x00+$/, ''))
138
+ };
139
+ break;
140
+ case 'PIC':
141
+ case 'APIC':
142
+ if (includeCovers) {
143
+ const pic = {};
144
+ offset += 1;
145
+ switch (this.major) {
146
+ case 2:
147
+ pic.format = util.decodeString(b.slice(offset, offset + 3), 'latin1'); // 'latin1'; // latin1 == iso-8859-1;
148
+ offset += 3;
149
+ break;
150
+ case 3:
151
+ case 4:
152
+ fzero = util.findZero(b, offset, length, defaultEnc);
153
+ pic.format = util.decodeString(b.slice(offset, fzero), defaultEnc);
154
+ offset = fzero + 1;
155
+ break;
156
+ default:
157
+ throw new Error('Warning: unexpected major versionIndex: ' + this.major);
158
+ }
159
+ pic.format = FrameParser.fixPictureMimeType(pic.format);
160
+ pic.type = ID3v2Token_1.AttachedPictureType[b[offset]];
161
+ offset += 1;
162
+ fzero = util.findZero(b, offset, length, encoding);
163
+ pic.description = util.decodeString(b.slice(offset, fzero), encoding);
164
+ offset = fzero + nullTerminatorLength;
165
+ pic.data = Buffer.from(b.slice(offset, length));
166
+ output = pic;
167
+ }
168
+ break;
169
+ case 'CNT':
170
+ case 'PCNT':
171
+ output = Token.UINT32_BE.get(b, 0);
172
+ break;
173
+ case 'SYLT':
174
+ // skip text encoding (1 byte),
175
+ // language (3 bytes),
176
+ // time stamp format (1 byte),
177
+ // content tagTypes (1 byte),
178
+ // content descriptor (1 byte)
179
+ offset += 7;
180
+ output = [];
181
+ while (offset < length) {
182
+ const txt = b.slice(offset, offset = util.findZero(b, offset, length, encoding));
183
+ offset += 5; // push offset forward one + 4 byte timestamp
184
+ output.push(util.decodeString(txt, encoding));
185
+ }
186
+ break;
187
+ case 'ULT':
188
+ case 'USLT':
189
+ case 'COM':
190
+ case 'COMM':
191
+ offset += 1;
192
+ out.language = util.decodeString(b.slice(offset, offset + 3), defaultEnc);
193
+ offset += 3;
194
+ fzero = util.findZero(b, offset, length, encoding);
195
+ out.description = util.decodeString(b.slice(offset, fzero), encoding);
196
+ offset = fzero + nullTerminatorLength;
197
+ out.text = util.decodeString(b.slice(offset, length), encoding).replace(/\x00+$/, '');
198
+ output = [out];
199
+ break;
200
+ case 'UFID':
201
+ output = FrameParser.readIdentifierAndData(b, offset, length, defaultEnc);
202
+ output = { owner_identifier: output.id, identifier: output.data };
203
+ break;
204
+ case 'PRIV': // private frame
205
+ output = FrameParser.readIdentifierAndData(b, offset, length, defaultEnc);
206
+ output = { owner_identifier: output.id, data: output.data };
207
+ break;
208
+ case 'POPM': // Popularimeter
209
+ fzero = util.findZero(b, offset, length, defaultEnc);
210
+ const email = util.decodeString(b.slice(offset, fzero), defaultEnc);
211
+ offset = fzero + 1;
212
+ const dataLen = length - offset;
213
+ output = {
214
+ email,
215
+ rating: b.readUInt8(offset),
216
+ counter: dataLen >= 5 ? b.readUInt32BE(offset + 1) : undefined
217
+ };
218
+ break;
219
+ case 'GEOB': { // General encapsulated object
220
+ fzero = util.findZero(b, offset + 1, length, encoding);
221
+ const mimeType = util.decodeString(b.slice(offset + 1, fzero), defaultEnc);
222
+ offset = fzero + 1;
223
+ fzero = util.findZero(b, offset, length - offset, encoding);
224
+ const filename = util.decodeString(b.slice(offset, fzero), defaultEnc);
225
+ offset = fzero + 1;
226
+ fzero = util.findZero(b, offset, length - offset, encoding);
227
+ const description = util.decodeString(b.slice(offset, fzero), defaultEnc);
228
+ output = {
229
+ type: mimeType,
230
+ filename,
231
+ description,
232
+ data: b.slice(offset + 1, length)
233
+ };
234
+ break;
235
+ }
236
+ // W-Frames:
237
+ case 'WCOM':
238
+ case 'WCOP':
239
+ case 'WOAF':
240
+ case 'WOAR':
241
+ case 'WOAS':
242
+ case 'WORS':
243
+ case 'WPAY':
244
+ case 'WPUB':
245
+ // Decode URL
246
+ output = util.decodeString(b.slice(offset, fzero), defaultEnc);
247
+ break;
248
+ case 'WXXX': {
249
+ // Decode URL
250
+ fzero = util.findZero(b, offset + 1, length, encoding);
251
+ const description = util.decodeString(b.slice(offset + 1, fzero), encoding);
252
+ offset = fzero + (encoding === 'utf16le' ? 2 : 1);
253
+ output = { description, url: util.decodeString(b.slice(offset, length), defaultEnc) };
254
+ break;
255
+ }
256
+ case 'WFD':
257
+ case 'WFED':
258
+ output = util.decodeString(b.slice(offset + 1, util.findZero(b, offset + 1, length, encoding)), encoding);
259
+ break;
260
+ case 'MCDI': {
261
+ // Music CD identifier
262
+ output = b.slice(0, length);
263
+ break;
264
+ }
265
+ default:
266
+ debug('Warning: unsupported id3v2-tag-type: ' + type);
267
+ break;
268
+ }
269
+ return output;
270
+ }
271
+ static fixPictureMimeType(pictureType) {
272
+ pictureType = pictureType.toLocaleLowerCase();
273
+ switch (pictureType) {
274
+ case 'jpg':
275
+ return 'image/jpeg';
276
+ case 'png':
277
+ return 'image/png';
278
+ }
279
+ return pictureType;
280
+ }
281
+ /**
282
+ * Converts TMCL (Musician credits list) or TIPL (Involved people list)
283
+ * @param entries
284
+ */
285
+ static functionList(entries) {
286
+ const res = {};
287
+ for (let i = 0; i + 1 < entries.length; i += 2) {
288
+ const names = entries[i + 1].split(',');
289
+ res[entries[i]] = res.hasOwnProperty(entries[i]) ? res[entries[i]].concat(names) : names;
290
+ }
291
+ return res;
292
+ }
293
+ /**
294
+ * id3v2.4 defines that multiple T* values are separated by 0x00
295
+ * id3v2.3 defines that TCOM, TEXT, TOLY, TOPE & TPE1 values are separated by /
296
+ * @param tag - Tag name
297
+ * @param text - Concatenated tag value
298
+ * @returns Split tag value
299
+ */
300
+ splitValue(tag, text) {
301
+ let values;
302
+ if (this.major < 4) {
303
+ values = text.split(/\x00/g);
304
+ if (values.length > 1) {
305
+ this.warningCollector.addWarning(`ID3v2.${this.major} ${tag} uses non standard null-separator.`);
306
+ }
307
+ else {
308
+ values = text.split(/\//g);
309
+ }
310
+ }
311
+ else {
312
+ values = text.split(/\x00/g);
313
+ }
314
+ return FrameParser.trimArray(values);
315
+ }
316
+ static trimArray(values) {
317
+ return values.map(value => value.replace(/\x00+$/, '').trim());
318
+ }
319
+ static readIdentifierAndData(b, offset, length, encoding) {
320
+ const fzero = util.findZero(b, offset, length, encoding);
321
+ const id = util.decodeString(b.slice(offset, fzero), encoding);
322
+ offset = fzero + FrameParser.getNullTerminatorLength(encoding);
323
+ return { id, data: b.slice(offset, length) };
324
+ }
325
+ static getNullTerminatorLength(enc) {
326
+ return enc === 'utf16le' ? 2 : 1;
327
+ }
328
+ }
329
+ exports.FrameParser = FrameParser;