UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

274 lines (273 loc) 9.87 kB
import { o as __toESM } from "./chunk-CNf5ZN-e.js"; import { t as require_src } from "./src-6PhJB9jV.js"; import { t as fromBuffer } from "./lib-X3TAZBh2.js"; import { E as Uint8ArrayType, O as textDecode, S as UINT32_LE, g as StringType, i as InternalParserError, k as textEncode, o as makeUnexpectedFileContentError, r as FieldDecodingError, t as BasicParser, v as UINT16_LE } from "./BasicParser-CSYy5xVJ.js"; import { i as findZero, t as a2hex } from "./Util--vOO_7DL.js"; //#region node_modules/music-metadata/lib/common/FourCC.js var import_src = /* @__PURE__ */ __toESM(require_src(), 1); const validFourCC = /^[\x21-\x7e©][\x20-\x7e\x00()]{3}/; /** * Token for read FourCC * Ref: https://en.wikipedia.org/wiki/FourCC */ const FourCcToken = { len: 4, get: (buf, off) => { const id = textDecode(buf.subarray(off, off + FourCcToken.len), "latin1"); if (!id.match(validFourCC)) throw new FieldDecodingError(`FourCC contains invalid characters: ${a2hex(id)} "${id}"`); return id; }, put: (buffer, offset, id) => { const str = textEncode(id, "latin1"); if (str.length !== 4) throw new InternalParserError("Invalid length"); buffer.set(str, offset); return offset + 4; } }; //#endregion //#region node_modules/music-metadata/lib/apev2/APEv2Token.js const DataType = { text_utf8: 0, binary: 1, external_info: 2, reserved: 3 }; /** * APE_DESCRIPTOR: defines the sizes (and offsets) of all the pieces, as well as the MD5 checksum */ const DescriptorParser = { len: 52, get: (buf, off) => { return { ID: FourCcToken.get(buf, off), version: UINT32_LE.get(buf, off + 4) / 1e3, descriptorBytes: UINT32_LE.get(buf, off + 8), headerBytes: UINT32_LE.get(buf, off + 12), seekTableBytes: UINT32_LE.get(buf, off + 16), headerDataBytes: UINT32_LE.get(buf, off + 20), apeFrameDataBytes: UINT32_LE.get(buf, off + 24), apeFrameDataBytesHigh: UINT32_LE.get(buf, off + 28), terminatingDataBytes: UINT32_LE.get(buf, off + 32), fileMD5: new Uint8ArrayType(16).get(buf, off + 36) }; } }; /** * APE_HEADER: describes all of the necessary information about the APE file */ const Header = { len: 24, get: (buf, off) => { return { compressionLevel: UINT16_LE.get(buf, off), formatFlags: UINT16_LE.get(buf, off + 2), blocksPerFrame: UINT32_LE.get(buf, off + 4), finalFrameBlocks: UINT32_LE.get(buf, off + 8), totalFrames: UINT32_LE.get(buf, off + 12), bitsPerSample: UINT16_LE.get(buf, off + 16), channel: UINT16_LE.get(buf, off + 18), sampleRate: UINT32_LE.get(buf, off + 20) }; } }; /** * APE Tag Header/Footer Version 2.0 * TAG: describes all the properties of the file [optional] */ const TagFooter = { len: 32, get: (buf, off) => { return { ID: new StringType(8, "ascii").get(buf, off), version: UINT32_LE.get(buf, off + 8), size: UINT32_LE.get(buf, off + 12), fields: UINT32_LE.get(buf, off + 16), flags: parseTagFlags(UINT32_LE.get(buf, off + 20)) }; } }; /** * APE Tag v2.0 Item Header */ const TagItemHeader = { len: 8, get: (buf, off) => { return { size: UINT32_LE.get(buf, off), flags: parseTagFlags(UINT32_LE.get(buf, off + 4)) }; } }; function parseTagFlags(flags) { return { containsHeader: isBitSet(flags, 31), containsFooter: isBitSet(flags, 30), isHeader: isBitSet(flags, 29), readOnly: isBitSet(flags, 0), dataType: (flags & 6) >> 1 }; } /** * @param num {number} * @param bit 0 is least significant bit (LSB) * @return {boolean} true if bit is 1; otherwise false */ function isBitSet(num, bit) { return (num & 1 << bit) !== 0; } //#endregion //#region node_modules/music-metadata/lib/apev2/APEv2Parser.js const debug = (0, import_src.default)("music-metadata:parser:APEv2"); const tagFormat = "APEv2"; const preamble = "APETAGEX"; var ApeContentError = class extends makeUnexpectedFileContentError("APEv2") {}; function tryParseApeHeader(metadata, tokenizer, options) { return new APEv2Parser(metadata, tokenizer, options).tryParseApeHeader(); } var APEv2Parser = class APEv2Parser extends BasicParser { constructor() { super(...arguments); this.ape = {}; } /** * Calculate the media file duration * @param ah ApeHeader * @return {number} duration in seconds */ static calculateDuration(ah) { let duration = ah.totalFrames > 1 ? ah.blocksPerFrame * (ah.totalFrames - 1) : 0; duration += ah.finalFrameBlocks; return duration / ah.sampleRate; } /** * Calculates the APEv1 / APEv2 first field offset * @param tokenizer * @param offset */ static async findApeFooterOffset(tokenizer, offset) { const apeBuf = new Uint8Array(TagFooter.len); const position = tokenizer.position; if (offset <= TagFooter.len) { debug(`Offset is too small to read APE footer: offset=${offset}`); return; } if (offset > TagFooter.len) { await tokenizer.readBuffer(apeBuf, { position: offset - TagFooter.len }); tokenizer.setPosition(position); const tagFooter = TagFooter.get(apeBuf, 0); if (tagFooter.ID === "APETAGEX") { if (tagFooter.flags.isHeader) debug(`APE Header found at offset=${offset - TagFooter.len}`); else { debug(`APE Footer found at offset=${offset - TagFooter.len}`); offset -= tagFooter.size; } return { footer: tagFooter, offset }; } } } static parseTagFooter(metadata, buffer, options) { const footer = TagFooter.get(buffer, buffer.length - TagFooter.len); if (footer.ID !== preamble) throw new ApeContentError("Unexpected APEv2 Footer ID preamble value"); fromBuffer(buffer); return new APEv2Parser(metadata, fromBuffer(buffer), options).parseTags(footer); } /** * Parse APEv1 / APEv2 header if header signature found */ async tryParseApeHeader() { if (this.tokenizer.fileInfo.size && this.tokenizer.fileInfo.size - this.tokenizer.position < TagFooter.len) { debug("No APEv2 header found, end-of-file reached"); return; } const footer = await this.tokenizer.peekToken(TagFooter); if (footer.ID === preamble) { await this.tokenizer.ignore(TagFooter.len); return this.parseTags(footer); } debug(`APEv2 header not found at offset=${this.tokenizer.position}`); if (this.tokenizer.fileInfo.size) { const remaining = this.tokenizer.fileInfo.size - this.tokenizer.position; const buffer = new Uint8Array(remaining); await this.tokenizer.readBuffer(buffer); return APEv2Parser.parseTagFooter(this.metadata, buffer, this.options); } } async parse() { const descriptor = await this.tokenizer.readToken(DescriptorParser); if (descriptor.ID !== "MAC ") throw new ApeContentError("Unexpected descriptor ID"); this.ape.descriptor = descriptor; const lenExp = descriptor.descriptorBytes - DescriptorParser.len; const header = await (lenExp > 0 ? this.parseDescriptorExpansion(lenExp) : this.parseHeader()); this.metadata.setAudioOnly(); await this.tokenizer.ignore(header.forwardBytes); return this.tryParseApeHeader(); } async parseTags(footer) { const keyBuffer = new Uint8Array(256); let bytesRemaining = footer.size - TagFooter.len; debug(`Parse APE tags at offset=${this.tokenizer.position}, size=${bytesRemaining}`); for (let i = 0; i < footer.fields; i++) { if (bytesRemaining < TagItemHeader.len) { this.metadata.addWarning(`APEv2 Tag-header: ${footer.fields - i} items remaining, but no more tag data to read.`); break; } const tagItemHeader = await this.tokenizer.readToken(TagItemHeader); bytesRemaining -= TagItemHeader.len + tagItemHeader.size; await this.tokenizer.peekBuffer(keyBuffer, { length: Math.min(keyBuffer.length, bytesRemaining) }); let zero = findZero(keyBuffer); const key = await this.tokenizer.readToken(new StringType(zero, "ascii")); await this.tokenizer.ignore(1); bytesRemaining -= key.length + 1; switch (tagItemHeader.flags.dataType) { case DataType.text_utf8: { const values = (await this.tokenizer.readToken(new StringType(tagItemHeader.size, "utf8"))).split(/\x00/g); await Promise.all(values.map((val) => this.metadata.addTag(tagFormat, key, val))); break; } case DataType.binary: if (this.options.skipCovers) await this.tokenizer.ignore(tagItemHeader.size); else { const picData = new Uint8Array(tagItemHeader.size); await this.tokenizer.readBuffer(picData); zero = findZero(picData); const description = textDecode(picData.subarray(0, zero), "utf-8"); const data = picData.subarray(zero + 1); await this.metadata.addTag(tagFormat, key, { description, data }); } break; case DataType.external_info: debug(`Ignore external info ${key}`); await this.tokenizer.ignore(tagItemHeader.size); break; case DataType.reserved: debug(`Ignore external info ${key}`); this.metadata.addWarning(`APEv2 header declares a reserved datatype for "${key}"`); await this.tokenizer.ignore(tagItemHeader.size); break; } } } async parseDescriptorExpansion(lenExp) { await this.tokenizer.ignore(lenExp); return this.parseHeader(); } async parseHeader() { const header = await this.tokenizer.readToken(Header); this.metadata.setFormat("lossless", true); this.metadata.setFormat("container", "Monkey's Audio"); this.metadata.setFormat("bitsPerSample", header.bitsPerSample); this.metadata.setFormat("sampleRate", header.sampleRate); this.metadata.setFormat("numberOfChannels", header.channel); this.metadata.setFormat("duration", APEv2Parser.calculateDuration(header)); if (!this.ape.descriptor) throw new ApeContentError("Missing APE descriptor"); return { forwardBytes: this.ape.descriptor.seekTableBytes + this.ape.descriptor.headerDataBytes + this.ape.descriptor.apeFrameDataBytes + this.ape.descriptor.terminatingDataBytes }; } }; //#endregion export { FourCcToken as i, ApeContentError as n, tryParseApeHeader as r, APEv2Parser as t };