music-metadata 12.2.0 → 12.2.2

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.
@@ -4,6 +4,10 @@ import { DataType } from '../ebml/types.js';
4
4
  * Derived from https://github.com/tungol/EBML/blob/master/doctypes/matroska.dtd
5
5
  * Extended with:
6
6
  * - https://www.matroska.org/technical/specs/index.html
7
+ * Cardinality and paths audited against:
8
+ * https://github.com/ietf-wg-cellar/matroska-specification/blob/master/ebml_matroska.xml
9
+ * UID integers deliberately decode as bytes to preserve all 64 bits.
10
+ * Recursive ChapterAtom/SimpleTag children are not expanded by this metadata subset.
7
11
  */
8
12
  export const matroskaDtd = {
9
13
  name: 'dtd',
@@ -28,6 +32,7 @@ export const matroskaDtd = {
28
32
  // Meta Seek Information (also known as MetaSeek)
29
33
  0x114d9b74: {
30
34
  name: 'seekHead',
35
+ multiple: true,
31
36
  container: {
32
37
  0x4dbb: {
33
38
  name: 'seek',
@@ -51,7 +56,7 @@ export const matroskaDtd = {
51
56
  0x3e83bb: { name: 'nextFilename', value: DataType.string },
52
57
  0x2ad7b1: { name: 'timecodeScale', value: DataType.uint },
53
58
  0x4489: { name: 'duration', value: DataType.float },
54
- 0x4461: { name: 'dateUTC', value: DataType.uint },
59
+ 0x4461: { name: 'dateUTC', value: DataType.int },
55
60
  0x7ba9: { name: 'title', value: DataType.string },
56
61
  0x4d80: { name: 'muxingApp', value: DataType.string },
57
62
  0x5741: { name: 'writingApp', value: DataType.string }
@@ -62,12 +67,17 @@ export const matroskaDtd = {
62
67
  name: 'cluster',
63
68
  multiple: true,
64
69
  container: {
65
- 0xe7: { name: 'timecode', value: DataType.uid },
66
- 0x58d7: { name: 'silentTracks ', multiple: true },
67
- 0xa7: { name: 'position', value: DataType.uid },
68
- 0xab: { name: 'prevSize', value: DataType.uid },
69
- 0xa0: { name: 'blockGroup' },
70
- 0xa3: { name: 'simpleBlock' }
70
+ 0xe7: { name: 'timecode', value: DataType.uint },
71
+ 0x5854: {
72
+ name: 'silentTracks',
73
+ container: {
74
+ 0x58d7: { name: 'trackNumber', value: DataType.uint, multiple: true }
75
+ }
76
+ },
77
+ 0xa7: { name: 'position', value: DataType.uint },
78
+ 0xab: { name: 'prevSize', value: DataType.uint },
79
+ 0xa0: { name: 'blockGroup', multiple: true },
80
+ 0xa3: { name: 'simpleBlock', value: DataType.binary, multiple: true }
71
81
  }
72
82
  },
73
83
  // Track
@@ -86,9 +96,9 @@ export const matroskaDtd = {
86
96
  0x55aa: { name: 'flagForced', value: DataType.bool }, // extended
87
97
  0x9c: { name: 'flagLacing', value: DataType.bool },
88
98
  0x6de7: { name: 'minCache', value: DataType.uint },
89
- 0x6de8: { name: 'maxCache', value: DataType.uint },
99
+ 0x6df8: { name: 'maxCache', value: DataType.uint },
90
100
  0x23e383: { name: 'defaultDuration', value: DataType.uint },
91
- 0x23314f: { name: 'timecodeScale', value: DataType.float },
101
+ 0x23314f: { name: 'trackTimecodeScale', value: DataType.float },
92
102
  0x536e: { name: 'name', value: DataType.string },
93
103
  0x22b59c: { name: 'language', value: DataType.string },
94
104
  0x22b59d: { name: 'languageIETF', value: DataType.string },
@@ -96,22 +106,23 @@ export const matroskaDtd = {
96
106
  0x63a2: { name: 'codecPrivate', value: DataType.binary },
97
107
  0x258688: { name: 'codecName', value: DataType.string },
98
108
  0x3a9697: { name: 'codecSettings', value: DataType.string },
99
- 0x3b4040: { name: 'codecInfoUrl', value: DataType.string },
100
- 0x26b240: { name: 'codecDownloadUrl', value: DataType.string },
109
+ 0x3b4040: { name: 'codecInfoUrl', value: DataType.string, multiple: true },
110
+ 0x26b240: { name: 'codecDownloadUrl', value: DataType.string, multiple: true },
101
111
  0xaa: { name: 'codecDecodeAll', value: DataType.bool },
102
- 0x6fab: { name: 'trackOverlay', value: DataType.uint },
112
+ 0x6fab: { name: 'trackOverlay', value: DataType.uint, multiple: true },
103
113
  // Video
104
114
  0xe0: {
105
115
  name: 'video',
106
116
  container: {
107
- 0x9a: { name: 'flagInterlaced', value: DataType.bool },
117
+ 0x9a: { name: 'flagInterlaced', value: DataType.uint },
108
118
  0x53b8: { name: 'stereoMode', value: DataType.uint },
109
119
  0xb0: { name: 'pixelWidth', value: DataType.uint },
110
120
  0xba: { name: 'pixelHeight', value: DataType.uint },
111
121
  0x54b0: { name: 'displayWidth', value: DataType.uint },
112
122
  0x54ba: { name: 'displayHeight', value: DataType.uint },
123
+ 0x54b2: { name: 'displayUnit', value: DataType.uint },
113
124
  0x54b3: { name: 'aspectRatioType', value: DataType.uint },
114
- 0x2eb524: { name: 'colourSpace', value: DataType.uint },
125
+ 0x2eb524: { name: 'colourSpace', value: DataType.binary },
115
126
  0x2fb523: { name: 'gammaValue', value: DataType.float }
116
127
  }
117
128
  },
@@ -122,7 +133,6 @@ export const matroskaDtd = {
122
133
  0xb5: { name: 'samplingFrequency', value: DataType.float },
123
134
  0x78b5: { name: 'outputSamplingFrequency', value: DataType.float },
124
135
  0x9f: { name: 'channels', value: DataType.uint }, // https://www.matroska.org/technical/specs/index.html
125
- 0x94: { name: 'channels', value: DataType.uint },
126
136
  0x7d7b: { name: 'channelPositions', value: DataType.binary },
127
137
  0x6264: { name: 'bitDepth', value: DataType.uint }
128
138
  }
@@ -133,29 +143,29 @@ export const matroskaDtd = {
133
143
  container: {
134
144
  0x6240: {
135
145
  name: 'contentEncoding',
146
+ multiple: true,
136
147
  container: {
137
148
  0x5031: { name: 'order', value: DataType.uint },
138
- 0x5032: { name: 'scope', value: DataType.bool },
149
+ 0x5032: { name: 'scope', value: DataType.uint },
139
150
  0x5033: { name: 'type', value: DataType.uint },
140
151
  0x5034: {
141
- name: 'contentEncoding',
152
+ name: 'contentCompression',
142
153
  container: {
143
154
  0x4254: { name: 'contentCompAlgo', value: DataType.uint },
144
155
  0x4255: { name: 'contentCompSettings', value: DataType.binary }
145
156
  }
146
157
  },
147
158
  0x5035: {
148
- name: 'contentEncoding',
159
+ name: 'contentEncryption',
149
160
  container: {
150
161
  0x47e1: { name: 'contentEncAlgo', value: DataType.uint },
151
162
  0x47e2: { name: 'contentEncKeyID', value: DataType.binary },
152
- 0x47e3: { name: 'contentSignature ', value: DataType.binary },
153
- 0x47e4: { name: 'ContentSigKeyID ', value: DataType.binary },
154
- 0x47e5: { name: 'contentSigAlgo ', value: DataType.uint },
155
- 0x47e6: { name: 'contentSigHashAlgo ', value: DataType.uint }
163
+ 0x47e3: { name: 'contentSignature', value: DataType.binary },
164
+ 0x47e4: { name: 'contentSigKeyID', value: DataType.binary },
165
+ 0x47e5: { name: 'contentSigAlgo', value: DataType.uint },
166
+ 0x47e6: { name: 'contentSigHashAlgo', value: DataType.uint }
156
167
  }
157
- },
158
- 0x6264: { name: 'bitDepth', value: DataType.uint }
168
+ }
159
169
  }
160
170
  }
161
171
  }
@@ -170,10 +180,12 @@ export const matroskaDtd = {
170
180
  container: {
171
181
  0xbb: {
172
182
  name: 'cuePoint',
183
+ multiple: true,
173
184
  container: {
174
- 0xb3: { name: 'cueTime', value: DataType.uid },
185
+ 0xb3: { name: 'cueTime', value: DataType.uint },
175
186
  0xb7: {
176
187
  name: 'positions',
188
+ multiple: true,
177
189
  container: {
178
190
  0xf7: { name: 'track', value: DataType.uint },
179
191
  0xf1: { name: 'clusterPosition', value: DataType.uint },
@@ -181,6 +193,7 @@ export const matroskaDtd = {
181
193
  0xea: { name: 'codecState', value: DataType.uint },
182
194
  0xdb: {
183
195
  name: 'reference',
196
+ multiple: true,
184
197
  container: {
185
198
  0x96: { name: 'time', value: DataType.uint },
186
199
  0x97: { name: 'cluster', value: DataType.uint },
@@ -218,27 +231,30 @@ export const matroskaDtd = {
218
231
  container: {
219
232
  0x45b9: {
220
233
  name: 'editionEntry',
234
+ multiple: true,
221
235
  container: {
222
236
  0xb6: {
223
237
  name: 'chapterAtom',
238
+ multiple: true,
224
239
  container: {
225
240
  0x73c4: { name: 'uid', value: DataType.uid },
226
241
  0x91: { name: 'timeStart', value: DataType.uint },
227
- 0x92: { name: 'timeEnd', value: DataType.uid },
242
+ 0x92: { name: 'timeEnd', value: DataType.uint },
228
243
  0x98: { name: 'hidden', value: DataType.bool },
229
- 0x4598: { name: 'enabled', value: DataType.uid },
244
+ 0x4598: { name: 'enabled', value: DataType.bool },
230
245
  0x8f: {
231
246
  name: 'track',
232
247
  container: {
233
- 0x89: { name: 'trackNumber', value: DataType.uid },
234
- 0x80: {
235
- name: 'display',
236
- container: {
237
- 0x85: { name: 'string', value: DataType.string },
238
- 0x437c: { name: 'language ', value: DataType.string },
239
- 0x437e: { name: 'country ', value: DataType.string }
240
- }
241
- }
248
+ 0x89: { name: 'trackUID', value: DataType.uid, multiple: true }
249
+ }
250
+ },
251
+ 0x80: {
252
+ name: 'display',
253
+ multiple: true,
254
+ container: {
255
+ 0x85: { name: 'string', value: DataType.string },
256
+ 0x437c: { name: 'language', value: DataType.string, multiple: true },
257
+ 0x437e: { name: 'country', value: DataType.string, multiple: true }
242
258
  }
243
259
  }
244
260
  }
@@ -250,6 +266,7 @@ export const matroskaDtd = {
250
266
  // Tagging
251
267
  0x1254c367: {
252
268
  name: 'tags',
269
+ multiple: true,
253
270
  container: {
254
271
  0x7373: {
255
272
  name: 'tag',
@@ -258,12 +275,12 @@ export const matroskaDtd = {
258
275
  0x63c0: {
259
276
  name: 'target',
260
277
  container: {
261
- 0x63c5: { name: 'tagTrackUID', value: DataType.uid },
262
- 0x63c4: { name: 'tagChapterUID', value: DataType.uint },
263
- 0x63c6: { name: 'tagAttachmentUID', value: DataType.uid },
278
+ 0x63c5: { name: 'tagTrackUID', value: DataType.uid, multiple: true },
279
+ 0x63c4: { name: 'tagChapterUID', value: DataType.uid, multiple: true },
280
+ 0x63c6: { name: 'tagAttachmentUID', value: DataType.uid, multiple: true },
264
281
  0x63ca: { name: 'targetType', value: DataType.string }, // extended
265
282
  0x68ca: { name: 'targetTypeValue', value: DataType.uint }, // extended
266
- 0x63c9: { name: 'tagEditionUID', value: DataType.uid } // extended
283
+ 0x63c9: { name: 'tagEditionUID', value: DataType.uid, multiple: true } // extended
267
284
  }
268
285
  },
269
286
  0x67c8: {
@@ -4,6 +4,30 @@ import { EbmlIterator, ParseAction } from '../ebml/EbmlIterator.js';
4
4
  import { matroskaDtd } from './MatroskaDtd.js';
5
5
  import { TargetType, TrackType } from './types.js';
6
6
  const debug = initDebug('music-metadata:parser:matroska');
7
+ const targetTypes = TargetType;
8
+ function isCompleteTrack(entry) {
9
+ return (typeof entry.trackNumber === 'number' &&
10
+ Number.isSafeInteger(entry.trackNumber) &&
11
+ entry.trackNumber > 0 &&
12
+ typeof entry.codecID === 'string' &&
13
+ entry.codecID.length > 0 &&
14
+ Object.values(TrackType).some(type => type === entry.trackType));
15
+ }
16
+ function isCompleteFile(file) {
17
+ return (typeof file.name === 'string' &&
18
+ typeof file.mimeType === 'string' &&
19
+ file.data instanceof Uint8Array &&
20
+ file.uid instanceof Uint8Array);
21
+ }
22
+ function videoInfo(video) {
23
+ const { flagInterlaced, ...properties } = video;
24
+ const result = properties;
25
+ // Matroska uses 0=undetermined, 1=interlaced, 2=progressive.
26
+ if (flagInterlaced === 1 || flagInterlaced === 2) {
27
+ result.flagInterlaced = flagInterlaced === 1;
28
+ }
29
+ return result;
30
+ }
7
31
  /**
8
32
  * Extensible Binary Meta Language (EBML) parser
9
33
  * https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Language
@@ -23,23 +47,44 @@ export class MatroskaParser extends BasicParser {
23
47
  flagUseIndexToSkipClusters = this.options.mkvUseIndex ?? false;
24
48
  async parse() {
25
49
  const containerSize = this.tokenizer.fileInfo.size ?? Number.MAX_SAFE_INTEGER;
50
+ const attachmentMimeTypes = new WeakMap();
26
51
  const matroskaIterator = new EbmlIterator(this.tokenizer, this.options);
27
52
  debug('Initializing DTD end MatroskaIterator');
28
53
  await matroskaIterator.iterate(matroskaDtd, containerSize, {
29
- startNext: element => {
54
+ // Process tags and attachments one at a time. Never assemble a Segment/root tree.
55
+ collect: element => [0x114d9b74, 0x1549a966, 0x1654ae6b, 0x7373, 0x61a7].includes(element.id),
56
+ startNext: async (element) => {
30
57
  switch (element.id) {
31
- // case 0x1f43b675: // cluster
58
+ case 0x18538067: // SeekPosition is relative to the Segment payload, not the SeekHead.
59
+ this.seekHeadOffset = this.tokenizer.position;
60
+ return ParseAction.ReadNext;
61
+ case 0x1941a469: // Attachments are only used for cover pictures.
62
+ return this.options.skipCovers ? ParseAction.IgnoreElement : ParseAction.ReadNext;
63
+ case 0x465c: {
64
+ // FileData
65
+ const mimeType = element.parent && attachmentMimeTypes.get(element.parent);
66
+ // If MIME follows FileData, retain one payload under maxMemoryAllocation
67
+ // until AttachedFile ends, then classify it. This also works on streams.
68
+ return mimeType !== undefined && !mimeType.startsWith('image/')
69
+ ? ParseAction.IgnoreElement
70
+ : ParseAction.ReadNext;
71
+ }
72
+ case 0x114d9b74:
73
+ return this.flagUseIndexToSkipClusters ? ParseAction.ReadNext : ParseAction.IgnoreElement;
74
+ case 0x63a2: // CodecPrivate is not exposed as metadata.
75
+ case 0x6d80: // ContentEncodings
76
+ case 0x1043a770: // Chapters are not currently exposed by this parser.
32
77
  case 0x1c53bb6b: // Cueing Data
33
78
  debug(`Skip element: name=${element.name}, id=0x${element.id.toString(16)}`);
34
79
  return ParseAction.IgnoreElement;
35
80
  case 0x1f43b675: // cluster
36
81
  if (this.flagUseIndexToSkipClusters && this.seekHead) {
37
- const index = this.seekHead.seek.find(index => index.position + this.seekHeadOffset > this.tokenizer.position);
38
- if (index) {
82
+ const index = (this.seekHead.seek ?? []).find(index => index.position !== undefined && index.position + this.seekHeadOffset > this.tokenizer.position);
83
+ if (index?.position !== undefined) {
39
84
  // Go to next index position
40
85
  const ignoreSize = index.position + this.seekHeadOffset - this.tokenizer.position;
41
86
  debug(`Use index to go to next position, ignoring ${ignoreSize} bytes`);
42
- this.tokenizer.ignore(ignoreSize);
87
+ await this.tokenizer.ignore(ignoreSize);
43
88
  return ParseAction.SkipElement;
44
89
  }
45
90
  }
@@ -48,24 +93,28 @@ export class MatroskaParser extends BasicParser {
48
93
  return ParseAction.ReadNext;
49
94
  }
50
95
  },
51
- elementValue: async (element, value, offset) => {
52
- debug(`Received: name=${element.name}, value=${value}`);
53
- switch (element.id) {
96
+ onElement: async (event) => {
97
+ if (debug.enabled) {
98
+ const value = event.value instanceof Uint8Array ? `${event.value.byteLength} bytes` : event.value;
99
+ debug(`Received: name=${event.element.name}, value=${value}`);
100
+ }
101
+ switch (event.id) {
54
102
  case 0x4282: // docType
55
- this.metadata.setFormat('container', `EBML/${value}`);
103
+ this.metadata.setFormat('container', `EBML/${event.value}`);
56
104
  break;
57
105
  case 0x114d9b74:
58
- this.seekHead = value;
59
- this.seekHeadOffset = offset;
106
+ this.seekHead = event.value;
60
107
  break;
61
108
  case 0x1549a966:
62
109
  {
63
110
  // Info (Segment Information)
64
- const info = value;
111
+ const info = event.value;
112
+ if (info.title !== undefined) {
113
+ await this.addTag('segment:title', info.title);
114
+ }
65
115
  const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
66
116
  if (typeof info.duration === 'number') {
67
117
  const duration = (info.duration * timecodeScale) / 1000000000;
68
- await this.addTag('segment:title', info.title);
69
118
  if (Number.isFinite(duration) && duration > 0) {
70
119
  this.metadata.setFormat('duration', duration);
71
120
  this.metadata.setFormat('containerDuration', duration);
@@ -76,9 +125,18 @@ export class MatroskaParser extends BasicParser {
76
125
  case 0x1654ae6b:
77
126
  {
78
127
  // tracks
79
- const audioTracks = value;
128
+ const audioTracks = event.value;
80
129
  if (audioTracks?.entries) {
81
- audioTracks.entries.forEach(entry => {
130
+ const entries = audioTracks.entries
131
+ .filter(entry => {
132
+ if (isCompleteTrack(entry)) {
133
+ return true;
134
+ }
135
+ this.metadata.addWarning('Ignoring Matroska track without valid TrackNumber, TrackType or CodecID');
136
+ return false;
137
+ })
138
+ .filter(isCompleteTrack);
139
+ entries.forEach(entry => {
82
140
  const stream = {
83
141
  id: entry.trackNumber,
84
142
  codecId: entry.codecID,
@@ -98,7 +156,7 @@ export class MatroskaParser extends BasicParser {
98
156
  channels: entry.audio?.channels ?? 1
99
157
  }
100
158
  : undefined,
101
- video: entry.trackType === TrackType.video ? entry.video : undefined
159
+ video: entry.trackType === TrackType.video && entry.video ? videoInfo(entry.video) : undefined
102
160
  };
103
161
  if (stream.video && entry.defaultDuration && entry.defaultDuration > 0) {
104
162
  stream.video = { ...stream.video, frameRate: 1e9 / entry.defaultDuration };
@@ -108,7 +166,7 @@ export class MatroskaParser extends BasicParser {
108
166
  }
109
167
  this.metadata.addStreamInfo(stream);
110
168
  });
111
- const audioTrack = audioTracks.entries
169
+ const audioTrack = entries
112
170
  .filter(entry => entry.trackType === TrackType.audio && entry.flagEnabled !== false)
113
171
  .reduce((acc, cur) => {
114
172
  if (!acc) {
@@ -136,50 +194,56 @@ export class MatroskaParser extends BasicParser {
136
194
  }
137
195
  }
138
196
  break;
139
- case 0x1254c367:
140
- {
141
- // tags
142
- const tags = value;
143
- await Promise.all(tags.tag.map(async (tag) => {
144
- const target = tag.target;
145
- if (target?.tagTrackUID) {
146
- const key = this.uidKey(target.tagTrackUID);
147
- if (key !== '0') {
148
- const statistics = this.trackStatistics.get(key) ?? new Map();
149
- for (const simpleTag of tag.simpleTags) {
150
- if (simpleTag.name && typeof simpleTag.string === 'string') {
151
- statistics.set(simpleTag.name.toUpperCase(), simpleTag.string);
152
- }
153
- }
154
- this.trackStatistics.set(key, statistics);
197
+ case 0x7373: {
198
+ // One Tag, released before the next sibling.
199
+ const tag = event.value;
200
+ const target = tag.target;
201
+ for (const uid of target?.tagTrackUID ?? []) {
202
+ const key = this.uidKey(uid);
203
+ if (key !== '0') {
204
+ const statistics = this.trackStatistics.get(key) ?? new Map();
205
+ for (const simpleTag of tag.simpleTags ?? []) {
206
+ if (simpleTag.name && typeof simpleTag.string === 'string') {
207
+ statistics.set(simpleTag.name.toUpperCase(), simpleTag.string);
155
208
  }
156
209
  }
157
- const targetType = target?.targetTypeValue
158
- ? TargetType[target.targetTypeValue]
159
- : target?.targetType
160
- ? target.targetType
161
- : 'track';
162
- await Promise.all(tag.simpleTags.map(async (simpleTag) => {
163
- const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
164
- await this.addTag(`${targetType}:${simpleTag.name}`, value);
165
- }));
166
- }));
210
+ this.trackStatistics.set(key, statistics);
211
+ }
212
+ }
213
+ const targetType = target?.targetTypeValue !== undefined
214
+ ? (targetTypes[target.targetTypeValue] ?? 'track')
215
+ : (target?.targetType ?? 'track');
216
+ for (const simpleTag of tag.simpleTags ?? []) {
217
+ const value = simpleTag.string ?? simpleTag.binary;
218
+ if (simpleTag.name && value !== undefined) {
219
+ await this.addTag(`${targetType}:${simpleTag.name}`, value);
220
+ }
167
221
  }
168
222
  break;
169
- case 0x1941a469:
170
- {
171
- // attachments
172
- const attachments = value;
173
- await Promise.all(attachments.attachedFiles
174
- .filter(file => file.mimeType.startsWith('image/'))
175
- .map(file => this.addTag('picture', {
176
- data: file.data,
177
- format: file.mimeType,
178
- description: file.description,
179
- name: file.name
180
- })));
223
+ }
224
+ case 0x4660: // FileMediaType
225
+ if (event.element.parent) {
226
+ attachmentMimeTypes.set(event.element.parent, event.value);
181
227
  }
182
228
  break;
229
+ case 0x61a7: {
230
+ // One AttachedFile, released before the next sibling.
231
+ const file = event.value;
232
+ if (file.mimeType?.startsWith('image/')) {
233
+ if (isCompleteFile(file)) {
234
+ await this.addTag('picture', {
235
+ data: file.data,
236
+ format: file.mimeType,
237
+ description: file.description,
238
+ name: file.name
239
+ });
240
+ }
241
+ else {
242
+ this.metadata.addWarning('Ignoring incomplete Matroska image attachment');
243
+ }
244
+ }
245
+ break;
246
+ }
183
247
  }
184
248
  }
185
249
  });
@@ -1,73 +1,38 @@
1
- import type { IEbmlDoc } from '../ebml/types.js';
2
- import type { IAudioTrack, IVideoTrack, TrackType } from '../type.js';
1
+ import type { DecodedElement } from '../ebml/types.js';
2
+ import type { TrackType } from '../type.js';
3
+ import type { matroskaDtd } from './MatroskaDtd.js';
3
4
  export type { TrackTypeKey } from '../type.js';
4
5
  export { TrackType, TrackTypeValueToKeyMap } from '../type.js';
5
- export interface ISeek {
6
- id: Uint8Array;
7
- position: number;
8
- }
9
- export interface ISeekHead {
10
- seek: ISeek[];
11
- }
12
- export interface ISegmentInformation {
13
- uid?: Uint8Array;
14
- timecodeScale?: number;
15
- duration?: number;
16
- dateUTC?: number;
17
- title?: string;
18
- muxingApp?: string;
19
- writingApp?: string;
20
- }
21
- export interface ITrackEntry {
22
- uid?: Uint8Array;
6
+ export type IMatroskaDoc = DecodedElement<typeof matroskaDtd>;
7
+ export type IMatroskaSegment = NonNullable<IMatroskaDoc['segment']>;
8
+ export type ISeekHead = NonNullable<IMatroskaSegment['seekHead']>[number];
9
+ export type ISeek = NonNullable<ISeekHead['seek']>[number];
10
+ export type ISegmentInformation = NonNullable<IMatroskaSegment['info']>;
11
+ export type ITrackElement = NonNullable<IMatroskaSegment['tracks']>;
12
+ export type IDecodedTrackEntry = NonNullable<ITrackElement['entries']>[number];
13
+ /** A decoded entry is promoted only after validating these mandatory fields. */
14
+ export type ITrackEntry = IDecodedTrackEntry & {
23
15
  trackNumber: number;
24
- trackType?: TrackType;
25
- audio?: ITrackAudio;
26
- video?: ITrackVideo;
27
- flagEnabled?: boolean;
28
- flagDefault?: boolean;
29
- flagForced?: boolean;
30
- flagLacing?: boolean;
31
- defaultDuration?: number;
32
- trackTimecodeScale?: number;
33
- name?: string;
34
- language?: string;
35
- languageIETF?: string;
36
16
  codecID: string;
37
- codecPrivate?: Uint8Array;
38
- codecName?: string;
39
- codecSettings?: string;
40
- codecInfoUrl?: string;
41
- codecDownloadUrl?: string;
42
- codecDecodeAll?: string;
43
- trackOverlay?: string;
44
- }
45
- export type ITrackVideo = IVideoTrack;
46
- export type ITrackAudio = IAudioTrack;
47
- export interface ICuePoint {
48
- cueTime?: number;
49
- cueTrackPositions: ICueTrackPosition[];
50
- }
51
- export interface ICueTrackPosition {
52
- cueTrack?: number;
53
- cueClusterPosition?: number;
54
- cueBlockNumber?: number;
55
- cueCodecState?: number;
56
- cueReference?: ICueReference;
57
- }
58
- export interface ICueReference {
59
- cueRefTime?: number;
60
- cueRefCluster?: number;
61
- cueRefNumber?: number;
62
- cueRefCodecState?: number;
63
- }
64
- export interface ISimpleTag {
65
- name?: string;
66
- string?: string;
67
- binary?: Uint8Array;
68
- language?: string;
69
- default?: boolean;
70
- }
17
+ trackType: TrackType;
18
+ };
19
+ export type ITrackVideo = NonNullable<IDecodedTrackEntry['video']>;
20
+ export type ITrackAudio = NonNullable<IDecodedTrackEntry['audio']>;
21
+ export type ICuePoint = NonNullable<NonNullable<IMatroskaSegment['cues']>['cuePoint']>[number];
22
+ export type ICueTrackPosition = NonNullable<ICuePoint['positions']>[number];
23
+ export type ICueReference = NonNullable<ICueTrackPosition['reference']>[number];
24
+ export type ITags = NonNullable<IMatroskaSegment['tags']>[number];
25
+ export type ITag = NonNullable<ITags['tag']>[number];
26
+ export type ITarget = NonNullable<ITag['target']>;
27
+ export type ISimpleTag = NonNullable<ITag['simpleTags']>[number];
28
+ export type IAttachments = NonNullable<IMatroskaSegment['attachments']>;
29
+ export type IDecodedAttachedFile = NonNullable<IAttachments['attachedFiles']>[number];
30
+ export type IAttachedFile = IDecodedAttachedFile & {
31
+ name: string;
32
+ mimeType: string;
33
+ data: Uint8Array;
34
+ uid: Uint8Array;
35
+ };
71
36
  export declare const TargetType: {
72
37
  10: string;
73
38
  20: string;
@@ -77,43 +42,3 @@ export declare const TargetType: {
77
42
  60: string;
78
43
  70: string;
79
44
  };
80
- export interface ITarget {
81
- tagTrackUID?: Uint8Array;
82
- trackUID?: Uint8Array;
83
- chapterUID?: Uint8Array;
84
- attachmentUID?: Uint8Array;
85
- targetTypeValue?: keyof typeof TargetType;
86
- targetType?: string;
87
- }
88
- export interface ITag {
89
- target: ITarget;
90
- simpleTags: ISimpleTag[];
91
- }
92
- export interface ITags {
93
- tag: ITag[];
94
- }
95
- export interface ITrackElement {
96
- entries?: ITrackEntry[];
97
- }
98
- export interface IAttachmedFile {
99
- description?: string;
100
- name: string;
101
- mimeType: string;
102
- data: Uint8Array;
103
- uid: string;
104
- }
105
- export interface IAttachments {
106
- attachedFiles: IAttachmedFile[];
107
- }
108
- export interface IMatroskaSegment {
109
- metaSeekInfo?: ISeekHead;
110
- seekHeads?: ISeek[];
111
- info?: ISegmentInformation;
112
- tracks?: ITrackElement;
113
- tags?: ITags;
114
- cues?: ICuePoint[];
115
- attachments?: IAttachments;
116
- }
117
- export interface IMatroskaDoc extends IEbmlDoc {
118
- segment: IMatroskaSegment;
119
- }