@openclaw/whatsapp 2026.8.1-beta.3 → 2026.8.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.
- package/dist/{accounts-BX2fZUyR.js → accounts-CeKQbu_s.js} +9 -26
- package/dist/{action-runtime-pM-ExU0l.js → action-runtime-Bw0loEdy.js} +2 -2
- package/dist/action-runtime-api.js +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/{call-tool-api.js → agent-tools-api.js} +13 -6
- package/dist/{agent-tools-login-BjRpD-JQ.js → agent-tools-login-B-2qZPzc.js} +12 -3
- package/dist/api.js +10 -10
- package/dist/{approval-handler.runtime-CkMMvUjk.js → approval-handler.runtime-DeyPb57-.js} +3 -3
- package/dist/{approval-reactions-Jyr1JaAK.js → approval-reactions-DGDA6hQ7.js} +7 -5
- package/dist/{auth-store-CtpBqH5S.js → auth-store-DTj1H9pv.js} +5 -3
- package/dist/{channel-DRMYRfZ8.js → channel-DxMmDjqK.js} +26 -24
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel-react-action-CstrR0CT.js → channel-react-action-BUp7Wbfr.js} +3 -3
- package/dist/{channel.runtime-X3JC2gaq.js → channel.runtime-BwSnyXRX.js} +4 -4
- package/dist/{channel.setup-DhBBhbQv.js → channel.setup-aOFLSQyw.js} +1 -1
- package/dist/{config-schema-BQP5YCeK.js → config-schema-BYA6e_Ii.js} +6 -4
- package/dist/{connection-controller-CIYuBJrs.js → connection-controller-BvkxsbWi.js} +2 -2
- package/dist/{constants-HU41RHGI.js → constants-C7m81BNe.js} +1 -1
- package/dist/{directory-config-P7_3U73Z.js → directory-config-LgxYJLWY.js} +3 -3
- package/dist/directory-contract-api.js +1 -1
- package/dist/{group-session-key-DmFc_Cqi.js → group-session-key-KUguLmSZ.js} +4 -4
- package/dist/index.js +4 -4
- package/dist/light-runtime-api.js +2 -2
- package/dist/{login-BkpKpndm.js → login-Bk_jiXKD.js} +6 -5
- package/dist/{login-qr-1dpv6jjT.js → login-qr-B3m61NqA.js} +43 -20
- package/dist/login-qr-runtime.js +1 -1
- package/dist/{monitor-IzwQKizW.js → monitor-CTsyo-HE.js} +185 -162
- package/dist/{question-reactions-D3eAeB0i.js → question-reactions-B9D6MHXZ.js} +1 -1
- package/dist/runtime-api.js +9 -9
- package/dist/{send-CwvTU4RI.js → send-DiPjiygK.js} +54 -29
- package/dist/{send-api-obOSm2cC.js → send-api-kFcuhSiY.js} +43 -41
- package/dist/{setup-finalize-n4K2I2gJ.js → setup-finalize-CqH5wTK5.js} +7 -6
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{setup-surface-Cwn3zyxK.js → setup-surface-Dm3T6S8t.js} +3 -3
- package/dist/{shared-wg42NKFB.js → shared-DDjonGys.js} +7 -6
- package/dist/{socket-close-2T0_jZTN.js → socket-close-CpkkJZCU.js} +9 -6
- package/dist/{targets-runtime-TMk0lGuy.js → targets-runtime-RBjx3pg_.js} +1 -2
- package/dist/{text-runtime-CaKLhowd.js → text-runtime-DyfmcATV.js} +1 -1
- package/node_modules/baileys/lib/Defaults/index.js +1 -1
- package/node_modules/baileys/lib/Socket/business.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/chats.d.ts +1 -0
- package/node_modules/baileys/lib/Socket/chats.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/chats.js +10 -5
- package/node_modules/baileys/lib/Socket/chats.js.map +1 -1
- package/node_modules/baileys/lib/Socket/communities.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/groups.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/index.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/messages-recv.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/messages-send.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/newsletter.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/socket.d.ts.map +1 -1
- package/node_modules/baileys/lib/Socket/socket.js +3 -0
- package/node_modules/baileys/lib/Socket/socket.js.map +1 -1
- package/node_modules/baileys/lib/Types/index.d.ts +1 -0
- package/node_modules/baileys/lib/Types/index.d.ts.map +1 -1
- package/node_modules/baileys/lib/Types/index.js.map +1 -1
- package/node_modules/baileys/lib/Utils/browser-utils.d.ts.map +1 -1
- package/node_modules/baileys/lib/Utils/browser-utils.js +1 -0
- package/node_modules/baileys/lib/Utils/browser-utils.js.map +1 -1
- package/node_modules/baileys/lib/Utils/generics.d.ts +1 -0
- package/node_modules/baileys/lib/Utils/generics.d.ts.map +1 -1
- package/node_modules/baileys/lib/Utils/generics.js +1 -1
- package/node_modules/baileys/lib/Utils/generics.js.map +1 -1
- package/node_modules/baileys/lib/Utils/tc-token-utils.d.ts.map +1 -1
- package/node_modules/baileys/lib/Utils/tc-token-utils.js +3 -2
- package/node_modules/baileys/lib/Utils/tc-token-utils.js.map +1 -1
- package/node_modules/baileys/lib/Utils/validate-connection.d.ts.map +1 -1
- package/node_modules/baileys/lib/Utils/validate-connection.js +9 -2
- package/node_modules/baileys/lib/Utils/validate-connection.js.map +1 -1
- package/node_modules/baileys/node_modules/content-type/README.md +2 -0
- package/node_modules/baileys/node_modules/content-type/dist/index.d.ts +20 -0
- package/node_modules/baileys/node_modules/content-type/dist/index.js +20 -14
- package/node_modules/baileys/node_modules/content-type/dist/index.js.map +1 -1
- package/node_modules/baileys/node_modules/content-type/package.json +1 -1
- package/node_modules/baileys/node_modules/file-type/package.json +1 -1
- package/node_modules/baileys/node_modules/file-type/readme.md +3 -0
- package/node_modules/baileys/node_modules/file-type/source/detectors/zip.js +6 -1
- package/node_modules/baileys/node_modules/file-type/source/index.d.ts +3 -0
- package/node_modules/baileys/node_modules/file-type/source/index.js +25 -13
- package/node_modules/baileys/node_modules/music-metadata/LICENSE.txt +9 -9
- package/node_modules/baileys/node_modules/music-metadata/README.md +878 -878
- package/node_modules/baileys/node_modules/music-metadata/lib/ParseError.d.ts +1 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/ParseError.js +1 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfObject.d.ts +1 -4
- package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfObject.js +66 -30
- package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfParser.d.ts +5 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/asf/AsfParser.js +94 -25
- package/node_modules/baileys/node_modules/music-metadata/lib/dsf/DsfChunk.js +2 -2
- package/node_modules/baileys/node_modules/music-metadata/lib/dsf/DsfParser.js +38 -13
- package/node_modules/baileys/node_modules/music-metadata/lib/mp4/AtomToken.js +2 -1
- package/node_modules/baileys/node_modules/music-metadata/lib/mp4/MP4Parser.js +34 -7
- package/node_modules/baileys/node_modules/music-metadata/lib/ogg/speex/SpeexStream.d.ts +7 -0
- package/node_modules/baileys/node_modules/music-metadata/lib/ogg/speex/SpeexStream.js +13 -0
- package/node_modules/baileys/node_modules/music-metadata/lib/riff/RiffChunk.js +1 -1
- package/node_modules/baileys/node_modules/music-metadata/package.json +156 -156
- package/node_modules/baileys/node_modules/process-warning/.github/dependabot.yml +42 -2
- package/node_modules/baileys/node_modules/process-warning/.github/workflows/ci.yml +12 -1
- package/node_modules/baileys/node_modules/process-warning/.github/workflows/lock-threads.yml +19 -0
- package/node_modules/baileys/node_modules/process-warning/LICENSE +1 -1
- package/node_modules/baileys/node_modules/process-warning/README.md +41 -2
- package/node_modules/baileys/node_modules/process-warning/index.js +85 -17
- package/node_modules/baileys/node_modules/process-warning/package.json +6 -7
- package/node_modules/baileys/node_modules/process-warning/test/emit-once-only.test.js +3 -3
- package/node_modules/baileys/node_modules/process-warning/test/emit-reset.test.js +4 -4
- package/node_modules/baileys/node_modules/process-warning/test/emit-unlimited.test.js +2 -2
- package/node_modules/baileys/node_modules/process-warning/test/spy-warning.test.js +143 -0
- package/node_modules/baileys/node_modules/process-warning/types/index.d.ts +15 -1
- package/node_modules/baileys/node_modules/process-warning/types/index.tst.ts +48 -0
- package/node_modules/baileys/node_modules/protobufjs/README.md +61 -84
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.js +66 -45
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.min.js +3 -3
- package/node_modules/baileys/node_modules/protobufjs/dist/light/protobuf.min.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.js +47 -40
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.min.js +3 -3
- package/node_modules/baileys/node_modules/protobufjs/dist/minimal/protobuf.min.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.js +92 -68
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.min.js +3 -3
- package/node_modules/baileys/node_modules/protobufjs/dist/protobuf.min.js.map +1 -1
- package/node_modules/baileys/node_modules/protobufjs/ext/descriptor.js +26 -10
- package/node_modules/baileys/node_modules/protobufjs/index.d.ts +3 -0
- package/node_modules/baileys/node_modules/protobufjs/package.json +1 -1
- package/node_modules/baileys/node_modules/protobufjs/src/converter.js +5 -1
- package/node_modules/baileys/node_modules/protobufjs/src/enum.js +5 -2
- package/node_modules/baileys/node_modules/protobufjs/src/field.js +6 -1
- package/node_modules/baileys/node_modules/protobufjs/src/parse.js +26 -23
- package/node_modules/baileys/node_modules/protobufjs/src/reader.js +36 -9
- package/node_modules/baileys/node_modules/protobufjs/src/util/codegen.js +3 -1
- package/node_modules/baileys/node_modules/protobufjs/src/util/utf8.js +2 -28
- package/node_modules/baileys/node_modules/protobufjs/src/writer.js +7 -1
- package/node_modules/baileys/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/baileys/node_modules/ws/package.json +1 -1
- package/node_modules/baileys/package.json +1 -1
- package/node_modules/typebox/build/format/date.d.mts +1 -1
- package/node_modules/typebox/build/format/date.mjs +1 -1
- package/node_modules/typebox/build/format/date_time.d.mts +2 -2
- package/node_modules/typebox/build/format/date_time.mjs +3 -3
- package/node_modules/typebox/build/format/email.d.mts +1 -1
- package/node_modules/typebox/build/format/email.mjs +2 -2
- package/node_modules/typebox/build/format/hostname.mjs +2 -10
- package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
- package/node_modules/typebox/build/format/idn_email.mjs +3 -3
- package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
- package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
- package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
- package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
- package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/idn-hostname.mjs +41 -0
- package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
- package/node_modules/typebox/build/format/idna/index.mjs +2 -0
- package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
- package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
- package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
- package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
- package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
- package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
- package/node_modules/typebox/build/format/ipv4.mjs +2 -32
- package/node_modules/typebox/build/format/ipv6.mjs +2 -61
- package/node_modules/typebox/build/format/iri.d.mts +2 -2
- package/node_modules/typebox/build/format/iri.mjs +26 -8
- package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
- package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
- package/node_modules/typebox/build/format/regex.d.mts +1 -2
- package/node_modules/typebox/build/format/regex.mjs +2 -6
- package/node_modules/typebox/build/format/time.d.mts +2 -2
- package/node_modules/typebox/build/format/time.mjs +19 -14
- package/node_modules/typebox/build/format/uri.d.mts +1 -1
- package/node_modules/typebox/build/format/uri.mjs +3 -130
- package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
- package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
- package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
- package/node_modules/typebox/build/format/uri_template.mjs +4 -5
- package/node_modules/typebox/build/format/url.d.mts +3 -3
- package/node_modules/typebox/build/format/url.mjs +4 -5
- package/node_modules/typebox/build/guard/emit.d.mts +3 -1
- package/node_modules/typebox/build/guard/emit.mjs +12 -5
- package/node_modules/typebox/build/guard/guard.d.mts +8 -2
- package/node_modules/typebox/build/guard/guard.mjs +25 -4
- package/node_modules/typebox/build/guard/string.d.mts +2 -2
- package/node_modules/typebox/build/guard/string.mjs +7 -7
- package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
- package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
- package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
- package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
- package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
- package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
- package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
- package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
- package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
- package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
- package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
- package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
- package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
- package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
- package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
- package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
- package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
- package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
- package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
- package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
- package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
- package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
- package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
- package/node_modules/typebox/build/schema/types/index.mjs +0 -2
- package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
- package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
- package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
- package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
- package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
- package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
- package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
- package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
- package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
- package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
- package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
- package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
- package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
- package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
- package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
- package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
- package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
- package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
- package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
- package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
- package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
- package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
- package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
- package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
- package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
- package/node_modules/typebox/build/type/script/parser.mjs +27 -23
- package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
- package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
- package/node_modules/typebox/package.json +27 -27
- package/node_modules/typebox/readme.md +79 -82
- package/openclaw.plugin.json +2 -1
- package/package.json +6 -6
- package/node_modules/baileys/node_modules/process-warning/types/index.test-d.ts +0 -36
- package/node_modules/typebox/build/format/_idna.d.mts +0 -2
- package/node_modules/typebox/build/format/_idna.mjs +0 -246
- package/node_modules/typebox/build/format/_puny.d.mts +0 -1
- package/node_modules/typebox/build/format/_puny.mjs +0 -77
- package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
- package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
- package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
- package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
- package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
|
@@ -43,7 +43,7 @@ declare const UnexpectedFileContentError_base: {
|
|
|
43
43
|
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
44
44
|
stackTraceLimit: number;
|
|
45
45
|
};
|
|
46
|
-
declare class UnexpectedFileContentError extends UnexpectedFileContentError_base {
|
|
46
|
+
export declare class UnexpectedFileContentError extends UnexpectedFileContentError_base {
|
|
47
47
|
readonly fileType: string;
|
|
48
48
|
constructor(fileType: string, message: string);
|
|
49
49
|
toString(): string;
|
|
@@ -13,7 +13,7 @@ export class CouldNotDetermineFileTypeError extends makeParseError('CouldNotDete
|
|
|
13
13
|
export class UnsupportedFileTypeError extends makeParseError('UnsupportedFileTypeError') {
|
|
14
14
|
}
|
|
15
15
|
// Concrete error class representing unexpected file content.
|
|
16
|
-
class UnexpectedFileContentError extends makeParseError('UnexpectedFileContentError') {
|
|
16
|
+
export class UnexpectedFileContentError extends makeParseError('UnexpectedFileContentError') {
|
|
17
17
|
constructor(fileType, message) {
|
|
18
18
|
super(message);
|
|
19
19
|
this.fileType = fileType;
|
|
@@ -81,9 +81,6 @@ export declare abstract class State<T> implements IGetToken<T> {
|
|
|
81
81
|
abstract get(buf: Uint8Array, off: number): T;
|
|
82
82
|
protected postProcessTag(tags: ITag[], name: string, valueType: number, data: AnyTagValue): void;
|
|
83
83
|
}
|
|
84
|
-
export declare class IgnoreObjectState extends State<unknown> {
|
|
85
|
-
get(_buf: Uint8Array, _off: number): null;
|
|
86
|
-
}
|
|
87
84
|
/**
|
|
88
85
|
* Interface for: 3.2: File Properties Object (mandatory, one only)
|
|
89
86
|
*
|
|
@@ -238,7 +235,7 @@ export interface ICodecEntry {
|
|
|
238
235
|
* 3.5: Read the Codec-List-Object, which provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
|
239
236
|
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_5
|
|
240
237
|
*/
|
|
241
|
-
export declare function readCodecEntries(tokenizer: ITokenizer): Promise<ICodecEntry[]>;
|
|
238
|
+
export declare function readCodecEntries(tokenizer: ITokenizer, payloadSize: number): Promise<ICodecEntry[]>;
|
|
242
239
|
/**
|
|
243
240
|
* 3.10 Content Description Object (optional, one only)
|
|
244
241
|
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_10
|
|
@@ -43,11 +43,14 @@ export const DataType = {
|
|
|
43
43
|
export const TopLevelHeaderObjectToken = {
|
|
44
44
|
len: 30,
|
|
45
45
|
get: (buf, off) => {
|
|
46
|
+
const base = HeaderObjectToken.get(buf, off);
|
|
47
|
+
if (base.objectSize < TopLevelHeaderObjectToken.len) {
|
|
48
|
+
throw new AsfContentParseError(`Invalid ASF top-level header object size: ${base.objectSize}`);
|
|
49
|
+
}
|
|
46
50
|
return {
|
|
47
|
-
|
|
48
|
-
objectSize: Number(Token.UINT64_LE.get(buf, off + 16)),
|
|
51
|
+
...base,
|
|
49
52
|
numberOfHeaderObjects: Token.UINT32_LE.get(buf, off + 24)
|
|
50
|
-
//
|
|
53
|
+
// reserved: 2 bytes
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
56
|
};
|
|
@@ -58,15 +61,20 @@ export const TopLevelHeaderObjectToken = {
|
|
|
58
61
|
export const HeaderObjectToken = {
|
|
59
62
|
len: 24,
|
|
60
63
|
get: (buf, off) => {
|
|
61
|
-
|
|
64
|
+
const objectSize = Token.UINT64_LE.get(buf, off + 16);
|
|
65
|
+
if (objectSize < BigInt(HeaderObjectToken.len) || objectSize > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
66
|
+
throw new AsfContentParseError(`Invalid ASF header object size: ${objectSize}`);
|
|
67
|
+
}
|
|
68
|
+
const header = {
|
|
62
69
|
objectId: AsfGuid.fromBin(buf, off),
|
|
63
|
-
objectSize: Number(
|
|
70
|
+
objectSize: Number(objectSize)
|
|
64
71
|
};
|
|
72
|
+
return header;
|
|
65
73
|
}
|
|
66
74
|
};
|
|
67
75
|
export class State {
|
|
68
76
|
constructor(header) {
|
|
69
|
-
this.len =
|
|
77
|
+
this.len = header.objectSize - HeaderObjectToken.len;
|
|
70
78
|
}
|
|
71
79
|
postProcessTag(tags, name, valueType, data) {
|
|
72
80
|
if (name === 'WM/Picture') {
|
|
@@ -81,12 +89,6 @@ export class State {
|
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
|
-
// ToDo: use ignore type
|
|
85
|
-
export class IgnoreObjectState extends State {
|
|
86
|
-
get(_buf, _off) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
92
|
/**
|
|
91
93
|
* Token for: 3.2: File Properties Object (mandatory, one only)
|
|
92
94
|
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_2
|
|
@@ -140,7 +142,7 @@ export class HeaderExtensionObject {
|
|
|
140
142
|
return {
|
|
141
143
|
reserved1: AsfGuid.fromBin(buf, off),
|
|
142
144
|
reserved2: view.getUint16(16, true),
|
|
143
|
-
extensionDataSize: view.
|
|
145
|
+
extensionDataSize: view.getUint32(18, true)
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
148
|
}
|
|
@@ -158,42 +160,76 @@ const CodecListObjectHeader = {
|
|
|
158
160
|
};
|
|
159
161
|
}
|
|
160
162
|
};
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
163
|
+
class BoundedReader {
|
|
164
|
+
constructor(tokenizer, size) {
|
|
165
|
+
this.tokenizer = tokenizer;
|
|
166
|
+
this.remaining = size;
|
|
167
|
+
}
|
|
168
|
+
async readToken(token) {
|
|
169
|
+
this.ensureAvailable(token.len);
|
|
170
|
+
const value = await this.tokenizer.readToken(token);
|
|
171
|
+
this.remaining -= token.len;
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
async readNumber(token) {
|
|
175
|
+
return this.readToken(token);
|
|
176
|
+
}
|
|
177
|
+
async readBuffer(length) {
|
|
178
|
+
this.ensureAvailable(length);
|
|
179
|
+
const buffer = new Uint8Array(length);
|
|
180
|
+
await this.tokenizer.readBuffer(buffer);
|
|
181
|
+
this.remaining -= length;
|
|
182
|
+
return buffer;
|
|
183
|
+
}
|
|
184
|
+
async ignoreRemaining() {
|
|
185
|
+
const ignored = await this.tokenizer.ignore(this.remaining);
|
|
186
|
+
if (ignored !== this.remaining) {
|
|
187
|
+
throw new AsfContentParseError(`Unexpected end of ASF Codec List Object; missing ${this.remaining - ignored} bytes`);
|
|
188
|
+
}
|
|
189
|
+
this.remaining = 0;
|
|
190
|
+
}
|
|
191
|
+
ensureAvailable(length) {
|
|
192
|
+
if (length > this.remaining) {
|
|
193
|
+
throw new AsfContentParseError(`ASF Codec List field size ${length} exceeds remaining object payload size ${this.remaining}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function readString(reader) {
|
|
198
|
+
const length = await reader.readNumber(Token.UINT16_LE);
|
|
199
|
+
return (await reader.readToken(new Token.StringType(length * 2, 'utf-16le'))).replace('\0', '');
|
|
164
200
|
}
|
|
165
201
|
/**
|
|
166
202
|
* 3.5: Read the Codec-List-Object, which provides user-friendly information about the codecs and formats used to encode the content found in the ASF file.
|
|
167
203
|
* Ref: http://drang.s4.xrea.com/program/tips/id3tag/wmp/03_asf_top_level_header_object.html#3_5
|
|
168
204
|
*/
|
|
169
|
-
export async function readCodecEntries(tokenizer) {
|
|
170
|
-
const
|
|
205
|
+
export async function readCodecEntries(tokenizer, payloadSize) {
|
|
206
|
+
const reader = new BoundedReader(tokenizer, payloadSize);
|
|
207
|
+
const codecHeader = await reader.readToken(CodecListObjectHeader);
|
|
171
208
|
const entries = [];
|
|
172
209
|
for (let i = 0; i < codecHeader.entryCount; ++i) {
|
|
173
|
-
entries.push(await readCodecEntry(
|
|
210
|
+
entries.push(await readCodecEntry(reader));
|
|
174
211
|
}
|
|
212
|
+
await reader.ignoreRemaining();
|
|
175
213
|
return entries;
|
|
176
214
|
}
|
|
177
|
-
async function readInformation(
|
|
178
|
-
const length = await
|
|
179
|
-
|
|
180
|
-
await tokenizer.readBuffer(buf);
|
|
181
|
-
return buf;
|
|
215
|
+
async function readInformation(reader) {
|
|
216
|
+
const length = await reader.readNumber(Token.UINT16_LE);
|
|
217
|
+
return reader.readBuffer(length);
|
|
182
218
|
}
|
|
183
219
|
/**
|
|
184
220
|
* Read Codec-Entries
|
|
185
|
-
* @param
|
|
221
|
+
* @param reader
|
|
186
222
|
*/
|
|
187
|
-
async function readCodecEntry(
|
|
188
|
-
const type = await
|
|
223
|
+
async function readCodecEntry(reader) {
|
|
224
|
+
const type = await reader.readNumber(Token.UINT16_LE);
|
|
189
225
|
return {
|
|
190
226
|
type: {
|
|
191
227
|
videoCodec: (type & 0x0001) === 0x0001,
|
|
192
228
|
audioCodec: (type & 0x0002) === 0x0002
|
|
193
229
|
},
|
|
194
|
-
codecName: await readString(
|
|
195
|
-
description: await readString(
|
|
196
|
-
information: await readInformation(
|
|
230
|
+
codecName: await readString(reader),
|
|
231
|
+
description: await readString(reader),
|
|
232
|
+
information: await readInformation(reader)
|
|
197
233
|
};
|
|
198
234
|
}
|
|
199
235
|
/**
|
|
@@ -11,7 +11,11 @@ import { BasicParser } from '../common/BasicParser.js';
|
|
|
11
11
|
*/
|
|
12
12
|
export declare class AsfParser extends BasicParser {
|
|
13
13
|
parse(): Promise<void>;
|
|
14
|
-
private
|
|
14
|
+
private parseObjectHeaders;
|
|
15
15
|
private addTags;
|
|
16
|
+
private readObjectHeader;
|
|
17
|
+
private validatePayloadAvailability;
|
|
18
|
+
private validateAllocationSize;
|
|
19
|
+
private ignorePayload;
|
|
16
20
|
private parseExtensionObject;
|
|
17
21
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import initDebug from 'debug';
|
|
2
|
+
import { EndOfStreamError } from 'strtok3';
|
|
2
3
|
import { TrackType } from '../type.js';
|
|
3
4
|
import AsfGuid from './AsfGuid.js';
|
|
4
5
|
import * as AsfObject from './AsfObject.js';
|
|
@@ -6,6 +7,7 @@ import { BasicParser } from '../common/BasicParser.js';
|
|
|
6
7
|
import { AsfContentParseError } from './AsfObject.js';
|
|
7
8
|
const debug = initDebug('music-metadata:parser:ASF');
|
|
8
9
|
const headerType = 'asf';
|
|
10
|
+
const maxAsfMetadataObjectSize = 16 * 1024 * 1024;
|
|
9
11
|
/**
|
|
10
12
|
* Windows Media Metadata Usage Guidelines
|
|
11
13
|
* - Ref: https://msdn.microsoft.com/en-us/library/ms867702.aspx
|
|
@@ -22,42 +24,64 @@ export class AsfParser extends BasicParser {
|
|
|
22
24
|
if (!header.objectId.equals(AsfGuid.HeaderObject)) {
|
|
23
25
|
throw new AsfContentParseError(`expected asf header; but was not found; got: ${header.objectId.str}`);
|
|
24
26
|
}
|
|
25
|
-
|
|
27
|
+
if (header.numberOfHeaderObjects < 1 || header.numberOfHeaderObjects > 10000) {
|
|
28
|
+
throw new AsfContentParseError(`Unrealistic number of ASF header objects: ${header.numberOfHeaderObjects}`);
|
|
29
|
+
}
|
|
30
|
+
await this.parseObjectHeaders(header.numberOfHeaderObjects, header.objectSize - AsfObject.TopLevelHeaderObjectToken.len);
|
|
26
31
|
}
|
|
27
|
-
async
|
|
32
|
+
async parseObjectHeaders(numberOfObjectHeaders, remainingHeaderSize) {
|
|
28
33
|
let tags;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const header = await this.tokenizer.readToken(AsfObject.HeaderObjectToken);
|
|
34
|
+
for (let index = 0; index < numberOfObjectHeaders; ++index) {
|
|
35
|
+
const { header, payloadSize } = await this.readObjectHeader(remainingHeaderSize, 'header');
|
|
32
36
|
// Parse data part of the ASF Object
|
|
33
37
|
debug('header GUID=%s', header.objectId.str);
|
|
34
38
|
switch (header.objectId.str) {
|
|
35
39
|
case AsfObject.FilePropertiesObject.guid.str: { // 3.2
|
|
40
|
+
this.validateAllocationSize(payloadSize, 'File Properties Object');
|
|
36
41
|
const fpo = await this.tokenizer.readToken(new AsfObject.FilePropertiesObject(header));
|
|
37
42
|
this.metadata.setFormat('duration', Number(fpo.playDuration / BigInt(1000)) / 10000 - Number(fpo.preroll) / 1000);
|
|
38
43
|
this.metadata.setFormat('bitrate', fpo.maximumBitrate);
|
|
39
44
|
break;
|
|
40
45
|
}
|
|
41
46
|
case AsfObject.StreamPropertiesObject.guid.str: { // 3.3
|
|
47
|
+
this.validateAllocationSize(payloadSize, 'Stream Properties Object');
|
|
42
48
|
const spo = await this.tokenizer.readToken(new AsfObject.StreamPropertiesObject(header));
|
|
43
49
|
this.metadata.setFormat('container', `ASF/${spo.streamType}`);
|
|
44
50
|
break;
|
|
45
51
|
}
|
|
46
52
|
case AsfObject.HeaderExtensionObject.guid.str: { // 3.4
|
|
47
|
-
const
|
|
53
|
+
const extensionHeaderToken = new AsfObject.HeaderExtensionObject();
|
|
54
|
+
if (payloadSize < extensionHeaderToken.len) {
|
|
55
|
+
throw new AsfContentParseError(`ASF Header Extension Object payload is too small: ${payloadSize} bytes`);
|
|
56
|
+
}
|
|
57
|
+
const extHeader = await this.tokenizer.readToken(extensionHeaderToken);
|
|
58
|
+
const expectedExtensionSize = payloadSize - extensionHeaderToken.len;
|
|
59
|
+
if (extHeader.extensionDataSize !== expectedExtensionSize) {
|
|
60
|
+
throw new AsfContentParseError(`ASF extension data size ${extHeader.extensionDataSize} does not match enclosing payload size ${expectedExtensionSize}`);
|
|
61
|
+
}
|
|
48
62
|
await this.parseExtensionObject(extHeader.extensionDataSize);
|
|
49
63
|
break;
|
|
50
64
|
}
|
|
51
65
|
case AsfObject.ContentDescriptionObjectState.guid.str: // 3.10
|
|
66
|
+
this.validateAllocationSize(payloadSize, 'Content Description Object');
|
|
52
67
|
tags = await this.tokenizer.readToken(new AsfObject.ContentDescriptionObjectState(header));
|
|
53
68
|
await this.addTags(tags);
|
|
54
69
|
break;
|
|
55
70
|
case AsfObject.ExtendedContentDescriptionObjectState.guid.str: // 3.11
|
|
71
|
+
this.validateAllocationSize(payloadSize, 'Extended Content Description Object');
|
|
56
72
|
tags = await this.tokenizer.readToken(new AsfObject.ExtendedContentDescriptionObjectState(header));
|
|
57
73
|
await this.addTags(tags);
|
|
58
74
|
break;
|
|
59
75
|
case AsfGuid.CodecListObject.str: {
|
|
60
|
-
|
|
76
|
+
this.validateAllocationSize(payloadSize, 'Codec List Object');
|
|
77
|
+
let codecs;
|
|
78
|
+
try {
|
|
79
|
+
codecs = await AsfObject.readCodecEntries(this.tokenizer, payloadSize);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
throw new AsfContentParseError(`Invalid ASF Codec List Object: ${message}`);
|
|
84
|
+
}
|
|
61
85
|
codecs.forEach(codec => {
|
|
62
86
|
this.metadata.addStreamInfo({
|
|
63
87
|
type: codec.type.videoCodec ? TrackType.video : TrackType.audio,
|
|
@@ -70,65 +94,110 @@ export class AsfParser extends BasicParser {
|
|
|
70
94
|
}
|
|
71
95
|
case AsfGuid.StreamBitratePropertiesObject.str:
|
|
72
96
|
// ToDo?
|
|
73
|
-
await this.
|
|
97
|
+
await this.ignorePayload(payloadSize, 'Stream Bitrate Properties Object');
|
|
74
98
|
break;
|
|
75
99
|
case AsfGuid.PaddingObject.str:
|
|
76
100
|
// ToDo: register bytes pad
|
|
77
|
-
debug('Padding: %s bytes',
|
|
78
|
-
await this.
|
|
101
|
+
debug('Padding: %s bytes', payloadSize);
|
|
102
|
+
await this.ignorePayload(payloadSize, 'Padding Object');
|
|
79
103
|
break;
|
|
80
104
|
default:
|
|
81
105
|
this.metadata.addWarning(`Ignore ASF-Object-GUID: ${header.objectId.str}`);
|
|
82
106
|
debug('Ignore ASF-Object-GUID: %s', header.objectId.str);
|
|
83
|
-
await this.
|
|
107
|
+
await this.ignorePayload(payloadSize, `object ${header.objectId.str}`);
|
|
84
108
|
}
|
|
85
|
-
|
|
86
|
-
|
|
109
|
+
remainingHeaderSize -= header.objectSize;
|
|
110
|
+
}
|
|
111
|
+
if (remainingHeaderSize !== 0) {
|
|
112
|
+
throw new AsfContentParseError(`ASF header child objects leave ${remainingHeaderSize} payload byte(s) unaccounted`);
|
|
113
|
+
}
|
|
87
114
|
}
|
|
88
115
|
async addTags(tags) {
|
|
89
116
|
await Promise.all(tags.map(({ id, value }) => this.metadata.addTag(headerType, id, value)));
|
|
90
117
|
}
|
|
91
|
-
async
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
118
|
+
async readObjectHeader(remainingSize, containerType) {
|
|
119
|
+
if (remainingSize < AsfObject.HeaderObjectToken.len) {
|
|
120
|
+
throw new AsfContentParseError(`Insufficient ASF ${containerType} data for an object header: ${remainingSize} bytes`);
|
|
121
|
+
}
|
|
122
|
+
const header = await this.tokenizer.readToken(AsfObject.HeaderObjectToken);
|
|
123
|
+
if (header.objectSize > remainingSize) {
|
|
124
|
+
throw new AsfContentParseError(`ASF ${containerType} object size ${header.objectSize} exceeds remaining ${containerType} payload size ${remainingSize}`);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
header,
|
|
128
|
+
payloadSize: this.validatePayloadAvailability(header, `${containerType} object`)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
validatePayloadAvailability(header, objectType) {
|
|
132
|
+
const payloadSize = header.objectSize - AsfObject.HeaderObjectToken.len;
|
|
133
|
+
const fileSize = this.tokenizer.fileInfo.size;
|
|
134
|
+
if (fileSize !== undefined) {
|
|
135
|
+
const available = fileSize - this.tokenizer.position;
|
|
136
|
+
if (payloadSize > available) {
|
|
137
|
+
throw new AsfContentParseError(`ASF ${objectType} payload size ${payloadSize} exceeds available input size ${available}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return payloadSize;
|
|
141
|
+
}
|
|
142
|
+
validateAllocationSize(payloadSize, objectType) {
|
|
143
|
+
if (payloadSize > maxAsfMetadataObjectSize) {
|
|
144
|
+
throw new AsfContentParseError(`ASF ${objectType} payload size ${payloadSize} exceeds allocation limit ${maxAsfMetadataObjectSize}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async ignorePayload(payloadSize, objectType) {
|
|
148
|
+
let ignored;
|
|
149
|
+
try {
|
|
150
|
+
ignored = await this.tokenizer.ignore(payloadSize);
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (error instanceof EndOfStreamError) {
|
|
154
|
+
throw new AsfContentParseError(`Unexpected end of ASF ${objectType}`);
|
|
98
155
|
}
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
if (ignored !== payloadSize) {
|
|
159
|
+
throw new AsfContentParseError(`Unexpected end of ASF ${objectType}; missing ${payloadSize - ignored} bytes`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async parseExtensionObject(extensionSize) {
|
|
163
|
+
while (extensionSize > 0) {
|
|
164
|
+
const { header, payloadSize } = await this.readObjectHeader(extensionSize, 'extension');
|
|
99
165
|
// Parse data part of the ASF Object
|
|
100
166
|
switch (header.objectId.str) {
|
|
101
167
|
case AsfObject.ExtendedStreamPropertiesObjectState.guid.str: // 4.1
|
|
102
168
|
// ToDo: extended stream header properties are ignored
|
|
169
|
+
this.validateAllocationSize(payloadSize, 'Extended Stream Properties Object');
|
|
103
170
|
await this.tokenizer.readToken(new AsfObject.ExtendedStreamPropertiesObjectState(header));
|
|
104
171
|
break;
|
|
105
172
|
case AsfObject.MetadataObjectState.guid.str: { // 4.7
|
|
173
|
+
this.validateAllocationSize(payloadSize, 'Metadata Object');
|
|
106
174
|
const moTags = await this.tokenizer.readToken(new AsfObject.MetadataObjectState(header));
|
|
107
175
|
await this.addTags(moTags);
|
|
108
176
|
break;
|
|
109
177
|
}
|
|
110
178
|
case AsfObject.MetadataLibraryObjectState.guid.str: { // 4.8
|
|
179
|
+
this.validateAllocationSize(payloadSize, 'Metadata Library Object');
|
|
111
180
|
const mlTags = await this.tokenizer.readToken(new AsfObject.MetadataLibraryObjectState(header));
|
|
112
181
|
await this.addTags(mlTags);
|
|
113
182
|
break;
|
|
114
183
|
}
|
|
115
184
|
case AsfGuid.PaddingObject.str:
|
|
116
185
|
// ToDo: register bytes pad
|
|
117
|
-
await this.
|
|
186
|
+
await this.ignorePayload(payloadSize, 'extension Padding Object');
|
|
118
187
|
break;
|
|
119
188
|
case AsfGuid.CompatibilityObject.str:
|
|
120
|
-
await this.
|
|
189
|
+
await this.ignorePayload(payloadSize, 'Compatibility Object');
|
|
121
190
|
break;
|
|
122
191
|
case AsfGuid.ASF_Index_Placeholder_Object.str:
|
|
123
|
-
await this.
|
|
192
|
+
await this.ignorePayload(payloadSize, 'Index Placeholder Object');
|
|
124
193
|
break;
|
|
125
194
|
default:
|
|
126
195
|
this.metadata.addWarning(`Ignore ASF-Object-GUID: ${header.objectId.str}`);
|
|
127
196
|
// console.log("Ignore ASF-Object-GUID: %s", header.objectId.str);
|
|
128
|
-
await this.
|
|
197
|
+
await this.ignorePayload(payloadSize, `extension object ${header.objectId.str}`);
|
|
129
198
|
break;
|
|
130
199
|
}
|
|
131
200
|
extensionSize -= header.objectSize;
|
|
132
|
-
}
|
|
201
|
+
}
|
|
133
202
|
}
|
|
134
203
|
}
|
|
@@ -16,8 +16,8 @@ export const DsdChunk = {
|
|
|
16
16
|
len: 16,
|
|
17
17
|
get: (buf, off) => {
|
|
18
18
|
return {
|
|
19
|
-
fileSize: Token.
|
|
20
|
-
metadataPointer: Token.
|
|
19
|
+
fileSize: Token.UINT64_LE.get(buf, off),
|
|
20
|
+
metadataPointer: Token.UINT64_LE.get(buf, off + 8)
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import initDebug from 'debug';
|
|
2
2
|
import { AbstractID3Parser } from '../id3v2/AbstractID3Parser.js';
|
|
3
3
|
import { ChunkHeader, DsdChunk, FormatChunk } from './DsfChunk.js';
|
|
4
|
-
import { ID3v2Parser } from
|
|
4
|
+
import { ID3v2Parser } from '../id3v2/ID3v2Parser.js';
|
|
5
5
|
import { makeUnexpectedFileContentError } from '../ParseError.js';
|
|
6
|
+
import { ID3v2Header } from '../id3v2/ID3v2Token.js';
|
|
6
7
|
const debug = initDebug('music-metadata:parser:DSF');
|
|
7
8
|
export class DsdContentParseError extends makeUnexpectedFileContentError('DSD') {
|
|
8
9
|
}
|
|
@@ -14,41 +15,65 @@ export class DsfParser extends AbstractID3Parser {
|
|
|
14
15
|
async postId3v2Parse() {
|
|
15
16
|
const p0 = this.tokenizer.position; // mark start position, normally 0
|
|
16
17
|
const chunkHeader = await this.tokenizer.readToken(ChunkHeader);
|
|
17
|
-
if (chunkHeader.id !== 'DSD ')
|
|
18
|
+
if (chunkHeader.id !== 'DSD ') {
|
|
18
19
|
throw new DsdContentParseError('Invalid chunk signature');
|
|
20
|
+
}
|
|
21
|
+
if (chunkHeader.size !== BigInt(ChunkHeader.len + DsdChunk.len)) {
|
|
22
|
+
throw new DsdContentParseError(`Invalid DSD chunk size: ${chunkHeader.size}`);
|
|
23
|
+
}
|
|
19
24
|
this.metadata.setFormat('container', 'DSF');
|
|
20
25
|
this.metadata.setFormat('lossless', true);
|
|
21
26
|
this.metadata.setAudioOnly();
|
|
22
27
|
const dsdChunk = await this.tokenizer.readToken(DsdChunk);
|
|
23
|
-
if (dsdChunk.
|
|
24
|
-
|
|
28
|
+
if (dsdChunk.fileSize < chunkHeader.size) {
|
|
29
|
+
throw new DsdContentParseError(`Invalid DSF file size: ${dsdChunk.fileSize}`);
|
|
30
|
+
}
|
|
31
|
+
await this.parseChunks(dsdChunk.fileSize - chunkHeader.size);
|
|
32
|
+
if (dsdChunk.metadataPointer === 0n) {
|
|
33
|
+
debug('No ID3v2 tag present');
|
|
34
|
+
return;
|
|
25
35
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
debug(`expect ID3v2 at offset=${dsdChunk.metadataPointer}`);
|
|
37
|
+
const metadataOffset = dsdChunk.metadataPointer - BigInt(this.tokenizer.position - p0);
|
|
38
|
+
if (metadataOffset < 0n ||
|
|
39
|
+
metadataOffset > BigInt(Number.MAX_SAFE_INTEGER) ||
|
|
40
|
+
dsdChunk.metadataPointer + BigInt(ID3v2Header.len) > dsdChunk.fileSize) {
|
|
41
|
+
throw new DsdContentParseError(`Invalid metadata pointer: ${dsdChunk.metadataPointer}`);
|
|
32
42
|
}
|
|
43
|
+
await this.tokenizer.ignore(Number(metadataOffset));
|
|
44
|
+
return new ID3v2Parser().parse(this.metadata, this.tokenizer, this.options);
|
|
33
45
|
}
|
|
34
46
|
async parseChunks(bytesRemaining) {
|
|
35
|
-
|
|
47
|
+
const chunkHeaderSize = BigInt(ChunkHeader.len);
|
|
48
|
+
while (bytesRemaining >= chunkHeaderSize) {
|
|
36
49
|
const chunkHeader = await this.tokenizer.readToken(ChunkHeader);
|
|
37
50
|
debug(`Parsing chunk name=${chunkHeader.id} size=${chunkHeader.size}`);
|
|
51
|
+
if (chunkHeader.size < chunkHeaderSize) {
|
|
52
|
+
throw new DsdContentParseError(`Invalid ${chunkHeader.id} chunk size: ${chunkHeader.size}`);
|
|
53
|
+
}
|
|
54
|
+
if (chunkHeader.size > bytesRemaining) {
|
|
55
|
+
throw new DsdContentParseError(`${chunkHeader.id} chunk exceeds remaining file size`);
|
|
56
|
+
}
|
|
57
|
+
const payloadSize = chunkHeader.size - chunkHeaderSize;
|
|
38
58
|
switch (chunkHeader.id) {
|
|
39
59
|
case 'fmt ': {
|
|
60
|
+
if (payloadSize < BigInt(FormatChunk.len)) {
|
|
61
|
+
throw new DsdContentParseError(`Invalid fmt chunk size: ${chunkHeader.size}`);
|
|
62
|
+
}
|
|
40
63
|
const formatChunk = await this.tokenizer.readToken(FormatChunk);
|
|
41
64
|
this.metadata.setFormat('numberOfChannels', formatChunk.channelNum);
|
|
42
65
|
this.metadata.setFormat('sampleRate', formatChunk.samplingFrequency);
|
|
43
66
|
this.metadata.setFormat('bitsPerSample', formatChunk.bitsPerSample);
|
|
44
67
|
this.metadata.setFormat('numberOfSamples', formatChunk.sampleCount);
|
|
45
68
|
this.metadata.setFormat('duration', Number(formatChunk.sampleCount) / formatChunk.samplingFrequency);
|
|
46
|
-
const bitrate = formatChunk.bitsPerSample *
|
|
69
|
+
const bitrate = formatChunk.bitsPerSample *
|
|
70
|
+
formatChunk.samplingFrequency *
|
|
71
|
+
formatChunk.channelNum;
|
|
47
72
|
this.metadata.setFormat('bitrate', bitrate);
|
|
48
73
|
return; // We got what we want, stop further processing of chunks
|
|
49
74
|
}
|
|
50
75
|
default:
|
|
51
|
-
this.tokenizer.ignore(Number(
|
|
76
|
+
await this.tokenizer.ignore(Number(payloadSize));
|
|
52
77
|
break;
|
|
53
78
|
}
|
|
54
79
|
bytesRemaining -= chunkHeader.size;
|
|
@@ -313,7 +313,8 @@ export class StsdAtom {
|
|
|
313
313
|
const size = Token.UINT32_BE.get(buf, off); // Sample description size
|
|
314
314
|
off += Token.UINT32_BE.len;
|
|
315
315
|
table.push(new SampleDescriptionTable(size - Token.UINT32_BE.len).get(buf, off));
|
|
316
|
-
|
|
316
|
+
// A SampleEntry extends Box, so its size covers the size field already stepped over
|
|
317
|
+
off += size - Token.UINT32_BE.len;
|
|
317
318
|
}
|
|
318
319
|
return {
|
|
319
320
|
header,
|
|
@@ -72,6 +72,26 @@ const encoderDict = {
|
|
|
72
72
|
function distinct(value, index, self) {
|
|
73
73
|
return self.indexOf(value) === index;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Determine if a track carries audio.
|
|
77
|
+
*
|
|
78
|
+
* Ref: ISO/IEC 14496-12, 8.5.2: the sample entries in a sample description box are track-type specific,
|
|
79
|
+
* selected by the handler type of the enclosing track. Only an audio track holds an AudioSampleEntry, so
|
|
80
|
+
* only such a track may be described by a sound sample description; `isAudio()` treats both the 'soun'
|
|
81
|
+
* and 'audi' handler types as audio.
|
|
82
|
+
*
|
|
83
|
+
* Falls back to the sample description when no handler box was found, preserving the previous behavior.
|
|
84
|
+
*/
|
|
85
|
+
function isAudioTrack(track) {
|
|
86
|
+
const [ssd] = track.soundSampleDescription ?? [];
|
|
87
|
+
if (!ssd) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (track.isAudio) {
|
|
91
|
+
return track.isAudio();
|
|
92
|
+
}
|
|
93
|
+
return !!ssd.description && ssd.description.numAudioChannels > 0;
|
|
94
|
+
}
|
|
75
95
|
/*
|
|
76
96
|
* Parser for the MP4 (MPEG-4 Part 14) container format
|
|
77
97
|
* Standard: ISO/IEC 14496-14
|
|
@@ -209,6 +229,7 @@ export class MP4Parser extends BasicParser {
|
|
|
209
229
|
const formatList = [];
|
|
210
230
|
this.tracks.forEach(track => {
|
|
211
231
|
const trackFormats = [];
|
|
232
|
+
const trackIsAudio = isAudioTrack(track);
|
|
212
233
|
track.soundSampleDescription.forEach(ssd => {
|
|
213
234
|
const streamInfo = {};
|
|
214
235
|
const encoderInfo = encoderDict[ssd.dataFormat];
|
|
@@ -219,7 +240,7 @@ export class MP4Parser extends BasicParser {
|
|
|
219
240
|
else {
|
|
220
241
|
streamInfo.codecName = `<${ssd.dataFormat}>`;
|
|
221
242
|
}
|
|
222
|
-
if (ssd.description) {
|
|
243
|
+
if (trackIsAudio && ssd.description) {
|
|
223
244
|
const { description } = ssd;
|
|
224
245
|
if (description.sampleRate > 0) {
|
|
225
246
|
streamInfo.type = TrackType.audio;
|
|
@@ -239,9 +260,7 @@ export class MP4Parser extends BasicParser {
|
|
|
239
260
|
if (formatList.length > 0) {
|
|
240
261
|
this.metadata.setFormat('codec', formatList.filter(distinct).join('+'));
|
|
241
262
|
}
|
|
242
|
-
const audioTracks = [...this.tracks.values()].filter(
|
|
243
|
-
return track.soundSampleDescription.length >= 1 && track.soundSampleDescription[0].description && track.soundSampleDescription[0].description.numAudioChannels > 0;
|
|
244
|
-
});
|
|
263
|
+
const audioTracks = [...this.tracks.values()].filter(isAudioTrack);
|
|
245
264
|
// Calculate duration and bitrate of audio tracks
|
|
246
265
|
for (const audioTrack of audioTracks) {
|
|
247
266
|
if (audioTrack.media.header && audioTrack.media.header.timeScale > 0) {
|
|
@@ -561,12 +580,20 @@ export class MP4Parser extends BasicParser {
|
|
|
561
580
|
dataReferenceIndex: sampleDescription.dataReferenceIndex
|
|
562
581
|
};
|
|
563
582
|
let offset = 0;
|
|
564
|
-
|
|
565
|
-
|
|
583
|
+
const { description } = sampleDescription;
|
|
584
|
+
// Only an AudioSampleEntry carries a sound sample description; it is at least 20 bytes beyond the
|
|
585
|
+
// 16-byte SampleEntry base. A shorter description belongs to another sample entry class, or is truncated.
|
|
586
|
+
if (description && description.length >= AtomToken.SoundSampleDescriptionVersion.len) {
|
|
587
|
+
const version = AtomToken.SoundSampleDescriptionVersion.get(description, offset);
|
|
566
588
|
offset += AtomToken.SoundSampleDescriptionVersion.len;
|
|
567
589
|
if (version.version === 0 || version.version === 1) {
|
|
568
590
|
// Sound Sample Description (Version 0)
|
|
569
|
-
|
|
591
|
+
if (description.length >= offset + AtomToken.SoundSampleDescriptionV0.len) {
|
|
592
|
+
ssd.description = AtomToken.SoundSampleDescriptionV0.get(description, offset);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
debug(`Warning: sound-sample-description too short: ${description.length} bytes`);
|
|
596
|
+
}
|
|
570
597
|
}
|
|
571
598
|
else {
|
|
572
599
|
debug(`Warning: sound-sample-description ${version} not implemented`);
|
|
@@ -10,6 +10,7 @@ import type { INativeMetadataCollector } from '../../common/MetadataCollector.js
|
|
|
10
10
|
* - https://tools.ietf.org/html/rfc5574
|
|
11
11
|
*/
|
|
12
12
|
export declare class SpeexStream extends VorbisStream {
|
|
13
|
+
private commentHeaderParsed;
|
|
13
14
|
constructor(metadata: INativeMetadataCollector, options: IOptions, _tokenizer: ITokenizer);
|
|
14
15
|
/**
|
|
15
16
|
* Parse first Speex Ogg page
|
|
@@ -17,4 +18,10 @@ export declare class SpeexStream extends VorbisStream {
|
|
|
17
18
|
* @param {Uint8Array} pageData
|
|
18
19
|
*/
|
|
19
20
|
protected parseFirstPage(_header: IPageHeader, pageData: Uint8Array): void;
|
|
21
|
+
/**
|
|
22
|
+
* The packet following the identification header is the comment header, in
|
|
23
|
+
* Vorbis comment format but without a packet type or magic signature.
|
|
24
|
+
* Ref: https://tools.ietf.org/html/rfc5574#section-3
|
|
25
|
+
*/
|
|
26
|
+
protected parseFullPage(pageData: Uint8Array): Promise<void>;
|
|
20
27
|
}
|