@openclaw/whatsapp 2026.9.34 → 2026.9.36

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 (105) hide show
  1. package/dist/.source-module-urls/c3803b0fa58e9b1fe1f7418a46193ebc53b9c744f6359f04d40d05af48c1dce2.json +1 -0
  2. package/node_modules/baileys/node_modules/music-metadata/README.md +35 -32
  3. package/node_modules/baileys/node_modules/music-metadata/lib/ParserFactory.d.ts +1 -1
  4. package/node_modules/baileys/node_modules/music-metadata/lib/ParserFactory.js +17 -13
  5. package/node_modules/baileys/node_modules/music-metadata/lib/aiff/AiffParser.js +19 -12
  6. package/node_modules/baileys/node_modules/music-metadata/lib/aiff/AiffToken.d.ts +1 -1
  7. package/node_modules/baileys/node_modules/music-metadata/lib/aiff/AiffToken.js +2 -1
  8. package/node_modules/baileys/node_modules/music-metadata/lib/apev2/APEv2Parser.d.ts +4 -3
  9. package/node_modules/baileys/node_modules/music-metadata/lib/apev2/APEv2Parser.js +59 -16
  10. package/node_modules/baileys/node_modules/music-metadata/lib/apev2/APEv2TagMapper.js +1 -0
  11. package/node_modules/baileys/node_modules/music-metadata/lib/apev2/APEv2Token.d.ts +1 -1
  12. package/node_modules/baileys/node_modules/music-metadata/lib/apev2/APEv2Token.js +1 -1
  13. package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfGuid.js +56 -50
  14. package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfObject.d.ts +1 -1
  15. package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfObject.js +3 -2
  16. package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfParser.js +15 -7
  17. package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfTagMapper.d.ts +3 -2
  18. package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfTagMapper.js +21 -4
  19. package/node_modules/baileys/node_modules/music-metadata/lib/common/CaseInsensitiveTagMap.d.ts +1 -1
  20. package/node_modules/baileys/node_modules/music-metadata/lib/common/CombinedTagMapper.d.ts +2 -2
  21. package/node_modules/baileys/node_modules/music-metadata/lib/common/CombinedTagMapper.js +6 -6
  22. package/node_modules/baileys/node_modules/music-metadata/lib/common/FourCC.js +3 -2
  23. package/node_modules/baileys/node_modules/music-metadata/lib/common/GenericTagMapper.d.ts +1 -1
  24. package/node_modules/baileys/node_modules/music-metadata/lib/common/GenericTagTypes.js +1 -1
  25. package/node_modules/baileys/node_modules/music-metadata/lib/common/MetadataCollector.d.ts +3 -1
  26. package/node_modules/baileys/node_modules/music-metadata/lib/common/MetadataCollector.js +28 -9
  27. package/node_modules/baileys/node_modules/music-metadata/lib/common/Util.js +25 -17
  28. package/node_modules/baileys/node_modules/music-metadata/lib/core.d.ts +4 -4
  29. package/node_modules/baileys/node_modules/music-metadata/lib/core.js +18 -11
  30. package/node_modules/baileys/node_modules/music-metadata/lib/dsdiff/DsdiffParser.js +29 -16
  31. package/node_modules/baileys/node_modules/music-metadata/lib/dsf/DsfChunk.d.ts +1 -1
  32. package/node_modules/baileys/node_modules/music-metadata/lib/dsf/DsfParser.js +3 -5
  33. package/node_modules/baileys/node_modules/music-metadata/lib/ebml/EbmlIterator.d.ts +1 -1
  34. package/node_modules/baileys/node_modules/music-metadata/lib/ebml/EbmlIterator.js +35 -6
  35. package/node_modules/baileys/node_modules/music-metadata/lib/ebml/types.d.ts +1 -1
  36. package/node_modules/baileys/node_modules/music-metadata/lib/ebml/types.js +1 -1
  37. package/node_modules/baileys/node_modules/music-metadata/lib/flac/FlacParser.d.ts +1 -1
  38. package/node_modules/baileys/node_modules/music-metadata/lib/flac/FlacParser.js +6 -5
  39. package/node_modules/baileys/node_modules/music-metadata/lib/flac/FlacToken.d.ts +1 -1
  40. package/node_modules/baileys/node_modules/music-metadata/lib/flac/FlacToken.js +1 -1
  41. package/node_modules/baileys/node_modules/music-metadata/lib/id3v1/ID3v1Parser.d.ts +1 -1
  42. package/node_modules/baileys/node_modules/music-metadata/lib/id3v1/ID3v1Parser.js +212 -42
  43. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/AbstractID3Parser.js +5 -5
  44. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/FrameHeader.d.ts +1 -1
  45. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/FrameHeader.js +8 -5
  46. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/FrameParser.d.ts +2 -1
  47. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/FrameParser.js +53 -22
  48. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v22TagMapper.d.ts +1 -1
  49. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v22TagMapper.js +1 -1
  50. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v24TagMapper.js +3 -3
  51. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v2ChapterToken.js +2 -2
  52. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v2Parser.d.ts +1 -1
  53. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v2Parser.js +32 -16
  54. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v2Token.d.ts +2 -2
  55. package/node_modules/baileys/node_modules/music-metadata/lib/id3v2/ID3v2Token.js +2 -3
  56. package/node_modules/baileys/node_modules/music-metadata/lib/index.js +6 -3
  57. package/node_modules/baileys/node_modules/music-metadata/lib/lrc/LyricsParser.js +3 -5
  58. package/node_modules/baileys/node_modules/music-metadata/lib/matroska/MatroskaDtd.js +7 -3
  59. package/node_modules/baileys/node_modules/music-metadata/lib/matroska/MatroskaParser.js +22 -11
  60. package/node_modules/baileys/node_modules/music-metadata/lib/matroska/types.d.ts +2 -2
  61. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/Atom.d.ts +4 -3
  62. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/Atom.js +44 -11
  63. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/AtomToken.d.ts +1 -1
  64. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/AtomToken.js +30 -8
  65. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/MP4Parser.d.ts +2 -0
  66. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/MP4Parser.js +79 -54
  67. package/node_modules/baileys/node_modules/music-metadata/lib/mp4/MP4TagMapper.d.ts +2 -2
  68. package/node_modules/baileys/node_modules/music-metadata/lib/mpeg/MpegLoader.js +1 -1
  69. package/node_modules/baileys/node_modules/music-metadata/lib/mpeg/MpegParser.js +41 -19
  70. package/node_modules/baileys/node_modules/music-metadata/lib/mpeg/ReplayGainDataFormat.d.ts +2 -2
  71. package/node_modules/baileys/node_modules/music-metadata/lib/mpeg/ReplayGainDataFormat.js +1 -1
  72. package/node_modules/baileys/node_modules/music-metadata/lib/mpeg/XingTag.d.ts +1 -1
  73. package/node_modules/baileys/node_modules/music-metadata/lib/musepack/MusepackParser.js +2 -2
  74. package/node_modules/baileys/node_modules/music-metadata/lib/musepack/sv7/BitReader.js +1 -1
  75. package/node_modules/baileys/node_modules/music-metadata/lib/musepack/sv7/MpcSv7Parser.js +4 -3
  76. package/node_modules/baileys/node_modules/music-metadata/lib/musepack/sv7/StreamVersion7.js +3 -3
  77. package/node_modules/baileys/node_modules/music-metadata/lib/musepack/sv8/MpcSv8Parser.js +7 -5
  78. package/node_modules/baileys/node_modules/music-metadata/lib/musepack/sv8/StreamVersion8.js +2 -2
  79. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/OggParser.js +16 -12
  80. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/OggToken.js +1 -1
  81. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/flac/FlacStream.d.ts +2 -2
  82. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/flac/FlacStream.js +2 -2
  83. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/opus/OpusStream.d.ts +2 -2
  84. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/opus/OpusStream.js +8 -4
  85. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/speex/SpeexStream.d.ts +2 -2
  86. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/speex/SpeexStream.js +2 -1
  87. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/theora/TheoraStream.d.ts +2 -2
  88. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/vorbis/Vorbis.d.ts +1 -1
  89. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/vorbis/VorbisDecoder.js +1 -1
  90. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/vorbis/VorbisStream.d.ts +3 -3
  91. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/vorbis/VorbisStream.js +14 -9
  92. package/node_modules/baileys/node_modules/music-metadata/lib/ogg/vorbis/VorbisTagMapper.js +4 -0
  93. package/node_modules/baileys/node_modules/music-metadata/lib/riff/RiffChunk.d.ts +3 -1
  94. package/node_modules/baileys/node_modules/music-metadata/lib/riff/RiffChunk.js +3 -2
  95. package/node_modules/baileys/node_modules/music-metadata/lib/riff/RiffInfoTagMap.d.ts +1 -1
  96. package/node_modules/baileys/node_modules/music-metadata/lib/type.d.ts +3 -3
  97. package/node_modules/baileys/node_modules/music-metadata/lib/type.js +1 -1
  98. package/node_modules/baileys/node_modules/music-metadata/lib/wav/WaveChunk.d.ts +1 -1
  99. package/node_modules/baileys/node_modules/music-metadata/lib/wav/WaveChunk.js +3 -2
  100. package/node_modules/baileys/node_modules/music-metadata/lib/wav/WaveParser.d.ts +6 -2
  101. package/node_modules/baileys/node_modules/music-metadata/lib/wav/WaveParser.js +75 -22
  102. package/node_modules/baileys/node_modules/music-metadata/lib/wavpack/WavPackParser.js +22 -13
  103. package/node_modules/baileys/node_modules/music-metadata/lib/wavpack/WavPackToken.js +3 -2
  104. package/node_modules/baileys/node_modules/music-metadata/package.json +3 -2
  105. package/package.json +4 -4
@@ -1,7 +1,7 @@
1
1
  import * as Token from 'token-types';
2
+ import { getFrameHeaderLength, readFrameHeader } from './FrameHeader.js';
2
3
  import { FrameParser, Id3v2ContentError } from './FrameParser.js';
3
4
  import { ExtendedHeader, ID3v2Header } from './ID3v2Token.js';
4
- import { getFrameHeaderLength, readFrameHeader } from './FrameHeader.js';
5
5
  export class ID3v2Parser {
6
6
  constructor() {
7
7
  this.tokenizer = undefined;
@@ -17,7 +17,7 @@ export class ID3v2Parser {
17
17
  if (readI !== writeI) {
18
18
  buffer[writeI] = buffer[readI];
19
19
  }
20
- readI += (buffer[readI] === 0xFF && buffer[readI + 1] === 0) ? 2 : 1;
20
+ readI += buffer[readI] === 0xff && buffer[readI + 1] === 0 ? 2 : 1;
21
21
  writeI++;
22
22
  }
23
23
  if (readI < buffer.length) {
@@ -58,10 +58,17 @@ export class ID3v2Parser {
58
58
  this.options = options;
59
59
  const id3Header = await this.tokenizer.readToken(ID3v2Header);
60
60
  if (id3Header.fileIdentifier !== 'ID3') {
61
- throw new Id3v2ContentError('expected ID3-header file-identifier \'ID3\' was not found');
61
+ throw new Id3v2ContentError("expected ID3-header file-identifier 'ID3' was not found");
62
+ }
63
+ const fileSize = this.tokenizer.fileInfo.size;
64
+ if (fileSize !== undefined) {
65
+ const remainingBytes = fileSize - this.tokenizer.position;
66
+ if (id3Header.size > remainingBytes) {
67
+ throw new Id3v2ContentError(`ID3v2 tag size ${id3Header.size} exceeds remaining file size ${remainingBytes}`);
68
+ }
62
69
  }
63
70
  this.id3Header = id3Header;
64
- this.headerType = (`ID3v2.${id3Header.version.major}`);
71
+ this.headerType = `ID3v2.${id3Header.version.major}`;
65
72
  await (id3Header.flags.isExtendedHeader ? this.parseExtendedHeader() : this.parseId3Data(id3Header.size));
66
73
  // Post process
67
74
  const chapters = ID3v2Parser.mapId3v2Chapters(this.metadata.native[this.headerType]);
@@ -70,7 +77,9 @@ export class ID3v2Parser {
70
77
  async parseExtendedHeader() {
71
78
  const extendedHeader = await this.tokenizer.readToken(ExtendedHeader);
72
79
  const dataRemaining = extendedHeader.size - ExtendedHeader.len;
73
- return dataRemaining > 0 ? this.parseExtendedHeaderData(dataRemaining, extendedHeader.size) : this.parseId3Data(this.id3Header.size - extendedHeader.size);
80
+ return dataRemaining > 0
81
+ ? this.parseExtendedHeaderData(dataRemaining, extendedHeader.size)
82
+ : this.parseId3Data(this.id3Header.size - extendedHeader.size);
74
83
  }
75
84
  async parseExtendedHeaderData(dataRemaining, extendedHeaderSize) {
76
85
  await this.tokenizer.ignore(dataRemaining);
@@ -86,7 +95,9 @@ export class ID3v2Parser {
86
95
  }
87
96
  break;
88
97
  default:
89
- await (Array.isArray(tag.value) ? Promise.all(tag.value.map(value => this.addTag(tag.id, value))) : this.addTag(tag.id, tag.value));
98
+ await (Array.isArray(tag.value)
99
+ ? Promise.all(tag.value.map(value => this.addTag(tag.id, value)))
100
+ : this.addTag(tag.id, tag.value));
90
101
  }
91
102
  }
92
103
  }
@@ -100,8 +111,9 @@ export class ID3v2Parser {
100
111
  let offset = 0;
101
112
  const tags = [];
102
113
  while (true) {
103
- if (offset === data.length)
114
+ if (offset === data.length) {
104
115
  break;
116
+ }
105
117
  const frameHeaderLength = getFrameHeaderLength(this.id3Header.version.major);
106
118
  if (offset + frameHeaderLength > data.length) {
107
119
  this.metadata.addWarning('Illegal ID3v2 tag length');
@@ -126,28 +138,32 @@ export class ID3v2Parser {
126
138
  * as produced by `FrameParser.readData`.
127
139
  */
128
140
  static mapId3v2Chapters(id3Tags) {
129
- if (!id3Tags)
141
+ if (!id3Tags) {
130
142
  return;
131
- const chapFrames = id3Tags.filter(t => t.id === 'CHAP');
132
- if (!chapFrames?.length)
143
+ }
144
+ const chapFrames = id3Tags.filter(t => t.id === 'CHAP').map(t => t.value);
145
+ if (!chapFrames.length) {
133
146
  return;
134
- const tocFrames = id3Tags.filter(t => t.id === 'CTOC');
135
- const topLevelToc = tocFrames?.find(t => t.value.flags?.topLevel);
147
+ }
148
+ const tocFrames = id3Tags.filter(t => t.id === 'CTOC').map(t => t.value);
149
+ const topLevelToc = tocFrames.find(t => t.flags.topLevel);
136
150
  const chapterById = new Map();
137
151
  for (const chap of chapFrames) {
138
- chapterById.set(chap.value.label, chap.value);
152
+ chapterById.set(chap.label, chap);
139
153
  }
140
- const orderedIds = topLevelToc?.value.childElementIds;
154
+ const orderedIds = topLevelToc?.childElementIds;
141
155
  const chapters = [];
142
156
  const source = orderedIds ?? [...chapterById.keys()];
143
157
  for (const id of source) {
144
158
  const chap = chapterById.get(id);
145
- if (!chap)
159
+ if (!chap) {
146
160
  continue;
161
+ }
147
162
  const frames = chap.frames;
148
163
  const title = frames.get('TIT2');
149
- if (!title)
164
+ if (!title) {
150
165
  continue; // title is required
166
+ }
151
167
  chapters.push({
152
168
  id,
153
169
  title,
@@ -46,13 +46,13 @@ export declare const LyricsContentType: {
46
46
  chord: number;
47
47
  trivia_pop: number;
48
48
  };
49
- export type LyricsContentType = typeof LyricsContentType[keyof typeof LyricsContentType];
49
+ export type LyricsContentType = (typeof LyricsContentType)[keyof typeof LyricsContentType];
50
50
  export declare const TimestampFormat: {
51
51
  notSynchronized: number;
52
52
  mpegFrameNumber: number;
53
53
  milliseconds: number;
54
54
  };
55
- export type TimestampFormat = typeof TimestampFormat[keyof typeof TimestampFormat];
55
+ export type TimestampFormat = (typeof TimestampFormat)[keyof typeof TimestampFormat];
56
56
  /**
57
57
  * 28 bits (representing up to 256MB) integer, the msb is 0 to avoid 'false syncsignals'.
58
58
  * 4 * %0xxxxxxx
@@ -37,7 +37,7 @@ export const LyricsContentType = {
37
37
  movement_part: 3,
38
38
  events: 4,
39
39
  chord: 5,
40
- trivia_pop: 6,
40
+ trivia_pop: 6
41
41
  };
42
42
  export const TimestampFormat = {
43
43
  notSynchronized: 0,
@@ -50,8 +50,7 @@ export const TimestampFormat = {
50
50
  */
51
51
  export const UINT32SYNCSAFE = {
52
52
  get: (buf, off) => {
53
- return buf[off + 3] & 0x7f | ((buf[off + 2]) << 7) |
54
- ((buf[off + 1]) << 14) | ((buf[off]) << 21);
53
+ return (buf[off + 3] & 0x7f) | (buf[off + 2] << 7) | (buf[off + 1] << 14) | (buf[off] << 21);
55
54
  },
56
55
  len: 4
57
56
  };
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Node.js specific entry point.
3
3
  */
4
- import { fromFile, fromStream } from 'strtok3';
5
4
  import initDebug from 'debug';
5
+ import { fromFile, fromStream } from 'strtok3';
6
6
  import { CouldNotDetermineFileTypeError, parseFromTokenizer, UnsupportedFileTypeError } from './core.js';
7
7
  import { ParserFactory } from './ParserFactory.js';
8
8
  export * from './core.js';
@@ -15,7 +15,9 @@ const debug = initDebug('music-metadata:parser');
15
15
  * @returns Metadata
16
16
  */
17
17
  export async function parseStream(stream, fileInfo, options = {}) {
18
- const tokenizer = await fromStream(stream, { fileInfo: typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo });
18
+ const tokenizer = await fromStream(stream, {
19
+ fileInfo: typeof fileInfo === 'string' ? { mimeType: fileInfo } : fileInfo
20
+ });
19
21
  try {
20
22
  return await parseFromTokenizer(tokenizer, options);
21
23
  }
@@ -35,8 +37,9 @@ export async function parseFile(filePath, options = {}) {
35
37
  const parserFactory = new ParserFactory();
36
38
  try {
37
39
  const parserLoader = parserFactory.findLoaderForExtension(filePath);
38
- if (!parserLoader)
40
+ if (!parserLoader) {
39
41
  debug('Parser could not be determined by file extension');
42
+ }
40
43
  try {
41
44
  return await parserFactory.parse(fileTokenizer, parserLoader, options);
42
45
  }
@@ -12,7 +12,7 @@ export function toUnsyncedLyrics(lyrics) {
12
12
  contentType: LyricsContentType.lyrics,
13
13
  timeStampFormat: TimestampFormat.notSynchronized,
14
14
  text: lyrics.trim(),
15
- syncText: [],
15
+ syncText: []
16
16
  };
17
17
  }
18
18
  /**
@@ -28,9 +28,7 @@ export function parseLrc(lrcString) {
28
28
  if (match) {
29
29
  const minutes = Number.parseInt(match[1], 10);
30
30
  const seconds = Number.parseInt(match[2], 10);
31
- const ms = match[3].length === 3
32
- ? Number.parseInt(match[3], 10)
33
- : Number.parseInt(match[3], 10) * 10;
31
+ const ms = match[3].length === 3 ? Number.parseInt(match[3], 10) : Number.parseInt(match[3], 10) * 10;
34
32
  const timestamp = (minutes * 60 + seconds) * 1000 + ms;
35
33
  const text = line.replace(TIMESTAMP_REGEX, '').trim();
36
34
  syncText.push({ timestamp, text });
@@ -40,6 +38,6 @@ export function parseLrc(lrcString) {
40
38
  contentType: LyricsContentType.lyrics,
41
39
  timeStampFormat: TimestampFormat.milliseconds,
42
40
  text: syncText.map(line => line.text).join('\n'),
43
- syncText,
41
+ syncText
44
42
  };
45
43
  }
@@ -9,6 +9,7 @@ export const matroskaDtd = {
9
9
  name: 'dtd',
10
10
  container: {
11
11
  0x1a45dfa3: {
12
+ // 5.1
12
13
  name: 'ebml',
13
14
  container: {
14
15
  0x4286: { name: 'ebmlVersion', value: DataType.uint }, // 5.1.1
@@ -178,7 +179,8 @@ export const matroskaDtd = {
178
179
  0x5378: { name: 'blockNumber', value: DataType.uint },
179
180
  0xea: { name: 'codecState', value: DataType.uint },
180
181
  0xdb: {
181
- name: 'reference', container: {
182
+ name: 'reference',
183
+ container: {
182
184
  0x96: { name: 'time', value: DataType.uint },
183
185
  0x97: { name: 'cluster', value: DataType.uint },
184
186
  0x535f: { name: 'number', value: DataType.uint },
@@ -225,10 +227,12 @@ export const matroskaDtd = {
225
227
  0x98: { name: 'hidden', value: DataType.bool },
226
228
  0x4598: { name: 'enabled', value: DataType.uid },
227
229
  0x8f: {
228
- name: 'track', container: {
230
+ name: 'track',
231
+ container: {
229
232
  0x89: { name: 'trackNumber', value: DataType.uid },
230
233
  0x80: {
231
- name: 'display', container: {
234
+ name: 'display',
235
+ container: {
232
236
  0x85: { name: 'string', value: DataType.string },
233
237
  0x437c: { name: 'language ', value: DataType.string },
234
238
  0x437e: { name: 'country ', value: DataType.string }
@@ -1,8 +1,8 @@
1
1
  import initDebug from 'debug';
2
2
  import { BasicParser } from '../common/BasicParser.js';
3
+ import { EbmlIterator, ParseAction } from '../ebml/EbmlIterator.js';
3
4
  import { matroskaDtd } from './MatroskaDtd.js';
4
5
  import { TargetType, TrackType } from './types.js';
5
- import { EbmlIterator, ParseAction } from '../ebml/EbmlIterator.js';
6
6
  const debug = initDebug('music-metadata:parser:matroska');
7
7
  /**
8
8
  * Extensible Binary Meta Language (EBML) parser
@@ -26,7 +26,7 @@ export class MatroskaParser extends BasicParser {
26
26
  const matroskaIterator = new EbmlIterator(this.tokenizer);
27
27
  debug('Initializing DTD end MatroskaIterator');
28
28
  await matroskaIterator.iterate(matroskaDtd, containerSize, {
29
- startNext: (element) => {
29
+ startNext: element => {
30
30
  switch (element.id) {
31
31
  // case 0x1f43b675: // cluster
32
32
  case 0x1c53bb6b: // Cueing Data
@@ -59,18 +59,20 @@ export class MatroskaParser extends BasicParser {
59
59
  this.seekHeadOffset = offset;
60
60
  break;
61
61
  case 0x1549a966:
62
- { // Info (Segment Information)
62
+ {
63
+ // Info (Segment Information)
63
64
  const info = value;
64
65
  const timecodeScale = info.timecodeScale ? info.timecodeScale : 1000000;
65
66
  if (typeof info.duration === 'number') {
66
- const duration = info.duration * timecodeScale / 1000000000;
67
+ const duration = (info.duration * timecodeScale) / 1000000000;
67
68
  await this.addTag('segment:title', info.title);
68
69
  this.metadata.setFormat('duration', Number(duration));
69
70
  }
70
71
  }
71
72
  break;
72
73
  case 0x1654ae6b:
73
- { // tracks
74
+ {
75
+ // tracks
74
76
  const audioTracks = value;
75
77
  if (audioTracks?.entries) {
76
78
  audioTracks.entries.forEach(entry => {
@@ -91,12 +93,15 @@ export class MatroskaParser extends BasicParser {
91
93
  const audioTrack = audioTracks.entries
92
94
  .filter(entry => entry.trackType === TrackType.audio)
93
95
  .reduce((acc, cur) => {
94
- if (!acc)
96
+ if (!acc) {
95
97
  return cur;
96
- if (cur.flagDefault && !acc.flagDefault)
98
+ }
99
+ if (cur.flagDefault && !acc.flagDefault) {
97
100
  return cur;
98
- if (cur.trackNumber < acc.trackNumber)
101
+ }
102
+ if (cur.trackNumber < acc.trackNumber) {
99
103
  return cur;
104
+ }
100
105
  return acc;
101
106
  }, null);
102
107
  if (audioTrack) {
@@ -108,11 +113,16 @@ export class MatroskaParser extends BasicParser {
108
113
  }
109
114
  break;
110
115
  case 0x1254c367:
111
- { // tags
116
+ {
117
+ // tags
112
118
  const tags = value;
113
119
  await Promise.all(tags.tag.map(async (tag) => {
114
120
  const target = tag.target;
115
- const targetType = target?.targetTypeValue ? TargetType[target.targetTypeValue] : (target?.targetType ? target.targetType : 'track');
121
+ const targetType = target?.targetTypeValue
122
+ ? TargetType[target.targetTypeValue]
123
+ : target?.targetType
124
+ ? target.targetType
125
+ : 'track';
116
126
  await Promise.all(tag.simpleTags.map(async (simpleTag) => {
117
127
  const value = simpleTag.string ? simpleTag.string : simpleTag.binary;
118
128
  await this.addTag(`${targetType}:${simpleTag.name}`, value);
@@ -121,7 +131,8 @@ export class MatroskaParser extends BasicParser {
121
131
  }
122
132
  break;
123
133
  case 0x1941a469:
124
- { // attachments
134
+ {
135
+ // attachments
125
136
  const attachments = value;
126
137
  await Promise.all(attachments.attachedFiles
127
138
  .filter(file => file.mimeType.startsWith('image/'))
@@ -75,7 +75,7 @@ export interface ICueReference {
75
75
  }
76
76
  export interface ISimpleTag {
77
77
  name?: string;
78
- 'string'?: string;
78
+ string?: string;
79
79
  binary?: Uint8Array;
80
80
  language?: string;
81
81
  default?: boolean;
@@ -98,7 +98,7 @@ export declare const TrackType: {
98
98
  button: number;
99
99
  control: number;
100
100
  };
101
- export type TrackType = typeof TrackType[keyof typeof TrackType];
101
+ export type TrackType = (typeof TrackType)[keyof typeof TrackType];
102
102
  export type TrackTypeKey = keyof typeof TrackType;
103
103
  export declare const TrackTypeValueToKeyMap: Record<TrackType, TrackTypeKey>;
104
104
  export interface ITarget {
@@ -1,16 +1,17 @@
1
- import * as AtomToken from './AtomToken.js';
2
1
  import type { ITokenizer } from 'strtok3';
2
+ import * as AtomToken from './AtomToken.js';
3
3
  export type AtomDataHandler = (atom: Atom, remaining: number) => Promise<void>;
4
4
  export declare class Atom {
5
+ private readonly payloadLength;
5
6
  static readAtom(tokenizer: ITokenizer, dataHandler: AtomDataHandler, parent: Atom | null, remaining: number): Promise<Atom>;
6
7
  readonly children: Atom[];
7
8
  readonly atomPath: string;
8
9
  readonly header: AtomToken.IAtomHeader;
9
10
  extended: boolean;
10
11
  readonly parent: Atom | null;
11
- constructor(header: AtomToken.IAtomHeader, extended: boolean, parent: Atom | null);
12
+ constructor(header: AtomToken.IAtomHeader, extended: boolean, parent: Atom | null, payloadLength: number);
12
13
  getHeaderLength(): number;
13
- getPayloadLength(remaining: number): number;
14
+ getPayloadLength(): number;
14
15
  readAtoms(tokenizer: ITokenizer, dataHandler: AtomDataHandler, size: number): Promise<void>;
15
16
  private readData;
16
17
  }
@@ -1,24 +1,43 @@
1
1
  import initDebug from 'debug';
2
2
  import * as AtomToken from './AtomToken.js';
3
- import { Header } from './AtomToken.js';
3
+ import { Header, Mp4ContentError } from './AtomToken.js';
4
4
  const debug = initDebug('music-metadata:parser:MP4:Atom');
5
5
  export class Atom {
6
6
  static async readAtom(tokenizer, dataHandler, parent, remaining) {
7
- // Parse atom header
7
+ // Validate boundaries before reading either header or dispatching a payload parser.
8
8
  const offset = tokenizer.position;
9
+ remaining = Math.min(remaining, (tokenizer.fileInfo.size ?? Number.POSITIVE_INFINITY) - offset);
10
+ if (remaining < AtomToken.Header.len) {
11
+ throw new Mp4ContentError('Truncated atom header');
12
+ }
9
13
  debug(`Reading next token on offset=${offset}...`); // buf.toString('ascii')
10
14
  const header = await tokenizer.readToken(AtomToken.Header);
11
15
  const extended = header.length === 1n;
12
16
  if (extended) {
17
+ if (remaining < 16) {
18
+ throw new Mp4ContentError('Truncated extended atom header');
19
+ }
13
20
  header.length = await tokenizer.readToken(AtomToken.ExtendedSize);
14
21
  }
15
- const atomBean = new Atom(header, extended, parent);
16
- const payloadLength = atomBean.getPayloadLength(remaining);
22
+ const headerLength = extended ? 16 : 8;
23
+ if ((extended || header.length !== 0n) && header.length < BigInt(headerLength)) {
24
+ throw new Mp4ContentError(`Invalid atom size: ${header.length}`);
25
+ }
26
+ if (header.length > BigInt(Number.MAX_SAFE_INTEGER)) {
27
+ throw new Mp4ContentError(`Atom size exceeds Number.MAX_SAFE_INTEGER: ${header.length}`);
28
+ }
29
+ const length = header.length === 0n ? remaining : Number(header.length);
30
+ if (length > remaining) {
31
+ throw new Mp4ContentError(`Atom size exceeds remaining bytes: ${header.length} > ${remaining}`);
32
+ }
33
+ const payloadLength = length - headerLength;
34
+ const atomBean = new Atom(header, extended, parent, payloadLength);
17
35
  debug(`parse atom name=${atomBean.atomPath}, extended=${atomBean.extended}, offset=${offset}, len=${atomBean.header.length}`); // buf.toString('ascii')
18
36
  await atomBean.readData(tokenizer, dataHandler, payloadLength);
19
37
  return atomBean;
20
38
  }
21
- constructor(header, extended, parent) {
39
+ constructor(header, extended, parent, payloadLength) {
40
+ this.payloadLength = payloadLength;
22
41
  this.header = header;
23
42
  this.extended = extended;
24
43
  this.parent = parent;
@@ -28,14 +47,24 @@ export class Atom {
28
47
  getHeaderLength() {
29
48
  return this.extended ? 16 : 8;
30
49
  }
31
- getPayloadLength(remaining) {
32
- return (this.header.length === 0n ? remaining : Number(this.header.length)) - this.getHeaderLength();
50
+ getPayloadLength() {
51
+ return this.payloadLength;
33
52
  }
34
53
  async readAtoms(tokenizer, dataHandler, size) {
35
54
  while (size > 0) {
55
+ if (size === Number.POSITIVE_INFINITY) {
56
+ // An open-ended container ends only at clean EOF, before the next header.
57
+ const bytesRead = await tokenizer.peekBuffer(new Uint8Array(Header.len), { mayBeLess: true });
58
+ if (bytesRead === 0) {
59
+ return;
60
+ }
61
+ if (bytesRead < Header.len) {
62
+ throw new Mp4ContentError('Truncated atom header');
63
+ }
64
+ }
36
65
  const atomBean = await Atom.readAtom(tokenizer, dataHandler, this, size);
37
66
  this.children.push(atomBean);
38
- size -= atomBean.header.length === 0n ? size : Number(atomBean.header.length);
67
+ size = atomBean.header.length === 0n ? 0 : size - Number(atomBean.header.length);
39
68
  }
40
69
  }
41
70
  async readData(tokenizer, dataHandler, remaining) {
@@ -50,13 +79,17 @@ export class Atom {
50
79
  case 'ilst':
51
80
  case 'tref':
52
81
  case 'moof':
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
82
+ return this.readAtoms(tokenizer, dataHandler, remaining);
83
+ case 'meta': {
84
+ // Metadata Atom, ref: https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW8
85
+ if (remaining < 8) {
86
+ throw new Mp4ContentError('Truncated meta atom');
87
+ }
55
88
  // meta has 4 bytes of padding, ignore
56
89
  const peekHeader = await tokenizer.peekToken(Header);
57
90
  const paddingLength = peekHeader.name === 'hdlr' ? 0 : 4;
58
91
  await tokenizer.ignore(paddingLength);
59
- return this.readAtoms(tokenizer, dataHandler, this.getPayloadLength(remaining) - paddingLength);
92
+ return this.readAtoms(tokenizer, dataHandler, remaining - paddingLength);
60
93
  }
61
94
  default:
62
95
  return dataHandler(this, remaining);
@@ -1,4 +1,4 @@
1
- import type { IToken, IGetToken } from 'strtok3';
1
+ import type { IGetToken, IToken } from 'strtok3';
2
2
  declare const Mp4ContentError_base: {
3
3
  new (message: string): {
4
4
  readonly fileType: string;
@@ -1,9 +1,8 @@
1
- import * as Token from 'token-types';
2
1
  import initDebug from 'debug';
2
+ import * as Token from 'token-types';
3
3
  import { FourCcToken } from '../common/FourCC.js';
4
- import { makeUnexpectedFileContentError } from '../ParseError.js';
5
4
  import * as util from '../common/Util.js';
6
- import { FieldDecodingError } from '../ParseError.js';
5
+ import { FieldDecodingError, makeUnexpectedFileContentError } from '../ParseError.js';
7
6
  const debug = initDebug('music-metadata:parser:MP4:atom');
8
7
  export class Mp4ContentError extends makeUnexpectedFileContentError('MP4') {
9
8
  }
@@ -11,8 +10,9 @@ export const Header = {
11
10
  len: 8,
12
11
  get: (buf, off) => {
13
12
  const length = Token.UINT32_BE.get(buf, off);
14
- if (length < 0)
13
+ if (length < 0) {
15
14
  throw new Mp4ContentError('Invalid atom header length');
15
+ }
16
16
  return {
17
17
  length: BigInt(length),
18
18
  name: new Token.StringType(4, 'latin1').get(buf, off + 4)
@@ -226,7 +226,15 @@ export class TrackHeaderAtom {
226
226
  this.len = len;
227
227
  }
228
228
  get(buf, off) {
229
+ const available = Math.min(this.len, buf.length - off);
230
+ if (available < 4) {
231
+ throw new Mp4ContentError('Truncated tkhd header');
232
+ }
229
233
  const version = Token.UINT8.get(buf, off);
234
+ // Validate through the volume field, the last field currently decoded.
235
+ if ((version === 0 && available < 38) || (version === 1 && available < 50)) {
236
+ throw new Mp4ContentError('Truncated tkhd header');
237
+ }
230
238
  const flags = Token.UINT24_BE.get(buf, off + 1);
231
239
  switch (version) {
232
240
  case 0:
@@ -306,11 +314,23 @@ export class StsdAtom {
306
314
  this.len = len;
307
315
  }
308
316
  get(buf, off) {
317
+ const end = Math.min(off + this.len, buf.length);
318
+ if (end - off < stsdHeader.len) {
319
+ throw new Mp4ContentError('Truncated stsd header');
320
+ }
309
321
  const header = stsdHeader.get(buf, off);
310
322
  off += stsdHeader.len;
311
323
  const table = [];
312
324
  for (let n = 0; n < header.numberOfEntries; ++n) {
325
+ if (end - off < Token.UINT32_BE.len) {
326
+ throw new Mp4ContentError('Truncated stsd sample entry');
327
+ }
313
328
  const size = Token.UINT32_BE.get(buf, off); // Sample description size
329
+ // A SampleEntry needs its size, format, reserved bytes and data reference index.
330
+ // Requiring it to fit also bounds the loop by the available payload, not entry_count.
331
+ if (size < 16 || size > end - off) {
332
+ throw new Mp4ContentError(`Invalid stsd sample entry size: ${size}`);
333
+ }
314
334
  off += Token.UINT32_BE.len;
315
335
  table.push(new SampleDescriptionTable(size - Token.UINT32_BE.len).get(buf, off));
316
336
  // A SampleEntry extends Box, so its size covers the size field already stepped over
@@ -449,10 +469,12 @@ export class ChapterText {
449
469
  }
450
470
  function readTokenTable(buf, token, off, remainingLen, numberOfEntries) {
451
471
  debug(`remainingLen=${remainingLen}, numberOfEntries=${numberOfEntries} * token-len=${token.len}`);
452
- if (remainingLen === 0)
472
+ if (remainingLen === 0) {
453
473
  return [];
454
- if (remainingLen !== numberOfEntries * token.len)
474
+ }
475
+ if (remainingLen !== numberOfEntries * token.len) {
455
476
  throw new Mp4ContentError('mismatch number-of-entries with remaining atom-length');
477
+ }
456
478
  const entries = [];
457
479
  // parse offset-table
458
480
  for (let n = 0; n < numberOfEntries; ++n) {
@@ -539,7 +561,7 @@ export class TrackRunBox {
539
561
  }
540
562
  for (let n = 0; n < trun.sampleCount; ++n) {
541
563
  if (dynOffset >= this.len) {
542
- debug("TrackRunBox size mismatch");
564
+ debug('TrackRunBox size mismatch');
543
565
  break;
544
566
  }
545
567
  const sample = {};
@@ -579,7 +601,7 @@ export class HandlerBox {
579
601
  flags: Token.UINT24_BE.get(buf, off + 1),
580
602
  componentType: charTypeToken.get(buf, off + 4),
581
603
  handlerType: charTypeToken.get(buf, off + 8),
582
- componentName: new Token.StringType(this.len - 28, 'utf-8').get(buf, off + 28),
604
+ componentName: new Token.StringType(this.len - 28, 'utf-8').get(buf, off + 28)
583
605
  };
584
606
  }
585
607
  }
@@ -2,6 +2,8 @@ import { BasicParser } from '../common/BasicParser.js';
2
2
  import { Atom } from './Atom.js';
3
3
  export declare class MP4Parser extends BasicParser {
4
4
  private static read_BE_Integer;
5
+ private validatePayloadLength;
6
+ private readToken;
5
7
  private tracks;
6
8
  private hasVideoTrack;
7
9
  private hasAudioTrack;