music-metadata 7.12.6 → 8.0.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/README.md +47 -31
- package/lib/ParserFactory.js +37 -41
- package/lib/aiff/AiffParser.js +12 -16
- package/lib/aiff/AiffToken.js +4 -8
- package/lib/apev2/APEv2Parser.js +29 -32
- package/lib/apev2/APEv2TagMapper.js +2 -6
- package/lib/apev2/APEv2Token.js +13 -19
- package/lib/asf/AsfObject.js +42 -56
- package/lib/asf/AsfParser.js +15 -19
- package/lib/asf/AsfTagMapper.js +2 -6
- package/lib/asf/AsfUtil.js +4 -7
- package/lib/asf/GUID.js +1 -4
- package/lib/common/BasicParser.js +1 -5
- package/lib/common/CaseInsensitiveTagMap.js +2 -6
- package/lib/common/CombinedTagMapper.js +20 -24
- package/lib/common/FourCC.js +3 -6
- package/lib/common/GenericTagMapper.js +2 -6
- package/lib/common/GenericTagTypes.js +5 -10
- package/lib/common/MetadataCollector.js +20 -25
- package/lib/common/RandomFileReader.js +2 -6
- package/lib/common/RandomUint8ArrayReader.js +1 -5
- package/lib/common/Util.js +11 -25
- package/lib/core.js +18 -28
- package/lib/dsdiff/DsdiffParser.js +24 -28
- package/lib/dsdiff/DsdiffToken.js +4 -7
- package/lib/dsf/DsfChunk.js +8 -11
- package/lib/dsf/DsfParser.js +13 -17
- package/lib/flac/FlacParser.js +22 -26
- package/lib/id3v1/ID3v1Parser.js +16 -21
- package/lib/id3v1/ID3v1TagMap.js +2 -6
- package/lib/id3v2/AbstractID3Parser.js +13 -17
- package/lib/id3v2/FrameParser.js +12 -17
- package/lib/id3v2/ID3v22TagMapper.js +4 -8
- package/lib/id3v2/ID3v24TagMapper.js +5 -9
- package/lib/id3v2/ID3v2Parser.js +10 -14
- package/lib/id3v2/ID3v2Token.js +9 -12
- package/lib/iff/index.js +4 -7
- package/lib/index.js +14 -44
- package/lib/lyrics3/Lyrics3.js +3 -7
- package/lib/matroska/MatroskaDtd.js +111 -114
- package/lib/matroska/MatroskaParser.js +20 -24
- package/lib/matroska/MatroskaTagMapper.js +2 -6
- package/lib/matroska/types.js +6 -9
- package/lib/mp4/Atom.js +4 -8
- package/lib/mp4/AtomToken.js +29 -44
- package/lib/mp4/MP4Parser.js +12 -16
- package/lib/mp4/MP4TagMapper.js +4 -8
- package/lib/mpeg/ExtendedLameHeader.js +6 -9
- package/lib/mpeg/MpegParser.js +17 -21
- package/lib/mpeg/ReplayGainDataFormat.js +2 -5
- package/lib/mpeg/XingTag.js +9 -13
- package/lib/musepack/index.js +10 -12
- package/lib/musepack/sv7/BitReader.js +2 -6
- package/lib/musepack/sv7/MpcSv7Parser.js +9 -13
- package/lib/musepack/sv7/StreamVersion7.js +3 -6
- package/lib/musepack/sv8/MpcSv8Parser.js +9 -13
- package/lib/musepack/sv8/StreamVersion8.js +5 -9
- package/lib/ogg/Ogg.js +1 -2
- package/lib/ogg/OggParser.js +19 -24
- package/lib/ogg/opus/Opus.js +2 -6
- package/lib/ogg/opus/OpusParser.js +4 -8
- package/lib/ogg/speex/Speex.js +3 -6
- package/lib/ogg/speex/SpeexParser.js +5 -9
- package/lib/ogg/theora/Theora.js +2 -5
- package/lib/ogg/theora/TheoraParser.js +5 -9
- package/lib/ogg/vorbis/Vorbis.js +6 -10
- package/lib/ogg/vorbis/VorbisDecoder.js +2 -6
- package/lib/ogg/vorbis/VorbisParser.js +18 -22
- package/lib/ogg/vorbis/VorbisTagMapper.js +3 -7
- package/lib/riff/RiffChunk.js +3 -7
- package/lib/riff/RiffInfoTagMap.js +4 -8
- package/lib/type.js +1 -5
- package/lib/wav/BwfChunk.js +8 -11
- package/lib/wav/WaveChunk.js +4 -9
- package/lib/wav/WaveParser.js +17 -21
- package/lib/wavpack/WavPackParser.js +17 -21
- package/lib/wavpack/WavPackToken.js +4 -8
- package/package.json +23 -33
- package/lib/ParserFactory.d.ts +0 -48
- package/lib/aiff/AiffParser.d.ts +0 -14
- package/lib/aiff/AiffToken.d.ts +0 -22
- package/lib/apev2/APEv2Parser.d.ts +0 -30
- package/lib/apev2/APEv2TagMapper.d.ts +0 -4
- package/lib/apev2/APEv2Token.d.ts +0 -100
- package/lib/asf/AsfObject.d.ts +0 -319
- package/lib/asf/AsfParser.d.ts +0 -17
- package/lib/asf/AsfTagMapper.d.ts +0 -7
- package/lib/asf/AsfUtil.d.ts +0 -13
- package/lib/asf/GUID.d.ts +0 -84
- package/lib/common/BasicParser.d.ts +0 -17
- package/lib/common/CaseInsensitiveTagMap.d.ts +0 -10
- package/lib/common/CombinedTagMapper.d.ts +0 -19
- package/lib/common/FourCC.d.ts +0 -6
- package/lib/common/GenericTagMapper.d.ts +0 -51
- package/lib/common/GenericTagTypes.d.ts +0 -33
- package/lib/common/MetadataCollector.d.ts +0 -76
- package/lib/common/RandomFileReader.d.ts +0 -22
- package/lib/common/RandomUint8ArrayReader.d.ts +0 -18
- package/lib/common/Util.d.ts +0 -57
- package/lib/core.d.ts +0 -48
- package/lib/dsdiff/DsdiffParser.d.ts +0 -14
- package/lib/dsdiff/DsdiffToken.d.ts +0 -9
- package/lib/dsf/DsfChunk.d.ts +0 -86
- package/lib/dsf/DsfParser.d.ts +0 -9
- package/lib/flac/FlacParser.d.ts +0 -28
- package/lib/id3v1/ID3v1Parser.d.ts +0 -13
- package/lib/id3v1/ID3v1TagMap.d.ts +0 -4
- package/lib/id3v2/AbstractID3Parser.d.ts +0 -17
- package/lib/id3v2/FrameParser.d.ts +0 -31
- package/lib/id3v2/ID3v22TagMapper.d.ts +0 -9
- package/lib/id3v2/ID3v24TagMapper.d.ts +0 -14
- package/lib/id3v2/ID3v2Parser.d.ts +0 -28
- package/lib/id3v2/ID3v2Token.d.ts +0 -73
- package/lib/iff/index.d.ts +0 -33
- package/lib/index.d.ts +0 -45
- package/lib/lyrics3/Lyrics3.d.ts +0 -3
- package/lib/matroska/MatroskaDtd.d.ts +0 -8
- package/lib/matroska/MatroskaParser.d.ts +0 -37
- package/lib/matroska/MatroskaTagMapper.d.ts +0 -4
- package/lib/matroska/types.d.ts +0 -175
- package/lib/mp4/Atom.d.ts +0 -16
- package/lib/mp4/AtomToken.d.ts +0 -395
- package/lib/mp4/MP4Parser.d.ts +0 -30
- package/lib/mp4/MP4TagMapper.d.ts +0 -5
- package/lib/mpeg/ExtendedLameHeader.d.ts +0 -27
- package/lib/mpeg/MpegParser.d.ts +0 -49
- package/lib/mpeg/ReplayGainDataFormat.d.ts +0 -55
- package/lib/mpeg/XingTag.d.ts +0 -45
- package/lib/musepack/index.d.ts +0 -5
- package/lib/musepack/sv7/BitReader.d.ts +0 -13
- package/lib/musepack/sv7/MpcSv7Parser.d.ts +0 -8
- package/lib/musepack/sv7/StreamVersion7.d.ts +0 -28
- package/lib/musepack/sv8/MpcSv8Parser.d.ts +0 -6
- package/lib/musepack/sv8/StreamVersion8.d.ts +0 -40
- package/lib/ogg/Ogg.d.ts +0 -72
- package/lib/ogg/OggParser.d.ts +0 -23
- package/lib/ogg/opus/Opus.d.ts +0 -48
- package/lib/ogg/opus/OpusParser.d.ts +0 -25
- package/lib/ogg/speex/Speex.d.ts +0 -36
- package/lib/ogg/speex/SpeexParser.d.ts +0 -22
- package/lib/ogg/theora/Theora.d.ts +0 -20
- package/lib/ogg/theora/TheoraParser.d.ts +0 -28
- package/lib/ogg/vorbis/Vorbis.d.ts +0 -69
- package/lib/ogg/vorbis/VorbisDecoder.d.ts +0 -12
- package/lib/ogg/vorbis/VorbisParser.d.ts +0 -36
- package/lib/ogg/vorbis/VorbisTagMapper.d.ts +0 -7
- package/lib/riff/RiffChunk.d.ts +0 -16
- package/lib/riff/RiffInfoTagMap.d.ts +0 -10
- package/lib/type.d.ts +0 -592
- package/lib/wav/BwfChunk.d.ts +0 -17
- package/lib/wav/WaveChunk.d.ts +0 -64
- package/lib/wav/WaveParser.d.ts +0 -24
- package/lib/wavpack/WavPackParser.d.ts +0 -14
- package/lib/wavpack/WavPackToken.d.ts +0 -64
package/lib/flac/FlacParser.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const VorbisParser_1 = require("../ogg/vorbis/VorbisParser");
|
|
11
|
-
const VorbisDecoder_1 = require("../ogg/vorbis/VorbisDecoder");
|
|
12
|
-
const debug = (0, debug_1.default)('music-metadata:parser:FLAC');
|
|
1
|
+
import { UINT16_BE, UINT24_BE, Uint8ArrayType } from 'token-types';
|
|
2
|
+
import initDebug from 'debug';
|
|
3
|
+
import * as util from '../common/Util.js';
|
|
4
|
+
import { VorbisPictureToken } from '../ogg/vorbis/Vorbis.js';
|
|
5
|
+
import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
|
|
6
|
+
import { FourCcToken } from '../common/FourCC.js';
|
|
7
|
+
import { VorbisParser } from '../ogg/vorbis/VorbisParser.js';
|
|
8
|
+
import { VorbisDecoder } from '../ogg/vorbis/VorbisDecoder.js';
|
|
9
|
+
const debug = initDebug('music-metadata:parser:FLAC');
|
|
13
10
|
/**
|
|
14
11
|
* FLAC supports up to 128 kinds of metadata blocks; currently the following are defined:
|
|
15
12
|
* ref: https://xiph.org/flac/format.html#metadata_block
|
|
@@ -24,7 +21,7 @@ var BlockType;
|
|
|
24
21
|
BlockType[BlockType["CUESHEET"] = 5] = "CUESHEET";
|
|
25
22
|
BlockType[BlockType["PICTURE"] = 6] = "PICTURE";
|
|
26
23
|
})(BlockType || (BlockType = {}));
|
|
27
|
-
class FlacParser extends
|
|
24
|
+
export class FlacParser extends AbstractID3Parser {
|
|
28
25
|
constructor() {
|
|
29
26
|
super(...arguments);
|
|
30
27
|
this.padding = 0;
|
|
@@ -37,11 +34,11 @@ class FlacParser extends AbstractID3Parser_1.AbstractID3Parser {
|
|
|
37
34
|
*/
|
|
38
35
|
init(metadata, tokenizer, options) {
|
|
39
36
|
super.init(metadata, tokenizer, options);
|
|
40
|
-
this.vorbisParser = new
|
|
37
|
+
this.vorbisParser = new VorbisParser(metadata, options);
|
|
41
38
|
return this;
|
|
42
39
|
}
|
|
43
40
|
async postId3v2Parse() {
|
|
44
|
-
const fourCC = await this.tokenizer.readToken(
|
|
41
|
+
const fourCC = await this.tokenizer.readToken(FourCcToken);
|
|
45
42
|
if (fourCC.toString() !== 'fLaC') {
|
|
46
43
|
throw new Error('Invalid FLAC preamble');
|
|
47
44
|
}
|
|
@@ -103,8 +100,8 @@ class FlacParser extends AbstractID3Parser_1.AbstractID3Parser {
|
|
|
103
100
|
* Ref: https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-640004.2.3
|
|
104
101
|
*/
|
|
105
102
|
async parseComment(dataLen) {
|
|
106
|
-
const data = await this.tokenizer.readToken(new
|
|
107
|
-
const decoder = new
|
|
103
|
+
const data = await this.tokenizer.readToken(new Uint8ArrayType(dataLen));
|
|
104
|
+
const decoder = new VorbisDecoder(data, 0);
|
|
108
105
|
decoder.readStringUtf8(); // vendor (skip)
|
|
109
106
|
const commentListLength = decoder.readInt32();
|
|
110
107
|
for (let i = 0; i < commentListLength; i++) {
|
|
@@ -117,12 +114,11 @@ class FlacParser extends AbstractID3Parser_1.AbstractID3Parser {
|
|
|
117
114
|
return this.tokenizer.ignore(dataLen);
|
|
118
115
|
}
|
|
119
116
|
else {
|
|
120
|
-
const picture = await this.tokenizer.readToken(new
|
|
117
|
+
const picture = await this.tokenizer.readToken(new VorbisPictureToken(dataLen));
|
|
121
118
|
this.vorbisParser.addTag('METADATA_BLOCK_PICTURE', picture);
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
|
-
exports.FlacParser = FlacParser;
|
|
126
122
|
class Metadata {
|
|
127
123
|
}
|
|
128
124
|
Metadata.BlockHeader = {
|
|
@@ -131,7 +127,7 @@ Metadata.BlockHeader = {
|
|
|
131
127
|
return {
|
|
132
128
|
lastBlock: util.getBit(buf, off, 7),
|
|
133
129
|
type: util.getBitAllignedNumber(buf, off, 1, 7),
|
|
134
|
-
length:
|
|
130
|
+
length: UINT24_BE.get(buf, off + 1)
|
|
135
131
|
};
|
|
136
132
|
}
|
|
137
133
|
};
|
|
@@ -144,20 +140,20 @@ Metadata.BlockStreamInfo = {
|
|
|
144
140
|
get: (buf, off) => {
|
|
145
141
|
return {
|
|
146
142
|
// The minimum block size (in samples) used in the stream.
|
|
147
|
-
minimumBlockSize:
|
|
143
|
+
minimumBlockSize: UINT16_BE.get(buf, off),
|
|
148
144
|
// The maximum block size (in samples) used in the stream.
|
|
149
145
|
// (Minimum blocksize == maximum blocksize) implies a fixed-blocksize stream.
|
|
150
|
-
maximumBlockSize:
|
|
146
|
+
maximumBlockSize: UINT16_BE.get(buf, off + 2) / 1000,
|
|
151
147
|
// The minimum frame size (in bytes) used in the stream.
|
|
152
148
|
// May be 0 to imply the value is not known.
|
|
153
|
-
minimumFrameSize:
|
|
149
|
+
minimumFrameSize: UINT24_BE.get(buf, off + 4),
|
|
154
150
|
// The maximum frame size (in bytes) used in the stream.
|
|
155
151
|
// May be 0 to imply the value is not known.
|
|
156
|
-
maximumFrameSize:
|
|
152
|
+
maximumFrameSize: UINT24_BE.get(buf, off + 7),
|
|
157
153
|
// Sample rate in Hz. Though 20 bits are available,
|
|
158
154
|
// the maximum sample rate is limited by the structure of frame headers to 655350Hz.
|
|
159
155
|
// Also, a value of 0 is invalid.
|
|
160
|
-
sampleRate:
|
|
156
|
+
sampleRate: UINT24_BE.get(buf, off + 10) >> 4,
|
|
161
157
|
// probably slower: sampleRate: common.getBitAllignedNumber(buf, off + 10, 0, 20),
|
|
162
158
|
// (number of channels)-1. FLAC supports from 1 to 8 channels
|
|
163
159
|
channels: util.getBitAllignedNumber(buf, off + 12, 4, 3) + 1,
|
|
@@ -169,7 +165,7 @@ Metadata.BlockStreamInfo = {
|
|
|
169
165
|
// A value of zero here means the number of total samples is unknown.
|
|
170
166
|
totalSamples: util.getBitAllignedNumber(buf, off + 13, 4, 36),
|
|
171
167
|
// the MD5 hash of the file (see notes for usage... it's a littly tricky)
|
|
172
|
-
fileMD5: new
|
|
168
|
+
fileMD5: new Uint8ArrayType(16).get(buf, off + 18)
|
|
173
169
|
};
|
|
174
170
|
}
|
|
175
171
|
};
|
package/lib/id3v1/ID3v1Parser.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const BasicParser_1 = require("../common/BasicParser");
|
|
8
|
-
const APEv2Parser_1 = require("../apev2/APEv2Parser");
|
|
9
|
-
const debug = (0, debug_1.default)('music-metadata:parser:ID3v1');
|
|
1
|
+
import initDebug from 'debug';
|
|
2
|
+
import { StringType, UINT8 } from 'token-types';
|
|
3
|
+
import * as util from '../common/Util.js';
|
|
4
|
+
import { BasicParser } from '../common/BasicParser.js';
|
|
5
|
+
import { APEv2Parser } from '../apev2/APEv2Parser.js';
|
|
6
|
+
const debug = initDebug('music-metadata:parser:ID3v1');
|
|
10
7
|
/**
|
|
11
8
|
* ID3v1 Genre mappings
|
|
12
9
|
* Ref: https://de.wikipedia.org/wiki/Liste_der_ID3v1-Genres
|
|
13
10
|
*/
|
|
14
|
-
|
|
11
|
+
export const Genres = [
|
|
15
12
|
'Blues', 'Classic Rock', 'Country', 'Dance', 'Disco', 'Funk', 'Grunge', 'Hip-Hop',
|
|
16
13
|
'Jazz', 'Metal', 'New Age', 'Oldies', 'Other', 'Pop', 'R&B', 'Rap', 'Reggae', 'Rock',
|
|
17
14
|
'Techno', 'Industrial', 'Alternative', 'Ska', 'Death Metal', 'Pranks', 'Soundtrack',
|
|
@@ -62,14 +59,14 @@ const Iid3v1Token = {
|
|
|
62
59
|
year: new Id3v1StringType(4).get(buf, off + 93),
|
|
63
60
|
comment: new Id3v1StringType(28).get(buf, off + 97),
|
|
64
61
|
// ID3v1.1 separator for track
|
|
65
|
-
zeroByte:
|
|
62
|
+
zeroByte: UINT8.get(buf, off + 127),
|
|
66
63
|
// track: ID3v1.1 field added by Michael Mutschler
|
|
67
|
-
track:
|
|
68
|
-
genre:
|
|
64
|
+
track: UINT8.get(buf, off + 126),
|
|
65
|
+
genre: UINT8.get(buf, off + 127)
|
|
69
66
|
} : null;
|
|
70
67
|
}
|
|
71
68
|
};
|
|
72
|
-
class Id3v1StringType extends
|
|
69
|
+
class Id3v1StringType extends StringType {
|
|
73
70
|
constructor(len) {
|
|
74
71
|
super(len, 'binary');
|
|
75
72
|
}
|
|
@@ -80,10 +77,10 @@ class Id3v1StringType extends token_types_1.StringType {
|
|
|
80
77
|
return value.length > 0 ? value : undefined;
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
|
-
class ID3v1Parser extends
|
|
80
|
+
export class ID3v1Parser extends BasicParser {
|
|
84
81
|
static getGenre(genreIndex) {
|
|
85
|
-
if (genreIndex <
|
|
86
|
-
return
|
|
82
|
+
if (genreIndex < Genres.length) {
|
|
83
|
+
return Genres[genreIndex];
|
|
87
84
|
}
|
|
88
85
|
return undefined; // ToDO: generate warning
|
|
89
86
|
}
|
|
@@ -94,7 +91,7 @@ class ID3v1Parser extends BasicParser_1.BasicParser {
|
|
|
94
91
|
}
|
|
95
92
|
if (this.options.apeHeader) {
|
|
96
93
|
this.tokenizer.ignore(this.options.apeHeader.offset - this.tokenizer.position);
|
|
97
|
-
const apeParser = new
|
|
94
|
+
const apeParser = new APEv2Parser();
|
|
98
95
|
apeParser.init(this.metadata, this.tokenizer, this.options);
|
|
99
96
|
await apeParser.parseTags(this.options.apeHeader.footer);
|
|
100
97
|
}
|
|
@@ -122,8 +119,7 @@ class ID3v1Parser extends BasicParser_1.BasicParser {
|
|
|
122
119
|
this.metadata.addTag('ID3v1', id, value);
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
|
-
|
|
126
|
-
async function hasID3v1Header(reader) {
|
|
122
|
+
export async function hasID3v1Header(reader) {
|
|
127
123
|
if (reader.fileSize >= 128) {
|
|
128
124
|
const tag = Buffer.alloc(3);
|
|
129
125
|
await reader.randomRead(tag, 0, tag.length, reader.fileSize - 128);
|
|
@@ -131,4 +127,3 @@ async function hasID3v1Header(reader) {
|
|
|
131
127
|
}
|
|
132
128
|
return false;
|
|
133
129
|
}
|
|
134
|
-
exports.hasID3v1Header = hasID3v1Header;
|
package/lib/id3v1/ID3v1TagMap.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ID3v1TagMapper = void 0;
|
|
4
|
-
const GenericTagMapper_1 = require("../common/GenericTagMapper");
|
|
1
|
+
import { CommonTagMapper } from '../common/GenericTagMapper.js';
|
|
5
2
|
/**
|
|
6
3
|
* ID3v1 tag mappings
|
|
7
4
|
*/
|
|
@@ -14,10 +11,9 @@ const id3v1TagMap = {
|
|
|
14
11
|
track: 'track',
|
|
15
12
|
genre: 'genre'
|
|
16
13
|
};
|
|
17
|
-
class ID3v1TagMapper extends
|
|
14
|
+
export class ID3v1TagMapper extends CommonTagMapper {
|
|
18
15
|
constructor() {
|
|
19
16
|
super(['ID3v1'], id3v1TagMap);
|
|
20
17
|
}
|
|
21
18
|
}
|
|
22
|
-
exports.ID3v1TagMapper = ID3v1TagMapper;
|
|
23
19
|
//# sourceMappingURL=ID3v1TagMap.js.map
|
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const ID3v1Parser_1 = require("../id3v1/ID3v1Parser");
|
|
9
|
-
const BasicParser_1 = require("../common/BasicParser");
|
|
10
|
-
const debug = (0, debug_1.default)('music-metadata:parser:ID3');
|
|
1
|
+
import { EndOfStreamError } from 'strtok3/core';
|
|
2
|
+
import initDebug from 'debug';
|
|
3
|
+
import { ID3v2Header } from './ID3v2Token.js';
|
|
4
|
+
import { ID3v2Parser } from './ID3v2Parser.js';
|
|
5
|
+
import { ID3v1Parser } from '../id3v1/ID3v1Parser.js';
|
|
6
|
+
import { BasicParser } from '../common/BasicParser.js';
|
|
7
|
+
const debug = initDebug('music-metadata:parser:ID3');
|
|
11
8
|
/**
|
|
12
9
|
* Abstract parser which tries take ID3v2 and ID3v1 headers.
|
|
13
10
|
*/
|
|
14
|
-
class AbstractID3Parser extends
|
|
11
|
+
export class AbstractID3Parser extends BasicParser {
|
|
15
12
|
constructor() {
|
|
16
13
|
super(...arguments);
|
|
17
|
-
this.id3parser = new
|
|
14
|
+
this.id3parser = new ID3v2Parser();
|
|
18
15
|
}
|
|
19
16
|
static async startsWithID3v2Header(tokenizer) {
|
|
20
|
-
return (await tokenizer.peekToken(
|
|
17
|
+
return (await tokenizer.peekToken(ID3v2Header)).fileIdentifier === 'ID3';
|
|
21
18
|
}
|
|
22
19
|
async parse() {
|
|
23
20
|
try {
|
|
24
21
|
await this.parseID3v2();
|
|
25
22
|
}
|
|
26
23
|
catch (err) {
|
|
27
|
-
if (err instanceof
|
|
24
|
+
if (err instanceof EndOfStreamError) {
|
|
28
25
|
debug(`End-of-stream`);
|
|
29
26
|
}
|
|
30
27
|
else {
|
|
@@ -43,13 +40,13 @@ class AbstractID3Parser extends BasicParser_1.BasicParser {
|
|
|
43
40
|
this.finalize();
|
|
44
41
|
}
|
|
45
42
|
else {
|
|
46
|
-
const id3v1parser = new
|
|
43
|
+
const id3v1parser = new ID3v1Parser();
|
|
47
44
|
await id3v1parser.init(this.metadata, this.tokenizer, this.options).parse();
|
|
48
45
|
this.finalize();
|
|
49
46
|
}
|
|
50
47
|
}
|
|
51
48
|
async tryReadId3v2Headers() {
|
|
52
|
-
const id3Header = await this.tokenizer.peekToken(
|
|
49
|
+
const id3Header = await this.tokenizer.peekToken(ID3v2Header);
|
|
53
50
|
if (id3Header.fileIdentifier === 'ID3') {
|
|
54
51
|
debug('Found ID3v2 header, pos=%s', this.tokenizer.position);
|
|
55
52
|
await this.id3parser.parse(this.metadata, this.tokenizer, this.options);
|
|
@@ -57,4 +54,3 @@ class AbstractID3Parser extends BasicParser_1.BasicParser {
|
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
}
|
|
60
|
-
exports.AbstractID3Parser = AbstractID3Parser;
|
package/lib/id3v2/FrameParser.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const ID3v2Token_1 = require("./ID3v2Token");
|
|
8
|
-
const ID3v1Parser_1 = require("../id3v1/ID3v1Parser");
|
|
9
|
-
const debug = (0, debug_1.default)('music-metadata:id3v2:frame-parser');
|
|
1
|
+
import initDebug from 'debug';
|
|
2
|
+
import * as Token from 'token-types';
|
|
3
|
+
import * as util from '../common/Util.js';
|
|
4
|
+
import { AttachedPictureType, TextEncodingToken } from './ID3v2Token.js';
|
|
5
|
+
import { Genres } from '../id3v1/ID3v1Parser.js';
|
|
6
|
+
const debug = initDebug('music-metadata:id3v2:frame-parser');
|
|
10
7
|
const defaultEnc = 'latin1'; // latin1 == iso-8859-1;
|
|
11
|
-
function parseGenre(origVal) {
|
|
8
|
+
export function parseGenre(origVal) {
|
|
12
9
|
// match everything inside parentheses
|
|
13
10
|
const genres = [];
|
|
14
11
|
let code;
|
|
@@ -42,23 +39,22 @@ function parseGenre(origVal) {
|
|
|
42
39
|
}
|
|
43
40
|
if (word) {
|
|
44
41
|
if (genres.length === 0 && word.match(/^\d*$/)) {
|
|
45
|
-
word =
|
|
42
|
+
word = Genres[word];
|
|
46
43
|
}
|
|
47
44
|
genres.push(word);
|
|
48
45
|
}
|
|
49
46
|
return genres;
|
|
50
47
|
}
|
|
51
|
-
exports.parseGenre = parseGenre;
|
|
52
48
|
function parseGenreCode(code) {
|
|
53
49
|
if (code === 'RX')
|
|
54
50
|
return 'Remix';
|
|
55
51
|
if (code === 'CR')
|
|
56
52
|
return 'Cover';
|
|
57
53
|
if (code.match(/^\d*$/)) {
|
|
58
|
-
return
|
|
54
|
+
return Genres[code];
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
|
-
class FrameParser {
|
|
57
|
+
export class FrameParser {
|
|
62
58
|
/**
|
|
63
59
|
* Create id3v2 frame parser
|
|
64
60
|
* @param major - Major version, e.g. (4) for id3v2.4
|
|
@@ -73,7 +69,7 @@ class FrameParser {
|
|
|
73
69
|
this.warningCollector.addWarning(`id3v2.${this.major} header has empty tag type=${type}`);
|
|
74
70
|
return;
|
|
75
71
|
}
|
|
76
|
-
const { encoding, bom } =
|
|
72
|
+
const { encoding, bom } = TextEncodingToken.get(uint8Array, 0);
|
|
77
73
|
const length = uint8Array.length;
|
|
78
74
|
let offset = 0;
|
|
79
75
|
let output = []; // ToDo
|
|
@@ -157,7 +153,7 @@ class FrameParser {
|
|
|
157
153
|
throw new Error('Warning: unexpected major versionIndex: ' + this.major);
|
|
158
154
|
}
|
|
159
155
|
pic.format = FrameParser.fixPictureMimeType(pic.format);
|
|
160
|
-
pic.type =
|
|
156
|
+
pic.type = AttachedPictureType[uint8Array[offset]];
|
|
161
157
|
offset += 1;
|
|
162
158
|
fzero = util.findZero(uint8Array, offset, length, encoding);
|
|
163
159
|
pic.description = util.decodeString(uint8Array.slice(offset, fzero), encoding);
|
|
@@ -326,4 +322,3 @@ class FrameParser {
|
|
|
326
322
|
return enc === 'utf16le' ? 2 : 1;
|
|
327
323
|
}
|
|
328
324
|
}
|
|
329
|
-
exports.FrameParser = FrameParser;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ID3v22TagMapper = exports.id3v22TagMap = void 0;
|
|
4
|
-
const CaseInsensitiveTagMap_1 = require("../common/CaseInsensitiveTagMap");
|
|
1
|
+
import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
|
|
5
2
|
/**
|
|
6
3
|
* ID3v2.2 tag mappings
|
|
7
4
|
*/
|
|
8
|
-
|
|
5
|
+
export const id3v22TagMap = {
|
|
9
6
|
TT2: 'title',
|
|
10
7
|
TP1: 'artist',
|
|
11
8
|
TP2: 'albumartist',
|
|
@@ -47,10 +44,9 @@ exports.id3v22TagMap = {
|
|
|
47
44
|
WFD: 'podcasturl',
|
|
48
45
|
TBP: 'bpm'
|
|
49
46
|
};
|
|
50
|
-
class ID3v22TagMapper extends
|
|
47
|
+
export class ID3v22TagMapper extends CaseInsensitiveTagMap {
|
|
51
48
|
constructor() {
|
|
52
|
-
super(['ID3v2.2'],
|
|
49
|
+
super(['ID3v2.2'], id3v22TagMap);
|
|
53
50
|
}
|
|
54
51
|
}
|
|
55
|
-
exports.ID3v22TagMapper = ID3v22TagMapper;
|
|
56
52
|
//# sourceMappingURL=ID3v22TagMapper.js.map
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const GenericTagMapper_1 = require("../common/GenericTagMapper");
|
|
5
|
-
const CaseInsensitiveTagMap_1 = require("../common/CaseInsensitiveTagMap");
|
|
6
|
-
const util = require("../common/Util");
|
|
1
|
+
import { CommonTagMapper } from '../common/GenericTagMapper.js';
|
|
2
|
+
import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
|
|
3
|
+
import * as util from '../common/Util.js';
|
|
7
4
|
/**
|
|
8
5
|
* ID3v2.3/ID3v2.4 tag mappings
|
|
9
6
|
*/
|
|
@@ -140,11 +137,11 @@ const id3v24TagMap = {
|
|
|
140
137
|
TKWD: 'keywords',
|
|
141
138
|
WFED: 'podcasturl'
|
|
142
139
|
};
|
|
143
|
-
class ID3v24TagMapper extends
|
|
140
|
+
export class ID3v24TagMapper extends CaseInsensitiveTagMap {
|
|
144
141
|
static toRating(popm) {
|
|
145
142
|
return {
|
|
146
143
|
source: popm.email,
|
|
147
|
-
rating: popm.rating > 0 ? (popm.rating - 1) / 254 *
|
|
144
|
+
rating: popm.rating > 0 ? (popm.rating - 1) / 254 * CommonTagMapper.maxRatingScore : undefined
|
|
148
145
|
};
|
|
149
146
|
}
|
|
150
147
|
constructor() {
|
|
@@ -190,5 +187,4 @@ class ID3v24TagMapper extends CaseInsensitiveTagMap_1.CaseInsensitiveTagMap {
|
|
|
190
187
|
}
|
|
191
188
|
}
|
|
192
189
|
}
|
|
193
|
-
exports.ID3v24TagMapper = ID3v24TagMapper;
|
|
194
190
|
//# sourceMappingURL=ID3v24TagMapper.js.map
|
package/lib/id3v2/ID3v2Parser.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const FrameParser_1 = require("./FrameParser");
|
|
7
|
-
const ID3v2Token_1 = require("./ID3v2Token");
|
|
8
|
-
class ID3v2Parser {
|
|
1
|
+
import * as Token from 'token-types';
|
|
2
|
+
import * as util from '../common/Util.js';
|
|
3
|
+
import { FrameParser } from './FrameParser.js';
|
|
4
|
+
import { ExtendedHeader, ID3v2Header, UINT32SYNCSAFE } from './ID3v2Token.js';
|
|
5
|
+
export class ID3v2Parser {
|
|
9
6
|
static removeUnsyncBytes(buffer) {
|
|
10
7
|
let readI = 0;
|
|
11
8
|
let writeI = 0;
|
|
@@ -49,7 +46,7 @@ class ID3v2Parser {
|
|
|
49
46
|
};
|
|
50
47
|
}
|
|
51
48
|
static readFrameData(uint8Array, frameHeader, majorVer, includeCovers, warningCollector) {
|
|
52
|
-
const frameParser = new
|
|
49
|
+
const frameParser = new FrameParser(majorVer, warningCollector);
|
|
53
50
|
switch (majorVer) {
|
|
54
51
|
case 2:
|
|
55
52
|
return frameParser.readData(uint8Array, frameHeader.id, includeCovers);
|
|
@@ -79,7 +76,7 @@ class ID3v2Parser {
|
|
|
79
76
|
this.tokenizer = tokenizer;
|
|
80
77
|
this.metadata = metadata;
|
|
81
78
|
this.options = options;
|
|
82
|
-
const id3Header = await this.tokenizer.readToken(
|
|
79
|
+
const id3Header = await this.tokenizer.readToken(ID3v2Header);
|
|
83
80
|
if (id3Header.fileIdentifier !== 'ID3') {
|
|
84
81
|
throw new Error('expected ID3-header file-identifier \'ID3\' was not found');
|
|
85
82
|
}
|
|
@@ -88,8 +85,8 @@ class ID3v2Parser {
|
|
|
88
85
|
return id3Header.flags.isExtendedHeader ? this.parseExtendedHeader() : this.parseId3Data(id3Header.size);
|
|
89
86
|
}
|
|
90
87
|
async parseExtendedHeader() {
|
|
91
|
-
const extendedHeader = await this.tokenizer.readToken(
|
|
92
|
-
const dataRemaining = extendedHeader.size -
|
|
88
|
+
const extendedHeader = await this.tokenizer.readToken(ExtendedHeader);
|
|
89
|
+
const dataRemaining = extendedHeader.size - ExtendedHeader.len;
|
|
93
90
|
return dataRemaining > 0 ? this.parseExtendedHeaderData(dataRemaining, extendedHeader.size) : this.parseId3Data(this.id3Header.size - extendedHeader.size);
|
|
94
91
|
}
|
|
95
92
|
async parseExtendedHeaderData(dataRemaining, extendedHeaderSize) {
|
|
@@ -166,7 +163,7 @@ class ID3v2Parser {
|
|
|
166
163
|
case 4:
|
|
167
164
|
header = {
|
|
168
165
|
id: Buffer.from(uint8Array.slice(0, 4)).toString('ascii'),
|
|
169
|
-
length: (majorVer === 4 ?
|
|
166
|
+
length: (majorVer === 4 ? UINT32SYNCSAFE : Token.UINT32_BE).get(uint8Array, 4),
|
|
170
167
|
flags: ID3v2Parser.readFrameFlags(uint8Array.slice(8, 10))
|
|
171
168
|
};
|
|
172
169
|
if (!header.id.match(/[A-Z0-9]{4}/g)) {
|
|
@@ -179,4 +176,3 @@ class ID3v2Parser {
|
|
|
179
176
|
return header;
|
|
180
177
|
}
|
|
181
178
|
}
|
|
182
|
-
exports.ID3v2Parser = ID3v2Parser;
|
package/lib/id3v2/ID3v2Token.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.TextEncodingToken = exports.ExtendedHeader = exports.ID3v2Header = exports.UINT32SYNCSAFE = exports.AttachedPictureType = void 0;
|
|
4
|
-
const Token = require("token-types");
|
|
5
|
-
const util = require("../common/Util");
|
|
1
|
+
import * as Token from 'token-types';
|
|
2
|
+
import * as util from '../common/Util.js';
|
|
6
3
|
/**
|
|
7
4
|
* The picture type according to the ID3v2 APIC frame
|
|
8
5
|
* Ref: http://id3.org/id3v2.3.0#Attached_picture
|
|
9
6
|
*/
|
|
10
|
-
var AttachedPictureType;
|
|
7
|
+
export var AttachedPictureType;
|
|
11
8
|
(function (AttachedPictureType) {
|
|
12
9
|
AttachedPictureType[AttachedPictureType["Other"] = 0] = "Other";
|
|
13
10
|
AttachedPictureType[AttachedPictureType["32x32 pixels 'file icon' (PNG only)"] = 1] = "32x32 pixels 'file icon' (PNG only)";
|
|
@@ -30,12 +27,12 @@ var AttachedPictureType;
|
|
|
30
27
|
AttachedPictureType[AttachedPictureType["Illustration"] = 18] = "Illustration";
|
|
31
28
|
AttachedPictureType[AttachedPictureType["Band/artist logotype"] = 19] = "Band/artist logotype";
|
|
32
29
|
AttachedPictureType[AttachedPictureType["Publisher/Studio logotype"] = 20] = "Publisher/Studio logotype";
|
|
33
|
-
})(AttachedPictureType =
|
|
30
|
+
})(AttachedPictureType = AttachedPictureType || (AttachedPictureType = {}));
|
|
34
31
|
/**
|
|
35
32
|
* 28 bits (representing up to 256MB) integer, the msb is 0 to avoid 'false syncsignals'.
|
|
36
33
|
* 4 * %0xxxxxxx
|
|
37
34
|
*/
|
|
38
|
-
|
|
35
|
+
export const UINT32SYNCSAFE = {
|
|
39
36
|
get: (buf, off) => {
|
|
40
37
|
return buf[off + 3] & 0x7f | ((buf[off + 2]) << 7) |
|
|
41
38
|
((buf[off + 1]) << 14) | ((buf[off]) << 21);
|
|
@@ -47,7 +44,7 @@ exports.UINT32SYNCSAFE = {
|
|
|
47
44
|
* Ref: http://id3.org/id3v2.3.0#ID3v2_header
|
|
48
45
|
* ToDo
|
|
49
46
|
*/
|
|
50
|
-
|
|
47
|
+
export const ID3v2Header = {
|
|
51
48
|
len: 10,
|
|
52
49
|
get: (buf, off) => {
|
|
53
50
|
return {
|
|
@@ -68,11 +65,11 @@ exports.ID3v2Header = {
|
|
|
68
65
|
expIndicator: util.getBit(buf, off + 5, 5),
|
|
69
66
|
footer: util.getBit(buf, off + 5, 4)
|
|
70
67
|
},
|
|
71
|
-
size:
|
|
68
|
+
size: UINT32SYNCSAFE.get(buf, off + 6)
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
71
|
};
|
|
75
|
-
|
|
72
|
+
export const ExtendedHeader = {
|
|
76
73
|
len: 10,
|
|
77
74
|
get: (buf, off) => {
|
|
78
75
|
return {
|
|
@@ -87,7 +84,7 @@ exports.ExtendedHeader = {
|
|
|
87
84
|
};
|
|
88
85
|
}
|
|
89
86
|
};
|
|
90
|
-
|
|
87
|
+
export const TextEncodingToken = {
|
|
91
88
|
len: 1,
|
|
92
89
|
get: (uint8Array, off) => {
|
|
93
90
|
switch (uint8Array[off]) {
|
package/lib/iff/index.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.Header = void 0;
|
|
4
|
-
const Token = require("token-types");
|
|
5
|
-
const FourCC_1 = require("../common/FourCC");
|
|
1
|
+
import * as Token from 'token-types';
|
|
2
|
+
import { FourCcToken } from '../common/FourCC.js';
|
|
6
3
|
/**
|
|
7
4
|
* Common AIFF chunk header
|
|
8
5
|
*/
|
|
9
|
-
|
|
6
|
+
export const Header = {
|
|
10
7
|
len: 8,
|
|
11
8
|
get: (buf, off) => {
|
|
12
9
|
return {
|
|
13
10
|
// Chunk type ID
|
|
14
|
-
chunkID:
|
|
11
|
+
chunkID: FourCcToken.get(buf, off),
|
|
15
12
|
// Chunk size
|
|
16
13
|
chunkSize: Number(BigInt(Token.UINT32_BE.get(buf, off + 4)))
|
|
17
14
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const RandomFileReader_1 = require("./common/RandomFileReader");
|
|
9
|
-
const debug = (0, debug_1.default)("music-metadata:parser");
|
|
10
|
-
var core_1 = require("./core");
|
|
11
|
-
Object.defineProperty(exports, "parseFromTokenizer", { enumerable: true, get: function () { return core_1.parseFromTokenizer; } });
|
|
12
|
-
Object.defineProperty(exports, "parseBuffer", { enumerable: true, get: function () { return core_1.parseBuffer; } });
|
|
13
|
-
Object.defineProperty(exports, "selectCover", { enumerable: true, get: function () { return core_1.selectCover; } });
|
|
1
|
+
import * as strtok3 from 'strtok3';
|
|
2
|
+
import initDebug from 'debug';
|
|
3
|
+
import { parseFromTokenizer, scanAppendingHeaders } from './core.js';
|
|
4
|
+
import { ParserFactory } from './ParserFactory.js';
|
|
5
|
+
import { RandomFileReader } from './common/RandomFileReader.js';
|
|
6
|
+
export { parseFromTokenizer, parseBuffer, selectCover, orderTags, ratingToStars } from './core.js';
|
|
7
|
+
const debug = initDebug('music-metadata:parser');
|
|
14
8
|
/**
|
|
15
9
|
* Parse audio from Node Stream.Readable
|
|
16
10
|
* @param stream - Stream to read the audio track from
|
|
@@ -18,57 +12,33 @@ Object.defineProperty(exports, "selectCover", { enumerable: true, get: function
|
|
|
18
12
|
* @param options - Parsing options
|
|
19
13
|
* @returns Metadata
|
|
20
14
|
*/
|
|
21
|
-
async function parseStream(stream, fileInfo, options = {}) {
|
|
15
|
+
export async function parseStream(stream, fileInfo, options = {}) {
|
|
22
16
|
const tokenizer = await strtok3.fromStream(stream, typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo);
|
|
23
|
-
return
|
|
17
|
+
return parseFromTokenizer(tokenizer, options);
|
|
24
18
|
}
|
|
25
|
-
exports.parseStream = parseStream;
|
|
26
19
|
/**
|
|
27
20
|
* Parse audio from Node file
|
|
28
21
|
* @param filePath - Media file to read meta-data from
|
|
29
22
|
* @param options - Parsing options
|
|
30
23
|
* @returns Metadata
|
|
31
24
|
*/
|
|
32
|
-
async function parseFile(filePath, options = {}) {
|
|
25
|
+
export async function parseFile(filePath, options = {}) {
|
|
33
26
|
debug(`parseFile: ${filePath}`);
|
|
34
27
|
const fileTokenizer = await strtok3.fromFile(filePath);
|
|
35
|
-
const fileReader = await
|
|
28
|
+
const fileReader = await RandomFileReader.init(filePath, fileTokenizer.fileInfo.size);
|
|
36
29
|
try {
|
|
37
|
-
await
|
|
30
|
+
await scanAppendingHeaders(fileReader, options);
|
|
38
31
|
}
|
|
39
32
|
finally {
|
|
40
33
|
await fileReader.close();
|
|
41
34
|
}
|
|
42
35
|
try {
|
|
43
|
-
const parserName =
|
|
36
|
+
const parserName = ParserFactory.getParserIdForExtension(filePath);
|
|
44
37
|
if (!parserName)
|
|
45
38
|
debug(' Parser could not be determined by file extension');
|
|
46
|
-
return await
|
|
39
|
+
return await ParserFactory.parse(fileTokenizer, parserName, options);
|
|
47
40
|
}
|
|
48
41
|
finally {
|
|
49
42
|
await fileTokenizer.close();
|
|
50
43
|
}
|
|
51
44
|
}
|
|
52
|
-
exports.parseFile = parseFile;
|
|
53
|
-
/**
|
|
54
|
-
* Create a dictionary ordered by their tag id (key)
|
|
55
|
-
* @param nativeTags - List of tags
|
|
56
|
-
* @returns Tags indexed by id
|
|
57
|
-
*/
|
|
58
|
-
exports.orderTags = Core.orderTags;
|
|
59
|
-
/**
|
|
60
|
-
* Convert rating to 1-5 star rating
|
|
61
|
-
* @param rating - Normalized rating [0..1] (common.rating[n].rating)
|
|
62
|
-
* @returns Number of stars: 1, 2, 3, 4 or 5 stars
|
|
63
|
-
*/
|
|
64
|
-
exports.ratingToStars = Core.ratingToStars;
|
|
65
|
-
/**
|
|
66
|
-
* Define default module exports
|
|
67
|
-
*/
|
|
68
|
-
exports.default = {
|
|
69
|
-
parseStream,
|
|
70
|
-
parseFile,
|
|
71
|
-
parseFromTokenizer: Core.parseFromTokenizer,
|
|
72
|
-
parseBuffer: Core.parseBuffer,
|
|
73
|
-
selectCover: Core.selectCover
|
|
74
|
-
};
|