music-metadata 11.16.1 → 12.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/README.md +78 -35
- package/lib/ParseError.d.ts +10 -3
- package/lib/ParseError.js +2 -0
- package/lib/ParserFactory.js +10 -4
- package/lib/aiff/AiffParser.js +1 -4
- package/lib/aiff/AiffToken.d.ts +4 -3
- package/lib/aiff/AiffToken.js +2 -0
- package/lib/apev2/APEv2Parser.d.ts +4 -3
- package/lib/apev2/APEv2Parser.js +2 -4
- package/lib/asf/AsfGuid.js +49 -49
- package/lib/asf/AsfObject.d.ts +11 -5
- package/lib/asf/AsfObject.js +117 -35
- package/lib/asf/AsfParser.d.ts +3 -0
- package/lib/asf/AsfParser.js +81 -12
- package/lib/common/BasicParser.js +3 -0
- package/lib/common/CombinedTagMapper.js +1 -1
- package/lib/common/GenericTagMapper.js +3 -1
- package/lib/common/MetadataCollector.d.ts +1 -0
- package/lib/common/MetadataCollector.js +35 -24
- package/lib/common/TrackInfo.d.ts +5 -0
- package/lib/common/TrackInfo.js +39 -0
- package/lib/common/Util.d.ts +1 -1
- package/lib/common/Util.js +6 -12
- package/lib/core.d.ts +1 -1
- package/lib/core.js +6 -11
- package/lib/dsdiff/DsdiffParser.d.ts +4 -3
- package/lib/dsf/DsfParser.d.ts +4 -3
- package/lib/ebml/EbmlIterator.d.ts +4 -3
- package/lib/ebml/EbmlIterator.js +4 -3
- package/lib/flac/FlacParser.js +1 -4
- package/lib/id3v1/ID3v1Parser.js +3 -0
- package/lib/id3v2/AbstractID3Parser.js +1 -4
- package/lib/id3v2/FrameParser.d.ts +4 -3
- package/lib/id3v2/FrameParser.js +2 -0
- package/lib/id3v2/ID3v2Parser.js +5 -7
- package/lib/matroska/MatroskaDtd.js +1 -0
- package/lib/matroska/MatroskaParser.js +38 -22
- package/lib/matroska/types.d.ts +8 -32
- package/lib/matroska/types.js +1 -18
- package/lib/mp4/Atom.js +6 -0
- package/lib/mp4/AtomToken.d.ts +15 -4
- package/lib/mp4/AtomToken.js +99 -13
- package/lib/mp4/MP4Parser.d.ts +6 -0
- package/lib/mp4/MP4Parser.js +350 -147
- package/lib/mpeg/AdtsFrameHeader.d.ts +17 -0
- package/lib/mpeg/AdtsFrameHeader.js +44 -0
- package/lib/mpeg/MpegFrameHeader.d.ts +41 -0
- package/lib/mpeg/MpegFrameHeader.js +84 -0
- package/lib/mpeg/MpegParser.d.ts +11 -24
- package/lib/mpeg/MpegParser.js +75 -335
- package/lib/musepack/MusepackConentError.d.ts +4 -3
- package/lib/musepack/sv7/BitReader.js +3 -2
- package/lib/musepack/sv7/MpcSv7Parser.js +3 -6
- package/lib/musepack/sv8/MpcSv8Parser.js +1 -4
- package/lib/musepack/sv8/StreamVersion8.js +1 -0
- package/lib/ogg/OggLoader.js +1 -1
- package/lib/ogg/OggParser.d.ts +4 -3
- package/lib/ogg/OggParser.js +49 -8
- package/lib/ogg/OggStreamMetadata.d.ts +14 -0
- package/lib/ogg/OggStreamMetadata.js +27 -0
- package/lib/ogg/OggToken.d.ts +1 -0
- package/lib/ogg/OggToken.js +3 -1
- package/lib/ogg/flac/FlacStream.d.ts +3 -1
- package/lib/ogg/flac/FlacStream.js +21 -3
- package/lib/ogg/opus/Opus.d.ts +4 -3
- package/lib/ogg/opus/Opus.js +1 -0
- package/lib/ogg/opus/OpusStream.js +4 -2
- package/lib/ogg/speex/SpeexStream.js +2 -1
- package/lib/ogg/theora/Theora.d.ts +4 -0
- package/lib/ogg/theora/Theora.js +5 -1
- package/lib/ogg/theora/TheoraStream.d.ts +1 -1
- package/lib/ogg/theora/TheoraStream.js +15 -1
- package/lib/ogg/vorbis/Vorbis.js +4 -3
- package/lib/ogg/vorbis/VorbisDecoder.js +2 -0
- package/lib/ogg/vorbis/VorbisStream.d.ts +4 -3
- package/lib/ogg/vorbis/VorbisStream.js +7 -3
- package/lib/riff/RiffChunk.js +3 -0
- package/lib/type.d.ts +34 -3
- package/lib/type.js +22 -1
- package/lib/wav/WaveChunk.d.ts +4 -3
- package/lib/wav/WaveChunk.js +2 -0
- package/lib/wav/WaveParser.js +7 -6
- package/lib/wavpack/WavPackParser.d.ts +4 -3
- package/lib/wavpack/WavPackParser.js +1 -4
- package/package.json +22 -17
package/README.md
CHANGED
|
@@ -23,12 +23,12 @@ The [`music-metadata`](https://github.com/Borewit/music-metadata) module is idea
|
|
|
23
23
|
## Compatibility
|
|
24
24
|
|
|
25
25
|
Module: version 8 migrated from [CommonJS](https://en.wikipedia.org/wiki/CommonJS) to [pure ECMAScript Module (ESM)](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).
|
|
26
|
-
The distributed JavaScript codebase
|
|
26
|
+
The distributed JavaScript codebase targets [ECMAScript 2023 (14th Edition)](https://262.ecma-international.org/14.0/).
|
|
27
27
|
|
|
28
28
|
> [!NOTE]
|
|
29
29
|
> See also [CommonJS backward Compatibility](#commonjs-backward-compatibility)
|
|
30
30
|
|
|
31
|
-
This module requires a [Node.js ≥
|
|
31
|
+
This module requires a [Node.js ≥ 22](https://nodejs.org/en/about/previous-releases) engine.
|
|
32
32
|
It can also be used in a browser environment when bundled with a module bundler.
|
|
33
33
|
|
|
34
34
|
## Support the Project
|
|
@@ -99,11 +99,12 @@ Support for encoding / format details:
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
## Online demo's
|
|
102
|
-
- [<img src="https://raw.githubusercontent.com/Borewit/audio-tag-analyzer/master/
|
|
103
|
-
- [<img src="https://cdn.sanity.io/images/3do82whm/next/ba8c847f13a5fa39d88f8bc9b7846b7886531b18-2500x2500.svg" width="40"> Webamp](https://webamp.org/)
|
|
102
|
+
- [<img src="https://raw.githubusercontent.com/Borewit/audio-tag-analyzer-vite/master/public/favicon.svg" width="40" alt="Audio Tag Analyzer logo"> Audio Tag Analyzer](https://audio-tag-analyzer.netlify.app/) ([source code](https://github.com/Borewit/audio-tag-analyzer-vite))
|
|
104
103
|
- [ICY Radio Stream Player](https://icy-radio-stream-player.netlify.app/)
|
|
105
104
|
- Expected to be released soon: [Overtone](https://overtone.pro/) by [Johannes Schickling](https://github.com/schickling)
|
|
106
105
|
|
|
106
|
+
Used by [around 50k+ GitHub projects](https://github.com/Borewit/music-metadata/network/dependents).
|
|
107
|
+
|
|
107
108
|
## Usage
|
|
108
109
|
|
|
109
110
|
### Installation
|
|
@@ -605,7 +606,10 @@ Returns a list of supported MIME-types. This may include some MIME-types which a
|
|
|
605
606
|
only that the parser will attempt to calculate it when possible, even if it requires reading the full file.
|
|
606
607
|
|
|
607
608
|
- `includeChapters`: `boolean` (default: `false`)
|
|
608
|
-
When `true`, the MP4 parser
|
|
609
|
+
When `true`, the MP4 parser reads QuickTime chapter tracks and Nero `chpl` chapter lists.
|
|
610
|
+
A usable QuickTime chapter track takes precedence over a Nero list.
|
|
611
|
+
Files, buffers, and Blobs support chapters whether `moov` appears before or after `mdat`.
|
|
612
|
+
For forward-only streams, `moov` must appear before the chapter data.
|
|
609
613
|
|
|
610
614
|
- `mkvUseIndex`: `boolean` (default: `false`)
|
|
611
615
|
|
|
@@ -664,45 +668,84 @@ Audio format information. Defined in the TypeScript `IFormat` interface:
|
|
|
664
668
|
|
|
665
669
|
#### `metadata.format.trackInfo`
|
|
666
670
|
|
|
667
|
-
|
|
668
|
-
|
|
671
|
+
`metadata.format.trackInfo` provides the same track abstraction for single-stream audio files and
|
|
672
|
+
multi-track containers (MPEG-4, Matroska, ASF, and Ogg). Tracks can carry audio, video, subtitles, or
|
|
673
|
+
metadata. Import `TrackType` from `music-metadata` to select tracks without knowing the container:
|
|
674
|
+
|
|
675
|
+
```js
|
|
676
|
+
import { parseFile, TrackType } from 'music-metadata';
|
|
677
|
+
|
|
678
|
+
const { format } = await parseFile('movie.mp4');
|
|
679
|
+
const audioTracks = format.trackInfo.filter(track => track.type === TrackType.audio);
|
|
680
|
+
const videoTracks = format.trackInfo.filter(track => track.type === TrackType.video);
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
Each entry describes one discovered track. The order follows container discovery and does not identify
|
|
684
|
+
a preferred track; use `type`, container flags, and language to choose one. In MPEG-4, the codec and
|
|
685
|
+
media properties describe the first sample description, even when the track has multiple sample descriptions. In Ogg, entries describe
|
|
686
|
+
logical media streams; Skeleton headers do not create media tracks. Single-stream audio parsers map
|
|
687
|
+
their format properties to one audio track after parsing.
|
|
688
|
+
|
|
689
|
+
Properties are optional when unavailable and are not copied between tracks. In particular, a container
|
|
690
|
+
or summary bitrate is not a per-track bitrate. MPEG-4 track timing comes from media headers (or parsed
|
|
691
|
+
audio fragments), and track bitrates from sample sizes and duration. ASF supplies audio/video stream
|
|
692
|
+
properties and stream bitrates from both standalone and nested Stream Properties Objects. Codec List
|
|
693
|
+
entries supply names for matching streams; unused codecs do not create tracks. Matroska applies
|
|
694
|
+
specified defaults for flags, language, channels, and sample rate, and prefers IETF language tags when present. Video duration/bitrate coverage and overall
|
|
695
|
+
video statistics remain incomplete. The existing `format` fields retain their summary semantics.
|
|
696
|
+
|
|
697
|
+
For Ogg/Opus, the per-track average bitrate uses the logical stream's payload bytes, including codec
|
|
698
|
+
headers and excluding Ogg framing and other streams. It requires the logical stream's end-of-stream
|
|
699
|
+
(EOS) page and a known duration; reaching input EOF alone is insufficient. Set `duration: true` to
|
|
700
|
+
allow scanning when needed. The summary `format.bitrate` retains
|
|
701
|
+
its existing file-size estimate and can differ from the per-track bitrate. Vorbis and Speex use nominal
|
|
702
|
+
bitrates when their headers provide them.
|
|
669
703
|
|
|
670
704
|
`metadata.format.trackInfo` is an array of [trackInfo](#trackinfo) objects, empty when no track information is available.
|
|
671
705
|
|
|
672
706
|
##### trackInfo
|
|
673
707
|
|
|
674
708
|
Individual track information. Defined in the TypeScript `ITrackInfo` interface:
|
|
675
|
-
- `trackInfo.
|
|
709
|
+
- `trackInfo.id?: number` Container track ID/number, or Ogg stream serial number; only meaningful within the file
|
|
710
|
+
- `trackInfo.type?: TrackType` Track type (`audio`, `video`, `subtitle`, `metadata`, or other known types)
|
|
711
|
+
- `trackInfo.codecId?: string` Container-specific codec identifier, such as `mp4a`, `A_AAC`, or ASF audio format tag `0x0161`
|
|
676
712
|
- `trackInfo.codecName?: string` Codec name
|
|
713
|
+
- `trackInfo.codecProfile?: string` Codec profile
|
|
714
|
+
- `trackInfo.duration?: number` Track duration in seconds
|
|
715
|
+
- `trackInfo.bitrate?: number` Encoded track bitrate in bits per second
|
|
716
|
+
- `trackInfo.lossless?: boolean` Whether the track uses lossless compression
|
|
677
717
|
- `trackInfo.codecSettings?: string` Codec settings
|
|
678
|
-
- `trackInfo.flagEnabled?: boolean`
|
|
679
|
-
- `trackInfo.flagDefault?: boolean`
|
|
680
|
-
- `trackInfo.
|
|
718
|
+
- `trackInfo.flagEnabled?: boolean` Whether the track is enabled; Matroska defaults to `true`, MPEG-4 uses the track header flag
|
|
719
|
+
- `trackInfo.flagDefault?: boolean` Whether the container marks the track as a default selection; Matroska defaults to `true`
|
|
720
|
+
- `trackInfo.flagForced?: boolean` Whether a subtitle track should be displayed without explicit selection
|
|
721
|
+
- `trackInfo.flagLacing?: boolean` Whether a Matroska track may contain blocks using lacing; defaults to `true`
|
|
681
722
|
- `trackInfo.name?: string` A human-readable track name.
|
|
682
723
|
- `trackInfo.language?: string` Specifies the language of the track
|
|
683
|
-
- `trackInfo.audio?: IAudioTrack`, see [`trackInfo.
|
|
684
|
-
- `trackInfo.video?: IVideoTrack`, see [`trackInfo.
|
|
685
|
-
|
|
686
|
-
##### `trackInfo.
|
|
687
|
-
|
|
688
|
-
- `
|
|
689
|
-
- `
|
|
690
|
-
- `
|
|
691
|
-
- `
|
|
692
|
-
- `
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
- `
|
|
698
|
-
- `
|
|
699
|
-
- `
|
|
700
|
-
- `
|
|
701
|
-
- `
|
|
702
|
-
- `
|
|
703
|
-
- `
|
|
704
|
-
- `
|
|
705
|
-
- `
|
|
724
|
+
- `trackInfo.audio?: IAudioTrack`, see [`trackInfo.audio`](#trackinfoaudio)
|
|
725
|
+
- `trackInfo.video?: IVideoTrack`, see [`trackInfo.video`](#trackinfovideo)
|
|
726
|
+
|
|
727
|
+
##### `trackInfo.audio`
|
|
728
|
+
|
|
729
|
+
- `trackInfo.audio.numberOfSamples?: number` Number of decoded sample frames (one sample per channel), when known
|
|
730
|
+
- `trackInfo.audio.samplingFrequency?: number` Sample rate in hertz; for Opus, the informational original input rate
|
|
731
|
+
- `trackInfo.audio.outputSamplingFrequency?: number` Output sample rate in hertz; Opus granules and sample counts use 48000 Hz
|
|
732
|
+
- `trackInfo.audio.channels?: number`
|
|
733
|
+
- `trackInfo.audio.channelPositions?: Uint8Array`
|
|
734
|
+
- `trackInfo.audio.bitDepth?: number`
|
|
735
|
+
|
|
736
|
+
##### `trackInfo.video`
|
|
737
|
+
|
|
738
|
+
- `trackInfo.video.frameRate?: number` Frames per second, when known
|
|
739
|
+
- `trackInfo.video.flagInterlaced?: boolean`
|
|
740
|
+
- `trackInfo.video.stereoMode?: number`
|
|
741
|
+
- `trackInfo.video.pixelWidth?: number`
|
|
742
|
+
- `trackInfo.video.pixelHeight?: number`
|
|
743
|
+
- `trackInfo.video.displayWidth?: number`
|
|
744
|
+
- `trackInfo.video.displayHeight?: number`
|
|
745
|
+
- `trackInfo.video.displayUnit?: number`
|
|
746
|
+
- `trackInfo.video.aspectRatioType?: number`
|
|
747
|
+
- `trackInfo.video.colourSpace?: Uint8Array`
|
|
748
|
+
- `trackInfo.video.gammaValue?: number`
|
|
706
749
|
|
|
707
750
|
#### `metadata.common`
|
|
708
751
|
|
|
@@ -799,7 +842,7 @@ Using Node.js ≥ 22, which is support loading ESM module via require
|
|
|
799
842
|
const mm = require('music-metadata');
|
|
800
843
|
```
|
|
801
844
|
|
|
802
|
-
|
|
845
|
+
Alternatively, dynamically import **music-metadata**:
|
|
803
846
|
```js
|
|
804
847
|
(async () => {
|
|
805
848
|
// Dynamically loads the ESM module in a CommonJS project
|
package/lib/ParseError.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare const makeParseError: <Name extends string>(name: Name) => {
|
|
|
4
4
|
name: Name;
|
|
5
5
|
message: string;
|
|
6
6
|
stack?: string;
|
|
7
|
+
cause?: unknown;
|
|
7
8
|
};
|
|
8
9
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
9
10
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -14,6 +15,7 @@ declare const CouldNotDetermineFileTypeError_base: {
|
|
|
14
15
|
name: "CouldNotDetermineFileTypeError";
|
|
15
16
|
message: string;
|
|
16
17
|
stack?: string;
|
|
18
|
+
cause?: unknown;
|
|
17
19
|
};
|
|
18
20
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
19
21
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -26,6 +28,7 @@ declare const UnsupportedFileTypeError_base: {
|
|
|
26
28
|
name: "UnsupportedFileTypeError";
|
|
27
29
|
message: string;
|
|
28
30
|
stack?: string;
|
|
31
|
+
cause?: unknown;
|
|
29
32
|
};
|
|
30
33
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
31
34
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -38,6 +41,7 @@ declare const UnexpectedFileContentError_base: {
|
|
|
38
41
|
name: "UnexpectedFileContentError";
|
|
39
42
|
message: string;
|
|
40
43
|
stack?: string;
|
|
44
|
+
cause?: unknown;
|
|
41
45
|
};
|
|
42
46
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
43
47
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -53,6 +57,7 @@ declare const FieldDecodingError_base: {
|
|
|
53
57
|
name: "FieldDecodingError";
|
|
54
58
|
message: string;
|
|
55
59
|
stack?: string;
|
|
60
|
+
cause?: unknown;
|
|
56
61
|
};
|
|
57
62
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
58
63
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -65,6 +70,7 @@ declare const InternalParserError_base: {
|
|
|
65
70
|
name: "InternalParserError";
|
|
66
71
|
message: string;
|
|
67
72
|
stack?: string;
|
|
73
|
+
cause?: unknown;
|
|
68
74
|
};
|
|
69
75
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
70
76
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -74,11 +80,12 @@ export declare class InternalParserError extends InternalParserError_base {
|
|
|
74
80
|
}
|
|
75
81
|
export declare const makeUnexpectedFileContentError: <FileType extends string>(fileType: FileType) => {
|
|
76
82
|
new (message: string): {
|
|
77
|
-
readonly fileType: string;
|
|
78
|
-
toString(): string;
|
|
79
|
-
name: "UnexpectedFileContentError";
|
|
80
83
|
message: string;
|
|
81
84
|
stack?: string;
|
|
85
|
+
cause?: unknown;
|
|
86
|
+
name: "UnexpectedFileContentError";
|
|
87
|
+
readonly fileType: string;
|
|
88
|
+
toString(): string;
|
|
82
89
|
};
|
|
83
90
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
84
91
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
package/lib/ParseError.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const makeParseError = (name) => {
|
|
2
2
|
return class ParseError extends Error {
|
|
3
|
+
name;
|
|
3
4
|
constructor(message) {
|
|
4
5
|
super(message);
|
|
5
6
|
this.name = name;
|
|
@@ -14,6 +15,7 @@ export class UnsupportedFileTypeError extends makeParseError('UnsupportedFileTyp
|
|
|
14
15
|
}
|
|
15
16
|
// Concrete error class representing unexpected file content.
|
|
16
17
|
export class UnexpectedFileContentError extends makeParseError('UnexpectedFileContentError') {
|
|
18
|
+
fileType;
|
|
17
19
|
constructor(fileType, message) {
|
|
18
20
|
super(message);
|
|
19
21
|
this.fileType = fileType;
|
package/lib/ParserFactory.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { parse as parseContentType } from 'content-type';
|
|
2
2
|
import initDebug from 'debug';
|
|
3
3
|
import { fileTypeFromBuffer } from 'file-type';
|
|
4
4
|
import { parse as mimeTypeParse } from 'media-typer';
|
|
@@ -6,6 +6,7 @@ import { aiffParserLoader } from './aiff/AiffLoader.js';
|
|
|
6
6
|
import { apeParserLoader } from './apev2/Apev2Loader.js';
|
|
7
7
|
import { asfParserLoader } from './asf/AsfLoader.js';
|
|
8
8
|
import { MetadataCollector } from './common/MetadataCollector.js';
|
|
9
|
+
import { createAudioTrackInfo } from './common/TrackInfo.js';
|
|
9
10
|
import { scanAppendingHeaders } from './core.js';
|
|
10
11
|
import { dsdiffParserLoader } from './dsdiff/DsdiffLoader.js';
|
|
11
12
|
import { dsfParserLoader } from './dsf/DsfLoader.js';
|
|
@@ -21,7 +22,7 @@ import { riffParserLoader } from './wav/WaveLoader.js';
|
|
|
21
22
|
import { wavpackParserLoader } from './wavpack/WavPackLoader.js';
|
|
22
23
|
const debug = initDebug('music-metadata:parser:factory');
|
|
23
24
|
export function parseHttpContentType(contentType) {
|
|
24
|
-
const type =
|
|
25
|
+
const type = parseContentType(contentType);
|
|
25
26
|
const mime = mimeTypeParse(type.type);
|
|
26
27
|
return {
|
|
27
28
|
type: mime.type,
|
|
@@ -31,8 +32,8 @@ export function parseHttpContentType(contentType) {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
export class ParserFactory {
|
|
35
|
+
parsers = [];
|
|
34
36
|
constructor() {
|
|
35
|
-
this.parsers = [];
|
|
36
37
|
[
|
|
37
38
|
flacParserLoader,
|
|
38
39
|
mpegParserLoader,
|
|
@@ -54,7 +55,7 @@ export class ParserFactory {
|
|
|
54
55
|
registerParser(parser) {
|
|
55
56
|
this.parsers.push(parser);
|
|
56
57
|
}
|
|
57
|
-
async parse(tokenizer, parserLoader, opts) {
|
|
58
|
+
async parse(tokenizer, parserLoader, opts = {}) {
|
|
58
59
|
if (tokenizer.supportsRandomAccess()) {
|
|
59
60
|
debug('tokenizer supports random-access, scanning for appending headers');
|
|
60
61
|
await scanAppendingHeaders(tokenizer, opts);
|
|
@@ -92,6 +93,11 @@ export class ParserFactory {
|
|
|
92
93
|
const parser = new ParserImpl(metadata, tokenizer, opts ?? {});
|
|
93
94
|
debug(`Parser ${parserLoader.parserType} loaded`);
|
|
94
95
|
await parser.parse();
|
|
96
|
+
// Audio-only parsers describe their single stream through the format fields.
|
|
97
|
+
// Container parsers supply their own tracks; never copy aggregate fields to them.
|
|
98
|
+
if (metadata.format.trackInfo.length === 0 && metadata.format.hasAudio === true) {
|
|
99
|
+
metadata.addStreamInfo(createAudioTrackInfo(metadata.format));
|
|
100
|
+
}
|
|
95
101
|
if (metadata.format.trackInfo) {
|
|
96
102
|
if (metadata.format.hasAudio === undefined) {
|
|
97
103
|
metadata.setFormat('hasAudio', !!metadata.format.trackInfo.find(track => track.type === TrackType.audio));
|
package/lib/aiff/AiffParser.js
CHANGED
|
@@ -16,10 +16,7 @@ const debug = initDebug('music-metadata:parser:aiff');
|
|
|
16
16
|
* - http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/Docs/AIFF-1.3.pdf
|
|
17
17
|
*/
|
|
18
18
|
export class AIFFParser extends BasicParser {
|
|
19
|
-
|
|
20
|
-
super(...arguments);
|
|
21
|
-
this.isCompressed = null;
|
|
22
|
-
}
|
|
19
|
+
isCompressed = null;
|
|
23
20
|
async parse() {
|
|
24
21
|
const header = await this.tokenizer.readToken(iff.Header);
|
|
25
22
|
if (header.chunkID !== 'FORM') {
|
package/lib/aiff/AiffToken.d.ts
CHANGED
|
@@ -14,11 +14,12 @@ export declare const compressionTypes: {
|
|
|
14
14
|
export type CompressionTypeCode = keyof typeof compressionTypes;
|
|
15
15
|
declare const AiffContentError_base: {
|
|
16
16
|
new (message: string): {
|
|
17
|
-
readonly fileType: string;
|
|
18
|
-
toString(): string;
|
|
19
|
-
name: "UnexpectedFileContentError";
|
|
20
17
|
message: string;
|
|
21
18
|
stack?: string;
|
|
19
|
+
cause?: unknown;
|
|
20
|
+
name: "UnexpectedFileContentError";
|
|
21
|
+
readonly fileType: string;
|
|
22
|
+
toString(): string;
|
|
22
23
|
};
|
|
23
24
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
24
25
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
package/lib/aiff/AiffToken.js
CHANGED
|
@@ -15,6 +15,8 @@ export const compressionTypes = {
|
|
|
15
15
|
export class AiffContentError extends makeUnexpectedFileContentError('AIFF') {
|
|
16
16
|
}
|
|
17
17
|
export class Common {
|
|
18
|
+
len;
|
|
19
|
+
isAifc;
|
|
18
20
|
constructor(header, isAifc) {
|
|
19
21
|
this.isAifc = isAifc;
|
|
20
22
|
const minimumChunkSize = isAifc ? 22 : 18;
|
|
@@ -6,11 +6,12 @@ import type { IApeHeader, IOptions } from '../type.js';
|
|
|
6
6
|
import { type IFooter, type IHeader } from './APEv2Token.js';
|
|
7
7
|
declare const ApeContentError_base: {
|
|
8
8
|
new (message: string): {
|
|
9
|
-
readonly fileType: string;
|
|
10
|
-
toString(): string;
|
|
11
|
-
name: "UnexpectedFileContentError";
|
|
12
9
|
message: string;
|
|
13
10
|
stack?: string;
|
|
11
|
+
cause?: unknown;
|
|
12
|
+
name: "UnexpectedFileContentError";
|
|
13
|
+
readonly fileType: string;
|
|
14
|
+
toString(): string;
|
|
14
15
|
};
|
|
15
16
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
16
17
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
package/lib/apev2/APEv2Parser.js
CHANGED
|
@@ -16,10 +16,6 @@ export function tryParseApeHeader(metadata, tokenizer, options) {
|
|
|
16
16
|
return apeParser.tryParseApeHeader();
|
|
17
17
|
}
|
|
18
18
|
export class APEv2Parser extends BasicParser {
|
|
19
|
-
constructor() {
|
|
20
|
-
super(...arguments);
|
|
21
|
-
this.ape = {};
|
|
22
|
-
}
|
|
23
19
|
/**
|
|
24
20
|
* Calculate the media file duration
|
|
25
21
|
* @param ah ApeHeader
|
|
@@ -68,6 +64,7 @@ export class APEv2Parser extends BasicParser {
|
|
|
68
64
|
const apeParser = new APEv2Parser(metadata, strtok3.fromBuffer(buffer), options);
|
|
69
65
|
return apeParser.parseTags(footer);
|
|
70
66
|
}
|
|
67
|
+
ape = {};
|
|
71
68
|
/**
|
|
72
69
|
* Parse APEv1 / APEv2 header if header signature found
|
|
73
70
|
*/
|
|
@@ -202,6 +199,7 @@ export class APEv2Parser extends BasicParser {
|
|
|
202
199
|
// ToDo before
|
|
203
200
|
this.metadata.setFormat('lossless', true);
|
|
204
201
|
this.metadata.setFormat('container', "Monkey's Audio");
|
|
202
|
+
this.metadata.setFormat('codec', "Monkey's Audio");
|
|
205
203
|
this.metadata.setFormat('bitsPerSample', header.bitsPerSample);
|
|
206
204
|
this.metadata.setFormat('sampleRate', header.sampleRate);
|
|
207
205
|
this.metadata.setFormat('numberOfChannels', header.channel);
|
package/lib/asf/AsfGuid.js
CHANGED
|
@@ -13,7 +13,54 @@ import { Guid, parseWindowsGuid } from 'win-guid';
|
|
|
13
13
|
* - http://drang.s4.xrea.com/program/tips/id3tag/wmp/10_asf_guids.html
|
|
14
14
|
* - https://github.com/dji-sdk/FFmpeg/blob/master/libavformat/asf.c
|
|
15
15
|
*/
|
|
16
|
-
class AsfGuid {
|
|
16
|
+
export default class AsfGuid {
|
|
17
|
+
// 10.1 Top-level ASF object GUIDs
|
|
18
|
+
static HeaderObject = new AsfGuid('75B22630-668E-11CF-A6D9-00AA0062CE6C');
|
|
19
|
+
static DataObject = new AsfGuid('75B22636-668E-11CF-A6D9-00AA0062CE6C');
|
|
20
|
+
static SimpleIndexObject = new AsfGuid('33000890-E5B1-11CF-89F4-00A0C90349CB');
|
|
21
|
+
static IndexObject = new AsfGuid('D6E229D3-35DA-11D1-9034-00A0C90349BE');
|
|
22
|
+
static MediaObjectIndexObject = new AsfGuid('FEB103F8-12AD-4C64-840F-2A1D2F7AD48C');
|
|
23
|
+
static TimecodeIndexObject = new AsfGuid('3CB73FD0-0C4A-4803-953D-EDF7B6228F0C');
|
|
24
|
+
// 10.2 Header Object GUIDs
|
|
25
|
+
static FilePropertiesObject = new AsfGuid('8CABDCA1-A947-11CF-8EE4-00C00C205365');
|
|
26
|
+
static StreamPropertiesObject = new AsfGuid('B7DC0791-A9B7-11CF-8EE6-00C00C205365');
|
|
27
|
+
static HeaderExtensionObject = new AsfGuid('5FBF03B5-A92E-11CF-8EE3-00C00C205365');
|
|
28
|
+
static CodecListObject = new AsfGuid('86D15240-311D-11D0-A3A4-00A0C90348F6');
|
|
29
|
+
static ScriptCommandObject = new AsfGuid('1EFB1A30-0B62-11D0-A39B-00A0C90348F6');
|
|
30
|
+
static MarkerObject = new AsfGuid('F487CD01-A951-11CF-8EE6-00C00C205365');
|
|
31
|
+
static BitrateMutualExclusionObject = new AsfGuid('D6E229DC-35DA-11D1-9034-00A0C90349BE');
|
|
32
|
+
static ErrorCorrectionObject = new AsfGuid('75B22635-668E-11CF-A6D9-00AA0062CE6C');
|
|
33
|
+
static ContentDescriptionObject = new AsfGuid('75B22633-668E-11CF-A6D9-00AA0062CE6C');
|
|
34
|
+
static ExtendedContentDescriptionObject = new AsfGuid('D2D0A440-E307-11D2-97F0-00A0C95EA850');
|
|
35
|
+
static ContentBrandingObject = new AsfGuid('2211B3FA-BD23-11D2-B4B7-00A0C955FC6E');
|
|
36
|
+
static StreamBitratePropertiesObject = new AsfGuid('7BF875CE-468D-11D1-8D82-006097C9A2B2');
|
|
37
|
+
static ContentEncryptionObject = new AsfGuid('2211B3FB-BD23-11D2-B4B7-00A0C955FC6E');
|
|
38
|
+
static ExtendedContentEncryptionObject = new AsfGuid('298AE614-2622-4C17-B935-DAE07EE9289C');
|
|
39
|
+
static DigitalSignatureObject = new AsfGuid('2211B3FC-BD23-11D2-B4B7-00A0C955FC6E');
|
|
40
|
+
static PaddingObject = new AsfGuid('1806D474-CADF-4509-A4BA-9AABCB96AAE8');
|
|
41
|
+
// 10.3 Header Extension Object GUIDs
|
|
42
|
+
static ExtendedStreamPropertiesObject = new AsfGuid('14E6A5CB-C672-4332-8399-A96952065B5A');
|
|
43
|
+
static AdvancedMutualExclusionObject = new AsfGuid('A08649CF-4775-4670-8A16-6E35357566CD');
|
|
44
|
+
static GroupMutualExclusionObject = new AsfGuid('D1465A40-5A79-4338-B71B-E36B8FD6C249');
|
|
45
|
+
static StreamPrioritizationObject = new AsfGuid('D4FED15B-88D3-454F-81F0-ED5C45999E24');
|
|
46
|
+
static BandwidthSharingObject = new AsfGuid('A69609E6-517B-11D2-B6AF-00C04FD908E9');
|
|
47
|
+
static LanguageListObject = new AsfGuid('7C4346A9-EFE0-4BFC-B229-393EDE415C85');
|
|
48
|
+
static MetadataObject = new AsfGuid('C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA');
|
|
49
|
+
static MetadataLibraryObject = new AsfGuid('44231C94-9498-49D1-A141-1D134E457054');
|
|
50
|
+
static IndexParametersObject = new AsfGuid('D6E229DF-35DA-11D1-9034-00A0C90349BE');
|
|
51
|
+
static MediaObjectIndexParametersObject = new AsfGuid('6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7');
|
|
52
|
+
static TimecodeIndexParametersObject = new AsfGuid('F55E496D-9797-4B5D-8C8B-604DFE9BFB24');
|
|
53
|
+
static CompatibilityObject = new AsfGuid('26F18B5D-4584-47EC-9F5F-0E651F0452C9');
|
|
54
|
+
static AdvancedContentEncryptionObject = new AsfGuid('43058533-6981-49E6-9B74-AD12CB86D58C');
|
|
55
|
+
// 10.4 Stream Properties Object Stream Type GUIDs
|
|
56
|
+
static AudioMedia = new AsfGuid('F8699E40-5B4D-11CF-A8FD-00805F5C442B');
|
|
57
|
+
static VideoMedia = new AsfGuid('BC19EFC0-5B4D-11CF-A8FD-00805F5C442B');
|
|
58
|
+
static CommandMedia = new AsfGuid('59DACFC0-59E6-11D0-A3AC-00A0C90348F6');
|
|
59
|
+
static JFIF_Media = new AsfGuid('B61BE100-5B4E-11CF-A8FD-00805F5C442B');
|
|
60
|
+
static Degradable_JPEG_Media = new AsfGuid('35907DE0-E415-11CF-A917-00805F5C442B');
|
|
61
|
+
static FileTransferMedia = new AsfGuid('91BD222C-F21C-497A-8B6D-5AA86BFC0185');
|
|
62
|
+
static BinaryMedia = new AsfGuid('3AFB65E2-47EF-40F2-AC2C-70A90D71D343');
|
|
63
|
+
static ASF_Index_Placeholder_Object = new AsfGuid('D9AADE20-7C17-4F9C-BC28-8555DD98E2A2');
|
|
17
64
|
static fromBin(bin, offset = 0) {
|
|
18
65
|
return new AsfGuid(AsfGuid.decode(bin, offset));
|
|
19
66
|
}
|
|
@@ -55,6 +102,7 @@ class AsfGuid {
|
|
|
55
102
|
static encode(guid) {
|
|
56
103
|
return parseWindowsGuid(guid);
|
|
57
104
|
}
|
|
105
|
+
str;
|
|
58
106
|
constructor(str) {
|
|
59
107
|
this.str = str;
|
|
60
108
|
}
|
|
@@ -65,51 +113,3 @@ class AsfGuid {
|
|
|
65
113
|
return AsfGuid.encode(this.str);
|
|
66
114
|
}
|
|
67
115
|
}
|
|
68
|
-
// 10.1 Top-level ASF object GUIDs
|
|
69
|
-
AsfGuid.HeaderObject = new AsfGuid('75B22630-668E-11CF-A6D9-00AA0062CE6C');
|
|
70
|
-
AsfGuid.DataObject = new AsfGuid('75B22636-668E-11CF-A6D9-00AA0062CE6C');
|
|
71
|
-
AsfGuid.SimpleIndexObject = new AsfGuid('33000890-E5B1-11CF-89F4-00A0C90349CB');
|
|
72
|
-
AsfGuid.IndexObject = new AsfGuid('D6E229D3-35DA-11D1-9034-00A0C90349BE');
|
|
73
|
-
AsfGuid.MediaObjectIndexObject = new AsfGuid('FEB103F8-12AD-4C64-840F-2A1D2F7AD48C');
|
|
74
|
-
AsfGuid.TimecodeIndexObject = new AsfGuid('3CB73FD0-0C4A-4803-953D-EDF7B6228F0C');
|
|
75
|
-
// 10.2 Header Object GUIDs
|
|
76
|
-
AsfGuid.FilePropertiesObject = new AsfGuid('8CABDCA1-A947-11CF-8EE4-00C00C205365');
|
|
77
|
-
AsfGuid.StreamPropertiesObject = new AsfGuid('B7DC0791-A9B7-11CF-8EE6-00C00C205365');
|
|
78
|
-
AsfGuid.HeaderExtensionObject = new AsfGuid('5FBF03B5-A92E-11CF-8EE3-00C00C205365');
|
|
79
|
-
AsfGuid.CodecListObject = new AsfGuid('86D15240-311D-11D0-A3A4-00A0C90348F6');
|
|
80
|
-
AsfGuid.ScriptCommandObject = new AsfGuid('1EFB1A30-0B62-11D0-A39B-00A0C90348F6');
|
|
81
|
-
AsfGuid.MarkerObject = new AsfGuid('F487CD01-A951-11CF-8EE6-00C00C205365');
|
|
82
|
-
AsfGuid.BitrateMutualExclusionObject = new AsfGuid('D6E229DC-35DA-11D1-9034-00A0C90349BE');
|
|
83
|
-
AsfGuid.ErrorCorrectionObject = new AsfGuid('75B22635-668E-11CF-A6D9-00AA0062CE6C');
|
|
84
|
-
AsfGuid.ContentDescriptionObject = new AsfGuid('75B22633-668E-11CF-A6D9-00AA0062CE6C');
|
|
85
|
-
AsfGuid.ExtendedContentDescriptionObject = new AsfGuid('D2D0A440-E307-11D2-97F0-00A0C95EA850');
|
|
86
|
-
AsfGuid.ContentBrandingObject = new AsfGuid('2211B3FA-BD23-11D2-B4B7-00A0C955FC6E');
|
|
87
|
-
AsfGuid.StreamBitratePropertiesObject = new AsfGuid('7BF875CE-468D-11D1-8D82-006097C9A2B2');
|
|
88
|
-
AsfGuid.ContentEncryptionObject = new AsfGuid('2211B3FB-BD23-11D2-B4B7-00A0C955FC6E');
|
|
89
|
-
AsfGuid.ExtendedContentEncryptionObject = new AsfGuid('298AE614-2622-4C17-B935-DAE07EE9289C');
|
|
90
|
-
AsfGuid.DigitalSignatureObject = new AsfGuid('2211B3FC-BD23-11D2-B4B7-00A0C955FC6E');
|
|
91
|
-
AsfGuid.PaddingObject = new AsfGuid('1806D474-CADF-4509-A4BA-9AABCB96AAE8');
|
|
92
|
-
// 10.3 Header Extension Object GUIDs
|
|
93
|
-
AsfGuid.ExtendedStreamPropertiesObject = new AsfGuid('14E6A5CB-C672-4332-8399-A96952065B5A');
|
|
94
|
-
AsfGuid.AdvancedMutualExclusionObject = new AsfGuid('A08649CF-4775-4670-8A16-6E35357566CD');
|
|
95
|
-
AsfGuid.GroupMutualExclusionObject = new AsfGuid('D1465A40-5A79-4338-B71B-E36B8FD6C249');
|
|
96
|
-
AsfGuid.StreamPrioritizationObject = new AsfGuid('D4FED15B-88D3-454F-81F0-ED5C45999E24');
|
|
97
|
-
AsfGuid.BandwidthSharingObject = new AsfGuid('A69609E6-517B-11D2-B6AF-00C04FD908E9');
|
|
98
|
-
AsfGuid.LanguageListObject = new AsfGuid('7C4346A9-EFE0-4BFC-B229-393EDE415C85');
|
|
99
|
-
AsfGuid.MetadataObject = new AsfGuid('C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA');
|
|
100
|
-
AsfGuid.MetadataLibraryObject = new AsfGuid('44231C94-9498-49D1-A141-1D134E457054');
|
|
101
|
-
AsfGuid.IndexParametersObject = new AsfGuid('D6E229DF-35DA-11D1-9034-00A0C90349BE');
|
|
102
|
-
AsfGuid.MediaObjectIndexParametersObject = new AsfGuid('6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7');
|
|
103
|
-
AsfGuid.TimecodeIndexParametersObject = new AsfGuid('F55E496D-9797-4B5D-8C8B-604DFE9BFB24');
|
|
104
|
-
AsfGuid.CompatibilityObject = new AsfGuid('26F18B5D-4584-47EC-9F5F-0E651F0452C9');
|
|
105
|
-
AsfGuid.AdvancedContentEncryptionObject = new AsfGuid('43058533-6981-49E6-9B74-AD12CB86D58C');
|
|
106
|
-
// 10.4 Stream Properties Object Stream Type GUIDs
|
|
107
|
-
AsfGuid.AudioMedia = new AsfGuid('F8699E40-5B4D-11CF-A8FD-00805F5C442B');
|
|
108
|
-
AsfGuid.VideoMedia = new AsfGuid('BC19EFC0-5B4D-11CF-A8FD-00805F5C442B');
|
|
109
|
-
AsfGuid.CommandMedia = new AsfGuid('59DACFC0-59E6-11D0-A3AC-00A0C90348F6');
|
|
110
|
-
AsfGuid.JFIF_Media = new AsfGuid('B61BE100-5B4E-11CF-A8FD-00805F5C442B');
|
|
111
|
-
AsfGuid.Degradable_JPEG_Media = new AsfGuid('35907DE0-E415-11CF-A917-00805F5C442B');
|
|
112
|
-
AsfGuid.FileTransferMedia = new AsfGuid('91BD222C-F21C-497A-8B6D-5AA86BFC0185');
|
|
113
|
-
AsfGuid.BinaryMedia = new AsfGuid('3AFB65E2-47EF-40F2-AC2C-70A90D71D343');
|
|
114
|
-
AsfGuid.ASF_Index_Placeholder_Object = new AsfGuid('D9AADE20-7C17-4F9C-BC28-8555DD98E2A2');
|
|
115
|
-
export default AsfGuid;
|
package/lib/asf/AsfObject.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { IGetToken, ITokenizer } from 'strtok3';
|
|
2
|
-
import type { AnyTagValue, IPicture, ITag } from '../type.js';
|
|
2
|
+
import type { AnyTagValue, IAudioTrack, IPicture, ITag, IVideoTrack } from '../type.js';
|
|
3
3
|
import AsfGuid from './AsfGuid.js';
|
|
4
4
|
declare const AsfContentParseError_base: {
|
|
5
5
|
new (message: string): {
|
|
6
|
-
readonly fileType: string;
|
|
7
|
-
toString(): string;
|
|
8
|
-
name: "UnexpectedFileContentError";
|
|
9
6
|
message: string;
|
|
10
7
|
stack?: string;
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
name: "UnexpectedFileContentError";
|
|
10
|
+
readonly fileType: string;
|
|
11
|
+
toString(): string;
|
|
11
12
|
};
|
|
12
13
|
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
13
14
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
@@ -198,6 +199,11 @@ export interface IStreamPropertiesObject {
|
|
|
198
199
|
* Error Correction Type
|
|
199
200
|
*/
|
|
200
201
|
errorCorrectionType: AsfGuid;
|
|
202
|
+
streamNumber: number;
|
|
203
|
+
codecId?: string;
|
|
204
|
+
audio?: IAudioTrack;
|
|
205
|
+
video?: IVideoTrack;
|
|
206
|
+
bitrate?: number;
|
|
201
207
|
}
|
|
202
208
|
/**
|
|
203
209
|
* Token for: 3.3 Stream Properties Object (mandatory, one per stream)
|
|
@@ -282,7 +288,7 @@ export interface IExtendedStreamPropertiesObject {
|
|
|
282
288
|
streamNameCount: number;
|
|
283
289
|
payloadExtensionSystems: number;
|
|
284
290
|
streamNames: IStreamName[];
|
|
285
|
-
streamPropertiesObject:
|
|
291
|
+
streamPropertiesObject: IStreamPropertiesObject | null;
|
|
286
292
|
}
|
|
287
293
|
/**
|
|
288
294
|
* 4.1 Extended Stream Properties Object (optional, 1 per media stream)
|