Пакетная обработка изображений
Создаёт уменьшенные, сконвертированные и пережатые версии растровых изображений пачками. Что именно получить — размеры, форматы, настройки кодеков и имена файлов — описывает набор правил: либо в переиспользуемой конфигурации, либо прямо во флагах. Откуда брать картинки и куда класть результат, решается при каждом запуске, поэтому одна конфигурация подходит для любого проекта.
mediatoolz image-batch [paths...] (-o <dir> | --beside | --replace) [options]Это генератор, как image-hash: команда читает изображения и пишет файлы. Куда пойдут результаты, выбираете вы:
--out <dir>— в отдельную папку с повторением структуры папок исходников. Исходники остаются нетронутыми.--beside— в папку каждого исходника, рядом с ним. Исходники остаются нетронутыми.--replace— поверх самих исходных файлов, каждый в своём формате: уменьшить слишком большие оригиналы или пережать их на месте. Оригиналы предварительно копируются в резервную копию, а вернуть их можно одной командой.
Реальный вывод — два изображения, две ширины, два формата, в отдельную папку:
mediatoolz image-batch img -r -o out -w 300,600 -f webp,jpg -q high2 images → out
┌───────────┬─────────────────┬─────────┬─────────┬─────────┐
│ Source │ Output │ Size │ Bytes │ Status │
├───────────┼─────────────────┼─────────┼─────────┼─────────┤
│ a.png │ a-300w.webp │ 300×200 │ 898 B │ written │
│ a.png │ a-600w.webp │ 600×400 │ 2.37 KB │ written │
│ a.png │ a-300w.jpg │ 300×200 │ 2.04 KB │ written │
│ a.png │ a-600w.jpg │ 600×400 │ 4.13 KB │ written │
│ sub/b.png │ sub/b-300w.webp │ 300×450 │ 1.76 KB │ written │
│ sub/b.png │ sub/b-600w.webp │ 600×900 │ 4.45 KB │ written │
│ sub/b.png │ sub/b-300w.jpg │ 300×450 │ 4.12 KB │ written │
│ sub/b.png │ sub/b-600w.jpg │ 600×900 │ 9.63 KB │ written │
└───────────┴─────────────────┴─────────┴─────────┴─────────┘
8 written
Wrote 8 files, 29.4 KB (the sources they came from: 240 KB).Каждая строка — один созданный файл. Размеры в таблице — это настоящие размеры результата, они вычисляются до записи.
Быстрый старт
Адаптивный набор для сайта — три ширины, сначала современные форматы, в public/images:
mediatoolz image-batch ./src/images -r -o ./public/images -w 400,800,1200 -f avif,webp,jpg -q highРазмер каждого изображения по длинной стороне, независимо от ориентации, и не больше 200 KB на файл:
mediatoolz image-batch ./photos -r -o ./out --long 1600 -f webp,jpg --max-size 200KBТот же набор из сохранённой конфигурации, с выбором файлов из списка:
mediatoolz image-batch ./src/images -r -o ./public/images -c web --selectТолько сменить формат — размер остаётся прежним, ничего не уменьшается и не повышается резкость:
mediatoolz image-batch ./photos -r -o ./out -f webpУменьшить слишком большие оригиналы до 1600 px в ширину на месте, сначала посмотрев предварительный результат:
mediatoolz image-batch ./assets -r --replace -w 1600 --dry-run
mediatoolz image-batch ./assets -r --replace -w 1600Куда попадают результаты
Указывается ровно один из флагов --out, --beside и --replace. Без них команда останавливается и сообщает об этом; в терминале вместо этого она спрашивает выходную папку.
В отдельную папку
-o <dir> пишет результаты в dir. Структура подпапок каждой входной папки повторяется внутри неё; --flat кладёт всё прямо в dir. Имя каждого результата задаёт шаблон (см. Имена файлов).
Если dir лежит внутри входной папки, при чтении входа она пропускается, поэтому результаты никогда не обрабатываются повторно.
Рядом с исходниками
--beside пишет каждый результат в папку его исходника: из photos/a.png получается photos/a-400w.webp. Шаблон имени должен давать имя, отличное от имени исходника ({width}, другой формат или суффикс); шаблон, который попал бы на сам исходник, — это ошибка с подсказкой про --replace.
Повторный запуск не принимает результаты прошлого за новые исходники. Команда хранит запись о созданных ею файлах, а когда записи нет, узнаёт файл, который в этом же запуске является запланированным результатом другого исходника. Такие файлы остаются нетронутыми и перечисляются в итоге.
Поверх исходников
--replace перезаписывает каждый исходный файл результатом в том же формате и под тем же именем. Типичные задачи — уменьшить изображения, которые больше нужного (-w 1600 означает «не шире 1600»), и пережать их (-q high, palette у png, mozjpeg).
Запуск, перезаписывающий файлы, которые непросто создать заново, защищён несколькими способами:
- Один результат на изображение, в формате самого изображения. Рецепт, который дал бы несколько файлов на изображение (
widths: [400, 800]) или другой формат (formats: ["webp"]), — это ошибка до начала записи. Не указывайтеformatsили укажитеoriginal. Шаблон имени не действует, а--nameотклоняется. - Только если стало меньше. Файл заменяется, лишь когда результат меньше оригинала; иначе он остаётся как был и в итоге помечен как kept (not smaller). Перекодирование JPEG с тем же качеством только отнимает детали, и это правило не даёт делать это зря.
--no-only-if-smallerзаменяет в любом случае. - Никогда не обрабатывается дважды. Каждый файл, который команда переписала или рассмотрела и оставила, запоминается вместе с настройками. При следующем запуске с теми же настройками он помечается как already done и не трогается, поэтому повторные запуски не ухудшают изображения дальше. Измените настройки (меньшая ширина, другое качество), и файл обработается снова;
--forceигнорирует запись. - Сначала резервная копия. Перед перезаписью файла оригинал копируется в
.image-batch-backup/<дата>/в текущей папке с сохранением относительного пути, а список копий записывается вjournal.jsonтам же.--backup <dir>выбирает другое место,--no-backupотключает копирование. - Подтверждение. В терминале команда сначала показывает, сколько изображений и сколько байт она собирается заменить и куда пойдут оригиналы, и спрашивает. Без терминала нужен
--yes; без него она останавливается с ошибкой. - Предварительный результат с настоящими числами.
--dry-runкодирует каждый файл в памяти и показывает для каждого размер сейчас и размер, который он получил бы, и общую экономию — и ничего не пишет. - Атомарная запись. Файл пишется рядом с назначением и переименовывается на место, поэтому прерванный запуск никогда не оставляет наполовину записанное изображение.
SVG — это векторы, им нечего сжимать; они остаются нетронутыми и учитываются в итоге, а не считаются ошибками.
Реальный вывод — три изображения уменьшаются до 600 px в ширину; третье и так компактное, поэтому остаётся как есть:
mediatoolz image-batch photos --replace -w 600 --dry-run3 images → in place
┌──────────────┬─────────┬────────┬────────┬───────────────┐
│ File │ Size │ Was │ Now │ Status │
├──────────────┼─────────┼────────┼────────┼───────────────┤
│ ai-block.png │ 600×400 │ 407 KB │ 376 KB │ would replace │
│ airport.png │ 600×354 │ 237 KB │ 193 KB │ would replace │
│ avatar.png │ 357×570 │ 250 KB │ 250 KB │ would keep │
└──────────────┴─────────┴────────┴────────┴───────────────┘
2 would be replaced · 1 would be kept
Would save 75.3 KB (12% of 644 KB): 644 KB → 569 KB.
(dry run — nothing written; drop --dry-run to write the files)Без --dry-run та же команда спросит подтверждение (или примет --yes), запишет файлы и закончит подсказкой, как всё вернуть:
2 replaced · 1 kept (not smaller)
Saved 75.3 KB (12% of 644 KB): 644 KB → 569 KB.
Originals saved to /work/site/.image-batch-backup/20261007-233706 — undo with: mediatoolz image-batch restore /work/site/.image-batch-backup/20261007-233706Второй такой же запуск ничего не меняет:
0 replaced · 3 already doneВозврат оригиналов
mediatoolz image-batch restore .image-batch-backup/20261007-233706restore читает journal.json в папке резервной копии и копирует каждый сохранённый оригинал обратно поверх его файла. Перед перезаписью файла команда проверяет, что он всё ещё тот, который записал --replace; файл, изменённый с тех пор, остаётся нетронутым и перечисляется (--force восстановит его в любом случае). Если копия пропала или перестала совпадать с сохранённой, об этом сообщается. --dry-run показывает, что было бы восстановлено. В терминале команда сначала спрашивает; без терминала нужен --yes. После восстановления следующий запуск --replace снова обработает эти файлы.
Папка резервной копии остаётся на месте; удалите её, когда оригиналы больше не нужны.
Выбор файлов
[paths...]— файлы изображений, папки и glob-шаблоны вродеphotos/**/*.jpg. Без них — текущая папка.-r, --recursiveобходит подпапки. Из папок читаются jpg, jpeg, png, webp, gif, avif, tif, tiff и svg; файл, названный явно, читается при любом расширении.--include <glob>,--exclude <glob>сужают набор по пути внутри входной папки. Повторяемые.--files-from <file>читает дополнительные пути из файла, по одному в строке.-i, --selectпоказывает все найденные файлы — путь, размеры, формат и вес — все отмечены, и позволяет снять отметку с ненужных до начала обработки. Клавиши: стрелки илиj/k— перемещение, пробел — отметка,a— все,n— никакие,i— инвертировать,/— фильтр по тексту, Enter — подтвердить,q— отмена. Нужен настоящий терминал.--listтолько печатает, что было бы обработано, и ничего не пишет — в терминале и вне его.
Папка .image-batch-backup никогда не читается как вход.
Конфигурации
Конфигурация — это JSON-файл с правилами. В нём нет входных и выходных путей, так что одна конфигурация подходит для любой папки и любого проекта.
{
"name": "web",
"description": "Адаптивные изображения для сайта",
"defaults": { "fit": "inside", "withoutEnlargement": true },
"presets": {
"responsive": { "widths": [400, 800, 1200], "formats": ["avif", "webp", "jpg"] }
},
"outputs": [
{ "id": "main", "preset": "responsive", "quality": "high" },
{
"id": "thumb",
"size": "200x200",
"fit": "cover",
"formats": { "webp": { "quality": 70 } },
"name": "{dir}/{name}-thumb.{format}"
}
],
"match": [
{ "glob": "hero/**", "outputs": [{ "widths": [1600, 2400], "formats": ["avif", "jpg"] }] }
],
"placeholders": { "hazehash": { "budget": 28 } }
}outputs— список рецептов. Каждый рецепт говорит, что получить из каждого исходника; см. Рецепты.presets— именованные наборы полей рецепта. Рецепт подключает набор через"preset", а набор может опираться на другой через"extends"; собственные поля рецепта главнее.defaultsдействуют на каждый рецепт.matchвыбирает рецепты по пути исходника внутри входной папки. Первое правило, чейglobподошёл, заменяетoutputsдля этого файла; файл, не подошедший ни под одно правило (и безoutputs), остаётся нетронутым и попадает в итоговый список.placeholdersназывает хеши, которые считаются для манифеста.nameиdescription— подписи в списках.
Размеры, заданные в конфигурации, можно отключить на один запуск флагом --no-resize; формат, качество и остальные настройки при этом действуют.
Имя конфигурации — это имя её файла: .mediatoolz/image-batch/web.json — это web. Конфигурации ищутся в проекте (.mediatoolz/image-batch/, вверх от текущей папки до корня репозитория, папки с .git) и в ~/.mediatoolz/image-batch/; проектная главнее глобальной с тем же именем. Файлы .js, .mjs и .ts тоже работают — они экспортируют объект или функцию, которая его возвращает, — но configs их менять не умеет, а .ts-файл транспилируется сам по себе, поэтому не может импортировать другие файлы.
-c <name|file> выбирает конфигурацию. Без -c единственная найденная конфигурация применяется сама, если только задачу уже не описывают флаги вроде -w или -f; если их несколько, терминал спросит, какую взять, а без терминала это ошибка со списком. Флаги всегда переопределяют конфигурацию на один запуск.
Рецепты
Рецепт — это один элемент outputs. Всё необязательно; пустой рецепт копирует каждый файл в его собственном размере и формате.
widths— список целых чисел. Один результат на ширину, высота следует пропорциям.heights— то же по высоте.size—"800x600": один результат, заполняющий эту рамку согласноfit.longEdge— список целых чисел. Один результат на значение: длинная сторона изображения становится такой, вторая следует пропорциям. Одинаково работает для альбомных и портретных изображений.shortEdge— то же по короткой стороне.megapixels— список чисел вроде[2, 0.5]. Один результат на значение с примерно таким числом миллионов пикселей, пропорции сохраняются.percent— список чисел вроде[50, 25]. Один результат на значение, в процентах от размера исходника.matchOrientation— по умолчаниюfalse. Сsizeразворачивает рамку (300×200 становится 200×300) для изображения другой ориентации.scale— список множителей к каждому размеру выше ([1, 2]даёт@1xи@2x; уmegapixelsплощадь растёт в квадрате); если размеров нет, масштабируется сам исходник.maxBytes— размер вроде"200KB"или"1.5MB", который не должен превышать ни один файл; см. Ограничение размера файла.formats—jpg,png,webp,avif,gif,tiff,jp2,heifилиoriginal(оставить формат исходника; SVG становится png). Список или карта формат → параметры кодека.quality— число 1–100, уровень (low,medium,high,best) или карта по форматам. См. Параметры кодеков.fit— как изображение заполняет рамку:inside(по умолчанию; целиком, пропорции сохранены),outside,cover(обрезать до точной рамки),contain(поля цветомbackground),fill(растянуть).position— какую часть оставляетcover:centre,north,top,right bottom,entropy,attention, …background— цвет полей;#rrggbb,#rrggbbaaили имя цвета.withoutEnlargement— по умолчаниюtrue: размер больше исходного (в том числе процент выше 100) сжимается до исходного. Несколько запрошенных размеров, которые сводятся к одному файлу, становятся одним файлом, а в итоге сказано, сколько склеено. SVG — векторы, поэтому всегда рисуются в запрошенном размере.name— шаблон имени файла.autoOrient— по умолчаниюtrue: повернуть изображение так, как велит его EXIF-ориентация.rotate—90,180или270.flip,flop— отразить по вертикали или по горизонтали.grayscale,blur(сигма).sharpen— резкость после изменения размера: имя сохранённого пресета или объект сfor,amount,radius,flat,jaggedиthreshold. См. Резкость.flatten— положить прозрачность на сплошной фон:true(белый) или цвет. JPEG всегда кладётся на белый, если здесь не сказано иное.metadata—strip(по умолчанию),keepилиkeep-icc.id— имя рецепта для сообщений и редактора конфигураций.
Размеры считаются до записи, поэтому {width} и {height} в имени — это настоящие размеры результата.
Методы задания размера
Рецепт может просить размеры сразу несколькими способами; каждое значение каждого метода даёт свой результат, а результаты, получившиеся одного размера, склеиваются в один файл.
- По ширине или высоте (
widths,heights) — задана одна сторона, вторая следует пропорциям. Обычный выбор, когда вёрстка определяется шириной. - По рамке (
sizeсfit) — изображение вписывается в рамку (inside, по умолчанию) или заполняет её точно (cover,contain,fill). СmatchOrientationрамка 1920×1080 для портретных кадров превращается в 1080×1920, поэтому одна рамка подходит для смешанного набора. - По длинной или короткой стороне (
longEdge,shortEdge) — не зависит от ориентации:longEdge: [2000]делает альбомное изображение шириной 2000 px, а портретное высотой 2000 px. Естественный выбор для смешанных галерей и для--replace. - По площади (
megapixels) — примерно заданное число миллионов пикселей при любых пропорциях; удобно для печати и чтобы ограничить память, которую занимает распакованное изображение. - В процентах (
percent) — доля размера исходника, для лесенки долей или быстрого уменьшения вдвое.
Без размера рецепт ничего не уменьшает: каждый результат сохраняет размеры своего исходника, действуют только формат, качество и остальные настройки. Так конвертируют папку в другой формат.
Пропорции сохраняются у всех методов, кроме точных рамок (cover, contain, fill). Ничего не увеличивается, пока withoutEnlargement не равен false, поэтому percent: [200] или longEdge: [4000] для меньшего изображения оставляют его как есть. С --replace размер — это максимум.
Резкость
Изменение размера смягчает изображение, а небольшая копия, у которой резкость подобрана под место показа, выглядит чище. sharpen добавляет этот последний шаг после изменения размера. Он устроен как выходная резкость в Lightroom: вы говорите, для чего предназначен результат и насколько сильно, а для редкого случая, которому нужен более точный контроль, можно задать числа самому.
{
"id": "web",
"longEdge": [1600],
"formats": ["jpg"],
"sharpen": { "for": "screen", "amount": "standard" }
}for—screen(по умолчанию; небольшой радиус для веба и экранов),matte(средний, для матовой бумаги) илиglossy(самый сильный, для глянцевой бумаги).amount—low,standard(по умолчанию) илиhigh.
Пара выбирает значения из фиксированной таблицы. Они не зависят от размера результата. Достаточно назвать любое из двух полей: резкость включится, а второе поле примет значение по умолчанию, так что "sharpen": { "amount": "high" } означает screen и high. Рецепт, в котором sharpen не упомянут, резкостью не обрабатывается вообще.
Тонкая настройка
Ещё четыре числа заменяют отдельные значения таблицы. Они передаются в резкость библиотеки sharp как есть:
radius— ширина подчёркиваемого края, сигма от0.000001до10. В таблице0.6дляscreen,1дляmatteи1.4дляglossy.flat— насколько сильно обрабатываются ровные и гладкие участки, от0до1000000. В таблице0.5(low),1(standard) или1.8(high). Уменьшите, чтобы небо и кожа оставались чистыми.jagged— насколько сильно обрабатываются резкие края, от0до1000000. В таблице1.5,3или5.threshold— контраст, отделяющий ровный участок от края, от0до1000000. Без него sharp берёт своё значение,2.
{ "sharpen": { "for": "matte", "radius": 1.2, "flat": 0.4, "jagged": 4 } }Число вне диапазона останавливает команду до записи, а в сообщении названы поле и диапазон. Резкость работает по яркости изображения, поэтому цветных ореолов не добавляет.
Пресеты резкости
Набор значений резкости можно сохранить один раз и использовать по имени в любой конфигурации и из командной строки. Пресетами управляет собственная команда image-batch sharpen, потому что пресет не принадлежит ни одной конфигурации.
mediatoolz image-batch sharpen # выбрать пресет: показать, изменить, скопировать, удалить
mediatoolz image-batch sharpen new # создать несколькими вопросами
mediatoolz image-batch sharpen new --name web-crisp --sharpen-for screen --sharpen-radius 0.8
mediatoolz image-batch sharpen list # только таблица (--json для скриптов)
mediatoolz image-batch sharpen show web-crisp # настройки и то, что получает sharp
mediatoolz image-batch sharpen rm web-crisp --yes # удалить (копия .bak остаётся без --force)Встроенные пресеты
Вместе с командой идут шесть пресетов, чтобы было от чего оттолкнуться. sharpen list показывает их как built-in, они работают в любом проекте без создания файла, их нельзя ни править, ни удалять. Чтобы изменить один из них, скопируйте его в проект из списка sharpen; ваш пресет с тем же именем заменяет встроенный.
| Пресет | Настройки | Для чего |
|---|---|---|
web-light | screen, low | лёгкое касание для веб-фото |
web-crisp | screen, standard | обычная резкость для веба |
web-detail | screen, high, радиус 0.8 | мелкие детали больших веб-фото |
thumbnail | screen, standard, радиус 0.4, flat 0.6, jagged 2.5 | маленькие превью: тонкий радиус, бережно к ровным участкам |
print-matte | matte, standard | матовая бумага |
print-glossy | glossy, standard | глянцевая бумага |
Собственные пресеты
Пресет — небольшой JSON-файл с именем пресета, с теми же полями, что у sharpen, и необязательным description, который показывается в списке:
{ "description": "Фото для веба", "for": "screen", "radius": 0.8, "flat": 0.4 }Ваши пресеты лежат в .mediatoolz/image-batch/sharpen/ проекта (ищутся от рабочей папки вверх до корня репозитория) или в ~/.mediatoolz/image-batch/sharpen/ для всех проектов. Проектный пресет скрывает глобальный с тем же именем, а оба скрывают встроенный.
Пресет, встроенный или свой, используется по имени:
{
"outputs": [
{ "id": "web", "longEdge": [1600], "sharpen": "web-crisp" },
{
"id": "print",
"longEdge": [3000],
"sharpen": { "preset": "web-crisp", "for": "glossy", "amount": "high" }
}
]
}mediatoolz image-batch ./photos -o ./out --long 1600 --sharpen web-crispПоля, записанные рядом с preset, заменяют такие же поля пресета. Между слоями конфигурации (defaults, пресет рецепта, сам рецепт) и флагами резкость объединяется по полям: более поздний слой меняет только то, что назвал. Слой, назвавший другой пресет, начинает с этого пресета и отбрасывает то, что задали прежние слои. Несуществующий пресет останавливает команду до записи, а в сообщении перечислены существующие. Результаты пересчитываются, когда меняются значения за пресетом, и остаются как есть, когда меняется только его имя или описание.
Имена файлов
Имя по умолчанию зависит от метода размера: {dir}/{name}-{width}w.{format} для widths, -{height}h для heights, -{size} для size, -{long}l для longEdge, -{short}s для shortEdge, -{mp}mp для megapixels, -{percent}pct для percent, @{scale}x для одного scale и {dir}/{name}.{format} в остальных случаях. Если рецепт смешивает методы, каждый результат получает имя своего метода. Свой вариант задаётся полем name рецепта или --name на один запуск. При --replace имя всегда совпадает с именем самого файла.
{name}— имя исходного файла без расширения.{ext}— расширение исходника без точки.{orig}— полное имя исходного файла,hero.png.{dir}— папка исходника относительно входной папки; для файлов в корне пусто (стоящий после неё/исчезает).--flatделает её пустой всегда.{format}— расширение выходного формата.{width},{height},{size}— настоящие размеры результата,{size}в виде800x450.{long},{short}— настоящая длинная и короткая сторона результата.{mp}— мегапиксели: запрошенное значение дляmegapixels, иначе настоящая площадь (0.06).{percent}— проценты: запрошенное значение дляpercent, иначе настоящая доля ширины исходника.{scale}— множитель изscale, например2.{index},{index:3}— номер исходника в этом запуске, с 1;:3дополняет нулями (007).{hash},{hash:6}— начало хеша содержимого исходника (по умолчанию 8 символов, до 40).{date}— сегодня,2026-10-07.
Шаблон отсчитывается от выходной папки. Ведущий /, буква диска, .., неизвестная {переменная} или символ, недопустимый в имени файла, — это ошибка до начала записи. Два результата с одинаковым именем — тоже ошибка, и сообщение их перечисляет; когда исходники различаются только расширением (ui.png и ui.webp), оно советует добавить {ext}.
Параметры кодеков
У каждого формата есть опции, влияющие на размер и качество. Задаются в конфигурации (defaults.codecs, набор, карта formats рецепта или codecs) или из командной строки.
jpg—quality,mozjpeg(лучшее сжатие при том же качестве, медленнее),progressive,chromaSubsampling(4:2:0или4:4:4, второе — для текста и резких контуров).png—compressionLevel(0–9),paletteсcolors(2–256),qualityиdither(8-битная палитра: самая большая экономия, с потерями),effort,progressive.webp—quality,lossless,nearLossless,alphaQuality,effort(0–6),smartSubsample,preset(photo,picture,drawing,icon,text),minSize.avif—quality,lossless,effort(0–9),chromaSubsampling,bitdepth(8, 10, 12),tune.gif—colors(2–256),effort,dither,loop,delay,reuse. Анимированный gif остаётся анимированным.tiff—compression(jpeg,deflate,lzw,packbits,webp,zstd, …),quality,predictor,bitdepth,xres,yres.jp2—quality,lossless,chromaSubsampling. Доступен только если libvips вsharpсобрана с OpenJPEG; готовая сборка — без него, и команда говорит об этом до записи.heif—quality,lossless,effort,compression(по умолчаниюav1, либоhevc),chromaSubsampling.
Одно и то же число quality у разных кодеков значит разное, поэтому часто удобнее уровень: он раскрывается в число для каждого формата (для jpg low/medium/high/best — 65/78/85/92, для webp 60/75/82/90, для avif 40/50/60/75). Просто число в quality действует на все форматы, у которых есть качество; карта задаёт своё по форматам: { "jpg": 82, "webp": "high" }.
Опции накладываются от слабых к сильным: встроенные умолчания → defaults → набор → рецепт (сначала короткая запись quality, затем карта formats и codecs) → флаги командной строки. Встроенные умолчания: jpg — quality 82, mozjpeg и progressive; webp — quality 80, effort 4; avif — quality 55, effort 4; png — compressionLevel 9, без потерь; heif — compression av1. Сжатие с потерями молча не включается: png квантуется только если вы задали palette.
Опция, которой у формата нет (lossless у jpg), неизвестное имя или значение вне диапазона — это ошибка с подсказкой (Did you mean "mozjpeg"?), она находится при чтении конфигурации.
Из командной строки: -q 82, -q high, -q jpg=82,webp=high, а для всего остального — повторяемый --codec jpg.mozjpeg=false --codec png.palette=true.
Ограничение размера файла
maxBytes (--max-size 200KB) ограничивает размер каждого результата: "200KB", "1.5MB" или число байт, не меньше 1 KB. Файл, который и так укладывается, записывается как есть. Больший кодируется заново с более низким качеством — настолько высоким, насколько позволяет предел; поиск занимает несколько проходов кодирования на файл. Работает для форматов, у которых есть качество, — jpg, webp, avif, heif, jp2 и png с palette; для любого другого формата команда останавливается до записи и сообщает об этом.
Если предел не достигается даже при самом низком качестве, файл не записывается; он попадает в отчёт вместе с наименьшим достижимым размером, а код выхода равен 1 — попросите ещё и меньший размер (--long 1200). Предел — часть настроек: если его изменить, файлы создаются заново, а с --replace он работает как любая другая настройка.
Если результат уже существует
Это относится к --out и --beside; при --replace речь идёт о самом исходнике, и действуют правила выше.
Результат, который прошлый запуск сделал из того же исходника с теми же настройками, остаётся нетронутым и считается актуальным; благодаря этому повторный запуск быстрый. Запись о сделанном хранится в ~/.mediatoolz/cache/image-batch/.
Всё остальное, что уже лежит на месте, — это конфликт: файл, которого команда не делала, или тот, что она сделала из исходника или с настройками, которые потом изменились.
- В терминале при первом конфликте спрашивается, что делать:
y— перезаписать этот,a— перезаписать этот и все следующие,s— пропустить этот,n— пропустить все следующие,q— выйти. «Все» действует до конца запуска. - Без терминала спросить некого: конфликтующие файлы пропускаются и перечисляются, код выхода 1, чтобы CI заметил.
--overwriteзаменяет их,--skip-existingпропускает и выходит с 0,--no-overwriteсчитает любой конфликт ошибкой.--forceпересоздаёт всё, не глядя на запись.
Исходный файл не перезаписывается ни при --out, ни при --beside, даже если шаблон указывает на него.
Хеши и манифест
--hazehash, --blurhash, --thumbhash и --dominant-color (или placeholders в конфигурации) считают плейсхолдер каждого исходника; --budget <bytes> задаёт размер hazehash (7–48, по умолчанию 28). Они записываются в файл, названный в --emit:
mediatoolz image-batch img -r -o out -c web --hazehash --budget 20 --emit out/images.json --key-prefix /images/В манифесте есть sources (размер, формат и хеши каждого исходника и список его результатов) и placeholders: одна запись на каждый созданный файл, ключом служит его путь с --key-prefix впереди, внутри хеши и собственные width и height файла — форма манифеста плейсхолдеров. Результаты, которые уже были актуальны, тоже перечислены.
Управление конфигурациями
mediatoolz image-batch init # создать конфигурацию, ответив на несколько вопросов
mediatoolz image-batch config # выбрать конфигурацию: применить, показать, изменить, скопировать, удалить
mediatoolz image-batch config list # только таблица (--json для скриптов)
mediatoolz image-batch config show web # что даёт конфигурация
mediatoolz image-batch config rm web --yes # удалить (копия .bak остаётся без --force)init спрашивает имя, сохранить для проекта или для всех проектов, как выбирается размер (список методов задания размера: ширина, высота, рамка, длинная сторона, короткая сторона, мегапиксели, проценты или исходный размер, а затем значения; для рамки ещё спрашивается, как изображение её заполняет и разворачивать ли её для другой ориентации), форматы (список с флажками, где отмечены avif, webp и jpg: пробел отмечает, Enter подтверждает), уровень качества, шаблон имени файла (переменные перечислены; Enter оставляет умолчание для метода размера, либо введите свой), нужен ли рецепт миниатюры нужна ли резкость (список назначений и ваших сохранённых пресетов, затем степень) и нужен ли hazehash. С --name, флагом размера (-w, --heights, --size вместе с --fit и --match-orientation, --long, --short, --megapixels, --percent), -f, -q, --file-name, --sharpen и остальными флагами --sharpen-*, --global, --thumbnail и --hazehash он ничего не спрашивает; --file-name <template> задаёт шаблон имени файла основного рецепта.
Конфигурациями занимаются две команды: init создаёт конфигурацию (config new делает то же самое), config управляет существующими. У пресетов резкости своя команда, sharpen.
config в терминале открывает список всех найденных конфигураций: где лежит и сколько в ней рецептов. Выберите одну, и откроется меню:
- Apply to a folder спрашивает входную и выходную папки и запускает обработку.
- Show печатает каждый рецепт: размеры, форматы,
fit, шаблон имени файла. - Edit открывает список рецептов, правил, умолчаний и плейсхолдеров; откройте рецепт, чтобы сменить метод размера (первый пункт; прежний метод заменяется, текущие значения — стартовый текст) или менять по одному полю (пустой ответ удаляет поле), и добавляйте или удаляйте рецепты; новый рецепт начинается с вопроса о методе размера. Каждое изменение проверяется на всей конфигурации до сохранения; неверное отклоняется, старое значение остаётся. Файл сохраняет свои отступы.
- Duplicate, Rename, Copy to global (или в проект), Delete (с подтверждением; копия
.bakостаётся).
Таким образом можно править только конфигурации .json.
Параметры
[paths...]
Файлы изображений, папки и glob-шаблоны. Без них — текущая папка.
-c, --config <name|file>
Правила: имя конфигурации или путь к её файлу.
-o, --out <dir>
Писать результаты в эту папку. В терминале, если место не указано, будет вопрос.
--beside
Писать каждый результат рядом с его исходником.
--replace
Переписать каждый исходный файл на месте, в его собственном формате. Спрашивает подтверждение; без терминала нужен --yes.
--backup [dir], --no-backup
С --replace: куда сначала сохраняются оригиналы (по умолчанию .image-batch-backup/<дата> в текущей папке) или не копировать вовсе.
--no-only-if-smaller
С --replace: заменять файл, даже если результат не меньше.
-r, --recursive
Обходить и подпапки каждой указанной папки.
--flat
С --out: класть все результаты прямо в выходную папку, без подпапок исходников.
--include <glob>, --exclude <glob>
Оставить только файлы, чей путь внутри входной папки подходит, или исключить их. Повторяемые.
--ext <list>
Расширения, которые берутся из папок.
--ignore <glob>, --no-respect-gitignore
Дополнительные шаблоны игнорирования и учёт .gitignore проекта.
--files-from <file>
Ещё пути, по одному в строке (- — stdin, # начинает комментарий).
-w, --widths <list>, --heights <list>
Выходные ширины или высоты в пикселях через запятую. С --replace действуют как максимум.
--long <list>, --short <list>
Размер по длинной или короткой стороне в пикселях через запятую, независимо от ориентации. С --replace действуют как максимум.
--megapixels <list>, --percent <list>
Размер по площади в мегапикселях (2,0.5) или в процентах от исходника (50,25) через запятую.
--match-orientation
Развернуть рамку size для изображений другой ориентации.
--no-resize
Отключить размеры конфигурации на этот запуск: все заданные в ней размеры (widths, size, longEdge, percent и остальные, а также scale) игнорируются, поэтому действуют только формат и прочие настройки, а в имени файла по умолчанию нет размера. Запуск без конфигурации и без флагов размера и так ничего не уменьшает. Не сочетается с флагами размера.
--max-size <size>
Ограничить каждый файл этим размером, например 200KB или 1.5MB, снижая качество настолько, насколько нужно.
--sharpen <preset>
Обработать результаты резкостью по сохранённому пресету резкости.
--sharpen-for <target>
Обработать результаты резкостью для screen, matte или glossy. См. Резкость.
--sharpen-amount <amount>
Степень резкости: low, standard или high.
--sharpen-radius <sigma>, --sharpen-flat <n>, --sharpen-jagged <n>, --sharpen-threshold <n>
Тонкие настройки резкости. Каждая заменяет значение таблицы (или пресета); значение вне диапазона завершает команду с кодом 2.
-f, --formats <list>
Выходные форматы через запятую.
-q, --quality <n|level|fmt=n,…>
Качество: число, уровень или значение по форматам.
--codec <fmt.option=value>
Одна опция кодека, например jpg.mozjpeg=true. Повторяемый.
--fit <mode>
cover, contain, inside, outside или fill.
--name <template>
Шаблон имени файла.
--flatten [color]
Положить прозрачность на сплошной фон, белый, если цвет не указан.
--hazehash, --blurhash, --thumbhash, --dominant-color
Посчитать это для каждого исходника; результат попадает в --emit.
--budget <bytes>
Размер hazehash, 7–48.
--emit <file>, --key-prefix <text>
Записать JSON-манифест и поставить этот текст перед путями в его placeholders.
-i, --select, --list
Выбрать файлы из списка или только напечатать файлы.
--overwrite, --no-overwrite, --skip-existing, --force
Что делать с уже существующими результатами; см. выше.
--cache [file], --no-cache
Где хранится запись о сделанном или не использовать её.
--concurrency <n>
Сколько изображений обрабатывается параллельно, по умолчанию 4.
--max-pixels <n>
Отказаться от изображений с большим числом пикселей (0 снимает предел).
--dry-run
Ничего не писать; показать, что было бы сделано и какие файлы были бы перезаписаны или заменены.
-y, --yes
Согласиться без вопросов: установить недостающую библиотеку sharp, подтвердить --replace.
--json, --quiet, --verbose, --plain, --color
Вывод для машин; тишина при успехе; перечислить каждый созданный файл (большой запуск по умолчанию перечисляет только проблемы); без цвета; принудительный цвет.
Команда restore принимает <dir>, --dry-run, --force, --yes, --json, --cwd, --plain и --color.
Коды выхода
0 — всё записано, заменено, оставлено, уже сделано или пропущено по просьбе (--skip-existing). 1 — файл не удалось прочитать или записать, найден конфликт, а спросить некого (или --no-overwrite), запуск остановлен клавишей q или отклонён на подтверждении --replace; для restore — файл оставлен нетронутым либо его копия пропала или повреждена. 2 — ошибка использования: неверный флаг, конфигурация, шаблон или опция, совпадение имён результатов или --replace без --yes вне терминала; о ней сообщается до начала записи.
Библиотека sharp
Работу делает sharp, та же нативная библиотека, что и у image-hash. Она идёт вместе с mediatoolz; только когда её бинарника нет (см. Хеши изображений), первый запуск предлагает установить её в ~/.mediatoolz/deps или делает это сразу с --yes.