music-metadata 10.0.1 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE.txt +9 -9
  2. package/README.md +535 -520
  3. package/lib/ParserFactory.d.ts +20 -28
  4. package/lib/ParserFactory.js +204 -207
  5. package/lib/aiff/AiffParser.js +20 -19
  6. package/lib/aiff/AiffToken.d.ts +14 -2
  7. package/lib/aiff/AiffToken.js +12 -0
  8. package/lib/apev2/APEv2Parser.d.ts +4 -4
  9. package/lib/apev2/APEv2Parser.js +12 -10
  10. package/lib/apev2/APEv2Token.d.ts +2 -4
  11. package/lib/apev2/APEv2Token.js +0 -3
  12. package/lib/asf/AsfObject.d.ts +7 -16
  13. package/lib/asf/AsfObject.js +8 -34
  14. package/lib/asf/AsfParser.js +17 -10
  15. package/lib/asf/AsfTagMapper.d.ts +1 -1
  16. package/lib/asf/AsfTagMapper.js +3 -2
  17. package/lib/asf/AsfUtil.d.ts +3 -11
  18. package/lib/asf/AsfUtil.js +29 -30
  19. package/lib/asf/GUID.js +6 -9
  20. package/lib/common/BasicParser.d.ts +4 -4
  21. package/lib/common/BasicParser.js +6 -0
  22. package/lib/common/CaseInsensitiveTagMap.d.ts +1 -1
  23. package/lib/common/CombinedTagMapper.d.ts +5 -5
  24. package/lib/common/CombinedTagMapper.js +1 -1
  25. package/lib/common/FourCC.d.ts +1 -1
  26. package/lib/common/GenericTagMapper.d.ts +8 -8
  27. package/lib/common/GenericTagMapper.js +4 -4
  28. package/lib/common/GenericTagTypes.d.ts +5 -4
  29. package/lib/common/GenericTagTypes.js +2 -2
  30. package/lib/common/MetadataCollector.d.ts +9 -9
  31. package/lib/common/MetadataCollector.js +22 -17
  32. package/lib/common/RandomFileReader.d.ts +1 -1
  33. package/lib/common/RandomFileReader.js +1 -1
  34. package/lib/common/RandomUint8ArrayReader.d.ts +1 -1
  35. package/lib/common/Util.d.ts +2 -6
  36. package/lib/common/Util.js +9 -11
  37. package/lib/core.d.ts +7 -9
  38. package/lib/core.js +10 -7
  39. package/lib/dsdiff/DsdiffParser.js +26 -14
  40. package/lib/dsdiff/DsdiffToken.d.ts +2 -2
  41. package/lib/dsdiff/DsdiffToken.js +1 -0
  42. package/lib/dsf/DsfChunk.js +1 -0
  43. package/lib/dsf/DsfParser.js +4 -2
  44. package/lib/ebml/EbmlIterator.d.ts +52 -0
  45. package/lib/ebml/EbmlIterator.js +220 -0
  46. package/lib/ebml/types.d.ts +36 -0
  47. package/lib/ebml/types.js +10 -0
  48. package/lib/flac/FlacParser.d.ts +3 -3
  49. package/lib/flac/FlacParser.js +9 -12
  50. package/lib/id3v1/ID3v1Parser.d.ts +1 -1
  51. package/lib/id3v1/ID3v1Parser.js +7 -4
  52. package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
  53. package/lib/id3v2/AbstractID3Parser.js +2 -1
  54. package/lib/id3v2/FrameParser.d.ts +28 -3
  55. package/lib/id3v2/FrameParser.js +50 -28
  56. package/lib/id3v2/ID3v22TagMapper.d.ts +1 -1
  57. package/lib/id3v2/ID3v24TagMapper.d.ts +2 -1
  58. package/lib/id3v2/ID3v24TagMapper.js +23 -16
  59. package/lib/id3v2/ID3v2Parser.d.ts +2 -2
  60. package/lib/id3v2/ID3v2Parser.js +18 -8
  61. package/lib/iff/index.js +1 -0
  62. package/lib/index.d.ts +5 -6
  63. package/lib/index.js +7 -8
  64. package/lib/lyrics3/Lyrics3.d.ts +1 -1
  65. package/lib/lyrics3/Lyrics3.js +1 -1
  66. package/lib/matroska/MatroskaDtd.d.ts +2 -2
  67. package/lib/matroska/MatroskaDtd.js +247 -239
  68. package/lib/matroska/MatroskaParser.d.ts +10 -24
  69. package/lib/matroska/MatroskaParser.js +120 -205
  70. package/lib/matroska/types.d.ts +12 -46
  71. package/lib/matroska/types.js +0 -9
  72. package/lib/mp4/Atom.d.ts +3 -3
  73. package/lib/mp4/Atom.js +4 -7
  74. package/lib/mp4/AtomToken.js +2 -1
  75. package/lib/mp4/MP4Parser.js +29 -20
  76. package/lib/mp4/MP4TagMapper.d.ts +2 -2
  77. package/lib/mp4/MP4TagMapper.js +1 -1
  78. package/lib/mpeg/ExtendedLameHeader.d.ts +4 -4
  79. package/lib/mpeg/ExtendedLameHeader.js +2 -1
  80. package/lib/mpeg/MpegParser.d.ts +1 -1
  81. package/lib/mpeg/MpegParser.js +145 -96
  82. package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
  83. package/lib/mpeg/ReplayGainDataFormat.js +1 -0
  84. package/lib/mpeg/XingTag.d.ts +4 -4
  85. package/lib/mpeg/XingTag.js +5 -4
  86. package/lib/musepack/index.js +1 -0
  87. package/lib/musepack/sv7/BitReader.js +14 -17
  88. package/lib/musepack/sv7/MpcSv7Parser.js +6 -1
  89. package/lib/musepack/sv7/StreamVersion7.js +1 -0
  90. package/lib/musepack/sv8/MpcSv8Parser.js +6 -2
  91. package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
  92. package/lib/musepack/sv8/StreamVersion8.js +1 -0
  93. package/lib/ogg/Ogg.d.ts +1 -1
  94. package/lib/ogg/Ogg.js +1 -0
  95. package/lib/ogg/OggParser.d.ts +3 -3
  96. package/lib/ogg/OggParser.js +25 -16
  97. package/lib/ogg/opus/Opus.d.ts +1 -1
  98. package/lib/ogg/opus/Opus.js +1 -0
  99. package/lib/ogg/opus/OpusParser.d.ts +4 -4
  100. package/lib/ogg/opus/OpusParser.js +2 -0
  101. package/lib/ogg/speex/Speex.js +1 -0
  102. package/lib/ogg/speex/SpeexParser.d.ts +4 -4
  103. package/lib/ogg/speex/SpeexParser.js +1 -0
  104. package/lib/ogg/theora/Theora.js +1 -0
  105. package/lib/ogg/theora/TheoraParser.d.ts +4 -4
  106. package/lib/ogg/theora/TheoraParser.js +1 -0
  107. package/lib/ogg/vorbis/Vorbis.d.ts +3 -3
  108. package/lib/ogg/vorbis/Vorbis.js +22 -11
  109. package/lib/ogg/vorbis/VorbisDecoder.d.ts +1 -1
  110. package/lib/ogg/vorbis/VorbisDecoder.js +1 -0
  111. package/lib/ogg/vorbis/VorbisParser.d.ts +4 -4
  112. package/lib/ogg/vorbis/VorbisParser.js +3 -2
  113. package/lib/ogg/vorbis/VorbisTagMapper.d.ts +2 -2
  114. package/lib/ogg/vorbis/VorbisTagMapper.js +2 -2
  115. package/lib/riff/RiffChunk.d.ts +3 -3
  116. package/lib/riff/RiffChunk.js +1 -0
  117. package/lib/riff/RiffInfoTagMap.d.ts +1 -1
  118. package/lib/type.d.ts +44 -25
  119. package/lib/wav/BwfChunk.js +1 -0
  120. package/lib/wav/WaveChunk.d.ts +1 -1
  121. package/lib/wav/WaveChunk.js +1 -0
  122. package/lib/wav/WaveParser.js +27 -17
  123. package/lib/wavpack/WavPackParser.d.ts +1 -1
  124. package/lib/wavpack/WavPackParser.js +22 -13
  125. package/lib/wavpack/WavPackToken.d.ts +13 -17
  126. package/lib/wavpack/WavPackToken.js +22 -23
  127. package/package.json +139 -150
@@ -1,9 +1,8 @@
1
- import { Float32_BE, Float64_BE, StringType, UINT8 } from 'token-types';
2
1
  import initDebug from 'debug';
3
2
  import { BasicParser } from '../common/BasicParser.js';
4
- import * as matroskaDtd from './MatroskaDtd.js';
5
- import { DataType, TargetType, TrackType } from './types.js';
6
- import * as Token from 'token-types';
3
+ import { matroskaDtd } from './MatroskaDtd.js';
4
+ import { TargetType, TrackType } from './types.js';
5
+ import { EbmlIterator, ParseAction } from '../ebml/EbmlIterator.js';
7
6
  const debug = initDebug('music-metadata:parser:matroska');
8
7
  /**
9
8
  * Extensible Binary Meta Language (EBML) parser
@@ -14,17 +13,13 @@ const debug = initDebug('music-metadata:parser:matroska');
14
13
  */
15
14
  export class MatroskaParser extends BasicParser {
16
15
  constructor() {
17
- super();
18
- this.padding = 0;
19
- this.parserMap = new Map();
20
- this.ebmlMaxIDLength = 4;
21
- this.ebmlMaxSizeLength = 8;
22
- this.parserMap.set(DataType.uint, e => this.readUint(e));
23
- this.parserMap.set(DataType.string, e => this.readString(e));
24
- this.parserMap.set(DataType.binary, e => this.readBuffer(e));
25
- this.parserMap.set(DataType.uid, async (e) => this.readBuffer(e));
26
- this.parserMap.set(DataType.bool, e => this.readFlag(e));
27
- this.parserMap.set(DataType.float, e => this.readFloat(e));
16
+ super(...arguments);
17
+ this.seekHeadOffset = 0;
18
+ /**
19
+ * Use index to skip multiple segment/cluster elements at once.
20
+ * Significant performance impact
21
+ */
22
+ this.flagUseIndexToSkipClusters = false;
28
23
  }
29
24
  /**
30
25
  * Initialize parser with output (metadata), input (tokenizer) & parsing options (options).
@@ -34,207 +29,127 @@ export class MatroskaParser extends BasicParser {
34
29
  */
35
30
  init(metadata, tokenizer, options) {
36
31
  super.init(metadata, tokenizer, options);
32
+ this.flagUseIndexToSkipClusters = options.mkvUseIndex ?? false;
37
33
  return this;
38
34
  }
39
35
  async parse() {
40
- var _a;
41
- const containerSize = (_a = this.tokenizer.fileInfo.size) !== null && _a !== void 0 ? _a : Number.MAX_SAFE_INTEGER;
42
- const matroska = await this.parseContainer(matroskaDtd.elements, containerSize, []);
43
- this.metadata.setFormat('container', `EBML/${matroska.ebml.docType}`);
44
- if (matroska.segment) {
45
- const info = matroska.segment.info;
46
- if (info) {
47
- const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
48
- if (typeof info.duration === 'number') {
49
- const duration = info.duration * timecodeScale / 1000000000;
50
- await this.addTag('segment:title', info.title);
51
- this.metadata.setFormat('duration', Number(duration));
52
- }
53
- }
54
- const audioTracks = matroska.segment.tracks;
55
- if (audioTracks && audioTracks.entries) {
56
- audioTracks.entries.forEach(entry => {
57
- const stream = {
58
- codecName: entry.codecID.replace('A_', '').replace('V_', ''),
59
- codecSettings: entry.codecSettings,
60
- flagDefault: entry.flagDefault,
61
- flagLacing: entry.flagLacing,
62
- flagEnabled: entry.flagEnabled,
63
- language: entry.language,
64
- name: entry.name,
65
- type: entry.trackType,
66
- audio: entry.audio,
67
- video: entry.video
68
- };
69
- this.metadata.addStreamInfo(stream);
70
- });
71
- const audioTrack = audioTracks.entries
72
- .filter(entry => {
73
- return entry.trackType === TrackType.audio.valueOf();
74
- })
75
- .reduce((acc, cur) => {
76
- if (!acc) {
77
- return cur;
78
- }
79
- if (!acc.flagDefault && cur.flagDefault) {
80
- return cur;
81
- }
82
- if (cur.trackNumber && cur.trackNumber < acc.trackNumber) {
83
- return cur;
84
- }
85
- return acc;
86
- }, null);
87
- if (audioTrack) {
88
- this.metadata.setFormat('codec', audioTrack.codecID.replace('A_', ''));
89
- this.metadata.setFormat('sampleRate', audioTrack.audio.samplingFrequency);
90
- this.metadata.setFormat('numberOfChannels', audioTrack.audio.channels);
91
- }
92
- if (matroska.segment.tags) {
93
- await Promise.all(matroska.segment.tags.tag.map(async (tag) => {
94
- const target = tag.target;
95
- const targetType = (target === null || target === void 0 ? void 0 : target.targetTypeValue) ? TargetType[target.targetTypeValue] : ((target === null || target === void 0 ? void 0 : target.targetType) ? target.targetType : 'track');
96
- await Promise.all(tag.simpleTags.map(async (simpleTag) => {
97
- const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
98
- await this.addTag(`${targetType}:${simpleTag.name}`, value);
99
- }));
100
- }));
101
- }
102
- if (matroska.segment.attachments) {
103
- await Promise.all(matroska.segment.attachments.attachedFiles
104
- .filter(file => file.mimeType.startsWith('image/'))
105
- .map(file => this.addTag('picture', {
106
- data: file.data,
107
- format: file.mimeType,
108
- description: file.description,
109
- name: file.name
110
- })));
111
- }
112
- }
113
- }
114
- }
115
- async parseContainer(container, posDone, path) {
116
- const tree = {};
117
- while (this.tokenizer.position < posDone) {
118
- let element;
119
- try {
120
- element = await this.readElement();
121
- }
122
- catch (error) {
123
- if (error.message === 'End-Of-Stream') {
124
- break;
125
- }
126
- throw error;
127
- }
128
- const type = container[element.id];
129
- if (type) {
130
- debug(`Element: name=${type.name}, container=${!!type.container}`);
131
- if (type.container) {
132
- const res = await this.parseContainer(type.container, element.len >= 0 ? this.tokenizer.position + element.len : -1, path.concat([type.name]));
133
- if (type.multiple) {
134
- if (!tree[type.name]) {
135
- tree[type.name] = [];
36
+ const containerSize = this.tokenizer.fileInfo.size ?? Number.MAX_SAFE_INTEGER;
37
+ const matroskaIterator = new EbmlIterator(this.tokenizer);
38
+ debug('Initializing DTD end MatroskaIterator');
39
+ await matroskaIterator.iterate(matroskaDtd, containerSize, {
40
+ startNext: (element) => {
41
+ switch (element.id) {
42
+ // case 0x1f43b675: // cluster
43
+ case 0x1c53bb6b: // Cueing Data
44
+ debug(`Skip element: name=${element.name}, id=0x${element.id.toString(16)}`);
45
+ return ParseAction.IgnoreElement;
46
+ case 0x1f43b675: // cluster
47
+ if (this.flagUseIndexToSkipClusters && this.seekHead) {
48
+ const index = this.seekHead.seek.find(index => index.position + this.seekHeadOffset > this.tokenizer.position);
49
+ if (index) {
50
+ // Go to next index position
51
+ const ignoreSize = index.position + this.seekHeadOffset - this.tokenizer.position;
52
+ debug(`Use index to go to next position, ignoring ${ignoreSize} bytes`);
53
+ this.tokenizer.ignore(ignoreSize);
54
+ return ParseAction.SkipElement;
55
+ }
136
56
  }
137
- tree[type.name].push(res);
138
- }
139
- else {
140
- tree[type.name] = res;
141
- }
142
- }
143
- else {
144
- tree[type.name] = await this.parserMap.get(type.value)(element);
57
+ return ParseAction.IgnoreElement;
58
+ default:
59
+ return ParseAction.ReadNext;
145
60
  }
146
- }
147
- else {
61
+ },
62
+ elementValue: async (element, value, offset) => {
63
+ debug(`Received: name=${element.name}, value=${value}`);
148
64
  switch (element.id) {
149
- case 0xec: // void
150
- this.padding += element.len;
151
- await this.tokenizer.ignore(element.len);
65
+ case 0x4282: // docType
66
+ this.metadata.setFormat('container', `EBML/${value}`);
67
+ break;
68
+ case 0x114d9b74:
69
+ this.seekHead = value;
70
+ this.seekHeadOffset = offset;
71
+ break;
72
+ case 0x1549a966:
73
+ { // Info (Segment Information)
74
+ const info = value;
75
+ const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
76
+ if (typeof info.duration === 'number') {
77
+ const duration = info.duration * timecodeScale / 1000000000;
78
+ await this.addTag('segment:title', info.title);
79
+ this.metadata.setFormat('duration', Number(duration));
80
+ }
81
+ }
82
+ break;
83
+ case 0x1654ae6b:
84
+ { // tracks
85
+ const audioTracks = value;
86
+ if (audioTracks?.entries) {
87
+ audioTracks.entries.forEach(entry => {
88
+ const stream = {
89
+ codecName: entry.codecID.replace('A_', '').replace('V_', ''),
90
+ codecSettings: entry.codecSettings,
91
+ flagDefault: entry.flagDefault,
92
+ flagLacing: entry.flagLacing,
93
+ flagEnabled: entry.flagEnabled,
94
+ language: entry.language,
95
+ name: entry.name,
96
+ type: entry.trackType,
97
+ audio: entry.audio,
98
+ video: entry.video
99
+ };
100
+ this.metadata.addStreamInfo(stream);
101
+ });
102
+ const audioTrack = audioTracks.entries
103
+ .filter(entry => entry.trackType === TrackType.audio)
104
+ .reduce((acc, cur) => {
105
+ if (!acc)
106
+ return cur;
107
+ if (cur.flagDefault && !acc.flagDefault)
108
+ return cur;
109
+ if (cur.trackNumber < acc.trackNumber)
110
+ return cur;
111
+ return acc;
112
+ }, null);
113
+ if (audioTrack) {
114
+ this.metadata.setFormat('codec', audioTrack.codecID.replace('A_', ''));
115
+ this.metadata.setFormat('sampleRate', audioTrack.audio.samplingFrequency);
116
+ this.metadata.setFormat('numberOfChannels', audioTrack.audio.channels);
117
+ }
118
+ }
119
+ }
120
+ break;
121
+ case 0x1254c367:
122
+ { // tags
123
+ const tags = value;
124
+ await Promise.all(tags.tag.map(async (tag) => {
125
+ const target = tag.target;
126
+ const targetType = target?.targetTypeValue ? TargetType[target.targetTypeValue] : (target?.targetType ? target.targetType : 'track');
127
+ await Promise.all(tag.simpleTags.map(async (simpleTag) => {
128
+ const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
129
+ await this.addTag(`${targetType}:${simpleTag.name}`, value);
130
+ }));
131
+ }));
132
+ }
133
+ break;
134
+ case 0x1941a469:
135
+ { // attachments
136
+ const attachments = value;
137
+ await Promise.all(attachments.attachedFiles
138
+ .filter(file => file.mimeType.startsWith('image/'))
139
+ .map(file => this.addTag('picture', {
140
+ data: file.data,
141
+ format: file.mimeType,
142
+ description: file.description,
143
+ name: file.name
144
+ })));
145
+ }
152
146
  break;
153
- default:
154
- debug(`parseEbml: path=${path.join('/')}, unknown element: id=${element.id.toString(16)}`);
155
- this.padding += element.len;
156
- await this.tokenizer.ignore(element.len);
157
147
  }
158
148
  }
159
- }
160
- return tree;
161
- }
162
- async readVintData(maxLength) {
163
- const msb = await this.tokenizer.peekNumber(UINT8);
164
- let mask = 0x80;
165
- let oc = 1;
166
- // Calculate VINT_WIDTH
167
- while ((msb & mask) === 0) {
168
- if (oc > maxLength) {
169
- throw new Error('VINT value exceeding maximum size');
170
- }
171
- ++oc;
172
- mask >>= 1;
173
- }
174
- const id = new Uint8Array(oc);
175
- await this.tokenizer.readBuffer(id);
176
- return id;
177
- }
178
- async readElement() {
179
- const id = await this.readVintData(this.ebmlMaxIDLength);
180
- const lenField = await this.readVintData(this.ebmlMaxSizeLength);
181
- lenField[0] ^= 0x80 >> (lenField.length - 1);
182
- return {
183
- id: MatroskaParser.readUIntBE(id, id.length),
184
- len: MatroskaParser.readUIntBE(lenField, lenField.length)
185
- };
186
- }
187
- async readFloat(e) {
188
- switch (e.len) {
189
- case 0:
190
- return 0.0;
191
- case 4:
192
- return this.tokenizer.readNumber(Float32_BE);
193
- case 8:
194
- return this.tokenizer.readNumber(Float64_BE);
195
- case 10:
196
- return this.tokenizer.readNumber(Float64_BE);
197
- default:
198
- throw new Error(`Invalid IEEE-754 float length: ${e.len}`);
199
- }
200
- }
201
- async readFlag(e) {
202
- return (await this.readUint(e)) === 1;
203
- }
204
- async readUint(e) {
205
- const buf = await this.readBuffer(e);
206
- return MatroskaParser.readUIntBE(buf, e.len);
207
- }
208
- async readString(e) {
209
- const rawString = await this.tokenizer.readToken(new StringType(e.len, 'utf-8'));
210
- return rawString.replace(/\x00.*$/g, '');
211
- }
212
- async readBuffer(e) {
213
- const buf = new Uint8Array(e.len);
214
- await this.tokenizer.readBuffer(buf);
215
- return buf;
149
+ });
216
150
  }
217
151
  async addTag(tagId, value) {
218
152
  await this.metadata.addTag('matroska', tagId, value);
219
153
  }
220
- static readUIntBE(buf, len) {
221
- return Number(MatroskaParser.readUIntBeAsBigInt(buf, len));
222
- }
223
- /**
224
- * Reeds an unsigned integer from a big endian buffer of length `len`
225
- * @param buf Buffer to decode from
226
- * @param len Number of bytes
227
- * @private
228
- */
229
- static readUIntBeAsBigInt(buf, len) {
230
- const normalizedNumber = new Uint8Array(8);
231
- const cleanNumber = buf.subarray(0, len);
232
- try {
233
- normalizedNumber.set(cleanNumber, 8 - len);
234
- return Token.UINT64_BE.get(normalizedNumber, 0);
235
- }
236
- catch (error) {
237
- return BigInt(-1);
238
- }
239
- }
240
154
  }
155
+ //# sourceMappingURL=MatroskaParser.js.map
@@ -1,33 +1,10 @@
1
- export interface IHeader {
2
- id: number;
3
- len: number;
4
- }
5
- export declare enum DataType {
6
- 'string' = 0,
7
- uint = 1,
8
- uid = 2,
9
- bool = 3,
10
- binary = 4,
11
- float = 5
12
- }
13
- export interface IElementType<T> {
14
- readonly name: string;
15
- readonly value?: DataType;
16
- readonly container?: IContainerType;
17
- readonly multiple?: boolean;
18
- }
19
- export interface IContainerType {
20
- [id: number]: IElementType<string | number | boolean | Uint8Array>;
21
- }
22
- export interface ITree {
23
- [name: string]: string | number | boolean | Uint8Array | ITree | ITree[];
1
+ import type { IEbmlDoc } from '../ebml/types.js';
2
+ export interface ISeek {
3
+ id: Uint8Array;
4
+ position: number;
24
5
  }
25
6
  export interface ISeekHead {
26
- id?: Uint8Array;
27
- position?: number;
28
- }
29
- export interface IMetaSeekInformation {
30
- seekHeads: ISeekHead[];
7
+ seek: ISeek[];
31
8
  }
32
9
  export interface ISegmentInformation {
33
10
  uid?: Uint8Array;
@@ -40,9 +17,9 @@ export interface ISegmentInformation {
40
17
  }
41
18
  export interface ITrackEntry {
42
19
  uid?: Uint8Array;
43
- trackNumber?: number;
20
+ trackNumber: number;
44
21
  trackType?: TrackType;
45
- audio?: ITrackAudio;
22
+ audio: ITrackAudio;
46
23
  video?: ITrackVideo;
47
24
  flagEnabled?: boolean;
48
25
  flagDefault?: boolean;
@@ -51,7 +28,7 @@ export interface ITrackEntry {
51
28
  trackTimecodeScale?: number;
52
29
  name?: string;
53
30
  language?: string;
54
- codecID?: string;
31
+ codecID: string;
55
32
  codecPrivate?: Uint8Array;
56
33
  codecName?: string;
57
34
  codecSettings?: string;
@@ -121,6 +98,7 @@ export declare enum TrackType {
121
98
  button = 18,
122
99
  control = 32
123
100
  }
101
+ export type TrackTypeKey = keyof TrackType;
124
102
  export interface ITarget {
125
103
  trackUID?: Uint8Array;
126
104
  chapterUID?: Uint8Array;
@@ -146,29 +124,17 @@ export interface IAttachmedFile {
146
124
  uid: string;
147
125
  }
148
126
  export interface IAttachments {
149
- attachedFiles?: IAttachmedFile[];
127
+ attachedFiles: IAttachmedFile[];
150
128
  }
151
129
  export interface IMatroskaSegment {
152
- metaSeekInfo?: IMetaSeekInformation;
153
- seekHeads?: ISeekHead[];
130
+ metaSeekInfo?: ISeekHead;
131
+ seekHeads?: ISeek[];
154
132
  info?: ISegmentInformation;
155
133
  tracks?: ITrackElement;
156
134
  tags?: ITags;
157
135
  cues?: ICuePoint[];
158
136
  attachments?: IAttachments;
159
137
  }
160
- export interface IEbmlElements {
161
- version?: number;
162
- readVersion?: number;
163
- maxIDWidth?: number;
164
- maxSizeWidth?: number;
165
- docType?: string;
166
- docTypeVersion?: number;
167
- docTypeReadVersion?: number;
168
- }
169
- export interface IEbmlDoc {
170
- ebml: IEbmlElements;
171
- }
172
138
  export interface IMatroskaDoc extends IEbmlDoc {
173
139
  segment: IMatroskaSegment;
174
140
  }
@@ -1,12 +1,3 @@
1
- export var DataType;
2
- (function (DataType) {
3
- DataType[DataType["string"] = 0] = "string";
4
- DataType[DataType["uint"] = 1] = "uint";
5
- DataType[DataType["uid"] = 2] = "uid";
6
- DataType[DataType["bool"] = 3] = "bool";
7
- DataType[DataType["binary"] = 4] = "binary";
8
- DataType[DataType["float"] = 5] = "float";
9
- })(DataType || (DataType = {}));
10
1
  export var TargetType;
11
2
  (function (TargetType) {
12
3
  TargetType[TargetType["shot"] = 10] = "shot";
package/lib/mp4/Atom.d.ts CHANGED
@@ -4,11 +4,11 @@ export type AtomDataHandler = (atom: Atom, remaining: number) => Promise<void>;
4
4
  export declare class Atom {
5
5
  readonly header: AtomToken.IAtomHeader;
6
6
  extended: boolean;
7
- readonly parent: Atom;
8
- static readAtom(tokenizer: ITokenizer, dataHandler: AtomDataHandler, parent: Atom, remaining: number): Promise<Atom>;
7
+ readonly parent: Atom | null;
8
+ static readAtom(tokenizer: ITokenizer, dataHandler: AtomDataHandler, parent: Atom | null, remaining: number): Promise<Atom>;
9
9
  readonly children: Atom[];
10
10
  readonly atomPath: string;
11
- constructor(header: AtomToken.IAtomHeader, extended: boolean, parent: Atom);
11
+ constructor(header: AtomToken.IAtomHeader, extended: boolean, parent: Atom | null);
12
12
  getHeaderLength(): number;
13
13
  getPayloadLength(remaining: number): number;
14
14
  readAtoms(tokenizer: ITokenizer, dataHandler: AtomDataHandler, size: number): Promise<void>;
package/lib/mp4/Atom.js CHANGED
@@ -23,7 +23,7 @@ export class Atom {
23
23
  this.extended = extended;
24
24
  this.parent = parent;
25
25
  this.children = [];
26
- this.atomPath = (this.parent ? this.parent.atomPath + '.' : '') + this.header.name;
26
+ this.atomPath = (this.parent ? `${this.parent.atomPath}.` : '') + this.header.name;
27
27
  }
28
28
  getHeaderLength() {
29
29
  return this.extended ? 16 : 8;
@@ -51,19 +51,16 @@ export class Atom {
51
51
  case 'ilst':
52
52
  case 'tref':
53
53
  return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining));
54
- case 'meta': // Metadata Atom, ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW8
54
+ case 'meta': { // Metadata Atom, ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW8
55
55
  // meta has 4 bytes of padding, ignore
56
56
  const peekHeader = await tokenizer.peekToken(Header);
57
57
  const paddingLength = peekHeader.name === 'hdlr' ? 0 : 4;
58
58
  await tokenizer.ignore(paddingLength);
59
59
  return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining) - paddingLength);
60
- case 'mdhd': // Media header atom
61
- case 'mvhd': // 'movie' => 'mvhd': movie header atom; child of Movie Atom
62
- case 'tkhd':
63
- case 'stsz':
64
- case 'mdat':
60
+ }
65
61
  default:
66
62
  return dataHandler(this, remaining);
67
63
  }
68
64
  }
69
65
  }
66
+ //# sourceMappingURL=Atom.js.map
@@ -68,7 +68,7 @@ export class FixedLengthAtom {
68
68
  if (len < expLen) {
69
69
  throw new Error(`Atom ${atomId} expected to be ${expLen}, but specifies ${len} bytes long.`);
70
70
  }
71
- else if (len > expLen) {
71
+ if (len > expLen) {
72
72
  debug(`Warning: atom ${atomId} expected to be ${expLen}, but was actually ${len} bytes long.`);
73
73
  }
74
74
  }
@@ -390,3 +390,4 @@ function readTokenTable(buf, token, off, remainingLen, numberOfEntries) {
390
390
  }
391
391
  return entries;
392
392
  }
393
+ //# sourceMappingURL=AtomToken.js.map
@@ -91,6 +91,7 @@ function distinct(value, index, self) {
91
91
  export class MP4Parser extends BasicParser {
92
92
  constructor() {
93
93
  super(...arguments);
94
+ this.tracks = [];
94
95
  this.atomParsers = {
95
96
  /**
96
97
  * Parse movie header (mvhd) atom
@@ -208,13 +209,13 @@ export class MP4Parser extends BasicParser {
208
209
  const integerType = (signed ? 'INT' : 'UINT') + array.length * 8 + (array.length > 1 ? '_BE' : '');
209
210
  const token = Token[integerType];
210
211
  if (!token) {
211
- throw new Error('Token for integer type not found: "' + integerType + '"');
212
+ throw new Error(`Token for integer type not found: "${integerType}"`);
212
213
  }
213
214
  return Number(token.get(array, 0));
214
215
  }
215
216
  async parse() {
216
217
  this.tracks = [];
217
- let remainingFileSize = this.tokenizer.fileInfo.size;
218
+ let remainingFileSize = this.tokenizer.fileInfo.size || 0;
218
219
  while (!this.tokenizer.fileInfo.size || remainingFileSize > 0) {
219
220
  try {
220
221
  const token = await this.tokenizer.peekToken(AtomToken.Header);
@@ -226,9 +227,13 @@ export class MP4Parser extends BasicParser {
226
227
  }
227
228
  }
228
229
  catch (error) {
229
- const errMsg = `Error at offset=${this.tokenizer.position}: ${error.message}`;
230
- debug(errMsg);
231
- this.addWarning(errMsg);
230
+ if (error instanceof Error) {
231
+ const errMsg = `Error at offset=${this.tokenizer.position}: ${error.message}`;
232
+ debug(errMsg);
233
+ this.addWarning(errMsg);
234
+ }
235
+ else
236
+ throw error;
232
237
  break;
233
238
  }
234
239
  const rootAtom = await Atom.readAtom(this.tokenizer, (atom, remaining) => this.handleAtom(atom, remaining), null, remainingFileSize);
@@ -309,10 +314,8 @@ export class MP4Parser extends BasicParser {
309
314
  if (this.atomParsers[atom.header.name]) {
310
315
  return this.atomParsers[atom.header.name](remaining);
311
316
  }
312
- else {
313
- debug(`No parser for atom path=${atom.atomPath}, payload-len=${remaining}, ignoring atom`);
314
- await this.tokenizer.ignore(remaining);
315
- }
317
+ debug(`No parser for atom path=${atom.atomPath}, payload-len=${remaining}, ignoring atom`);
318
+ await this.tokenizer.ignore(remaining);
316
319
  }
317
320
  getTrackDescription() {
318
321
  return this.tracks[this.tracks.length - 1];
@@ -326,7 +329,7 @@ export class MP4Parser extends BasicParser {
326
329
  await this.metadata.addTag(tagFormat, id, value);
327
330
  }
328
331
  addWarning(message) {
329
- debug('Warning: ' + message);
332
+ debug(`Warning: ${message}`);
330
333
  this.metadata.addWarning(message);
331
334
  }
332
335
  /**
@@ -343,20 +346,22 @@ export class MP4Parser extends BasicParser {
343
346
  return this.parseValueAtom(tagKey, child);
344
347
  case 'name': // name atom (optional)
345
348
  case 'mean':
346
- case 'rate':
349
+ case 'rate': {
347
350
  const name = await this.tokenizer.readToken(new AtomToken.NameAtom(payLoadLength));
348
- tagKey += ':' + name.name;
351
+ tagKey += `:${name.name}`;
349
352
  break;
350
- default:
353
+ }
354
+ default: {
351
355
  const uint8Array = await this.tokenizer.readToken(new Token.Uint8ArrayType(payLoadLength));
352
- this.addWarning('Unsupported meta-item: ' + tagKey + '[' + child.header.name + '] => value=' + uint8ArrayToHex(uint8Array) + ' ascii=' + uint8ArrayToString(uint8Array, 'ascii'));
356
+ this.addWarning(`Unsupported meta-item: ${tagKey}[${child.header.name}] => value=${uint8ArrayToHex(uint8Array)} ascii=${uint8ArrayToString(uint8Array, 'ascii')}`);
357
+ }
353
358
  }
354
359
  }, metaAtom.getPayloadLength(0));
355
360
  }
356
361
  async parseValueAtom(tagKey, metaAtom) {
357
362
  const dataAtom = await this.tokenizer.readToken(new AtomToken.DataAtom(Number(metaAtom.header.length) - AtomToken.Header.len));
358
363
  if (dataAtom.type.set !== 0) {
359
- throw new Error('Unsupported type-set != 0: ' + dataAtom.type.set);
364
+ throw new Error(`Unsupported type-set != 0: ${dataAtom.type.set}`);
360
365
  }
361
366
  // Use well-known-type table
362
367
  // Ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW35
@@ -364,24 +369,27 @@ export class MP4Parser extends BasicParser {
364
369
  case 0: // reserved: Reserved for use where no type needs to be indicated
365
370
  switch (tagKey) {
366
371
  case 'trkn':
367
- case 'disk':
372
+ case 'disk': {
368
373
  const num = Token.UINT8.get(dataAtom.value, 3);
369
374
  const of = Token.UINT8.get(dataAtom.value, 5);
370
375
  // console.log(" %s[data] = %s/%s", tagKey, num, of);
371
- await this.addTag(tagKey, num + '/' + of);
376
+ await this.addTag(tagKey, `${num}/${of}`);
372
377
  break;
373
- case 'gnre':
378
+ }
379
+ case 'gnre': {
374
380
  const genreInt = Token.UINT8.get(dataAtom.value, 1);
375
381
  const genreStr = Genres[genreInt - 1];
376
382
  // console.log(" %s[data] = %s", tagKey, genreStr);
377
383
  await this.addTag(tagKey, genreStr);
378
384
  break;
379
- case 'rate':
385
+ }
386
+ case 'rate': {
380
387
  const rate = new TextDecoder('ascii').decode(dataAtom.value);
381
388
  await this.addTag(tagKey, rate);
382
389
  break;
390
+ }
383
391
  default:
384
- debug('unknown proprietary value type for: ' + metaAtom.atomPath);
392
+ debug(`unknown proprietary value type for: ${metaAtom.atomPath}`);
385
393
  }
386
394
  break;
387
395
  case 1: // UTF-8: Without any count or NULL terminator
@@ -515,3 +523,4 @@ export class MP4Parser extends BasicParser {
515
523
  return stcTable[stcTable.length - 1].samplesPerChunk;
516
524
  }
517
525
  }
526
+ //# sourceMappingURL=MP4Parser.js.map