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
JavaScript
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
};