music-metadata 10.0.1 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +9 -9
- package/README.md +520 -520
- package/lib/ParserFactory.d.ts +20 -28
- package/lib/ParserFactory.js +204 -207
- package/lib/aiff/AiffParser.js +20 -18
- package/lib/aiff/AiffToken.d.ts +14 -2
- package/lib/aiff/AiffToken.js +12 -0
- package/lib/apev2/APEv2Parser.d.ts +4 -4
- package/lib/apev2/APEv2Parser.js +12 -10
- package/lib/apev2/APEv2Token.d.ts +2 -4
- package/lib/apev2/APEv2Token.js +0 -3
- package/lib/asf/AsfObject.d.ts +7 -16
- package/lib/asf/AsfObject.js +8 -34
- package/lib/asf/AsfParser.js +17 -10
- package/lib/asf/AsfTagMapper.d.ts +1 -1
- package/lib/asf/AsfTagMapper.js +3 -2
- package/lib/asf/AsfUtil.d.ts +3 -11
- package/lib/asf/AsfUtil.js +29 -30
- package/lib/asf/GUID.js +6 -9
- package/lib/common/BasicParser.d.ts +4 -4
- package/lib/common/BasicParser.js +6 -0
- package/lib/common/CaseInsensitiveTagMap.d.ts +1 -1
- package/lib/common/CombinedTagMapper.d.ts +5 -5
- package/lib/common/CombinedTagMapper.js +1 -1
- package/lib/common/FourCC.d.ts +1 -1
- package/lib/common/GenericTagMapper.d.ts +8 -8
- package/lib/common/GenericTagMapper.js +4 -4
- package/lib/common/GenericTagTypes.d.ts +5 -4
- package/lib/common/GenericTagTypes.js +2 -2
- package/lib/common/MetadataCollector.d.ts +9 -9
- package/lib/common/MetadataCollector.js +24 -17
- package/lib/common/RandomFileReader.d.ts +1 -1
- package/lib/common/RandomFileReader.js +1 -1
- package/lib/common/RandomUint8ArrayReader.d.ts +1 -1
- package/lib/common/Util.d.ts +2 -6
- package/lib/common/Util.js +9 -11
- package/lib/core.d.ts +7 -9
- package/lib/core.js +10 -7
- package/lib/dsdiff/DsdiffParser.js +26 -14
- package/lib/dsdiff/DsdiffToken.d.ts +2 -2
- package/lib/dsdiff/DsdiffToken.js +1 -0
- package/lib/dsf/DsfChunk.js +1 -0
- package/lib/dsf/DsfParser.js +4 -2
- package/lib/flac/FlacParser.d.ts +3 -3
- package/lib/flac/FlacParser.js +9 -12
- package/lib/id3v1/ID3v1Parser.d.ts +1 -1
- package/lib/id3v1/ID3v1Parser.js +7 -4
- package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
- package/lib/id3v2/AbstractID3Parser.js +2 -1
- package/lib/id3v2/FrameParser.d.ts +28 -3
- package/lib/id3v2/FrameParser.js +50 -28
- package/lib/id3v2/ID3v22TagMapper.d.ts +1 -1
- package/lib/id3v2/ID3v24TagMapper.d.ts +2 -1
- package/lib/id3v2/ID3v24TagMapper.js +23 -16
- package/lib/id3v2/ID3v2Parser.d.ts +2 -2
- package/lib/id3v2/ID3v2Parser.js +19 -8
- package/lib/iff/index.js +1 -0
- package/lib/index.d.ts +5 -6
- package/lib/index.js +7 -8
- package/lib/lyrics3/Lyrics3.d.ts +1 -1
- package/lib/lyrics3/Lyrics3.js +1 -1
- package/lib/matroska/MatroskaDtd.d.ts +1 -1
- package/lib/matroska/MatroskaDtd.js +139 -138
- package/lib/matroska/MatroskaParser.d.ts +4 -4
- package/lib/matroska/MatroskaParser.js +12 -12
- package/lib/matroska/types.d.ts +6 -4
- package/lib/mp4/Atom.d.ts +3 -3
- package/lib/mp4/Atom.js +4 -7
- package/lib/mp4/AtomToken.js +2 -1
- package/lib/mp4/MP4Parser.js +29 -20
- package/lib/mp4/MP4TagMapper.d.ts +2 -2
- package/lib/mp4/MP4TagMapper.js +1 -1
- package/lib/mpeg/ExtendedLameHeader.d.ts +4 -4
- package/lib/mpeg/ExtendedLameHeader.js +2 -1
- package/lib/mpeg/MpegParser.d.ts +1 -1
- package/lib/mpeg/MpegParser.js +145 -96
- package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
- package/lib/mpeg/ReplayGainDataFormat.js +1 -0
- package/lib/mpeg/XingTag.d.ts +4 -4
- package/lib/mpeg/XingTag.js +5 -4
- package/lib/musepack/index.js +1 -0
- package/lib/musepack/sv7/BitReader.js +14 -17
- package/lib/musepack/sv7/MpcSv7Parser.js +6 -1
- package/lib/musepack/sv7/StreamVersion7.js +1 -0
- package/lib/musepack/sv8/MpcSv8Parser.js +6 -2
- package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
- package/lib/musepack/sv8/StreamVersion8.js +1 -0
- package/lib/ogg/Ogg.d.ts +1 -1
- package/lib/ogg/Ogg.js +1 -0
- package/lib/ogg/OggParser.d.ts +3 -3
- package/lib/ogg/OggParser.js +25 -16
- package/lib/ogg/opus/Opus.d.ts +1 -1
- package/lib/ogg/opus/Opus.js +1 -0
- package/lib/ogg/opus/OpusParser.d.ts +4 -4
- package/lib/ogg/opus/OpusParser.js +2 -0
- package/lib/ogg/speex/Speex.js +1 -0
- package/lib/ogg/speex/SpeexParser.d.ts +4 -4
- package/lib/ogg/speex/SpeexParser.js +1 -0
- package/lib/ogg/theora/Theora.js +1 -0
- package/lib/ogg/theora/TheoraParser.d.ts +4 -4
- package/lib/ogg/theora/TheoraParser.js +1 -0
- package/lib/ogg/vorbis/Vorbis.d.ts +3 -3
- package/lib/ogg/vorbis/Vorbis.js +22 -11
- package/lib/ogg/vorbis/VorbisDecoder.d.ts +1 -1
- package/lib/ogg/vorbis/VorbisDecoder.js +1 -0
- package/lib/ogg/vorbis/VorbisParser.d.ts +4 -4
- package/lib/ogg/vorbis/VorbisParser.js +3 -2
- package/lib/ogg/vorbis/VorbisTagMapper.d.ts +2 -2
- package/lib/ogg/vorbis/VorbisTagMapper.js +2 -2
- package/lib/riff/RiffChunk.d.ts +3 -3
- package/lib/riff/RiffChunk.js +1 -0
- package/lib/riff/RiffInfoTagMap.d.ts +1 -1
- package/lib/type.d.ts +35 -25
- package/lib/wav/BwfChunk.js +1 -0
- package/lib/wav/WaveChunk.d.ts +1 -1
- package/lib/wav/WaveChunk.js +1 -0
- package/lib/wav/WaveParser.js +27 -17
- package/lib/wavpack/WavPackParser.d.ts +1 -1
- package/lib/wavpack/WavPackParser.js +22 -13
- package/lib/wavpack/WavPackToken.d.ts +13 -17
- package/lib/wavpack/WavPackToken.js +22 -23
- package/package.json +139 -150
package/lib/ogg/OggParser.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { IGetToken } from 'strtok3';
|
|
1
|
+
import { type IGetToken } from 'strtok3';
|
|
2
2
|
import { BasicParser } from '../common/BasicParser.js';
|
|
3
|
-
import * as Ogg from './Ogg.js';
|
|
3
|
+
import type * as Ogg from './Ogg.js';
|
|
4
4
|
export declare class SegmentTable implements IGetToken<Ogg.ISegmentTable> {
|
|
5
5
|
private static sum;
|
|
6
6
|
len: number;
|
|
7
7
|
constructor(header: Ogg.IPageHeader);
|
|
8
|
-
get(buf:
|
|
8
|
+
get(buf: Uint8Array, off: number): Ogg.ISegmentTable;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Parser for Ogg logical bitstream framing
|
package/lib/ogg/OggParser.js
CHANGED
|
@@ -11,9 +11,10 @@ import { TheoraParser } from './theora/TheoraParser.js';
|
|
|
11
11
|
const debug = initDebug('music-metadata:parser:ogg');
|
|
12
12
|
export class SegmentTable {
|
|
13
13
|
static sum(buf, off, len) {
|
|
14
|
+
const dv = new DataView(buf.buffer, 0);
|
|
14
15
|
let s = 0;
|
|
15
16
|
for (let i = off; i < off + len; ++i) {
|
|
16
|
-
s +=
|
|
17
|
+
s += dv.getUint8(i);
|
|
17
18
|
}
|
|
18
19
|
return s;
|
|
19
20
|
}
|
|
@@ -30,6 +31,12 @@ export class SegmentTable {
|
|
|
30
31
|
* Parser for Ogg logical bitstream framing
|
|
31
32
|
*/
|
|
32
33
|
export class OggParser extends BasicParser {
|
|
34
|
+
constructor() {
|
|
35
|
+
super(...arguments);
|
|
36
|
+
this.header = null;
|
|
37
|
+
this.pageNumber = 0;
|
|
38
|
+
this.pageConsumer = null;
|
|
39
|
+
}
|
|
33
40
|
/**
|
|
34
41
|
* Parse page
|
|
35
42
|
* @returns {Promise<void>}
|
|
@@ -71,31 +78,32 @@ export class OggParser extends BasicParser {
|
|
|
71
78
|
this.pageConsumer = new TheoraParser(this.metadata, this.options, this.tokenizer);
|
|
72
79
|
break;
|
|
73
80
|
default:
|
|
74
|
-
throw new Error(
|
|
81
|
+
throw new Error(`gg audio-codec not recognized (id=${id})`);
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
await this.pageConsumer.parsePage(header, pageData);
|
|
78
85
|
} while (!header.headerType.lastPage);
|
|
79
86
|
}
|
|
80
87
|
catch (err) {
|
|
81
|
-
if (err instanceof
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
if (err instanceof Error) {
|
|
89
|
+
if (err instanceof EndOfStreamError) {
|
|
90
|
+
this.metadata.addWarning('Last OGG-page is not marked with last-page flag');
|
|
91
|
+
debug("End-of-stream");
|
|
92
|
+
this.metadata.addWarning('Last OGG-page is not marked with last-page flag');
|
|
93
|
+
if (this.header) {
|
|
94
|
+
this.pageConsumer.calculateDuration(this.header);
|
|
95
|
+
}
|
|
87
96
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
else if (err.message.startsWith('FourCC')) {
|
|
98
|
+
if (this.pageNumber > 0) {
|
|
99
|
+
// ignore this error: work-around if last OGG-page is not marked with last-page flag
|
|
100
|
+
this.metadata.addWarning('Invalid FourCC ID, maybe last OGG-page is not marked with last-page flag');
|
|
101
|
+
await this.pageConsumer.flush();
|
|
102
|
+
}
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
|
-
else
|
|
105
|
+
else
|
|
97
106
|
throw err;
|
|
98
|
-
}
|
|
99
107
|
}
|
|
100
108
|
}
|
|
101
109
|
}
|
|
@@ -119,3 +127,4 @@ OggParser.Header = {
|
|
|
119
127
|
};
|
|
120
128
|
}
|
|
121
129
|
};
|
|
130
|
+
//# sourceMappingURL=OggParser.js.map
|
package/lib/ogg/opus/Opus.d.ts
CHANGED
package/lib/ogg/opus/Opus.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ITokenizer } from 'strtok3';
|
|
2
|
-
import { IPageHeader } from '../Ogg.js';
|
|
1
|
+
import type { ITokenizer } from 'strtok3';
|
|
2
|
+
import type { IPageHeader } from '../Ogg.js';
|
|
3
3
|
import { VorbisParser } from '../vorbis/VorbisParser.js';
|
|
4
|
-
import { IOptions } from '../../type.js';
|
|
5
|
-
import { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
4
|
+
import type { IOptions } from '../../type.js';
|
|
5
|
+
import type { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
6
6
|
/**
|
|
7
7
|
* Opus parser
|
|
8
8
|
* Internet Engineering Task Force (IETF) - RFC 6716
|
|
@@ -10,6 +10,7 @@ export class OpusParser extends VorbisParser {
|
|
|
10
10
|
constructor(metadata, options, tokenizer) {
|
|
11
11
|
super(metadata, options);
|
|
12
12
|
this.tokenizer = tokenizer;
|
|
13
|
+
this.idHeader = null;
|
|
13
14
|
this.lastPos = -1;
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
@@ -50,3 +51,4 @@ export class OpusParser extends VorbisParser {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
}
|
|
54
|
+
//# sourceMappingURL=OpusParser.js.map
|
package/lib/ogg/speex/Speex.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ITokenizer } from 'strtok3';
|
|
2
|
-
import { IPageHeader } from '../Ogg.js';
|
|
1
|
+
import type { ITokenizer } from 'strtok3';
|
|
2
|
+
import type { IPageHeader } from '../Ogg.js';
|
|
3
3
|
import { VorbisParser } from '../vorbis/VorbisParser.js';
|
|
4
|
-
import { IOptions } from '../../type.js';
|
|
5
|
-
import { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
4
|
+
import type { IOptions } from '../../type.js';
|
|
5
|
+
import type { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
6
6
|
/**
|
|
7
7
|
* Speex, RFC 5574
|
|
8
8
|
* Ref:
|
package/lib/ogg/theora/Theora.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ITokenizer } from 'strtok3';
|
|
2
|
-
import * as Ogg from '../Ogg.js';
|
|
3
|
-
import { IOptions } from '../../type.js';
|
|
4
|
-
import { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
1
|
+
import type { ITokenizer } from 'strtok3';
|
|
2
|
+
import type * as Ogg from '../Ogg.js';
|
|
3
|
+
import type { IOptions } from '../../type.js';
|
|
4
|
+
import type { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
5
5
|
/**
|
|
6
6
|
* Ref:
|
|
7
7
|
* - https://theora.org/doc/Theora.pdf
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IPicture } from '../../type.js';
|
|
1
|
+
import type { IPicture } from '../../type.js';
|
|
2
2
|
import type { IGetToken } from 'strtok3';
|
|
3
3
|
/**
|
|
4
4
|
* Interface to parsed result of METADATA_BLOCK_PICTURE
|
|
@@ -20,10 +20,10 @@ export interface IVorbisPicture extends IPicture {
|
|
|
20
20
|
* // ToDo: move to ID3 / APIC?
|
|
21
21
|
*/
|
|
22
22
|
export declare class VorbisPictureToken implements IGetToken<IVorbisPicture> {
|
|
23
|
-
len:
|
|
23
|
+
len: number;
|
|
24
24
|
static fromBase64(base64str: string): IVorbisPicture;
|
|
25
25
|
static fromBuffer(buffer: Uint8Array): IVorbisPicture;
|
|
26
|
-
constructor(len:
|
|
26
|
+
constructor(len: number);
|
|
27
27
|
get(buffer: Uint8Array, offset: number): IVorbisPicture;
|
|
28
28
|
}
|
|
29
29
|
/**
|
package/lib/ogg/vorbis/Vorbis.js
CHANGED
|
@@ -8,7 +8,7 @@ import { AttachedPictureType } from '../../id3v2/ID3v2Token.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export class VorbisPictureToken {
|
|
10
10
|
static fromBase64(base64str) {
|
|
11
|
-
return
|
|
11
|
+
return VorbisPictureToken.fromBuffer(Uint8Array.from(atob(base64str), c => c.charCodeAt(0)));
|
|
12
12
|
}
|
|
13
13
|
static fromBuffer(buffer) {
|
|
14
14
|
const pic = new VorbisPictureToken(buffer.length);
|
|
@@ -19,16 +19,26 @@ export class VorbisPictureToken {
|
|
|
19
19
|
}
|
|
20
20
|
get(buffer, offset) {
|
|
21
21
|
const type = AttachedPictureType[Token.UINT32_BE.get(buffer, offset)];
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
22
|
+
offset += 4;
|
|
23
|
+
const mimeLen = Token.UINT32_BE.get(buffer, offset);
|
|
24
|
+
offset += 4;
|
|
25
|
+
const format = new Token.StringType(mimeLen, 'utf-8').get(buffer, offset);
|
|
26
|
+
offset += mimeLen;
|
|
27
|
+
const descLen = Token.UINT32_BE.get(buffer, offset);
|
|
28
|
+
offset += 4;
|
|
29
|
+
const description = new Token.StringType(descLen, 'utf-8').get(buffer, offset);
|
|
30
|
+
offset += descLen;
|
|
31
|
+
const width = Token.UINT32_BE.get(buffer, offset);
|
|
32
|
+
offset += 4;
|
|
33
|
+
const height = Token.UINT32_BE.get(buffer, offset);
|
|
34
|
+
offset += 4;
|
|
35
|
+
const colour_depth = Token.UINT32_BE.get(buffer, offset);
|
|
36
|
+
offset += 4;
|
|
37
|
+
const indexed_color = Token.UINT32_BE.get(buffer, offset);
|
|
38
|
+
offset += 4;
|
|
39
|
+
const picDataLen = Token.UINT32_BE.get(buffer, offset);
|
|
40
|
+
offset += 4;
|
|
41
|
+
const data = Uint8Array.from(buffer.slice(offset, offset + picDataLen));
|
|
32
42
|
return {
|
|
33
43
|
type,
|
|
34
44
|
format,
|
|
@@ -71,3 +81,4 @@ export const IdentificationHeader = {
|
|
|
71
81
|
};
|
|
72
82
|
}
|
|
73
83
|
};
|
|
84
|
+
//# sourceMappingURL=Vorbis.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { IVorbisPicture } from './Vorbis.js';
|
|
2
|
-
import { IPageConsumer, IPageHeader } from '../Ogg.js';
|
|
3
|
-
import { IOptions } from '../../type.js';
|
|
4
|
-
import { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
1
|
+
import { type IVorbisPicture } from './Vorbis.js';
|
|
2
|
+
import type { IPageConsumer, IPageHeader } from '../Ogg.js';
|
|
3
|
+
import type { IOptions } from '../../type.js';
|
|
4
|
+
import type { INativeMetadataCollector } from '../../common/MetadataCollector.js';
|
|
5
5
|
/**
|
|
6
6
|
* Vorbis 1 Parser.
|
|
7
7
|
* Used by OggParser
|
|
@@ -64,7 +64,7 @@ export class VorbisParser {
|
|
|
64
64
|
async addTag(id, value) {
|
|
65
65
|
if (id === 'METADATA_BLOCK_PICTURE' && (typeof value === 'string')) {
|
|
66
66
|
if (this.options.skipCovers) {
|
|
67
|
-
debug(
|
|
67
|
+
debug("Ignore picture");
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
value = VorbisPictureToken.fromBase64(value);
|
|
@@ -79,7 +79,7 @@ export class VorbisParser {
|
|
|
79
79
|
if (this.metadata.format.sampleRate && header.absoluteGranulePosition >= 0) {
|
|
80
80
|
// Calculate duration
|
|
81
81
|
this.metadata.setFormat('numberOfSamples', header.absoluteGranulePosition);
|
|
82
|
-
this.metadata.setFormat('duration',
|
|
82
|
+
this.metadata.setFormat('duration', header.absoluteGranulePosition / this.metadata.format.sampleRate);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
@@ -131,3 +131,4 @@ export class VorbisParser {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
+
//# sourceMappingURL=VorbisParser.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CommonTagMapper } from '../../common/GenericTagMapper.js';
|
|
2
|
-
import { IRating, ITag } from '../../type.js';
|
|
2
|
+
import type { IRating, ITag } from '../../type.js';
|
|
3
3
|
export declare class VorbisTagMapper extends CommonTagMapper {
|
|
4
|
-
static toRating(email: string, rating: string, maxScore: number): IRating;
|
|
4
|
+
static toRating(email: string | undefined | null, rating: string, maxScore: number): IRating;
|
|
5
5
|
constructor();
|
|
6
6
|
protected postMap(tag: ITag): void;
|
|
7
7
|
}
|
|
@@ -112,8 +112,8 @@ const vorbisTagMap = {
|
|
|
112
112
|
export class VorbisTagMapper extends CommonTagMapper {
|
|
113
113
|
static toRating(email, rating, maxScore) {
|
|
114
114
|
return {
|
|
115
|
-
source: email ? email.toLowerCase() :
|
|
116
|
-
rating: (parseFloat(rating) / maxScore) * CommonTagMapper.maxRatingScore
|
|
115
|
+
source: email ? email.toLowerCase() : undefined,
|
|
116
|
+
rating: (Number.parseFloat(rating) / maxScore) * CommonTagMapper.maxRatingScore
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
constructor() {
|
package/lib/riff/RiffChunk.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IGetToken } from 'strtok3';
|
|
2
|
-
import { IChunkHeader } from '../iff/index.js';
|
|
3
|
-
export { IChunkHeader } from '../iff/index.js';
|
|
2
|
+
import type { IChunkHeader } from '../iff/index.js';
|
|
3
|
+
export { type IChunkHeader } from '../iff/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Common RIFF chunk header
|
|
6
6
|
*/
|
|
@@ -12,5 +12,5 @@ export declare class ListInfoTagValue implements IGetToken<string> {
|
|
|
12
12
|
private tagHeader;
|
|
13
13
|
len: number;
|
|
14
14
|
constructor(tagHeader: IChunkHeader);
|
|
15
|
-
get(buf:
|
|
15
|
+
get(buf: Uint8Array, off: number): string;
|
|
16
16
|
}
|
package/lib/riff/RiffChunk.js
CHANGED
package/lib/type.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { IFooter } from './apev2/APEv2Token.js';
|
|
3
|
-
import { TrackType } from './matroska/types.js';
|
|
4
|
-
import { LyricsContentType, TimestampFormat } from './id3v2/ID3v2Token.js';
|
|
1
|
+
import type { TagType } from './common/GenericTagTypes.js';
|
|
2
|
+
import type { IFooter } from './apev2/APEv2Token.js';
|
|
3
|
+
import type { TrackType } from './matroska/types.js';
|
|
4
|
+
import type { LyricsContentType, TimestampFormat } from './id3v2/ID3v2Token.js';
|
|
5
5
|
export { TrackType } from './matroska/types.js';
|
|
6
6
|
export { LyricsContentType, TimestampFormat } from './id3v2/ID3v2Token.js';
|
|
7
|
+
export type AnyTagValue = unknown;
|
|
7
8
|
/**
|
|
8
9
|
* Attached picture, typically used for cover art
|
|
9
10
|
*/
|
|
@@ -40,7 +41,7 @@ export interface IRating {
|
|
|
40
41
|
/**
|
|
41
42
|
* Rating [0..1]
|
|
42
43
|
*/
|
|
43
|
-
rating
|
|
44
|
+
rating?: number;
|
|
44
45
|
}
|
|
45
46
|
export interface ICommonTagsResult {
|
|
46
47
|
track: {
|
|
@@ -159,6 +160,10 @@ export interface ICommonTagsResult {
|
|
|
159
160
|
* Engineer(s)
|
|
160
161
|
*/
|
|
161
162
|
engineer?: string[];
|
|
163
|
+
/**
|
|
164
|
+
* Publisher(s)
|
|
165
|
+
*/
|
|
166
|
+
publisher?: string[];
|
|
162
167
|
/**
|
|
163
168
|
* Producer(s)
|
|
164
169
|
*/
|
|
@@ -293,9 +298,13 @@ export interface ICommonTagsResult {
|
|
|
293
298
|
rightChannel: number;
|
|
294
299
|
};
|
|
295
300
|
/**
|
|
296
|
-
* minimum & maximum global gain values across a set of
|
|
301
|
+
* minimum & maximum global gain values across a set of file
|
|
297
302
|
*/
|
|
298
303
|
replaygain_track_minmax?: number[];
|
|
304
|
+
/**
|
|
305
|
+
* minimum & maximum global gain values across a set of files scanned as an album
|
|
306
|
+
*/
|
|
307
|
+
replaygain_album_minmax?: number[];
|
|
299
308
|
/**
|
|
300
309
|
* The initial key of the music in the file, e.g. "A Minor".
|
|
301
310
|
* Ref: https://docs.microsoft.com/en-us/windows/win32/wmformat/wm-initialkey
|
|
@@ -325,8 +334,8 @@ export interface ICommonTagsResult {
|
|
|
325
334
|
* Movement Index/Total
|
|
326
335
|
*/
|
|
327
336
|
movementIndex: {
|
|
328
|
-
no
|
|
329
|
-
of
|
|
337
|
+
no: number | null;
|
|
338
|
+
of: number | null;
|
|
330
339
|
};
|
|
331
340
|
/**
|
|
332
341
|
* Podcast Identifier
|
|
@@ -402,7 +411,7 @@ export interface IFormat {
|
|
|
402
411
|
/**
|
|
403
412
|
* List of tags found in parsed audio file
|
|
404
413
|
*/
|
|
405
|
-
readonly tagTypes
|
|
414
|
+
readonly tagTypes: TagType[];
|
|
406
415
|
/**
|
|
407
416
|
* Duration in seconds
|
|
408
417
|
*/
|
|
@@ -464,7 +473,7 @@ export interface IFormat {
|
|
|
464
473
|
}
|
|
465
474
|
export interface ITag {
|
|
466
475
|
id: string;
|
|
467
|
-
value:
|
|
476
|
+
value: AnyTagValue;
|
|
468
477
|
}
|
|
469
478
|
export interface IChapter {
|
|
470
479
|
/**
|
|
@@ -487,7 +496,7 @@ export interface INativeTags {
|
|
|
487
496
|
* Tags ordered by tag-ID
|
|
488
497
|
*/
|
|
489
498
|
export interface INativeTagDict {
|
|
490
|
-
[tagId: string]:
|
|
499
|
+
[tagId: string]: AnyTagValue[];
|
|
491
500
|
}
|
|
492
501
|
export interface INativeAudioMetadata {
|
|
493
502
|
format: IFormat;
|
|
@@ -549,6 +558,20 @@ export interface IApeHeader extends IOptions {
|
|
|
549
558
|
export interface IPrivateOptions extends IOptions {
|
|
550
559
|
apeHeader?: IApeHeader;
|
|
551
560
|
}
|
|
561
|
+
export interface IMetadataEventTag {
|
|
562
|
+
/**
|
|
563
|
+
* Either `common` if it is a generic tag event, or `format` for format related updates
|
|
564
|
+
*/
|
|
565
|
+
type: 'common' | 'format';
|
|
566
|
+
/**
|
|
567
|
+
* Tag id
|
|
568
|
+
*/
|
|
569
|
+
id: keyof ICommonTagsResult | FormatId;
|
|
570
|
+
/**
|
|
571
|
+
* Tag value
|
|
572
|
+
*/
|
|
573
|
+
value: AnyTagValue;
|
|
574
|
+
}
|
|
552
575
|
/**
|
|
553
576
|
* Event definition send after each change to common/format metadata change to observer.
|
|
554
577
|
*/
|
|
@@ -556,20 +579,7 @@ export interface IMetadataEvent {
|
|
|
556
579
|
/**
|
|
557
580
|
* Tag which has been updated.
|
|
558
581
|
*/
|
|
559
|
-
tag:
|
|
560
|
-
/**
|
|
561
|
-
* Either 'common' if it a generic tag event, or 'format' for format related updates
|
|
562
|
-
*/
|
|
563
|
-
type: 'common' | 'format';
|
|
564
|
-
/**
|
|
565
|
-
* Tag id
|
|
566
|
-
*/
|
|
567
|
-
id: GenericTagId | FormatId;
|
|
568
|
-
/**
|
|
569
|
-
* Tag value
|
|
570
|
-
*/
|
|
571
|
-
value: any;
|
|
572
|
-
};
|
|
582
|
+
tag: IMetadataEventTag;
|
|
573
583
|
/**
|
|
574
584
|
* Metadata model including the attached tag
|
|
575
585
|
*/
|
package/lib/wav/BwfChunk.js
CHANGED
package/lib/wav/WaveChunk.d.ts
CHANGED
package/lib/wav/WaveChunk.js
CHANGED
package/lib/wav/WaveParser.js
CHANGED
|
@@ -2,12 +2,12 @@ import * as strtok3 from 'strtok3';
|
|
|
2
2
|
import * as Token from 'token-types';
|
|
3
3
|
import initDebug from 'debug';
|
|
4
4
|
import * as riff from '../riff/RiffChunk.js';
|
|
5
|
-
import * as WaveChunk from '
|
|
5
|
+
import * as WaveChunk from './WaveChunk.js';
|
|
6
6
|
import { ID3v2Parser } from '../id3v2/ID3v2Parser.js';
|
|
7
7
|
import * as util from '../common/Util.js';
|
|
8
8
|
import { FourCcToken } from '../common/FourCC.js';
|
|
9
9
|
import { BasicParser } from '../common/BasicParser.js';
|
|
10
|
-
import { BroadcastAudioExtensionChunk } from '
|
|
10
|
+
import { BroadcastAudioExtensionChunk } from './BwfChunk.js';
|
|
11
11
|
const debug = initDebug('music-metadata:parser:RIFF');
|
|
12
12
|
/**
|
|
13
13
|
* Resource Interchange File Format (RIFF) Parser
|
|
@@ -21,6 +21,10 @@ const debug = initDebug('music-metadata:parser:RIFF');
|
|
|
21
21
|
* ToDo: Split WAVE part from RIFF parser
|
|
22
22
|
*/
|
|
23
23
|
export class WaveParser extends BasicParser {
|
|
24
|
+
constructor() {
|
|
25
|
+
super(...arguments);
|
|
26
|
+
this.blockAlign = 0;
|
|
27
|
+
}
|
|
24
28
|
async parse() {
|
|
25
29
|
const riffHeader = await this.tokenizer.readToken(riff.Header);
|
|
26
30
|
debug(`pos=${this.tokenizer.position}, parse: chunkID=${riffHeader.chunkID}`);
|
|
@@ -59,12 +63,12 @@ export class WaveParser extends BasicParser {
|
|
|
59
63
|
this.metadata.setFormat('lossless', false);
|
|
60
64
|
this.fact = await this.tokenizer.readToken(new WaveChunk.FactChunk(header));
|
|
61
65
|
break;
|
|
62
|
-
case 'fmt ': // The Util Chunk, non-PCM Formats
|
|
66
|
+
case 'fmt ': { // The Util Chunk, non-PCM Formats
|
|
63
67
|
const fmt = await this.tokenizer.readToken(new WaveChunk.Format(header));
|
|
64
68
|
let subFormat = WaveChunk.WaveFormat[fmt.wFormatTag];
|
|
65
69
|
if (!subFormat) {
|
|
66
|
-
debug(
|
|
67
|
-
subFormat =
|
|
70
|
+
debug(`WAVE/non-PCM format=${fmt.wFormatTag}`);
|
|
71
|
+
subFormat = `non-PCM (${fmt.wFormatTag})`;
|
|
68
72
|
}
|
|
69
73
|
this.metadata.setFormat('codec', subFormat);
|
|
70
74
|
this.metadata.setFormat('bitsPerSample', fmt.wBitsPerSample);
|
|
@@ -73,13 +77,15 @@ export class WaveParser extends BasicParser {
|
|
|
73
77
|
this.metadata.setFormat('bitrate', fmt.nBlockAlign * fmt.nSamplesPerSec * 8);
|
|
74
78
|
this.blockAlign = fmt.nBlockAlign;
|
|
75
79
|
break;
|
|
80
|
+
}
|
|
76
81
|
case 'id3 ': // The way Picard, FooBar currently stores, ID3 meta-data
|
|
77
|
-
case 'ID3 ': // The way Mp3Tags stores ID3 meta-data
|
|
82
|
+
case 'ID3 ': { // The way Mp3Tags stores ID3 meta-data
|
|
78
83
|
const id3_data = await this.tokenizer.readToken(new Token.Uint8ArrayType(header.chunkSize));
|
|
79
84
|
const rst = strtok3.fromBuffer(id3_data);
|
|
80
85
|
await new ID3v2Parser().parse(this.metadata, rst, this.options);
|
|
81
86
|
break;
|
|
82
|
-
|
|
87
|
+
}
|
|
88
|
+
case 'data': { // PCM-data
|
|
83
89
|
if (this.metadata.format.lossless !== false) {
|
|
84
90
|
this.metadata.setFormat('lossless', true);
|
|
85
91
|
}
|
|
@@ -94,32 +100,36 @@ export class WaveParser extends BasicParser {
|
|
|
94
100
|
const numberOfSamples = this.fact ? this.fact.dwSampleLength : (chunkSize === 0xffffffff ? undefined : chunkSize / this.blockAlign);
|
|
95
101
|
if (numberOfSamples) {
|
|
96
102
|
this.metadata.setFormat('numberOfSamples', numberOfSamples);
|
|
97
|
-
|
|
103
|
+
if (this.metadata.format.sampleRate) {
|
|
104
|
+
this.metadata.setFormat('duration', numberOfSamples / this.metadata.format.sampleRate);
|
|
105
|
+
}
|
|
98
106
|
}
|
|
99
107
|
if (this.metadata.format.codec === 'ADPCM') { // ADPCM is 4 bits lossy encoding resulting in 352kbps
|
|
100
108
|
this.metadata.setFormat('bitrate', 352000);
|
|
101
109
|
}
|
|
102
|
-
else {
|
|
110
|
+
else if (this.metadata.format.sampleRate) {
|
|
103
111
|
this.metadata.setFormat('bitrate', this.blockAlign * this.metadata.format.sampleRate * 8);
|
|
104
112
|
}
|
|
105
113
|
await this.tokenizer.ignore(header.chunkSize);
|
|
106
114
|
break;
|
|
107
|
-
|
|
115
|
+
}
|
|
116
|
+
case 'bext': { // Broadcast Audio Extension chunk https://tech.ebu.ch/docs/tech/tech3285.pdf
|
|
108
117
|
const bext = await this.tokenizer.readToken(BroadcastAudioExtensionChunk);
|
|
109
118
|
Object.keys(bext).forEach(key => {
|
|
110
|
-
this.metadata.addTag('exif',
|
|
119
|
+
this.metadata.addTag('exif', `bext.${key}`, bext[key]);
|
|
111
120
|
});
|
|
112
121
|
const bextRemaining = header.chunkSize - BroadcastAudioExtensionChunk.len;
|
|
113
122
|
await this.tokenizer.ignore(bextRemaining);
|
|
114
123
|
break;
|
|
124
|
+
}
|
|
115
125
|
case '\x00\x00\x00\x00': // padding ??
|
|
116
126
|
debug(`Ignore padding chunk: RIFF/${header.chunkID} of ${header.chunkSize} bytes`);
|
|
117
|
-
this.metadata.addWarning(
|
|
127
|
+
this.metadata.addWarning(`Ignore chunk: RIFF/${header.chunkID}`);
|
|
118
128
|
await this.tokenizer.ignore(header.chunkSize);
|
|
119
129
|
break;
|
|
120
130
|
default:
|
|
121
131
|
debug(`Ignore chunk: RIFF/${header.chunkID} of ${header.chunkSize} bytes`);
|
|
122
|
-
this.metadata.addWarning(
|
|
132
|
+
this.metadata.addWarning(`Ignore chunk: RIFF/${header.chunkID}`);
|
|
123
133
|
await this.tokenizer.ignore(header.chunkSize);
|
|
124
134
|
}
|
|
125
135
|
if (this.header.chunkSize % 2 === 1) {
|
|
@@ -134,10 +144,9 @@ export class WaveParser extends BasicParser {
|
|
|
134
144
|
switch (listType) {
|
|
135
145
|
case 'INFO':
|
|
136
146
|
return this.parseRiffInfoTags(listHeader.chunkSize - 4);
|
|
137
|
-
case 'adtl':
|
|
138
147
|
default:
|
|
139
|
-
this.metadata.addWarning(
|
|
140
|
-
debug(
|
|
148
|
+
this.metadata.addWarning(`Ignore chunk: RIFF/WAVE/LIST/${listType}`);
|
|
149
|
+
debug(`Ignoring chunkID=RIFF/WAVE/LIST/${listType}`);
|
|
141
150
|
return this.tokenizer.ignore(listHeader.chunkSize - 4).then();
|
|
142
151
|
}
|
|
143
152
|
}
|
|
@@ -150,10 +159,11 @@ export class WaveParser extends BasicParser {
|
|
|
150
159
|
chunkSize -= (8 + valueToken.len);
|
|
151
160
|
}
|
|
152
161
|
if (chunkSize !== 0) {
|
|
153
|
-
throw Error(
|
|
162
|
+
throw new Error(`Illegal remaining size: ${chunkSize}`);
|
|
154
163
|
}
|
|
155
164
|
}
|
|
156
165
|
addTag(id, value) {
|
|
157
166
|
this.metadata.addTag('exif', id, value);
|
|
158
167
|
}
|
|
159
168
|
}
|
|
169
|
+
//# sourceMappingURL=WaveParser.js.map
|
|
@@ -9,7 +9,7 @@ export declare class WavPackParser extends BasicParser {
|
|
|
9
9
|
/**
|
|
10
10
|
* Ref: http://www.wavpack.com/WavPack5FileFormat.pdf, 3.0 Metadata Sub-blocks
|
|
11
11
|
* @param header Header
|
|
12
|
-
* @param remainingLength
|
|
12
|
+
* @param remainingLength Remaining length
|
|
13
13
|
*/
|
|
14
14
|
private parseMetadataSubBlock;
|
|
15
15
|
}
|