yt-dlp support

This commit is contained in:
synt-xerror
2026-03-12 01:22:29 -03:00
parent 10357ea4a7
commit baf10252d5
5435 changed files with 991931 additions and 137 deletions

361
node_modules/node-webpmux/parser.js generated vendored Normal file
View File

@@ -0,0 +1,361 @@
const IO = require('./io.js');
const nullByte = Buffer.alloc(1);
nullByte[0] = 0;
const intfTypes = {
NONE: 0,
FILE: 1,
BUFFER: 2
};
const constants = {
TYPE_LOSSY: 0,
TYPE_LOSSLESS: 1,
TYPE_EXTENDED: 2
};
function VP8Width(data) { return ((data[7] << 8) | data[6]) & 0b0011111111111111; }
function VP8Height(data) { return ((data[9] << 8) | data[8]) & 0b0011111111111111; }
function VP8LWidth(data) { return (((data[2] << 8) | data[1]) & 0b0011111111111111) + 1; }
function VP8LHeight(data) { return ((((data[4] << 16) | (data[3] << 8) | data[2]) >> 6) & 0b0011111111111111) + 1; }
function doesVP8LHaveAlpha(data) { return !!(data[4] & 0b00010000); }
function createBasicChunk(name, data) {
let header = Buffer.alloc(8), size = data.length;
header.write(name, 0);
header.writeUInt32LE(size, 4);
if (size&1) { return { size: size + 9, chunks: [ header, data, nullByte ] }; }
else { return { size: size + 8, chunks: [ header, data ] }; }
}
class WebPReader {
constructor() { this.type = intfTypes.NONE; }
readFile(path) { this.type = intfTypes.FILE; this.path = path; }
readBuffer(buf) { this.type = intfTypes.BUFFER; this.buf = buf; this.cursor = 0; }
async readBytes(n, mod) {
let { type } = this;
if (type == intfTypes.FILE) {
let b = Buffer.alloc(n), br;
br = (await IO.read(this.fp, b, 0, n, undefined)).bytesRead;
return mod ? b : br == n ? b : undefined;
} else if (type == intfTypes.BUFFER) { let b = this.buf.slice(this.cursor, this.cursor + n); this.cursor += n; return b; }
else { throw new Error('Reader not initialized'); }
}
async readFileHeader() {
let buf = await this.readBytes(12);
if (buf === undefined) { throw new Error('Reached end while reading header'); }
if (buf.toString('utf8', 0, 4) != 'RIFF') { throw new Error('Bad header (not RIFF)'); }
if (buf.toString('utf8', 8, 12) != 'WEBP') { throw new Error('Bad header (not WEBP)'); }
return { fileSize: buf.readUInt32LE(4) };
}
async readChunkHeader() {
let buf = await this.readBytes(8, true);
if (buf.length == 0) { return { fourCC: '\x00\x00\x00\x00', size: 0 }; }
else if (buf.length < 8) { throw new Error('Reached end while reading chunk header'); }
return { fourCC: buf.toString('utf8', 0, 4), size: buf.readUInt32LE(4) };
}
async readChunkContents(size) {
let buf = await this.readBytes(size);
if (size & 1) { await this.readBytes(1); }
return buf;
}
async readChunk_raw(n, size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error(`Reached end while reading ${n} chunk`); }
return { raw: buf };
}
async readChunk_VP8(size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error('Reached end while reading VP8 chunk'); }
return { raw: buf, width: VP8Width(buf), height: VP8Height(buf) };
}
async readChunk_VP8L(size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error('Reached end while reading VP8L chunk'); }
return { raw: buf, alpha: doesVP8LHaveAlpha(buf), width: VP8LWidth(buf), height: VP8LHeight(buf) };
}
async readChunk_VP8X(size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error('Reached end while reading VP8X chunk'); }
return {
raw: buf,
hasICCP: !!(buf[0] & 0b00100000),
hasAlpha: !!(buf[0] & 0b00010000),
hasEXIF: !!(buf[0] & 0b00001000),
hasXMP: !!(buf[0] & 0b00000100),
hasAnim: !!(buf[0] & 0b00000010),
width: buf.readUIntLE(4, 3) + 1,
height: buf.readUIntLE(7, 3) + 1
};
}
async readChunk_ANIM(size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error('Reached end while reading ANIM chunk'); }
return { raw: buf, bgColor: buf.slice(0, 4), loops: buf.readUInt16LE(4) };
}
async readChunk_ANMF(size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error('Reached end while reading ANMF chunk'); }
let out = {
raw: buf,
x: buf.readUIntLE(0, 3),
y: buf.readUIntLE(3, 3),
width: buf.readUIntLE(6, 3) + 1,
height: buf.readUIntLE(9, 3) + 1,
delay: buf.readUIntLE(12, 3),
blend: !(buf[15] & 0b00000010),
dispose: !!(buf[15] & 0b00000001)
}, keepLooping = true, anmfReader = new WebPReader();
anmfReader.readBuffer(buf);
anmfReader.cursor = 16;
while (keepLooping) {
let header = await anmfReader.readChunkHeader();
switch (header.fourCC) {
case 'VP8 ':
if (!out.vp8) {
out.type = constants.TYPE_LOSSY;
out.vp8 = await anmfReader.readChunk_VP8(header.size);
if (out.alph) { out.vp8.alpha = true; }
}
break;
case 'VP8L':
if (!out.vp8l) {
out.type = constants.TYPE_LOSSLESS;
out.vp8l = await anmfReader.readChunk_VP8L(header.size);
}
break;
case 'ALPH':
if (!out.alph) {
out.alph = await anmfReader.readChunk_ALPH(header.size);
if (out.vp8) { out.vp8.alpha = true; }
}
break;
case '\x00\x00\x00\x00':
default:
keepLooping = false;
break;
}
if (anmfReader.cursor >= buf.length) { break; }
}
return out;
}
async readChunk_ALPH(size) { return this.readChunk_raw('ALPH', size); }
async readChunk_ICCP(size) { return this.readChunk_raw('ICCP', size); }
async readChunk_EXIF(size) { return this.readChunk_raw('EXIF', size); }
async readChunk_XMP(size) { return this.readChunk_raw('XMP ', size); }
async readChunk_skip(size) {
let buf = await this.readChunkContents(size);
if (buf === undefined) { throw new Error('Reached end while skipping chunk'); }
}
async read() {
if (this.type == intfTypes.FILE) { this.fp = await IO.open(this.path, 'r'); }
let keepLooping = true, first = true, { fileSize } = await this.readFileHeader(), out = {};
while (keepLooping) {
let { fourCC, size } = await this.readChunkHeader();
switch (fourCC) {
case 'VP8 ':
if (!out.vp8) {
out.vp8 = await this.readChunk_VP8(size);
if (out.alph) { out.vp8.alpha = true; }
if (first) { out.type = constants.TYPE_LOSSY; keepLooping = false; }
} else { await this.readChunk_skip(size); }
break;
case 'VP8L':
if (!out.vp8l) {
out.vp8l = await this.readChunk_VP8L(size);
if (first) { out.type = constants.TYPE_LOSSLESS; keepLooping = false; }
} else { await this.readChunk_skip(size); }
break;
case 'VP8X':
if (!out.extended) {
out.type = constants.TYPE_EXTENDED;
out.extended = await this.readChunk_VP8X(size);
} else { await this.readChunk_skip(size); }
break;
case 'ANIM':
if (!out.anim) {
let { raw, bgColor, loops } = await this.readChunk_ANIM(size);
out.anim = {
bgColor: [ bgColor[2], bgColor[1], bgColor[0], bgColor[3] ],
loops,
frames: [],
raw
};
} else { await this.readChunk_skip(size); }
break;
case 'ANMF': out.anim.frames.push(await this.readChunk_ANMF(size)); break;
case 'ALPH':
if (!out.alph) {
out.alph = await this.readChunk_ALPH(size);
if (out.vp8) { out.vp8.alpha = true; }
} else { await this.readChunk_skip(size); }
break;
case 'ICCP':
if (!out.iccp) { out.iccp = await this.readChunk_ICCP(size); }
else { await this.readChunk_skip(size); }
break;
case 'EXIF':
if (!out.exif) { out.exif = await this.readChunk_EXIF(size); }
else { await this.readChunk_skip(size); }
break;
case 'XMP ':
if (!out.xmp) { out.xmp = await this.readChunk_XMP(size); }
else { await this.readChunk_skip(size); }
break;
case '\x00\x00\x00\x00': keepLooping = false; break;
default: await this.readChunk_skip(size); break;
}
first = false;
}
if (this.type == intfTypes.FILE) { await IO.close(this.fp); }
return out;
}
}
class WebPWriter {
constructor() { this.type = intfTypes.NONE; this.chunks = []; this.width = this.height = 0; }
reset() { this.chunks.length = 0; width = 0; height = 0; }
writeFile(path) { this.type = intfTypes.FILE; this.path = path; }
writeBuffer() { this.type = intfTypes.BUFFER; }
async commit() {
let { chunks } = this, size = 4, fp;
if (this.type == intfTypes.NONE) { throw new Error('Writer not initialized'); }
if (chunks.length == 0) { throw new Error('Nothing to write'); }
for (let i = 1, l = chunks.length; i < l; i++) { size += chunks[i].length; }
chunks[0].writeUInt32LE(size, 4);
if (this.type == intfTypes.FILE) {
fp = await IO.open(this.path, 'w');
for (let i = 0, l = chunks.length; i < l; i++) { await IO.write(fp, chunks[i], 0, undefined, undefined); }
await IO.close(fp);
} else { return Buffer.concat(chunks); }
}
writeBytes(...chunks) {
if (this.type == intfTypes.NONE) { throw new Error('Writer not initialized'); }
this.chunks.push(...chunks);
}
writeFileHeader() {
let buf = Buffer.alloc(12);
buf.write('RIFF', 0);
buf.write('WEBP', 8);
this.writeBytes(buf);
}
writeChunk_VP8(vp8) { this.writeBytes(...((createBasicChunk('VP8 ', vp8.raw)).chunks)); }
writeChunk_VP8L(vp8l) { this.writeBytes(...((createBasicChunk('VP8L', vp8l.raw)).chunks)); }
writeChunk_VP8X(vp8x) {
let buf = Buffer.alloc(18);
buf.write('VP8X', 0);
buf.writeUInt32LE(10, 4);
buf.writeUIntLE(vp8x.width - 1, 12, 3);
buf.writeUIntLE(vp8x.height - 1, 15, 3);
if (vp8x.hasICCP) { buf[8] |= 0b00100000; }
if (vp8x.hasAlpha) { buf[8] |= 0b00010000; }
if (vp8x.hasEXIF) { buf[8] |= 0b00001000; }
if (vp8x.hasXMP) { buf[8] |= 0b00000100; }
if (vp8x.hasAnim) { buf[8] |= 0b00000010; }
this.vp8x = buf;
this.writeBytes(buf);
}
updateChunk_VP8X_size(width, height) {
this.vp8x.writeUIntLE(width, 12, 3);
this.vp8x.writeUIntLE(height, 15, 3);
}
writeChunk_ANIM(anim) {
let buf = Buffer.alloc(14);
buf.write('ANIM', 0);
buf.writeUInt32LE(6, 4);
buf.writeUInt8(anim.bgColor[2], 8);
buf.writeUInt8(anim.bgColor[1], 9);
buf.writeUInt8(anim.bgColor[0], 10);
buf.writeUInt8(anim.bgColor[3], 11);
buf.writeUInt16LE(anim.loops, 12);
this.writeBytes(buf);
}
writeChunk_ANMF(anmf) {
let buf = Buffer.alloc(24), { img } = anmf, size = 16, alpha = false;
buf.write('ANMF', 0);
buf.writeUIntLE(anmf.x, 8, 3);
buf.writeUIntLE(anmf.y, 11, 3);
buf.writeUIntLE(anmf.delay, 20, 3);
if (!anmf.blend) { buf[23] |= 0b00000010; }
if (anmf.dispose) { buf[23] |= 0b00000001; }
switch (img.type) {
case constants.TYPE_LOSSY:
{
let b;
this.width = Math.max(this.width, img.vp8.width);
this.height = Math.max(this.height, img.vp8.height);
buf.writeUIntLE(img.vp8.width - 1, 14, 3);
buf.writeUIntLE(img.vp8.height - 1, 17, 3);
this.writeBytes(buf);
if (img.vp8.alpha) {
b = createBasicChunk('ALPH', img.alph.raw);
this.writeBytes(...b.chunks);
size += b.size;
}
b = createBasicChunk('VP8 ', img.vp8.raw);
this.writeBytes(...b.chunks);
size += b.size;
}
break;
case constants.TYPE_LOSSLESS:
{
let b = createBasicChunk('VP8L', img.vp8l.raw);
this.width = Math.max(this.width, img.vp8l.width);
this.height = Math.max(this.height, img.vp8l.height);
buf.writeUIntLE(img.vp8l.width - 1, 14, 3);
buf.writeUIntLE(img.vp8l.height - 1, 17, 3);
if (img.vp8l.alpha) { alpha = true; }
this.writeBytes(buf, ...b.chunks);
size += b.size;
}
break;
case constants.TYPE_EXTENDED:
if (img.extended.hasAnim) {
let fr = img.anim.frames;
if (img.extended.hasAlpha) { alpha = true; }
for (let i = 0, l = fr.length; i < l; i++) {
let b = Buffer.alloc(8), c = fr[i].raw;
this.width = Math.max(this.width, fr[i].width + anmf.x);
this.height = Math.max(this.height, fr[i].height + anmf.y);
b.write('ANMF', 0);
b.writeUInt32LE(c.length, 4);
c.writeUIntLE(anmf.x, 0, 3);
c.writeUIntLE(anmf.y, 3, 3);
c.writeUIntLE(anmf.delay, 12, 3);
if (!anmf.blend) { c[15] |= 0b00000010; } else { c[15] &= 0b11111101; }
if (anmf.dispose) { c[15] |= 0b00000001; } else { c[15] &= 0b11111110; }
this.writeBytes(b, c);
if (c.length & 1) { this.writeBytes(nullByte); }
}
} else {
let b;
this.width = Math.max(this.width, img.extended.width);
this.height = Math.max(this.height, img.extended.height);
if (img.vp8) {
buf.writeUIntLE(img.vp8.width - 1, 14, 3);
buf.writeUIntLE(img.vp8.height - 1, 17, 3);
this.writeBytes(buf);
if (img.alph) {
b = createBasicChunk('ALPH', img.alph.raw);
alpha = true;
this.writeBytes(...b.chunks);
size += b.size;
}
b = createBasicChunk('VP8 ', img.vp8.raw);
this.writeBytes(...b.chunks);
size += b.size;
} else if (img.vp8l) {
buf.writeUIntLE(img.vp8l.width - 1, 14, 3);
buf.writeUIntLE(img.vp8l.height - 1, 17, 3);
if (img.vp8l.alpha) { alpha = true; }
b = createBasicChunk('VP8L', img.vp8l.raw);
this.writeBytes(buf, ...b.chunks);
size += b.size;
}
}
break;
default: throw new Error('Unknown image type');
}
buf.writeUInt32LE(size, 4);
if (alpha) { this.vp8x[8] |= 0b00010000; }
}
writeChunk_ALPH(alph) { this.writeBytes(...((createBasicChunk('ALPH', alph.raw)).chunks)); }
writeChunk_ICCP(iccp) { this.writeBytes(...((createBasicChunk('ICCP', iccp.raw)).chunks)); }
writeChunk_EXIF(exif) { this.writeBytes(...((createBasicChunk('EXIF', exif.raw)).chunks)); }
writeChunk_XMP(xmp) { this.writeBytes(...((createBasicChunk('XMP ', xmp.raw)).chunks)); }
}
module.exports = { WebPReader, WebPWriter };