UNPKG

taglib-wasm

Version:

TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps

129 lines (128 loc) 4.43 kB
function be32(b, offset) { return (b[offset] << 24 | b[offset + 1] << 16 | b[offset + 2] << 8 | b[offset + 3]) >>> 0; } function startsWith(bytes, magic, at = 0) { if (bytes.length < at + magic.length) return false; for (let i = 0; i < magic.length; i++) { if (bytes[at + i] !== magic.charCodeAt(i)) return false; } return true; } function id3v2End(bytes) { if (bytes.length < 10) return void 0; const size = bytes[6] << 21 | bytes[7] << 14 | bytes[8] << 7 | bytes[9]; const hasFooter = (bytes[5] & 16) !== 0; return 10 + size + (hasFooter ? 10 : 0); } function flacEnd(bytes, limit, start) { let offset = start + 4; for (; ; ) { if (offset + 4 > bytes.length) return void 0; const isLast = (bytes[offset] & 128) !== 0; const length = bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3]; offset += 4 + length; if (offset > limit) return offset; if (isLast) return offset; } } function mp4MoovEnd(bytes, limit, start) { let offset = start; const readable = Math.min(bytes.length, limit); while (offset + 8 <= readable) { const size = be32(bytes, offset); const type = String.fromCharCode( bytes[offset + 4], bytes[offset + 5], bytes[offset + 6], bytes[offset + 7] ); if (size < 8) return void 0; if (type === "moov") return offset + size; offset += size; } return void 0; } function oggMetadataEnd(bytes, limit, start) { let offset = start; let packetsCompleted = 0; let first = true; for (; ; ) { if (offset + 27 > bytes.length || !startsWith(bytes, "OggS", offset)) { return void 0; } const segmentCount = bytes[offset + 26]; const tableStart = offset + 27; if (tableStart + segmentCount > bytes.length) return void 0; let payload = 0; for (let i = 0; i < segmentCount; i++) { const segment = bytes[tableStart + i]; payload += segment; if (segment < 255) packetsCompleted++; } if (first) { const payloadStart = tableStart + segmentCount; if (payloadStart + 5 > bytes.length) return void 0; if (bytes[payloadStart] === 127 && startsWith(bytes, "FLAC", payloadStart + 1)) { return void 0; } first = false; } offset = tableStart + segmentCount + payload; if (packetsCompleted >= 2 || offset > limit) return offset; } } function isMpegFrameSync(bytes, at = 0) { if (bytes.length < at + 4) return false; if (bytes[at] !== 255 || (bytes[at + 1] & 224) !== 224) return false; const version = bytes[at + 1] >> 3 & 3; const layer = bytes[at + 1] >> 1 & 3; if (version === 1 || layer === 0) return false; const bitrateIndex = bytes[at + 2] >> 4 & 15; const sampleRateIndex = bytes[at + 2] >> 2 & 3; return bitrateIndex !== 0 && bitrateIndex !== 15 && sampleRateIndex !== 3; } function containerEnd(bytes, at, limit) { if (at >= bytes.length) return bytes.length < limit ? null : void 0; if (at + 8 > bytes.length) return void 0; if (startsWith(bytes, "fLaC", at)) return flacEnd(bytes, limit, at); if (startsWith(bytes, "ftyp", at + 4)) return mp4MoovEnd(bytes, limit, at); if (startsWith(bytes, "OggS", at)) return oggMetadataEnd(bytes, limit, at); return null; } function metadataFitsInHeader(header, limit) { let end; if (startsWith(header, "ID3")) { const tagEnd = id3v2End(header); if (tagEnd === void 0) return false; end = tagEnd; if (tagEnd <= limit) { const behind = containerEnd(header, tagEnd, limit); if (behind === void 0) return false; if (behind !== null) end = behind; } } else { const container = containerEnd(header, 0, limit); if (container === null) { end = isMpegFrameSync(header) ? 0 : void 0; } else { end = container; } } if (end === void 0 || end > header.length) return false; return end <= limit; } function trailerFitsInFooter(tail, footerSize) { if (tail.length < Math.min(footerSize, 32)) return false; for (const offsetFromEnd of [32, 32 + 128]) { const at = tail.length - offsetFromEnd; if (at < 0 || !startsWith(tail, "APETAGEX", at)) continue; const size = (tail[at + 12] | tail[at + 13] << 8 | tail[at + 14] << 16 | tail[at + 15] << 24) >>> 0; const trailing = offsetFromEnd === 32 ? 0 : 128; if (size + 32 + trailing > footerSize) return false; } return true; } export { metadataFitsInHeader, trailerFitsInFooter };