convert all images to webp
This commit is contained in:
1 parent
3e3d2dcbcb
commit
3df6d87ab5
30 files changed
+9721
-9615
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const SRC_DIR = path.resolve("docs/public"); // change if needed
|
||||
const OUT_DIR = path.resolve("docs/public/images"); // change if needed
|
||||
|
||||
const MAX_W = 800;
|
||||
const MAX_H = 600;
|
||||
|
||||
const FORMAT = "webp";
|
||||
const WEBP_QUALITY = 75; // tweak 60-85 if you want smaller/larger
|
||||
|
||||
async function walk(dir) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
const files = [];
|
||||
for (const e of entries) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) files.push(...(await walk(p)));
|
||||
else files.push(p);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function isImage(file) {
|
||||
return /\.(png|jpe?g|webp|avif)$/i.test(file);
|
||||
}
|
||||
|
||||
async function ensureDir(p) {
|
||||
await fs.mkdir(p, { recursive: true });
|
||||
}
|
||||
|
||||
async function processOne(inPath, outPathBase) {
|
||||
await ensureDir(path.dirname(outPathBase));
|
||||
|
||||
await sharp(inPath)
|
||||
.rotate() // respects EXIF orientation
|
||||
.resize({
|
||||
width: MAX_W,
|
||||
height: MAX_H,
|
||||
fit: "inside", // preserve aspect, fit within box
|
||||
withoutEnlargement: true, // don't upscale small images
|
||||
})
|
||||
.toFormat(FORMAT, {
|
||||
quality: WEBP_QUALITY,
|
||||
effort: 4,
|
||||
})
|
||||
.toFile(`${outPathBase}.${FORMAT}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const files = (await walk(SRC_DIR)).filter(isImage);
|
||||
await ensureDir(OUT_DIR);
|
||||
|
||||
let done = 0;
|
||||
|
||||
for (const inPath of files) {
|
||||
const rel = path.relative(SRC_DIR, inPath);
|
||||
|
||||
// strip original extension and write same relative path
|
||||
const outBase = path
|
||||
.join(OUT_DIR, rel)
|
||||
.replace(/\.(png|jpe?g|webp|avif)$/i, "");
|
||||
|
||||
const outPath = `${outBase}.${FORMAT}`;
|
||||
|
||||
// simple cache: skip if output exists and is newer than input
|
||||
try {
|
||||
const [stIn, stOut] = await Promise.all([
|
||||
fs.stat(inPath),
|
||||
fs.stat(outPath),
|
||||
]);
|
||||
if (stOut.mtimeMs >= stIn.mtimeMs) continue;
|
||||
} catch {
|
||||
// output missing -> process
|
||||
}
|
||||
|
||||
await processOne(inPath, outBase);
|
||||
done++;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Optimized ${done} image(s) to max ${MAX_W}x${MAX_H} (${FORMAT}).`,
|
||||
);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in new issue
Block a user