@macrulez/mediatoolz
v0.1.1
Published
A small toolbox of CLI commands for images — resize, convert and recompress them in bulk by rules, and generate hazehash, blurhash and thumbhash placeholders
Maintainers
Readme
MediaToolz

A small toolbox of CLI commands for images: resize, convert and recompress them in bulk by rules, and generate compact placeholders for them. One binary, one subcommand per job, each safe by default (a preview first, a backup before anything is overwritten).
It started as part of devtoolz and lives in its own package since devtoolz 0.5.0.
Features
image-hash— generates hazehash, blurhash and/or thumbhash placeholders for raster images (jpg, png, webp, gif, avif, tiff). Takes files and/or directories — several of each, comma-separated too — and-rwalks subdirectories. Prints to stdout by default;-o <file>collects everything into one file,--per-file/--out-dir <dir>writes a file per image (photo.jpg→photo.jpg.blurhash.txt, with--suffixand--out-extto rename). Formats:json,plain(just the hash text, no newline),csv, andts/jsmodules you can import straight into the app. Without-tit makes blurhash and thumbhash;-t hazehashadds the more compact and more accurate hazehash (--budgetsets its size in bytes, 28 by default). Besides the hashes it can produce the dominant color and a tiny PNG preview (-t all), acceptshttp(s)URLs and path lists from a file or stdin, and can keep an output file in sync:--cacheskips unchanged images,--update --prunemerges into the existing file,--checkfails with exit code 1 when it is stale (made for CI).--dry-runwrites nothing and shows the result as a table instead. Uses the nativesharplibrary, which comes with the package — see Requirements.image-batch— produces resized, converted and recompressed versions of raster images in bulk, by rules kept in a reusable config or given as flags:widths,heights,size,longEdge/shortEdge(the same for landscape and portrait),megapixels,percent,scale(@2x),matchOrientation,maxBytes(--max-size 200KBlowers the quality as far as needed),formats(jpg, png, webp, avif, gif, tiff, heif ororiginal),fit(inside,cover,contain, …), codec options (qualityas a number or a levellow/medium/high/best,mozjpeg, a pngpalette, webplossless, avifeffort, …) and file names from a template ({dir}/{name}-{width}w.{format}, with{ext},{index:3},{hash},{scale},{date}). The config holds only the rules; the folders are chosen on every run. Where the results go is one of three:-o <dir>— a separate folder that repeats the source structure (--flatdrops it);--beside— next to each source;--replace— over the sources themselves, each in its own format, to shrink oversized originals or recompress them in place.--replaceis guarded: it asks for confirmation (--yeswithout a terminal), copies the originals to.image-batch-backup/<date>/first with ajournal.json, replaces a file only if the result is smaller, never processes the same file twice with the same settings, and--dry-runshows the real before/after sizes;image-batch restore <backup>puts the originals back. Without a size flag it only converts the format.-rwalks subfolders,-i/--selectticks files from an interactive list,--listonly prints them. A result an earlier run already made is skipped; any other existing file is a conflict — a terminal asks once (overwrite this / this and all next / skip / skip all / quit), without a terminal it is skipped with exit code 1, or use--overwrite,--skip-existing,--no-overwrite.--hazehash(--budget),--blurhash,--thumbhash,--dominant-colorwith--emit <file>write a manifest of everything produced, with the placeholders.initcreates configs, andconfigshows, edits, copies and deletes them (.mediatoolz/image-batch/in the project or~/.mediatoolz/image-batch/for all projects).sharpenadds output sharpening after the resize — by target and amount, withradius,flat,jaggedandthresholdfor fine control — andimage-batch sharpensaves such settings as presets that configs and--sharpen <name>use by name. Usessharptoo — see Requirements.ui— starts a local web interface and opens it in the browser, so the commands can be run with forms, tables and previews instead of flags. Two modules: Image Hash (a gallery of the placeholders decoded next to each image) and Image Batch (convert, edit configs and sharpening presets with a before and after preview, restore backups). It is part of the package, listens on127.0.0.1only behind a one-time token, and writes nothing without an explicit choice.--port,--no-open,--cwd.
image-hash writes files only when you name an output (-o, --per-file,
--out-dir) and prints to stdout otherwise; image-batch writes into the
folder given with -o, next to the sources with --beside, and over them
only with --replace — after a confirmation, with a backup. Every command
supports --json for machine-readable output.
Requirements
- Node.js 20+
- The
sharpimage library comes with the package: npm picks its prebuilt binary for your platform during the install, so there is nothing to set up. Only if that binary is missing (an install with--omit=optional, an unsupported platform) does the first run offer to installsharpinto~/.mediatoolz/deps(-y/--yesagrees up front, which is also what you want in CI; without a terminal and without--yesthe command stops and says so).
Installation
npm install -g @macrulez/mediatoolzOr run it without installing:
npx @macrulez/mediatoolz image-batch ./photos -r -o ./out -f webpQuick start
mediatoolz image-hash public/img # blurhash + thumbhash of every image, as JSON on stdout
mediatoolz image-hash public/img -r -t hazehash --budget 24 -o hashes.json # hazehash of 24 bytes at most
mediatoolz image-hash a.jpg,b.png,photos -r -t blurhash -f csv -o hashes.csv
mediatoolz image-hash public/img -r -f plain --per-file # photo.jpg.blurhash.txt + photo.jpg.thumbhash.txt
mediatoolz image-hash public/img -r -f ts -t thumbhash -o src/placeholders.ts --name placeholders
mediatoolz image-hash public/img -r --dry-run # write nothing, show a table of the hashes
mediatoolz image-hash public/img -r -t all --components auto -o hashes.json --cache --update --prune
mediatoolz image-hash public/img -r -o hashes.json --check # CI: exit 1 if hashes.json is stale
mediatoolz image-batch ./src/images -r -o ./public/images -w 400,800,1200 -f avif,webp,jpg -q high
mediatoolz image-batch ./photos -r -o ./out -c web # apply the saved config "web"
mediatoolz image-batch ./photos -r -o ./out -c web --select # tick the files from a list first
mediatoolz image-batch ./photos -r -o ./out -c web --dry-run # show what would be written
mediatoolz image-batch ./photos -r -o ./out -c web --hazehash --budget 20 --emit ./out/images.json
mediatoolz image-batch ./photos -r -o ./out -f webp # only change the format, keep the size
mediatoolz image-batch ./photos -r -o ./out --long 1600 -f webp,jpg --max-size 200KB # by long side, capped weight
mediatoolz image-batch ./assets -r --replace -w 1600 --dry-run # preview shrinking originals in place
mediatoolz image-batch ./assets -r --replace -w 1600 # do it: backup first, asks to confirm
mediatoolz image-batch restore .image-batch-backup/20261007-233706 # put the originals back
mediatoolz image-batch ./photos -r --beside -w 400 -f webp # results next to each source
mediatoolz image-batch init # create a config with a few questions
mediatoolz image-batch config # pick a config: apply, show, edit, copy, delete
mediatoolz image-batch sharpen new # save a sharpening preset, then --sharpen <name>
mediatoolz ui # web interface for both commands
mediatoolz ui --no-open --port 4477 # just print the addressEvery command has built-in --help — mediatoolz --help lists both commands
with their own flags inline; mediatoolz <command> --help for one command's
full flag list with defaults.
Development
npm install
npm run build # tsc -> dist/, and the web interface
npm test # vitest
npm run lint # eslint .
npm run format # prettier --check .
npm run typecheck # tsc --noEmitDocumentation & links
- 📖 Full documentation: npm.vuecraft.ru/en/packages/mediatoolz
- 🌐 VueCraft: vuecraft.ru/en
- 👤 Author: macrulez.ru/en
- 💻 GitHub: macrulezru/mediatoolz
- 📦 NPM: @macrulez/mediatoolz
- 🐛 Issues: github.com/macrulezru/mediatoolz/issues
License
MIT
💖 Support the project
Open source takes time and effort. If this library saves you time or brings value, consider supporting further development.
Thank you for being part of this journey. ❤️
