update logos, and image decoder settings in config
This commit is contained in:
@@ -1338,28 +1338,49 @@ async function sendToClient(socket, headers_obj, data = null) {
|
|||||||
delete headers_obj['minisrv-no-last-modified'];
|
delete headers_obj['minisrv-no-last-modified'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minisrv_config.config.decode_unsupported_images) {
|
if (minisrv_config.config.image_decoder && minisrv_config.config.image_decoder.enabled) {
|
||||||
const contype_key = wtvshared.getCaseInsensitiveKey('content-type', headers_obj);
|
const contype_key = wtvshared.getCaseInsensitiveKey('content-type', headers_obj);
|
||||||
if (contype_key) {
|
if (contype_key) {
|
||||||
if (headers_obj[contype_key].toLowerCase() === "image/png" ||
|
if (minisrv_config.config.image_decoder.image_formats && minisrv_config.config.image_decoder.image_formats.includes(headers_obj[contype_key].toLowerCase())) {
|
||||||
headers_obj[contype_key].toLowerCase() === "image/svg+xml" ||
|
|
||||||
headers_obj[contype_key].toLowerCase() === "image/avif" ||
|
|
||||||
headers_obj[contype_key].toLowerCase() === "image/tiff" ||
|
|
||||||
headers_obj[contype_key].toLowerCase() === "image/webp") {
|
|
||||||
const convertOpts = {
|
const convertOpts = {
|
||||||
jpegQuality: minisrv_config.config.decode_unsupported_images_quality,
|
jpegQuality: minisrv_config.config.image_decoder.jpg_quality,
|
||||||
type: 'ALF'
|
type: 'ALF'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (minisrv_config.config.image_decoder.max_height > 0) convertOpts.maxHeight = minisrv_config.config.image_decoder.max_height;
|
||||||
|
if (minisrv_config.config.image_decoder.max_width > 0) convertOpts.maxWidth = minisrv_config.config.image_decoder.max_width;
|
||||||
|
|
||||||
const sourceData = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
const sourceData = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
||||||
try {
|
try {
|
||||||
const converted = await WTVImage.ImageToWebTV(sourceData, convertOpts);
|
const converted = await WTVImage.ImageToWebTV(sourceData, convertOpts);
|
||||||
data = converted.data;
|
data = converted.data;
|
||||||
content_length = data.length;
|
content_length = data.length;
|
||||||
headers_obj[contype_key] = (converted.mime === 'image/jpeg') ? 'image/jpeg' : 'image/gif';
|
var i=0;
|
||||||
|
while (content_length > minisrv_config.config.image_decoder.max_size && converted.mime === 'image/jpeg') {
|
||||||
|
// Image is too big, try to reduce quality
|
||||||
|
if (i < minisrv_config.config.image_decoder.max_quality_tries) {
|
||||||
|
convertOpts.jpegQuality -= minisrv_config.config.image_decoder.jpeg_interval;
|
||||||
|
var converted2 = await WTVImage.ImageToWebTV(sourceData, convertOpts);
|
||||||
|
data = converted2.data;
|
||||||
|
content_length = data.length;
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (content_length > minisrv_config.config.image_decoder.max_size) {
|
||||||
|
headers_obj = {
|
||||||
|
"Status": `400 ${minisrv_config.config.service_name} ran into a technical problem. (Image too large)`,
|
||||||
|
"Content-type": "text/html"
|
||||||
|
}
|
||||||
|
data = "";
|
||||||
|
} else {
|
||||||
|
headers_obj[contype_key] = (converted.mime === 'image/jpeg') ? 'image/jpeg' : 'image/gif';
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error converting image for client:", e);
|
console.error("Error converting image for client:", e);
|
||||||
headers_obj = {
|
headers_obj = {
|
||||||
"Status": `400 ${minisrv_config.config.service_name} ran into a technical problem. (Image not supported)`,
|
"Status": `400 ${minisrv_config.config.service_name} ran into a technical problem. (Image not supported by backend, it may be corrupt)`,
|
||||||
"Content-type": "text/html"
|
"Content-type": "text/html"
|
||||||
}
|
}
|
||||||
data = "";
|
data = "";
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 11 KiB |
@@ -23,6 +23,8 @@
|
|||||||
*
|
*
|
||||||
* Decoder reverse-engineered from:
|
* Decoder reverse-engineered from:
|
||||||
* https://gist.github.com/PajamaFrix/399c0785c5bb3b1d80757e84a0c1d6ab
|
* https://gist.github.com/PajamaFrix/399c0785c5bb3b1d80757e84a0c1d6ab
|
||||||
|
*
|
||||||
|
* TODO: Fix ALP Generation (decoding works but encoding does not yet produce correct ALP files)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
@@ -958,10 +960,23 @@ class WTVImage {
|
|||||||
* @param {number} [opts.colors=256] - palette size for full-color quantization
|
* @param {number} [opts.colors=256] - palette size for full-color quantization
|
||||||
* @param {'ALP'|'ALF'} [opts.type='ALF'] - Artemis variant
|
* @param {'ALP'|'ALF'} [opts.type='ALF'] - Artemis variant
|
||||||
* @param {number} [opts.jpegQuality=85] - JPEG quality (0-100) when no alpha
|
* @param {number} [opts.jpegQuality=85] - JPEG quality (0-100) when no alpha
|
||||||
|
* @param {number} [opts.maxWidth] - maximum width to scale to before encoding
|
||||||
|
* @param {number} [opts.maxHeight] - maximum height to scale to before encoding
|
||||||
* @returns {Promise<{ data: Buffer, mime: string }>}
|
* @returns {Promise<{ data: Buffer, mime: string }>}
|
||||||
*/
|
*/
|
||||||
async ImageToWebTV(input, opts = {}) {
|
async ImageToWebTV(input, opts = {}) {
|
||||||
const pngBuf = Buffer.isBuffer(input) ? input : require('fs').readFileSync(input);
|
let pngBuf = Buffer.isBuffer(input) ? input : require('fs').readFileSync(input);
|
||||||
|
const maxWidth = Number(opts.maxWidth) > 0 ? Number(opts.maxWidth) : null;
|
||||||
|
const maxHeight = Number(opts.maxHeight) > 0 ? Number(opts.maxHeight) : null;
|
||||||
|
if (maxWidth || maxHeight) {
|
||||||
|
const resizeOpts = { fit: 'inside', withoutEnlargement: true };
|
||||||
|
if (maxWidth) resizeOpts.width = maxWidth;
|
||||||
|
if (maxHeight) resizeOpts.height = maxHeight;
|
||||||
|
pngBuf = await sharp(pngBuf)
|
||||||
|
.resize(resizeOpts)
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
}
|
||||||
const meta = await sharp(pngBuf).metadata();
|
const meta = await sharp(pngBuf).metadata();
|
||||||
let usesAlpha = false;
|
let usesAlpha = false;
|
||||||
|
|
||||||
@@ -989,8 +1004,10 @@ class WTVImage {
|
|||||||
|
|
||||||
if (this.isPalettePNG(pngBuf)) {
|
if (this.isPalettePNG(pngBuf)) {
|
||||||
// Palette/indexed PNGs should preserve palette + tRNS alpha exactly by default.
|
// Palette/indexed PNGs should preserve palette + tRNS alpha exactly by default.
|
||||||
// Allow forcing re-quantization only when explicitly requested.
|
// If resizing was applied, the palette is no longer preserved and we must
|
||||||
const data = opts.forceRequantizePalette
|
// re-quantize the image before producing an Artemis GIF.
|
||||||
|
const forceRequantize = opts.forceRequantizePalette || maxWidth || maxHeight;
|
||||||
|
const data = forceRequantize
|
||||||
? await this.encodeArtemisGIF(pngBuf, opts)
|
? await this.encodeArtemisGIF(pngBuf, opts)
|
||||||
: await this.paletteImageToArtemisGIF(pngBuf, opts);
|
: await this.paletteImageToArtemisGIF(pngBuf, opts);
|
||||||
return { data, mime: 'image/gif' };
|
return { data, mime: 'image/gif' };
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
"cgi_enabled": false, // Disable CGI by default
|
"cgi_enabled": false, // Disable CGI by default
|
||||||
"php_enabled": false, // Disable PHP by default
|
"php_enabled": false, // Disable PHP by default
|
||||||
"php_binpath": "php-cgi",
|
"php_binpath": "php-cgi",
|
||||||
"decode_unsupported_images": true, // Attempt to decode images WebTV doesn't support into JPG/ALF/GIF
|
|
||||||
"decode_unsupported_images_quality": 75, // JPEG quality for decoded PNGs, 0-100 lower is worse quality but smaller files.
|
|
||||||
"SessionStore": "SessionStore", // Where we store account (session) data. Best left unchanged.
|
"SessionStore": "SessionStore", // Where we store account (session) data. Best left unchanged.
|
||||||
"SharedROMCache": "SharedROMCache", // Shared ROMCache (wtv-service:/ROMCache/, where wtv-service is any configured service). Found under service vault. Best left unchanged.
|
"SharedROMCache": "SharedROMCache", // Shared ROMCache (wtv-service:/ROMCache/, where wtv-service is any configured service). Found under service vault. Best left unchanged.
|
||||||
"enable_shared_romcache": true, // Disabling this will cause a lot of problems without manual intervention. Best left unchanged.
|
"enable_shared_romcache": true, // Disabling this will cause a lot of problems without manual intervention. Best left unchanged.
|
||||||
@@ -112,7 +110,23 @@
|
|||||||
Each level of shenanigans includes the previous level (eg 5 will also disable filters like 4)
|
Each level of shenanigans includes the previous level (eg 5 will also disable filters like 4)
|
||||||
See WTVShenanigans.js for more info.
|
See WTVShenanigans.js for more info.
|
||||||
*/
|
*/
|
||||||
"shenanigans": false
|
"shenanigans": false,
|
||||||
|
"image_decoder": {
|
||||||
|
"enabled": true,
|
||||||
|
"jpg_quality": 75,
|
||||||
|
"image_formats": [
|
||||||
|
"image/png",
|
||||||
|
"image/svg+xml",
|
||||||
|
"image/avif",
|
||||||
|
"image/tiff",
|
||||||
|
"image/webp"
|
||||||
|
],
|
||||||
|
"max_height": 0,
|
||||||
|
"max_width": 640,
|
||||||
|
"max_file_size": 524288,
|
||||||
|
"jpeg_interval": 5, // lower quality by this amount to try to lower filesize
|
||||||
|
"max_quality_tries": 5 // try to decode up to this many times, reducing quality each time, until the file is under the max_file_size. After this many tries, it will error out rather than sending an oversized file to the client.
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
// service definitions
|
// service definitions
|
||||||
@@ -218,7 +232,7 @@
|
|||||||
"flags": "0x00000001",
|
"flags": "0x00000001",
|
||||||
"privileged": true,
|
"privileged": true,
|
||||||
"send_tellyscripts": true, // Best left untouched
|
"send_tellyscripts": true, // Best left untouched
|
||||||
"send_tellyscript_to_mame": false,
|
"send_tellyscript_to_mame": true,
|
||||||
"dialin_number": 18006138199,
|
"dialin_number": 18006138199,
|
||||||
"dns1ip": "10.0.0.50",
|
"dns1ip": "10.0.0.50",
|
||||||
"dns2ip": "8.8.8.8",
|
"dns2ip": "8.8.8.8",
|
||||||
|
|||||||
207
zefie_wtvp_minisrv/wtv_img_converter.js
Normal file
207
zefie_wtvp_minisrv/wtv_img_converter.js
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* wtv_png_converter.js - WebTV PNG/GIF conversion CLI
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node wtv_png_converter.js <command> [options] <input> [output]
|
||||||
|
*
|
||||||
|
* Commands:
|
||||||
|
* convert Convert a PNG to the best WebTV format (auto: JPEG or Artemis GIF)
|
||||||
|
* encode Convert a PNG with alpha to an Artemis ALP/ALF GIF
|
||||||
|
* decode Convert a WebTV Artemis ALP/ALF GIF back to a PNG
|
||||||
|
* detect Report whether a GIF contains an Artemis ALP/ALF block
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* --type <ALP|ALF> Artemis variant to use for encoding (default: ALP)
|
||||||
|
* --colors <n> Palette size for full-color quantization (default: 256)
|
||||||
|
* --quality <n> JPEG quality when output is JPEG (default: 85)
|
||||||
|
* --output, -o <file> Output file path (alternative to positional argument)
|
||||||
|
* --help, -h Show this help
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const WTVImage = require('./includes/classes/WTVImage');
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Argument parser
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const args = { options: {}, positional: [] };
|
||||||
|
for (let i = 0; i < argv.length; i++) {
|
||||||
|
const a = argv[i];
|
||||||
|
if (a === '--help' || a === '-h') {
|
||||||
|
args.options.help = true;
|
||||||
|
} else if (a === '--type') {
|
||||||
|
args.options.type = argv[++i];
|
||||||
|
} else if (a === '--colors') {
|
||||||
|
args.options.colors = parseInt(argv[++i], 10);
|
||||||
|
} else if (a === '--quality') {
|
||||||
|
args.options.quality = parseInt(argv[++i], 10);
|
||||||
|
} else if (a === '--max-width') {
|
||||||
|
args.options.maxWidth = parseInt(argv[++i], 10);
|
||||||
|
} else if (a === '--max-height') {
|
||||||
|
args.options.maxHeight = parseInt(argv[++i], 10);
|
||||||
|
} else if (a === '--output' || a === '-o') {
|
||||||
|
args.options.output = argv[++i];
|
||||||
|
} else if (a.startsWith('--')) {
|
||||||
|
console.error(`Unknown option: ${a}`);
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
args.positional.push(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log(`
|
||||||
|
WebTV Image Converter
|
||||||
|
=======================
|
||||||
|
Usage: node wtv_img_converter.js <command> [options] <input> [output]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
convert Convert an image to the best WebTV format
|
||||||
|
- No alpha → JPEG
|
||||||
|
- Palette PNG → Artemis GIF (palette 1:1, no requantization)
|
||||||
|
- Full-color RGBA → Artemis GIF (quantized)
|
||||||
|
|
||||||
|
encode Convert a PNG with alpha to an Artemis ALP or ALF GIF
|
||||||
|
(throws if the PNG has no alpha channel)
|
||||||
|
|
||||||
|
decode Convert a WebTV Artemis ALP/ALF GIF back to a standard RGBA PNG
|
||||||
|
|
||||||
|
detect Report whether a file is an Artemis ALP/ALF GIF (no output file needed)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--type <ALP|ALF> Artemis variant for encoding/convert (default: ALF)
|
||||||
|
--colors <n> Palette size for full-color quantization (default: 256)
|
||||||
|
--quality <n> JPEG quality when output is JPEG (default: 85)
|
||||||
|
--max-width <n> Maximum width to scale input before encoding
|
||||||
|
--max-height <n> Maximum height to scale input before encoding
|
||||||
|
--output, -o <file> Output file path
|
||||||
|
--help, -h Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
node wtv_png_converter.js convert logo.png
|
||||||
|
node wtv_png_converter.js convert logo.png logo_wtv.gif --type ALF --colors 128
|
||||||
|
node wtv_png_converter.js encode icon.png icon.gif --type ALP
|
||||||
|
node wtv_png_converter.js decode artemis.gif result.png
|
||||||
|
node wtv_png_converter.js detect artemis.gif
|
||||||
|
`.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Output path helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
function resolveOutput(inputFile, suggestedExt, override) {
|
||||||
|
if (override) return override;
|
||||||
|
const base = path.join(
|
||||||
|
path.dirname(inputFile),
|
||||||
|
path.basename(inputFile, path.extname(inputFile))
|
||||||
|
);
|
||||||
|
return base + suggestedExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Commands
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
async function cmdConvert(inputFile, outputFile, opts) {
|
||||||
|
const ImageBuf = fs.readFileSync(inputFile);
|
||||||
|
const { data, mime } = await WTVImage.ImageToWebTV(ImageBuf, {
|
||||||
|
type: opts.type || 'ALF',
|
||||||
|
colors: opts.colors || 256,
|
||||||
|
jpegQuality: opts.quality || 85,
|
||||||
|
maxWidth: opts.maxWidth,
|
||||||
|
maxHeight: opts.maxHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
const ext = mime === 'image/gif' ? '.gif' : '.jpg';
|
||||||
|
const dest = resolveOutput(inputFile, ext, outputFile);
|
||||||
|
fs.writeFileSync(dest, data);
|
||||||
|
console.log(`[convert] ${inputFile} → ${dest} (${mime}, ${data.length} bytes)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cmdEncode(inputFile, outputFile, opts) {
|
||||||
|
const ImageBuf = fs.readFileSync(inputFile);
|
||||||
|
const gifBuf = await WTVImage.ImageToGIF(ImageBuf, {
|
||||||
|
type: opts.type || 'ALF',
|
||||||
|
colors: opts.colors || 256
|
||||||
|
});
|
||||||
|
|
||||||
|
const dest = resolveOutput(inputFile, '.gif', outputFile);
|
||||||
|
fs.writeFileSync(dest, gifBuf);
|
||||||
|
const type = WTVImage.detect(gifBuf);
|
||||||
|
console.log(`[encode] ${inputFile} → ${dest} (Artemis ${type}, ${gifBuf.length} bytes)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cmdDecode(inputFile, outputFile, opts) {
|
||||||
|
const gifBuf = fs.readFileSync(inputFile);
|
||||||
|
const type = WTVImage.detect(gifBuf);
|
||||||
|
if (!type) {
|
||||||
|
console.error(`[decode] ${inputFile} does not contain an Artemis ALP/ALF block.`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageBuf = await WTVImage.gifToPNG(gifBuf);
|
||||||
|
const dest = resolveOutput(inputFile, '.png', outputFile);
|
||||||
|
fs.writeFileSync(dest, ImageBuf);
|
||||||
|
console.log(`[decode] ${inputFile} (Artemis ${type}) → ${dest} (${ImageBuf.length} bytes)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cmdDetect(inputFile) {
|
||||||
|
const buf = fs.readFileSync(inputFile);
|
||||||
|
const type = WTVImage.detect(buf);
|
||||||
|
if (type) {
|
||||||
|
console.log(`[detect] ${inputFile} → Artemis ${type}`);
|
||||||
|
} else {
|
||||||
|
console.log(`[detect] ${inputFile} → Not an Artemis ALP/ALF GIF`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Entry point
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
(async () => {
|
||||||
|
const raw = process.argv.slice(2);
|
||||||
|
const args = parseArgs(raw);
|
||||||
|
|
||||||
|
if (args.options.help || args.positional.length === 0) {
|
||||||
|
printHelp();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = args.positional[0];
|
||||||
|
const inputFile = args.positional[1];
|
||||||
|
const outputFile = args.options.output || args.positional[2] || null;
|
||||||
|
|
||||||
|
if (!inputFile) {
|
||||||
|
console.error('Error: no input file specified.');
|
||||||
|
printHelp();
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(inputFile)) {
|
||||||
|
console.error(`Error: input file not found: ${inputFile}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (command) {
|
||||||
|
case 'convert': await cmdConvert(inputFile, outputFile, args.options); break;
|
||||||
|
case 'encode': await cmdEncode(inputFile, outputFile, args.options); break;
|
||||||
|
case 'decode': await cmdDecode(inputFile, outputFile, args.options); break;
|
||||||
|
case 'detect': cmdDetect(inputFile); break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown command: ${command}`);
|
||||||
|
printHelp();
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error: ${err.message}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -41,6 +41,10 @@ function parseArgs(argv) {
|
|||||||
args.options.colors = parseInt(argv[++i], 10);
|
args.options.colors = parseInt(argv[++i], 10);
|
||||||
} else if (a === '--quality') {
|
} else if (a === '--quality') {
|
||||||
args.options.quality = parseInt(argv[++i], 10);
|
args.options.quality = parseInt(argv[++i], 10);
|
||||||
|
} else if (a === '--max-width') {
|
||||||
|
args.options.maxWidth = parseInt(argv[++i], 10);
|
||||||
|
} else if (a === '--max-height') {
|
||||||
|
args.options.maxHeight = parseInt(argv[++i], 10);
|
||||||
} else if (a === '--output' || a === '-o') {
|
} else if (a === '--output' || a === '-o') {
|
||||||
args.options.output = argv[++i];
|
args.options.output = argv[++i];
|
||||||
} else if (a.startsWith('--')) {
|
} else if (a.startsWith('--')) {
|
||||||
@@ -76,6 +80,8 @@ Options:
|
|||||||
--type <ALP|ALF> Artemis variant for encoding/convert (default: ALF)
|
--type <ALP|ALF> Artemis variant for encoding/convert (default: ALF)
|
||||||
--colors <n> Palette size for full-color quantization (default: 256)
|
--colors <n> Palette size for full-color quantization (default: 256)
|
||||||
--quality <n> JPEG quality when output is JPEG (default: 85)
|
--quality <n> JPEG quality when output is JPEG (default: 85)
|
||||||
|
--max-width <n> Maximum width to scale input before encoding
|
||||||
|
--max-height <n> Maximum height to scale input before encoding
|
||||||
--output, -o <file> Output file path
|
--output, -o <file> Output file path
|
||||||
--help, -h Show this help
|
--help, -h Show this help
|
||||||
|
|
||||||
@@ -106,9 +112,11 @@ function resolveOutput(inputFile, suggestedExt, override) {
|
|||||||
async function cmdConvert(inputFile, outputFile, opts) {
|
async function cmdConvert(inputFile, outputFile, opts) {
|
||||||
const ImageBuf = fs.readFileSync(inputFile);
|
const ImageBuf = fs.readFileSync(inputFile);
|
||||||
const { data, mime } = await WTVImage.ImageToWebTV(ImageBuf, {
|
const { data, mime } = await WTVImage.ImageToWebTV(ImageBuf, {
|
||||||
type: opts.type || 'ALF',
|
type: opts.type || 'ALF',
|
||||||
colors: opts.colors || 256,
|
colors: opts.colors || 256,
|
||||||
jpegQuality: opts.quality || 85
|
jpegQuality: opts.quality || 85,
|
||||||
|
maxWidth: opts.maxWidth,
|
||||||
|
maxHeight: opts.maxHeight
|
||||||
});
|
});
|
||||||
|
|
||||||
const ext = mime === 'image/gif' ? '.gif' : '.jpg';
|
const ext = mime === 'image/gif' ? '.gif' : '.jpg';
|
||||||
|
|||||||
Reference in New Issue
Block a user