music-metadata 8.3.0 → 9.0.1
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 +519 -488
- package/lib/ParserFactory.d.ts +1 -1
- package/lib/ParserFactory.js +1 -2
- package/lib/aiff/AiffParser.js +3 -4
- package/lib/aiff/AiffToken.d.ts +2 -4
- package/lib/aiff/AiffToken.js +7 -7
- package/lib/apev2/APEv2Parser.d.ts +1 -1
- package/lib/apev2/APEv2Parser.js +10 -12
- package/lib/apev2/APEv2Token.d.ts +1 -1
- package/lib/asf/AsfObject.d.ts +15 -17
- package/lib/asf/AsfObject.js +51 -45
- package/lib/asf/AsfParser.js +6 -8
- package/lib/asf/AsfUtil.d.ts +1 -3
- package/lib/asf/AsfUtil.js +4 -5
- package/lib/asf/GUID.d.ts +4 -5
- package/lib/asf/GUID.js +14 -11
- package/lib/common/BasicParser.d.ts +1 -1
- package/lib/common/FourCC.d.ts +1 -1
- package/lib/common/FourCC.js +5 -3
- package/lib/common/MetadataCollector.d.ts +3 -3
- package/lib/common/MetadataCollector.js +7 -8
- package/lib/common/RandomFileReader.d.ts +2 -3
- package/lib/common/RandomFileReader.js +1 -1
- package/lib/common/Util.d.ts +1 -1
- package/lib/common/Util.js +4 -3
- package/lib/core.d.ts +16 -8
- package/lib/core.js +19 -5
- package/lib/dsdiff/DsdiffParser.js +1 -1
- package/lib/dsdiff/DsdiffToken.d.ts +1 -1
- package/lib/dsf/DsfChunk.d.ts +1 -1
- package/lib/flac/FlacParser.d.ts +1 -1
- package/lib/flac/FlacParser.js +6 -4
- package/lib/id3v1/ID3v1Parser.js +7 -7
- package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
- package/lib/id3v2/AbstractID3Parser.js +1 -1
- package/lib/id3v2/FrameParser.js +3 -3
- package/lib/id3v2/ID3v24TagMapper.d.ts +2 -3
- package/lib/id3v2/ID3v24TagMapper.js +4 -4
- package/lib/id3v2/ID3v2Parser.d.ts +1 -1
- package/lib/id3v2/ID3v2Parser.js +10 -17
- package/lib/id3v2/ID3v2Token.d.ts +1 -1
- package/lib/id3v2/ID3v2Token.js +2 -2
- package/lib/iff/index.d.ts +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.js +1 -1
- package/lib/lyrics3/Lyrics3.js +4 -4
- package/lib/matroska/MatroskaParser.d.ts +9 -2
- package/lib/matroska/MatroskaParser.js +44 -39
- package/lib/matroska/types.d.ts +13 -14
- package/lib/mp4/Atom.d.ts +1 -1
- package/lib/mp4/AtomToken.d.ts +11 -11
- package/lib/mp4/AtomToken.js +3 -2
- package/lib/mp4/MP4Parser.js +20 -19
- package/lib/mpeg/ExtendedLameHeader.d.ts +1 -1
- package/lib/mpeg/MpegParser.js +4 -4
- package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
- package/lib/mpeg/XingTag.d.ts +2 -3
- package/lib/mpeg/XingTag.js +1 -1
- package/lib/musepack/index.js +1 -1
- package/lib/musepack/sv7/BitReader.d.ts +1 -1
- package/lib/musepack/sv7/StreamVersion7.d.ts +1 -1
- package/lib/musepack/sv7/StreamVersion7.js +1 -1
- package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
- package/lib/musepack/sv8/StreamVersion8.js +1 -1
- package/lib/ogg/Ogg.d.ts +2 -2
- package/lib/ogg/OggParser.d.ts +1 -1
- package/lib/ogg/OggParser.js +5 -5
- package/lib/ogg/opus/Opus.d.ts +1 -1
- package/lib/ogg/opus/Opus.js +6 -6
- package/lib/ogg/opus/OpusParser.d.ts +4 -5
- package/lib/ogg/opus/OpusParser.js +3 -3
- package/lib/ogg/speex/Speex.d.ts +1 -1
- package/lib/ogg/speex/Speex.js +13 -13
- package/lib/ogg/speex/SpeexParser.d.ts +3 -4
- package/lib/ogg/speex/SpeexParser.js +1 -1
- package/lib/ogg/theora/Theora.d.ts +1 -1
- package/lib/ogg/theora/Theora.js +6 -6
- package/lib/ogg/theora/TheoraParser.d.ts +4 -5
- package/lib/ogg/theora/TheoraParser.js +4 -4
- package/lib/ogg/vorbis/Vorbis.d.ts +3 -4
- package/lib/ogg/vorbis/Vorbis.js +11 -12
- package/lib/ogg/vorbis/VorbisDecoder.js +1 -1
- package/lib/ogg/vorbis/VorbisParser.d.ts +8 -8
- package/lib/ogg/vorbis/VorbisParser.js +21 -12
- package/lib/riff/RiffChunk.d.ts +1 -1
- package/lib/riff/RiffChunk.js +2 -2
- package/lib/type.d.ts +9 -11
- package/lib/wav/BwfChunk.d.ts +1 -1
- package/lib/wav/WaveChunk.d.ts +3 -4
- package/lib/wav/WaveChunk.js +8 -7
- package/lib/wav/WaveParser.js +2 -2
- package/lib/wavpack/WavPackParser.d.ts +1 -0
- package/lib/wavpack/WavPackParser.js +5 -3
- package/lib/wavpack/WavPackToken.d.ts +1 -1
- package/package.json +141 -140
package/lib/ParserFactory.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { INativeMetadataCollector } from './common/MetadataCollector.js';
|
|
2
2
|
import { IOptions, IAudioMetadata, ParserType } from './type.js';
|
|
3
|
-
import { ITokenizer } from 'strtok3
|
|
3
|
+
import type { ITokenizer } from 'strtok3';
|
|
4
4
|
export interface ITokenParser {
|
|
5
5
|
/**
|
|
6
6
|
* Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
|
package/lib/ParserFactory.js
CHANGED
|
@@ -2,7 +2,6 @@ import { fileTypeFromBuffer } from 'file-type';
|
|
|
2
2
|
import ContentType from 'content-type';
|
|
3
3
|
import MimeType from 'media-typer';
|
|
4
4
|
import initDebug from 'debug';
|
|
5
|
-
import { Buffer } from 'node:buffer';
|
|
6
5
|
import { MetadataCollector } from './common/MetadataCollector.js';
|
|
7
6
|
import { AIFFParser } from './aiff/AiffParser.js';
|
|
8
7
|
import { APEv2Parser } from './apev2/APEv2Parser.js';
|
|
@@ -55,7 +54,7 @@ export class ParserFactory {
|
|
|
55
54
|
if (!parserId) {
|
|
56
55
|
// Parser could not be determined on MIME-type or extension
|
|
57
56
|
debug('Guess parser on content...');
|
|
58
|
-
const buf =
|
|
57
|
+
const buf = new Uint8Array(4100);
|
|
59
58
|
await tokenizer.peekBuffer(buf, { mayBeLess: true });
|
|
60
59
|
if (tokenizer.fileInfo.path) {
|
|
61
60
|
parserId = this.getParserIdForExtension(tokenizer.fileInfo.path);
|
package/lib/aiff/AiffParser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Token from 'token-types';
|
|
2
2
|
import initDebug from 'debug';
|
|
3
|
-
import * as strtok3 from 'strtok3
|
|
3
|
+
import * as strtok3 from 'strtok3';
|
|
4
4
|
import { ID3v2Parser } from '../id3v2/ID3v2Parser.js';
|
|
5
5
|
import { FourCcToken } from '../common/FourCC.js';
|
|
6
6
|
import { BasicParser } from '../common/BasicParser.js';
|
|
@@ -96,9 +96,8 @@ export class AIFFParser extends BasicParser {
|
|
|
96
96
|
}
|
|
97
97
|
async readTextChunk(header) {
|
|
98
98
|
const value = await this.tokenizer.readToken(new Token.StringType(header.chunkSize, 'ascii'));
|
|
99
|
-
value.split('\0').map(v => v.trim()).filter(v => v && v.length
|
|
100
|
-
|
|
101
|
-
});
|
|
99
|
+
const values = value.split('\0').map(v => v.trim()).filter(v => v && v.length);
|
|
100
|
+
await Promise.all(values.map(v => this.metadata.addTag('AIFF', header.chunkID, v)));
|
|
102
101
|
return header.chunkSize;
|
|
103
102
|
}
|
|
104
103
|
}
|
package/lib/aiff/AiffToken.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { Buffer } from 'node:buffer';
|
|
3
1
|
import * as iff from '../iff/index.js';
|
|
4
|
-
import { IGetToken } from 'strtok3';
|
|
2
|
+
import type { IGetToken } from 'strtok3';
|
|
5
3
|
/**
|
|
6
4
|
* The Common Chunk.
|
|
7
5
|
* Describes fundamental parameters of the waveform data such as sample rate, bit resolution, and how many channels of
|
|
@@ -19,5 +17,5 @@ export declare class Common implements IGetToken<ICommon> {
|
|
|
19
17
|
private isAifc;
|
|
20
18
|
len: number;
|
|
21
19
|
constructor(header: iff.IChunkHeader, isAifc: boolean);
|
|
22
|
-
get(buf:
|
|
20
|
+
get(buf: Uint8Array, off: number): ICommon;
|
|
23
21
|
}
|
package/lib/aiff/AiffToken.js
CHANGED
|
@@ -10,22 +10,22 @@ export class Common {
|
|
|
10
10
|
}
|
|
11
11
|
get(buf, off) {
|
|
12
12
|
// see: https://cycling74.com/forums/aiffs-80-bit-sample-rate-value
|
|
13
|
-
const shift =
|
|
14
|
-
const baseSampleRate =
|
|
13
|
+
const shift = Token.UINT16_BE.get(buf, off + 8) - 16398;
|
|
14
|
+
const baseSampleRate = Token.UINT16_BE.get(buf, off + 8 + 2);
|
|
15
15
|
const res = {
|
|
16
|
-
numChannels:
|
|
17
|
-
numSampleFrames:
|
|
18
|
-
sampleSize:
|
|
16
|
+
numChannels: Token.UINT16_BE.get(buf, off),
|
|
17
|
+
numSampleFrames: Token.UINT32_BE.get(buf, off + 2),
|
|
18
|
+
sampleSize: Token.UINT16_BE.get(buf, off + 6),
|
|
19
19
|
sampleRate: shift < 0 ? baseSampleRate >> Math.abs(shift) : baseSampleRate << shift
|
|
20
20
|
};
|
|
21
21
|
if (this.isAifc) {
|
|
22
22
|
res.compressionType = FourCcToken.get(buf, off + 18);
|
|
23
23
|
if (this.len > 22) {
|
|
24
|
-
const strLen =
|
|
24
|
+
const strLen = Token.UINT8.get(buf, off + 22);
|
|
25
25
|
if (strLen > 0) {
|
|
26
26
|
const padding = (strLen + 1) % 2;
|
|
27
27
|
if (23 + strLen + padding === this.len) {
|
|
28
|
-
res.compressionName = new Token.StringType(strLen, '
|
|
28
|
+
res.compressionName = new Token.StringType(strLen, 'latin1').get(buf, off + 23);
|
|
29
29
|
}
|
|
30
30
|
else {
|
|
31
31
|
throw new Error('Illegal pstring length');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as strtok3 from 'strtok3
|
|
1
|
+
import * as strtok3 from 'strtok3';
|
|
2
2
|
import { IOptions, IRandomReader, IApeHeader } from '../type.js';
|
|
3
3
|
import { INativeMetadataCollector } from '../common/MetadataCollector.js';
|
|
4
4
|
import { BasicParser } from '../common/BasicParser.js';
|
package/lib/apev2/APEv2Parser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import initDebug from 'debug';
|
|
2
|
-
import * as strtok3 from 'strtok3
|
|
2
|
+
import * as strtok3 from 'strtok3';
|
|
3
3
|
import { StringType } from 'token-types';
|
|
4
|
-
import {
|
|
4
|
+
import { uint8ArrayToString } from 'uint8array-extras';
|
|
5
5
|
import * as util from '../common/Util.js';
|
|
6
6
|
import { BasicParser } from '../common/BasicParser.js';
|
|
7
7
|
import { DataType, DescriptorParser, Header, TagFooter, TagItemHeader } from './APEv2Token.js';
|
|
@@ -35,7 +35,7 @@ export class APEv2Parser extends BasicParser {
|
|
|
35
35
|
*/
|
|
36
36
|
static async findApeFooterOffset(reader, offset) {
|
|
37
37
|
// Search for APE footer header at the end of the file
|
|
38
|
-
const apeBuf =
|
|
38
|
+
const apeBuf = new Uint8Array(TagFooter.len);
|
|
39
39
|
await reader.randomRead(apeBuf, 0, TagFooter.len, offset - TagFooter.len);
|
|
40
40
|
const tagFooter = TagFooter.get(apeBuf, 0);
|
|
41
41
|
if (tagFooter.ID === 'APETAGEX') {
|
|
@@ -70,7 +70,7 @@ export class APEv2Parser extends BasicParser {
|
|
|
70
70
|
if (this.tokenizer.fileInfo.size) {
|
|
71
71
|
// Try to read the APEv2 header using just the footer-header
|
|
72
72
|
const remaining = this.tokenizer.fileInfo.size - this.tokenizer.position; // ToDo: take ID3v1 into account
|
|
73
|
-
const buffer =
|
|
73
|
+
const buffer = new Uint8Array(remaining);
|
|
74
74
|
await this.tokenizer.readBuffer(buffer);
|
|
75
75
|
return APEv2Parser.parseTagFooter(this.metadata, buffer, this.options);
|
|
76
76
|
}
|
|
@@ -87,7 +87,7 @@ export class APEv2Parser extends BasicParser {
|
|
|
87
87
|
return this.tryParseApeHeader();
|
|
88
88
|
}
|
|
89
89
|
async parseTags(footer) {
|
|
90
|
-
const keyBuffer =
|
|
90
|
+
const keyBuffer = new Uint8Array(256); // maximum tag key length
|
|
91
91
|
let bytesRemaining = footer.size - TagFooter.len;
|
|
92
92
|
debug(`Parse APE tags at offset=${this.tokenizer.position}, size=${bytesRemaining}`);
|
|
93
93
|
for (let i = 0; i < footer.fields; i++) {
|
|
@@ -107,9 +107,7 @@ export class APEv2Parser extends BasicParser {
|
|
|
107
107
|
case DataType.text_utf8: { // utf-8 text-string
|
|
108
108
|
const value = await this.tokenizer.readToken(new StringType(tagItemHeader.size, 'utf8'));
|
|
109
109
|
const values = value.split(/\x00/g);
|
|
110
|
-
|
|
111
|
-
this.metadata.addTag(tagFormat, key, val);
|
|
112
|
-
}
|
|
110
|
+
await Promise.all(values.map(val => this.metadata.addTag(tagFormat, key, val)));
|
|
113
111
|
break;
|
|
114
112
|
}
|
|
115
113
|
case DataType.binary: // binary (probably artwork)
|
|
@@ -117,12 +115,12 @@ export class APEv2Parser extends BasicParser {
|
|
|
117
115
|
await this.tokenizer.ignore(tagItemHeader.size);
|
|
118
116
|
}
|
|
119
117
|
else {
|
|
120
|
-
const picData =
|
|
118
|
+
const picData = new Uint8Array(tagItemHeader.size);
|
|
121
119
|
await this.tokenizer.readBuffer(picData);
|
|
122
120
|
zero = util.findZero(picData, 0, picData.length);
|
|
123
|
-
const description = picData.
|
|
124
|
-
const data =
|
|
125
|
-
this.metadata.addTag(tagFormat, key, {
|
|
121
|
+
const description = uint8ArrayToString(picData.slice(0, zero));
|
|
122
|
+
const data = picData.slice(zero + 1);
|
|
123
|
+
await this.metadata.addTag(tagFormat, key, {
|
|
126
124
|
description,
|
|
127
125
|
data
|
|
128
126
|
});
|
package/lib/asf/AsfObject.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { IGetToken, ITokenizer } from 'strtok3/core';
|
|
3
|
-
import { Buffer } from 'node:buffer';
|
|
1
|
+
import type { IGetToken, ITokenizer } from 'strtok3';
|
|
4
2
|
import { IPicture, ITag } from '../type.js';
|
|
5
3
|
import GUID from './GUID.js';
|
|
6
4
|
/**
|
|
@@ -56,21 +54,21 @@ export interface IAsfTopLevelObjectHeader extends IAsfObjectHeader {
|
|
|
56
54
|
* Token for: 3. ASF top-level Header Object
|
|
57
55
|
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3
|
|
58
56
|
*/
|
|
59
|
-
export declare const TopLevelHeaderObjectToken: IGetToken<IAsfTopLevelObjectHeader,
|
|
57
|
+
export declare const TopLevelHeaderObjectToken: IGetToken<IAsfTopLevelObjectHeader, Uint8Array>;
|
|
60
58
|
/**
|
|
61
59
|
* Token for: 3.1 Header Object (mandatory, one only)
|
|
62
60
|
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_1
|
|
63
61
|
*/
|
|
64
|
-
export declare const HeaderObjectToken: IGetToken<IAsfObjectHeader,
|
|
62
|
+
export declare const HeaderObjectToken: IGetToken<IAsfObjectHeader, Uint8Array>;
|
|
65
63
|
export declare abstract class State<T> implements IGetToken<T> {
|
|
66
64
|
len: number;
|
|
67
65
|
constructor(header: IAsfObjectHeader);
|
|
68
|
-
abstract get(buf:
|
|
66
|
+
abstract get(buf: Uint8Array, off: number): T;
|
|
69
67
|
protected postProcessTag(tags: ITag[], name: string, valueType: number, data: any): void;
|
|
70
68
|
}
|
|
71
69
|
export declare class IgnoreObjectState extends State<any> {
|
|
72
70
|
constructor(header: IAsfObjectHeader);
|
|
73
|
-
get(buf:
|
|
71
|
+
get(buf: Uint8Array, off: number): null;
|
|
74
72
|
}
|
|
75
73
|
/**
|
|
76
74
|
* Interface for: 3.2: File Properties Object (mandatory, one only)
|
|
@@ -176,7 +174,7 @@ export interface IFilePropertiesObject {
|
|
|
176
174
|
export declare class FilePropertiesObject extends State<IFilePropertiesObject> {
|
|
177
175
|
static guid: GUID;
|
|
178
176
|
constructor(header: IAsfObjectHeader);
|
|
179
|
-
get(buf:
|
|
177
|
+
get(buf: Uint8Array, off: number): IFilePropertiesObject;
|
|
180
178
|
}
|
|
181
179
|
/**
|
|
182
180
|
* Interface for: 3.3 Stream Properties Object (mandatory, one per stream)
|
|
@@ -198,7 +196,7 @@ export interface IStreamPropertiesObject {
|
|
|
198
196
|
export declare class StreamPropertiesObject extends State<IStreamPropertiesObject> {
|
|
199
197
|
static guid: GUID;
|
|
200
198
|
constructor(header: IAsfObjectHeader);
|
|
201
|
-
get(buf:
|
|
199
|
+
get(buf: Uint8Array, off: number): IStreamPropertiesObject;
|
|
202
200
|
}
|
|
203
201
|
export interface IHeaderExtensionObject {
|
|
204
202
|
reserved1: GUID;
|
|
@@ -213,7 +211,7 @@ export declare class HeaderExtensionObject implements IGetToken<IHeaderExtension
|
|
|
213
211
|
static guid: GUID;
|
|
214
212
|
len: number;
|
|
215
213
|
constructor();
|
|
216
|
-
get(buf:
|
|
214
|
+
get(buf: Uint8Array, off: number): IHeaderExtensionObject;
|
|
217
215
|
}
|
|
218
216
|
export interface ICodecEntry {
|
|
219
217
|
type: {
|
|
@@ -222,7 +220,7 @@ export interface ICodecEntry {
|
|
|
222
220
|
};
|
|
223
221
|
codecName: string;
|
|
224
222
|
description: string;
|
|
225
|
-
information:
|
|
223
|
+
information: Uint8Array;
|
|
226
224
|
}
|
|
227
225
|
/**
|
|
228
226
|
* 3.5: Read the Codec-List-Object, which provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
|
@@ -237,7 +235,7 @@ export declare class ContentDescriptionObjectState extends State<ITag[]> {
|
|
|
237
235
|
static guid: GUID;
|
|
238
236
|
private static contentDescTags;
|
|
239
237
|
constructor(header: IAsfObjectHeader);
|
|
240
|
-
get(buf:
|
|
238
|
+
get(buf: Uint8Array, off: number): ITag[];
|
|
241
239
|
}
|
|
242
240
|
/**
|
|
243
241
|
* 3.11 Extended Content Description Object (optional, one only)
|
|
@@ -246,7 +244,7 @@ export declare class ContentDescriptionObjectState extends State<ITag[]> {
|
|
|
246
244
|
export declare class ExtendedContentDescriptionObjectState extends State<ITag[]> {
|
|
247
245
|
static guid: GUID;
|
|
248
246
|
constructor(header: IAsfObjectHeader);
|
|
249
|
-
get(buf:
|
|
247
|
+
get(buf: Uint8Array, off: number): ITag[];
|
|
250
248
|
}
|
|
251
249
|
export interface IStreamName {
|
|
252
250
|
streamLanguageId: number;
|
|
@@ -286,7 +284,7 @@ export interface IExtendedStreamPropertiesObject {
|
|
|
286
284
|
export declare class ExtendedStreamPropertiesObjectState extends State<IExtendedStreamPropertiesObject> {
|
|
287
285
|
static guid: GUID;
|
|
288
286
|
constructor(header: IAsfObjectHeader);
|
|
289
|
-
get(buf:
|
|
287
|
+
get(buf: Uint8Array, off: number): IExtendedStreamPropertiesObject;
|
|
290
288
|
}
|
|
291
289
|
/**
|
|
292
290
|
* 4.7 Metadata Object (optional, 0 or 1)
|
|
@@ -306,7 +304,7 @@ export interface IWmPicture extends IPicture {
|
|
|
306
304
|
format: string;
|
|
307
305
|
description: string;
|
|
308
306
|
size: number;
|
|
309
|
-
data:
|
|
307
|
+
data: Uint8Array;
|
|
310
308
|
}
|
|
311
309
|
/**
|
|
312
310
|
* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd757977(v=vs.85).aspx
|
|
@@ -314,7 +312,7 @@ export interface IWmPicture extends IPicture {
|
|
|
314
312
|
export declare class WmPictureToken implements IGetToken<IWmPicture> {
|
|
315
313
|
len: any;
|
|
316
314
|
static fromBase64(base64str: string): IPicture;
|
|
317
|
-
static fromBuffer(buffer:
|
|
315
|
+
static fromBuffer(buffer: Uint8Array): IWmPicture;
|
|
318
316
|
constructor(len: any);
|
|
319
|
-
get(buffer:
|
|
317
|
+
get(buffer: Uint8Array, offset: number): IWmPicture;
|
|
320
318
|
}
|
package/lib/asf/AsfObject.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// ASF Objects
|
|
2
2
|
import * as Token from 'token-types';
|
|
3
|
-
import { Buffer } from 'node:buffer';
|
|
4
3
|
import * as util from '../common/Util.js';
|
|
5
4
|
import GUID from './GUID.js';
|
|
6
5
|
import { AsfUtil } from './AsfUtil.js';
|
|
7
6
|
import { AttachedPictureType } from '../id3v2/ID3v2Token.js';
|
|
7
|
+
import { base64ToUint8Array } from 'uint8array-extras';
|
|
8
8
|
/**
|
|
9
9
|
* Data Type: Specifies the type of information being stored. The following values are recognized.
|
|
10
10
|
*/
|
|
@@ -43,7 +43,7 @@ export const TopLevelHeaderObjectToken = {
|
|
|
43
43
|
len: 30,
|
|
44
44
|
get: (buf, off) => {
|
|
45
45
|
return {
|
|
46
|
-
objectId: GUID.fromBin(
|
|
46
|
+
objectId: GUID.fromBin(buf, off),
|
|
47
47
|
objectSize: Number(Token.UINT64_LE.get(buf, off + 16)),
|
|
48
48
|
numberOfHeaderObjects: Token.UINT32_LE.get(buf, off + 24)
|
|
49
49
|
// Reserved: 2 bytes
|
|
@@ -58,7 +58,7 @@ export const HeaderObjectToken = {
|
|
|
58
58
|
len: 24,
|
|
59
59
|
get: (buf, off) => {
|
|
60
60
|
return {
|
|
61
|
-
objectId: GUID.fromBin(
|
|
61
|
+
objectId: GUID.fromBin(buf, off),
|
|
62
62
|
objectSize: Number(Token.UINT64_LE.get(buf, off + 16))
|
|
63
63
|
};
|
|
64
64
|
}
|
|
@@ -144,10 +144,11 @@ export class HeaderExtensionObject {
|
|
|
144
144
|
this.len = 22;
|
|
145
145
|
}
|
|
146
146
|
get(buf, off) {
|
|
147
|
+
const view = new DataView(buf.buffer, off);
|
|
147
148
|
return {
|
|
148
149
|
reserved1: GUID.fromBin(buf, off),
|
|
149
|
-
reserved2:
|
|
150
|
-
extensionDataSize:
|
|
150
|
+
reserved2: view.getUint16(16, true),
|
|
151
|
+
extensionDataSize: view.getUint16(18, true)
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -159,14 +160,15 @@ HeaderExtensionObject.guid = GUID.HeaderExtensionObject;
|
|
|
159
160
|
const CodecListObjectHeader = {
|
|
160
161
|
len: 20,
|
|
161
162
|
get: (buf, off) => {
|
|
163
|
+
const view = new DataView(buf.buffer, off);
|
|
162
164
|
return {
|
|
163
|
-
entryCount:
|
|
165
|
+
entryCount: view.getUint16(16, true)
|
|
164
166
|
};
|
|
165
167
|
}
|
|
166
168
|
};
|
|
167
169
|
async function readString(tokenizer) {
|
|
168
170
|
const length = await tokenizer.readNumber(Token.UINT16_LE);
|
|
169
|
-
return (await tokenizer.readToken(new Token.StringType(length * 2, '
|
|
171
|
+
return (await tokenizer.readToken(new Token.StringType(length * 2, 'utf-16le'))).replace('\0', '');
|
|
170
172
|
}
|
|
171
173
|
/**
|
|
172
174
|
* 3.5: Read the Codec-List-Object, which provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
|
@@ -182,7 +184,7 @@ export async function readCodecEntries(tokenizer) {
|
|
|
182
184
|
}
|
|
183
185
|
async function readInformation(tokenizer) {
|
|
184
186
|
const length = await tokenizer.readNumber(Token.UINT16_LE);
|
|
185
|
-
const buf =
|
|
187
|
+
const buf = new Uint8Array(length);
|
|
186
188
|
await tokenizer.readBuffer(buf);
|
|
187
189
|
return buf;
|
|
188
190
|
}
|
|
@@ -212,13 +214,14 @@ export class ContentDescriptionObjectState extends State {
|
|
|
212
214
|
}
|
|
213
215
|
get(buf, off) {
|
|
214
216
|
const tags = [];
|
|
215
|
-
|
|
217
|
+
const view = new DataView(buf.buffer, off);
|
|
218
|
+
let pos = 10;
|
|
216
219
|
for (let i = 0; i < ContentDescriptionObjectState.contentDescTags.length; ++i) {
|
|
217
|
-
const length =
|
|
220
|
+
const length = view.getUint16(i * 2, true);
|
|
218
221
|
if (length > 0) {
|
|
219
222
|
const tagName = ContentDescriptionObjectState.contentDescTags[i];
|
|
220
223
|
const end = pos + length;
|
|
221
|
-
tags.push({ id: tagName, value: AsfUtil.parseUnicodeAttr(buf.slice(pos, end)) });
|
|
224
|
+
tags.push({ id: tagName, value: AsfUtil.parseUnicodeAttr(buf.slice(off + pos, off + end)) });
|
|
222
225
|
pos = end;
|
|
223
226
|
}
|
|
224
227
|
}
|
|
@@ -237,18 +240,19 @@ export class ExtendedContentDescriptionObjectState extends State {
|
|
|
237
240
|
}
|
|
238
241
|
get(buf, off) {
|
|
239
242
|
const tags = [];
|
|
240
|
-
const
|
|
241
|
-
|
|
243
|
+
const view = new DataView(buf.buffer, off);
|
|
244
|
+
const attrCount = view.getUint16(0, true);
|
|
245
|
+
let pos = 2;
|
|
242
246
|
for (let i = 0; i < attrCount; i += 1) {
|
|
243
|
-
const nameLen =
|
|
247
|
+
const nameLen = view.getUint16(pos, true);
|
|
244
248
|
pos += 2;
|
|
245
|
-
const name = AsfUtil.parseUnicodeAttr(buf.slice(pos, pos + nameLen));
|
|
249
|
+
const name = AsfUtil.parseUnicodeAttr(buf.slice(off + pos, off + pos + nameLen));
|
|
246
250
|
pos += nameLen;
|
|
247
|
-
const valueType =
|
|
251
|
+
const valueType = view.getUint16(pos, true);
|
|
248
252
|
pos += 2;
|
|
249
|
-
const valueLen =
|
|
253
|
+
const valueLen = view.getUint16(pos, true);
|
|
250
254
|
pos += 2;
|
|
251
|
-
const value = buf.slice(pos, pos + valueLen);
|
|
255
|
+
const value = buf.slice(off + pos, off + pos + valueLen);
|
|
252
256
|
pos += valueLen;
|
|
253
257
|
this.postProcessTag(tags, name, valueType, value);
|
|
254
258
|
}
|
|
@@ -265,27 +269,28 @@ export class ExtendedStreamPropertiesObjectState extends State {
|
|
|
265
269
|
super(header);
|
|
266
270
|
}
|
|
267
271
|
get(buf, off) {
|
|
272
|
+
const view = new DataView(buf.buffer, off);
|
|
268
273
|
return {
|
|
269
274
|
startTime: Token.UINT64_LE.get(buf, off),
|
|
270
275
|
endTime: Token.UINT64_LE.get(buf, off + 8),
|
|
271
|
-
dataBitrate:
|
|
272
|
-
bufferSize:
|
|
273
|
-
initialBufferFullness:
|
|
274
|
-
alternateDataBitrate:
|
|
275
|
-
alternateBufferSize:
|
|
276
|
-
alternateInitialBufferFullness:
|
|
277
|
-
maximumObjectSize:
|
|
276
|
+
dataBitrate: view.getInt32(12, true),
|
|
277
|
+
bufferSize: view.getInt32(16, true),
|
|
278
|
+
initialBufferFullness: view.getInt32(20, true),
|
|
279
|
+
alternateDataBitrate: view.getInt32(24, true),
|
|
280
|
+
alternateBufferSize: view.getInt32(28, true),
|
|
281
|
+
alternateInitialBufferFullness: view.getInt32(32, true),
|
|
282
|
+
maximumObjectSize: view.getInt32(36, true),
|
|
278
283
|
flags: {
|
|
279
284
|
reliableFlag: util.getBit(buf, off + 40, 0),
|
|
280
285
|
seekableFlag: util.getBit(buf, off + 40, 1),
|
|
281
286
|
resendLiveCleanpointsFlag: util.getBit(buf, off + 40, 2)
|
|
282
287
|
},
|
|
283
288
|
// flagsNumeric: Token.UINT32_LE.get(buf, off + 64),
|
|
284
|
-
streamNumber:
|
|
285
|
-
streamLanguageId:
|
|
286
|
-
averageTimePerFrame:
|
|
287
|
-
streamNameCount:
|
|
288
|
-
payloadExtensionSystems:
|
|
289
|
+
streamNumber: view.getInt16(42, true),
|
|
290
|
+
streamLanguageId: view.getInt16(44, true),
|
|
291
|
+
averageTimePerFrame: view.getInt32(52, true),
|
|
292
|
+
streamNameCount: view.getInt32(54, true),
|
|
293
|
+
payloadExtensionSystems: view.getInt32(56, true),
|
|
289
294
|
streamNames: [], // ToDo
|
|
290
295
|
streamPropertiesObject: null
|
|
291
296
|
};
|
|
@@ -302,20 +307,20 @@ export class MetadataObjectState extends State {
|
|
|
302
307
|
}
|
|
303
308
|
get(uint8Array, off) {
|
|
304
309
|
const tags = [];
|
|
305
|
-
const
|
|
306
|
-
const descriptionRecordsCount =
|
|
307
|
-
let pos =
|
|
310
|
+
const view = new DataView(uint8Array.buffer, off);
|
|
311
|
+
const descriptionRecordsCount = view.getUint16(0, true);
|
|
312
|
+
let pos = 2;
|
|
308
313
|
for (let i = 0; i < descriptionRecordsCount; i += 1) {
|
|
309
314
|
pos += 4;
|
|
310
|
-
const nameLen =
|
|
315
|
+
const nameLen = view.getUint16(pos, true);
|
|
311
316
|
pos += 2;
|
|
312
|
-
const dataType =
|
|
317
|
+
const dataType = view.getUint16(pos, true);
|
|
313
318
|
pos += 2;
|
|
314
|
-
const dataLen =
|
|
319
|
+
const dataLen = view.getUint32(pos, true);
|
|
315
320
|
pos += 4;
|
|
316
|
-
const name = AsfUtil.parseUnicodeAttr(
|
|
321
|
+
const name = AsfUtil.parseUnicodeAttr(uint8Array.slice(off + pos, off + pos + nameLen));
|
|
317
322
|
pos += nameLen;
|
|
318
|
-
const data =
|
|
323
|
+
const data = uint8Array.slice(off + pos, off + pos + dataLen);
|
|
319
324
|
pos += dataLen;
|
|
320
325
|
this.postProcessTag(tags, name, dataType, data);
|
|
321
326
|
}
|
|
@@ -335,7 +340,7 @@ MetadataLibraryObjectState.guid = GUID.MetadataLibraryObject;
|
|
|
335
340
|
*/
|
|
336
341
|
export class WmPictureToken {
|
|
337
342
|
static fromBase64(base64str) {
|
|
338
|
-
return this.fromBuffer(
|
|
343
|
+
return this.fromBuffer(base64ToUint8Array(base64str));
|
|
339
344
|
}
|
|
340
345
|
static fromBuffer(buffer) {
|
|
341
346
|
const pic = new WmPictureToken(buffer.length);
|
|
@@ -345,17 +350,18 @@ export class WmPictureToken {
|
|
|
345
350
|
this.len = len;
|
|
346
351
|
}
|
|
347
352
|
get(buffer, offset) {
|
|
348
|
-
const
|
|
349
|
-
const
|
|
353
|
+
const view = new DataView(buffer.buffer, offset);
|
|
354
|
+
const typeId = view.getUint8(0);
|
|
355
|
+
const size = view.getInt32(1, true);
|
|
350
356
|
let index = 5;
|
|
351
|
-
while (
|
|
357
|
+
while (view.getUint16(index) !== 0) {
|
|
352
358
|
index += 2;
|
|
353
359
|
}
|
|
354
|
-
const format =
|
|
355
|
-
while (
|
|
360
|
+
const format = new Token.StringType(index - 5, 'utf-16le').get(buffer, 5);
|
|
361
|
+
while (view.getUint16(index) !== 0) {
|
|
356
362
|
index += 2;
|
|
357
363
|
}
|
|
358
|
-
const description =
|
|
364
|
+
const description = new Token.StringType(index - 5, 'utf-16le').get(buffer, 5);
|
|
359
365
|
return {
|
|
360
366
|
type: AttachedPictureType[typeId],
|
|
361
367
|
format,
|
package/lib/asf/AsfParser.js
CHANGED
|
@@ -51,11 +51,11 @@ export class AsfParser extends BasicParser {
|
|
|
51
51
|
break;
|
|
52
52
|
case AsfObject.ContentDescriptionObjectState.guid.str: // 3.10
|
|
53
53
|
tags = await this.tokenizer.readToken(new AsfObject.ContentDescriptionObjectState(header));
|
|
54
|
-
this.addTags(tags);
|
|
54
|
+
await this.addTags(tags);
|
|
55
55
|
break;
|
|
56
56
|
case AsfObject.ExtendedContentDescriptionObjectState.guid.str: // 3.11
|
|
57
57
|
tags = await this.tokenizer.readToken(new AsfObject.ExtendedContentDescriptionObjectState(header));
|
|
58
|
-
this.addTags(tags);
|
|
58
|
+
await this.addTags(tags);
|
|
59
59
|
break;
|
|
60
60
|
case GUID.CodecListObject.str:
|
|
61
61
|
const codecs = await AsfObject.readCodecEntries(this.tokenizer);
|
|
@@ -85,10 +85,8 @@ export class AsfParser extends BasicParser {
|
|
|
85
85
|
} while (--numberOfObjectHeaders);
|
|
86
86
|
// done
|
|
87
87
|
}
|
|
88
|
-
addTags(tags) {
|
|
89
|
-
tags.
|
|
90
|
-
this.metadata.addTag(headerType, tag.id, tag.value);
|
|
91
|
-
});
|
|
88
|
+
async addTags(tags) {
|
|
89
|
+
await Promise.all(tags.map(({ id, value }) => this.metadata.addTag(headerType, id, value)));
|
|
92
90
|
}
|
|
93
91
|
async parseExtensionObject(extensionSize) {
|
|
94
92
|
do {
|
|
@@ -103,11 +101,11 @@ export class AsfParser extends BasicParser {
|
|
|
103
101
|
break;
|
|
104
102
|
case AsfObject.MetadataObjectState.guid.str: // 4.7
|
|
105
103
|
const moTags = await this.tokenizer.readToken(new AsfObject.MetadataObjectState(header));
|
|
106
|
-
this.addTags(moTags);
|
|
104
|
+
await this.addTags(moTags);
|
|
107
105
|
break;
|
|
108
106
|
case AsfObject.MetadataLibraryObjectState.guid.str: // 4.8
|
|
109
107
|
const mlTags = await this.tokenizer.readToken(new AsfObject.MetadataLibraryObjectState(header));
|
|
110
|
-
this.addTags(mlTags);
|
|
108
|
+
await this.addTags(mlTags);
|
|
111
109
|
break;
|
|
112
110
|
case GUID.PaddingObject.str:
|
|
113
111
|
// ToDo: register bytes pad
|
package/lib/asf/AsfUtil.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { Buffer } from 'node:buffer';
|
|
3
1
|
import { DataType } from './AsfObject.js';
|
|
4
|
-
export type AttributeParser = (buf:
|
|
2
|
+
export type AttributeParser = (buf: Uint8Array) => boolean | string | number | bigint | Uint8Array;
|
|
5
3
|
export declare class AsfUtil {
|
|
6
4
|
static getParserForAttr(i: DataType): AttributeParser;
|
|
7
5
|
static parseUnicodeAttr(uint8Array: Uint8Array): string;
|
package/lib/asf/AsfUtil.js
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import * as Token from 'token-types';
|
|
2
|
-
import { Buffer } from 'node:buffer';
|
|
3
2
|
import * as util from '../common/Util.js';
|
|
4
3
|
export class AsfUtil {
|
|
5
4
|
static getParserForAttr(i) {
|
|
6
5
|
return AsfUtil.attributeParsers[i];
|
|
7
6
|
}
|
|
8
7
|
static parseUnicodeAttr(uint8Array) {
|
|
9
|
-
return util.stripNulls(util.decodeString(uint8Array, '
|
|
8
|
+
return util.stripNulls(util.decodeString(uint8Array, 'utf-16le'));
|
|
10
9
|
}
|
|
11
10
|
static parseByteArrayAttr(buf) {
|
|
12
|
-
return
|
|
11
|
+
return new Uint8Array(buf);
|
|
13
12
|
}
|
|
14
13
|
static parseBoolAttr(buf, offset = 0) {
|
|
15
14
|
return AsfUtil.parseWordAttr(buf, offset) === 1;
|
|
16
15
|
}
|
|
17
16
|
static parseDWordAttr(buf, offset = 0) {
|
|
18
|
-
return
|
|
17
|
+
return Token.UINT32_LE.get(buf, offset);
|
|
19
18
|
}
|
|
20
19
|
static parseQWordAttr(buf, offset = 0) {
|
|
21
20
|
return Token.UINT64_LE.get(buf, offset);
|
|
22
21
|
}
|
|
23
22
|
static parseWordAttr(buf, offset = 0) {
|
|
24
|
-
return
|
|
23
|
+
return Token.UINT16_LE.get(buf, offset);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
AsfUtil.attributeParsers = [
|
package/lib/asf/GUID.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
/**
|
|
3
2
|
* Ref:
|
|
4
3
|
* - https://tools.ietf.org/html/draft-fleischman-asf-01, Appendix A: ASF GUIDs
|
|
@@ -58,14 +57,14 @@ export default class GUID {
|
|
|
58
57
|
static FileTransferMedia: GUID;
|
|
59
58
|
static BinaryMedia: GUID;
|
|
60
59
|
static ASF_Index_Placeholder_Object: GUID;
|
|
61
|
-
static fromBin(bin:
|
|
60
|
+
static fromBin(bin: Uint8Array, offset?: number): GUID;
|
|
62
61
|
/**
|
|
63
62
|
* Decode GUID in format like "B503BF5F-2EA9-CF11-8EE3-00C00C205365"
|
|
64
63
|
* @param objectId Binary GUID
|
|
65
64
|
* @param offset Read offset in bytes, default 0
|
|
66
65
|
* @returns GUID as dashed hexadecimal representation
|
|
67
66
|
*/
|
|
68
|
-
static decode(objectId:
|
|
67
|
+
static decode(objectId: Uint8Array, offset?: number): string;
|
|
69
68
|
/**
|
|
70
69
|
* Decode stream type
|
|
71
70
|
* @param mediaType Media type GUID
|
|
@@ -77,8 +76,8 @@ export default class GUID {
|
|
|
77
76
|
* @param guid GUID like: "B503BF5F-2EA9-CF11-8EE3-00C00C205365"
|
|
78
77
|
* @returns Encoded Binary GUID
|
|
79
78
|
*/
|
|
80
|
-
static encode(str: string):
|
|
79
|
+
static encode(str: string): Uint8Array;
|
|
81
80
|
constructor(str: string);
|
|
82
81
|
equals(guid: GUID): boolean;
|
|
83
|
-
toBin():
|
|
82
|
+
toBin(): Uint8Array;
|
|
84
83
|
}
|