mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-12 16:57:26 +00:00
Fancy progress output
This commit is contained in:
@@ -24,6 +24,8 @@
|
|||||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||||
"commander": "^6.0.0",
|
"commander": "^6.0.0",
|
||||||
"json5": "^2.1.3",
|
"json5": "^2.1.3",
|
||||||
|
"kleur": "^4.1.3",
|
||||||
|
"ora": "^5.1.0",
|
||||||
"rollup": "^2.26.11",
|
"rollup": "^2.26.11",
|
||||||
"rollup-plugin-terser": "^7.0.2"
|
"rollup-plugin-terser": "^7.0.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { cpus } from "os";
|
|||||||
import { extname, join, basename } from "path";
|
import { extname, join, basename } from "path";
|
||||||
import { promises as fsp } from "fs";
|
import { promises as fsp } from "fs";
|
||||||
import { version } from "json:../package.json";
|
import { version } from "json:../package.json";
|
||||||
|
import ora from 'ora';
|
||||||
|
import kleur from 'kleur';
|
||||||
|
|
||||||
import supportedFormats from "./codecs.js";
|
import supportedFormats from "./codecs.js";
|
||||||
import WorkerPool from "./worker_pool.js";
|
import WorkerPool from "./worker_pool.js";
|
||||||
@@ -55,7 +57,7 @@ async function encodeFile({
|
|||||||
optimizerButteraugliTarget,
|
optimizerButteraugliTarget,
|
||||||
maxOptimizerRounds
|
maxOptimizerRounds
|
||||||
}) {
|
}) {
|
||||||
let out;
|
let out, infoText;
|
||||||
const encoder = await supportedFormats[encName].enc();
|
const encoder = await supportedFormats[encName].enc();
|
||||||
if (encConfig === "auto") {
|
if (encConfig === "auto") {
|
||||||
const optionToOptimize = supportedFormats[encName].autoOptimize.option;
|
const optionToOptimize = supportedFormats[encName].autoOptimize.option;
|
||||||
@@ -82,11 +84,11 @@ async function encodeFile({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
out = binary;
|
out = binary;
|
||||||
console.log(
|
const opts = {
|
||||||
`Used \`--${encName} '${JSON.stringify({
|
// 5 significant digits is enough
|
||||||
[optionToOptimize]: quality
|
[optionToOptimize]: Math.round(quality * 10000) / 10000
|
||||||
})}'\` for ${outputFile}`
|
};
|
||||||
);
|
infoText = ` using --${encName} '${JSON5.stringify(opts)}'`;
|
||||||
} else {
|
} else {
|
||||||
out = encoder.encode(
|
out = encoder.encode(
|
||||||
bitmapIn.data.buffer,
|
bitmapIn.data.buffer,
|
||||||
@@ -97,6 +99,7 @@ async function encodeFile({
|
|||||||
}
|
}
|
||||||
await fsp.writeFile(outputFile, out);
|
await fsp.writeFile(outputFile, out);
|
||||||
return {
|
return {
|
||||||
|
infoText,
|
||||||
inputSize: size,
|
inputSize: size,
|
||||||
inputFile: file,
|
inputFile: file,
|
||||||
outputFile,
|
outputFile,
|
||||||
@@ -114,8 +117,60 @@ function handleJob(params) {
|
|||||||
return decodeFile(params.file);
|
return decodeFile(params.file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function progressTracker(results) {
|
||||||
|
const spinner = ora();
|
||||||
|
const tracker = {};
|
||||||
|
tracker.spinner = spinner;
|
||||||
|
tracker.progressOffset = 0;
|
||||||
|
tracker.totalOffset = 0;
|
||||||
|
let status = '';
|
||||||
|
tracker.setStatus = (text) => {
|
||||||
|
status = text || '';
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
let progress = '';
|
||||||
|
tracker.setProgress = (done, total) => {
|
||||||
|
spinner.prefixText = kleur.dim(`${done}/${total}`);
|
||||||
|
const completeness = (tracker.progressOffset + done) / (tracker.totalOffset + total);
|
||||||
|
progress = kleur.cyan(`▐${'▨'.repeat(completeness*10|0).padEnd(10, '╌')}▌ `);
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
function update() {
|
||||||
|
spinner.text = progress + kleur.bold(status) + getResultsText();
|
||||||
|
}
|
||||||
|
tracker.finish = (text) => {
|
||||||
|
spinner.succeed(kleur.bold(text) + getResultsText());
|
||||||
|
}
|
||||||
|
function getResultsText() {
|
||||||
|
let out = '';
|
||||||
|
for (const [filename, result] of results.entries()) {
|
||||||
|
out += `\n ${kleur.cyan(filename)}: ${prettyPrintSize(result.size)}`;
|
||||||
|
for (const { outputFile, outputSize, infoText } of result.outputs) {
|
||||||
|
const name = (program.suffix + extname(outputFile)).padEnd(5);
|
||||||
|
out += `\n ${kleur.dim('└')} ${kleur.cyan(name)} → ${prettyPrintSize(outputSize)}`;
|
||||||
|
const percent = ((outputSize / result.size) * 100).toPrecision(3);
|
||||||
|
out += ` (${kleur[outputSize>result.size?'red':'green'](percent+'%')})`;
|
||||||
|
if (infoText) out += kleur.yellow(infoText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out || '\n';
|
||||||
|
}
|
||||||
|
spinner.start();
|
||||||
|
return tracker;
|
||||||
|
}
|
||||||
|
|
||||||
async function processFiles(files) {
|
async function processFiles(files) {
|
||||||
const workerPool = new WorkerPool(cpus().length, __filename);
|
const parallelism = cpus().length;
|
||||||
|
|
||||||
|
const results = new Map();
|
||||||
|
const progress = progressTracker(results);
|
||||||
|
|
||||||
|
progress.setStatus('Decoding...');
|
||||||
|
progress.totalOffset = files.length;
|
||||||
|
progress.setProgress(0, files.length);
|
||||||
|
|
||||||
|
const workerPool = new WorkerPool(parallelism, __filename);
|
||||||
// Create output directory
|
// Create output directory
|
||||||
await fsp.mkdir(program.outputDir, { recursive: true });
|
await fsp.mkdir(program.outputDir, { recursive: true });
|
||||||
|
|
||||||
@@ -131,8 +186,11 @@ async function processFiles(files) {
|
|||||||
return result;
|
return result;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const decodedFiles = await Promise.all(files.map(file => decodeFile(file)));
|
progress.progressOffset = decoded;
|
||||||
|
progress.setStatus('Encoding ' + kleur.dim(`(${parallelism} threads)`));
|
||||||
|
progress.setProgress(0, files.length);
|
||||||
|
|
||||||
|
const jobs = [];
|
||||||
let jobsStarted = 0;
|
let jobsStarted = 0;
|
||||||
let jobsFinished = 0;
|
let jobsFinished = 0;
|
||||||
for (const { file, bitmap, size } of decodedFiles) {
|
for (const { file, bitmap, size } of decodedFiles) {
|
||||||
@@ -155,7 +213,7 @@ async function processFiles(files) {
|
|||||||
);
|
);
|
||||||
const outputFile = join(program.outputDir, `${base}.${value.extension}`);
|
const outputFile = join(program.outputDir, `${base}.${value.extension}`);
|
||||||
jobsStarted++;
|
jobsStarted++;
|
||||||
workerPool
|
const p = workerPool
|
||||||
.dispatchJob({
|
.dispatchJob({
|
||||||
operation: 'encode',
|
operation: 'encode',
|
||||||
file,
|
file,
|
||||||
@@ -167,26 +225,21 @@ async function processFiles(files) {
|
|||||||
optimizerButteraugliTarget: Number(program.optimizerButteraugliTarget),
|
optimizerButteraugliTarget: Number(program.optimizerButteraugliTarget),
|
||||||
maxOptimizerRounds: Number(program.maxOptimizerRounds)
|
maxOptimizerRounds: Number(program.maxOptimizerRounds)
|
||||||
})
|
})
|
||||||
.then(({ outputFile, inputSize, outputSize }) => {
|
.then((output) => {
|
||||||
jobsFinished++;
|
jobsFinished++;
|
||||||
const numDigits = jobsStarted.toString().length;
|
results.get(file).outputs.push(output);
|
||||||
console.log(
|
progress.setProgress(jobsFinished, jobsStarted);
|
||||||
`${jobsFinished
|
|
||||||
.toString()
|
|
||||||
.padStart(
|
|
||||||
numDigits
|
|
||||||
)}/${jobsStarted}: ${outputFile} ${prettyPrintSize(
|
|
||||||
inputSize
|
|
||||||
)} -> ${prettyPrintSize(outputSize)} (${(
|
|
||||||
(outputSize / inputSize) *
|
|
||||||
100
|
|
||||||
).toFixed(1)}%)`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
jobs.push(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the progress to account for multi-format
|
||||||
|
progress.setProgress(jobsFinished, jobsStarted);
|
||||||
// Wait for all jobs to finish
|
// Wait for all jobs to finish
|
||||||
await workerPool.join();
|
await workerPool.join();
|
||||||
|
await Promise.all(jobs);
|
||||||
|
progress.finish('Squoosh results:');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMainThread) {
|
if (isMainThread) {
|
||||||
|
|||||||
Reference in New Issue
Block a user