yt-dlp support
This commit is contained in:
315
node_modules/node-webpmux/bin/webpmux
generated
vendored
Normal file
315
node_modules/node-webpmux/bin/webpmux
generated
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const WebP = require('../webp.js');
|
||||
const intTest = /^[0-9]+$/;
|
||||
|
||||
function parseDuration(d) {
|
||||
let a = d.split(',');
|
||||
if (a.length == 1) { return { dur: a[0], start: 0, end: 0 }; }
|
||||
if (a.length == 2) { return { dur: a[0], start: a[1], end: a[1] }; }
|
||||
if (a.length == 3) { return { dur: a[0], start: a[1], end: a[2] }; }
|
||||
throw new Error('Failed to parse duration');
|
||||
}
|
||||
function parseFrame(f) {
|
||||
let out = {}, a = f.split('+');
|
||||
if (a.length < 2) { throw new Error('Failed to parse frame setting shorthand'); }
|
||||
out.duration = a[1];
|
||||
out.x = a[2];
|
||||
out.y = a[3];
|
||||
if (a[4] == 1) { out.dispose = true; }
|
||||
else if (a[4] == 0) { out.dispose = false; }
|
||||
else if (a[4] !== undefined) {
|
||||
let x = a[4].split('-');
|
||||
if (x[0] == 1) { out.dispose = true; }
|
||||
else if (x[0] == 0) { out.dispose = false; }
|
||||
if (x[1] == 'b') { out.blend = false; }
|
||||
}
|
||||
if (a[5] == 'b') { out.blend = true; }
|
||||
return out;
|
||||
}
|
||||
function parseCmdLine(args) {
|
||||
let state = {}, tester = /^-/;
|
||||
let test = (_i) => {
|
||||
let i = _i+1;
|
||||
if (i >= args.length) { return false; }
|
||||
else if (tester.test(args[i])) { return false; }
|
||||
return true;
|
||||
};
|
||||
for (let i = 0, l = args.length; i < l; i++) {
|
||||
switch (args[i]) {
|
||||
case '-get':
|
||||
if (!test(i)) { throw new Error('GET_OPTS missing argument'); }
|
||||
state.get = { what: args[++i] };
|
||||
switch (state.get.what) {
|
||||
case 'icc': case 'iccp': state.get.what = 'iccp'; break;
|
||||
case 'exif': case 'xmp': break;
|
||||
case 'frame':
|
||||
if (!test(i)) { throw new Error('GET_OPTS frame missing argument'); }
|
||||
state.get.frame = args[++i];
|
||||
break;
|
||||
default: throw new Error(`Unknown GET_OPTS ${state.set.what}`);
|
||||
}
|
||||
break;
|
||||
case '-set':
|
||||
if (!test(i)) { throw new Error('SET_OPTS missing argument'); }
|
||||
state.set = { what: args[++i] };
|
||||
switch (state.set.what) {
|
||||
case 'loop': if (!test(i)) { throw new Error('SET_OPTS loop missing argument'); } state.set.loop = args[++i]; break;
|
||||
case 'iccp': case 'icc': if (!test(i)) { throw new Error(`SET_OPTS ${state.set.what} missing argument`); } state.set.what = 'iccp'; state.set.iccp = args[++i]; break;
|
||||
case 'exif': if (!test(i)) { throw new Error('SET_OPTS exif missing argument'); } state.set.exif = args[++i]; break;
|
||||
case 'xmp': if (!test(i)) { throw new Error('SET_OPTS xmp missing argument'); } state.set.xmp = args[++i]; break;
|
||||
default: throw new Error(`Unknown SET_OPTS ${state.set.what}`);
|
||||
}
|
||||
break;
|
||||
case '-strip': if (!test(i)) { throw new Error('STRIP_OPTS missing argument'); } state.strip = args[++i]; break;
|
||||
case '-duration': if (!test(i)) { throw new Error('DUR_OPTS missing argument'); } if (!state.duration) { state.duration = []; } state.duration.push(parseDuration(args[++i])); break;
|
||||
case '-frame':
|
||||
{
|
||||
let f = {};
|
||||
if (!test(i)) { throw new Error('FRAME_OPTS missing argument'); }
|
||||
if (!state.frames) { state.frames = []; }
|
||||
f.path = args[++i];
|
||||
if (!/\.webp$/i.test(f.path)) { throw new Error('First argument to -frame must be a webp image'); }
|
||||
if (!test(i)) { throw new Error('Missing arguments in -frame'); }
|
||||
if (args[i+1][0] == '+') { f.bin = parseFrame(args[++i]); }
|
||||
else {
|
||||
let ni;
|
||||
for (let x = i+1, xl = l; x < xl; x++) {
|
||||
switch (args[x]) {
|
||||
case 'duration': if (!test(x)) { throw new Error('FRAME_OPTS duration missing argument'); } f.duration = args[++x]; break;
|
||||
case 'x': if (!test(x)) { throw new Error('FRAME_OPTS x missing argument'); } f.x = args[++x]; break;
|
||||
case 'y': if (!test(x)) { throw new Error('FRAME_OPTS y missing argument'); } f.y = args[++x]; break;
|
||||
case 'dispose': if (!test(x)) { throw new Error('FRAME_OPTS dispose missing argument'); } f.dispose = args[++x]; break;
|
||||
case 'blend': if (!test(x)) { throw new Error('FRAME_OPTS blend missing argument'); } f.blend = args[++x]; break;
|
||||
default: ni = x-1; xl = x; break;
|
||||
}
|
||||
}
|
||||
i = ni;
|
||||
}
|
||||
state.frames.push(f);
|
||||
}
|
||||
break;
|
||||
case '-info': state.info = true; break;
|
||||
case '-h': case '-help': state.help = true; break;
|
||||
case '-version': state.version = true; break;
|
||||
case '-o': if (!test(i)) { throw new Error('OUT missing argument'); } state.out = args[++i]; break;
|
||||
case '-loop': if (!test(i)) { throw new Error('COUNT missing argument'); } state.loop = args[++i]; break;
|
||||
case '-bg': if (!test(i)) { throw new Error('COLOR missing argument'); } state.bg = args[++i].split(','); break;
|
||||
default: if (!state.in) { state.in = args[i]; } else { throw new Error(`Unknown flag ${args[i]}`); }
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
function printHelp() {
|
||||
console.log(`Usage: webpmux -get GET_OPTS IN -o OUT
|
||||
webpmux -set SET_OPTS IN -o OUT
|
||||
webpmux -strip STRIP_OPTS IN -o OUT
|
||||
webpmux -duration DUR_OPTS [-duration ...] IN -o OUT
|
||||
webpmux -frame FRAME_OPTS [-frame ...] [-loop COUNT] [-bg COLOR] -o OUT
|
||||
webpmux -info IN
|
||||
webpmux [-h|-help]
|
||||
webpmux -version
|
||||
|
||||
GET_OPTS:
|
||||
Extract the relevant data:
|
||||
iccp get ICC profile
|
||||
icc get ICC profile (backwards support)
|
||||
exif get EXIF metadata
|
||||
xmp get XMP metadata
|
||||
frame n get nth frame (first frame is frame 1)
|
||||
|
||||
SET_OPTS:
|
||||
Set color profile/metadata:
|
||||
loop COUNT set the loop count
|
||||
iccp file.iccp set the ICC profile
|
||||
icc file.icc set the ICC profile (backwards support)
|
||||
exif file.exif set the EXIF metadata
|
||||
xmp file.xmp set the XMP metadata
|
||||
where: 'file.icc'/'file.iccp' contains the ICC profile to be set.
|
||||
'file.exif' contains the EXIF metadata to be set.
|
||||
'file.xmp' contains the XMP metadata to be set.
|
||||
|
||||
DUR_OPTS:
|
||||
Set duration of selected frames
|
||||
duration set duration for each frame
|
||||
duration,frame set duration of a particular frame
|
||||
duration,start,end set duration of frames in the
|
||||
interval [start, end]
|
||||
where: 'duration' is the duration in milliseconds.
|
||||
'start' is the start frame index.
|
||||
'end' is the inclusive end frame index.
|
||||
The special 'end' value '0' means: last frame.
|
||||
|
||||
STRIP_OPTS:
|
||||
Strip color profile/metadata:
|
||||
iccp strip ICC profile
|
||||
icc strip ICC profile (for backwards support)
|
||||
exif strip EXIF metadata
|
||||
xmp strip XMP metadata
|
||||
|
||||
FRAME_OPTS:
|
||||
Create an animation frame:
|
||||
frame.webp the animation frame
|
||||
WEBPMUX_FRAMES legacy frame settings
|
||||
OR
|
||||
frame.webp the animation frame
|
||||
duration N the pause duration before next frame
|
||||
x X the x offset for this frame
|
||||
y Y the y offset for this frame
|
||||
dispose on/off dispose method for this frame (on: background, off: none)
|
||||
blend on/off blending method for this frame
|
||||
|
||||
COUNT:
|
||||
Number of times to repeat the animation.
|
||||
Valid range is 0 to 65535 [Default: 0 (infinite)]
|
||||
|
||||
COLOR:
|
||||
Background color of the animation canvas.
|
||||
R,G,B,A ('normal' mode)
|
||||
A,R,G,B ('legacy' mode)
|
||||
where: 'A', 'R', 'G', and 'B' are integers in the range 0 to 255 specifying
|
||||
the Alpha, Red, Green, and Blue component values respectively
|
||||
[Default: 255, 255, 255, 255]
|
||||
|
||||
WEBPMUX_FRAMES (for drop-in support for the upstream webpmux binary, puts it into 'legacy' mode):
|
||||
+d[+x+y[+m[+b]]]
|
||||
where: 'd' is the pause duration before next frame
|
||||
'x', 'y' specify the image offset for this frame
|
||||
'm' is the dispose method for this frame (0 or 1)
|
||||
'b' is the blending method for this frame (+b or -b)
|
||||
|
||||
IN & OUT are in WebP format.
|
||||
|
||||
Note: The nature of EXIF, XMP, and ICC data is not checked and is assumed to be valid.`);
|
||||
}
|
||||
function printInfo(img) {
|
||||
let f = [];
|
||||
let pad = (s, n) => { let o = `${s}`; while (o.length < n) { o = ` ${o}`; } return o; };
|
||||
let fra = (fr) => { return fr.vp8 ? fr.vp8.alpha : fr.vp8l ? fr.vp8l.alpha : false; };
|
||||
let bgcol = (c) => { return `0x${c[0].toString(16)}${c[0].toString(16)}${c[1].toString(16)}${c[2].toString(16)}${c[3].toString(16)}`.toUpperCase(); }
|
||||
console.log(`Canvas size: ${img.width} x ${img.height}`);
|
||||
if (img.hasAnim) { f.push('animation'); }
|
||||
if (img.hasAlpha) { f.push(!img.hasAnim ? 'transparency' : 'alpha'); }
|
||||
if (f.length == 0) { console.log('No features present.'); }
|
||||
else { console.log(`Features present: ${f.join(' ')}`); }
|
||||
if (img.hasAnim) {
|
||||
console.log(`Background color : ${bgcol(img.anim.bgColor)} Loop Count : ${img.anim.loops}`);
|
||||
console.log(`Number of frames: ${img.frames.length}`);
|
||||
console.log('No.: width height alpha x_offset y_offset duration dispose blend image_size compression');
|
||||
for (let i = 0, fr = img.frames, l = fr.length; i < l; i++) {
|
||||
let out = '';
|
||||
out += `${pad(i+1, 3)}: ${pad(fr[i].width, 5)} ${pad(fr[i].height, 5)} ${fra(fr[i]) ? 'yes' : ' no'} `;
|
||||
out += `${pad(fr[i].x, 8)} ${pad(fr[i].y, 8)} ${pad(fr[i].delay, 8)} ${pad(fr[i].dispose ? 'background' : 'none', 10)} `;
|
||||
out += `${pad(fr[i].blend ? 'yes' : 'no', 5)} ${pad(fr[i].alph ? fr[i].raw.length+14 : fr[i].raw.length-4, 10)} `;
|
||||
out += `${pad(fr[i].vp8 ? 'lossy' : 'lossless', 11)}`;
|
||||
console.log(out);
|
||||
}
|
||||
} else {
|
||||
let size = (fs.statSync(img.path)).size;
|
||||
if (img.hasAlpha) { console.log(`Size of the image (with alpha): ${size}`); }
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
let state = parseCmdLine(process.argv.slice(2)), img = new WebP.Image(), d;
|
||||
if (state.help) { printHelp(); }
|
||||
else if (state.version) { console.log(`node-webpmux ${JSON.parse(fs.readFileSync(`${__dirname}/../package.json`)).version}`); }
|
||||
else if (state.get) {
|
||||
if (!state.in) { console.log('Missing input file'); return; }
|
||||
if (!state.out) { console.log('Missing output file'); return; }
|
||||
try { await img.load(state.in); }
|
||||
catch (e) { console.log(`Error opening ${state.in}`); return; }
|
||||
switch (state.get.what) {
|
||||
case 'iccp': d = img.iccp; break;
|
||||
case 'exif': d = img.exif; break;
|
||||
case 'xmp': d = img.xmp; break;
|
||||
case 'frame': d = (await img.demuxToBuffers({ frame: d.frame-1 }))[0]; break;
|
||||
}
|
||||
fs.writeFileSync(state.out, d);
|
||||
}
|
||||
else if (state.set) {
|
||||
if (!state.in) { console.log('Missing input file'); return; }
|
||||
if (!state.out) { console.log('Missing output file'); return; }
|
||||
try { await img.load(state.in); }
|
||||
catch (e) { console.log(`Error opening ${state.in}`); return; }
|
||||
switch (state.set.what) {
|
||||
case 'loop':
|
||||
if (!img.hasAnim) { console.log("Image isn't an animation; cannot set loop count"); return; }
|
||||
if (!intTest(state.set.loop)) { console.log('Loop count must be a number 0 <= n <= 65535'); return; }
|
||||
if ((state.set.loop < 0) || (state.set.loop >= 65536)) { console.log('Loop count must be a number 0 <= n <= 65535'); return; }
|
||||
img.anim.loops = state.set.loop;
|
||||
try { await img.save(state.out); }
|
||||
catch (e) { console.log(e); }
|
||||
break;
|
||||
case 'iccp':
|
||||
case 'exif':
|
||||
case 'xmp':
|
||||
try { d = fs.readFileSync(state.set[state.set.what]); }
|
||||
catch (e) { console.log(`Could not open/read ${state.set[state.set.what]}`); return; }
|
||||
img[state.set.what] = d;
|
||||
try { await img.save(state.out); }
|
||||
catch (e) { console.log(e); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (state.strip) {
|
||||
if (!state.in) { console.log('Missing input file'); return; }
|
||||
if (!state.out) { console.log('Missing output file'); return; }
|
||||
try { await img.load(state.in); }
|
||||
catch (e) { console.log(`Error opening ${state.in}`); return; }
|
||||
img[state.strip.what] = undefined;
|
||||
try { await img.save(state.out); }
|
||||
catch (e) { console.log(e); }
|
||||
}
|
||||
else if (state.duration) {
|
||||
if (!state.in) { console.log('Missing input file'); return; }
|
||||
if (!state.out) { console.log('Missing output file'); return; }
|
||||
try { await img.load(state.in); }
|
||||
catch (e) { console.log(`Error opening ${state.in}`); return; }
|
||||
if (!img.hasAnim) { console.log("Image isn't an animation; cannot set frame durations"); return; }
|
||||
for (let i = 0, dur = state.duration, l = dur.length; i < l; i++) {
|
||||
if (!intTest(dur.dur)) { console.log('Duration must be a number'); return; }
|
||||
if (!intTest(dur.start)) { console.log('Start frame must be a number'); return; }
|
||||
if (!intTest(dur.end)) { console.log('End grame must be a number'); return; }
|
||||
if (dur.end == 0) { dur.end = img.frames.length-1; }
|
||||
if (dur.end >= img.frames.length) { console.log('Warning: End frame beyond frame count; clipping'); dur.end = img.frames.length-1; }
|
||||
if (dur.start >= img.frames.length) { console.log('Warning: Start frame beyond frame count; clipping'); dur.start = img.frames.length-1; }
|
||||
for (let x = dur.start, xl = dur.end; x <= xl; x++) { img.frames[x].delay = dur.dur; }
|
||||
}
|
||||
try { await img.save(state.out); }
|
||||
catch (e) { console.log(e); }
|
||||
}
|
||||
else if (state.frames) {
|
||||
let bin = false;
|
||||
if (!state.out) { console.log('Missing output file'); return; }
|
||||
img = await WebP.Image.getEmptyImage();
|
||||
img.convertToAnim();
|
||||
for (let i = 0, f = state.frames, l = f.length; i < l; i++) {
|
||||
if (f[i].bin) { bin = true; d = f[i].bin; }
|
||||
else { d = f[i]; }
|
||||
d = WebP.Image.generateFrame({
|
||||
path: f[i].path,
|
||||
x: d.x,
|
||||
y: d.y,
|
||||
delay: d.duration,
|
||||
dispose: d.dispose,
|
||||
blend: d.blend
|
||||
});
|
||||
img.frames.push(d);
|
||||
}
|
||||
if (state.loop !== undefined) { img.anim.loops = state.loop; }
|
||||
if (state.bg !== undefined) {
|
||||
if (bin) { img.anim.bgColor = [ state.bg[3], state.bg[0], state.bg[1], state.bg[2] ]; }
|
||||
else { img.anim.bgColor = [ state.bg[0], state.bg[1], state.bg[2], state.bg[3] ]; }
|
||||
}
|
||||
try { await img.save(state.out); }
|
||||
catch (e) { console.log(e); }
|
||||
}
|
||||
else if (state.info) {
|
||||
if (!state.in) { console.log('Missing input file'); return; }
|
||||
try { await img.load(state.in); }
|
||||
catch (e) { console.log(`Error opening ${state.in}`); return; }
|
||||
printInfo(img);
|
||||
} else { printHelp(); }
|
||||
}
|
||||
main().then(()=>{});
|
||||
Reference in New Issue
Block a user