Skip to content

Пакетная обработка изображений ​

Создаёт уменьшенные, сконвертированные и пережатые версии растровых изображений пачками. Что именно получить — размеры, форматы, настройки кодеков и имена файлов — описывает набор правил: либо в переиспользуемой конфигурации, либо прямо во флагах. Откуда брать картинки и куда класть результат, решается при каждом запуске, поэтому одна конфигурация подходит для любого проекта.

bash
mediatoolz image-batch [paths...] (-o <dir> | --beside | --replace) [options]

Это генератор, как image-hash: команда читает изображения и пишет файлы. Куда пойдут результаты, выбираете вы:

  • --out <dir> — в отдельную папку с повторением структуры папок исходников. Исходники остаются нетронутыми.
  • --beside — в папку каждого исходника, рядом с ним. Исходники остаются нетронутыми.
  • --replace — поверх самих исходных файлов, каждый в своём формате: уменьшить слишком большие оригиналы или пережать их на месте. Оригиналы предварительно копируются в резервную копию, а вернуть их можно одной командой.

Реальный вывод — два изображения, две ширины, два формата, в отдельную папку:

bash
mediatoolz image-batch img -r -o out -w 300,600 -f webp,jpg -q high
text
2 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:

bash
mediatoolz image-batch ./src/images -r -o ./public/images -w 400,800,1200 -f avif,webp,jpg -q high

Размер каждого изображения по длинной стороне, независимо от ориентации, и не больше 200 KB на файл:

bash
mediatoolz image-batch ./photos -r -o ./out --long 1600 -f webp,jpg --max-size 200KB

Тот же набор из сохранённой конфигурации, с выбором файлов из списка:

bash
mediatoolz image-batch ./src/images -r -o ./public/images -c web --select

Только сменить формат — размер остаётся прежним, ничего не уменьшается и не повышается резкость:

bash
mediatoolz image-batch ./photos -r -o ./out -f webp

Уменьшить слишком большие оригиналы до 1600 px в ширину на месте, сначала посмотрев предварительный результат:

bash
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 в ширину; третье и так компактное, поэтому остаётся как есть:

bash
mediatoolz image-batch photos --replace -w 600 --dry-run
text
3 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), запишет файлы и закончит подсказкой, как всё вернуть:

text
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

Второй такой же запуск ничего не меняет:

text
0 replaced · 3 already done

Возврат оригиналов ​

bash
mediatoolz image-batch restore .image-batch-backup/20261007-233706

restore читает 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-файл с правилами. В нём нет входных и выходных путей, так что одна конфигурация подходит для любой папки и любого проекта.

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: вы говорите, для чего предназначен результат и насколько сильно, а для редкого случая, которому нужен более точный контроль, можно задать числа самому.

json
{
  "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.
json
{ "sharpen": { "for": "matte", "radius": 1.2, "flat": 0.4, "jagged": 4 } }

Число вне диапазона останавливает команду до записи, а в сообщении названы поле и диапазон. Резкость работает по яркости изображения, поэтому цветных ореолов не добавляет.

Пресеты резкости ​

Набор значений резкости можно сохранить один раз и использовать по имени в любой конфигурации и из командной строки. Пресетами управляет собственная команда image-batch sharpen, потому что пресет не принадлежит ни одной конфигурации.

bash
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-lightscreen, lowлёгкое касание для веб-фото
web-crispscreen, standardобычная резкость для веба
web-detailscreen, high, радиус 0.8мелкие детали больших веб-фото
thumbnailscreen, standard, радиус 0.4, flat 0.6, jagged 2.5маленькие превью: тонкий радиус, бережно к ровным участкам
print-mattematte, standardматовая бумага
print-glossyglossy, standardглянцевая бумага

Собственные пресеты ​

Пресет — небольшой JSON-файл с именем пресета, с теми же полями, что у sharpen, и необязательным description, который показывается в списке:

json
{ "description": "Фото для веба", "for": "screen", "radius": 0.8, "flat": 0.4 }

Ваши пресеты лежат в .mediatoolz/image-batch/sharpen/ проекта (ищутся от рабочей папки вверх до корня репозитория) или в ~/.mediatoolz/image-batch/sharpen/ для всех проектов. Проектный пресет скрывает глобальный с тем же именем, а оба скрывают встроенный.

Пресет, встроенный или свой, используется по имени:

json
{
  "outputs": [
    { "id": "web", "longEdge": [1600], "sharpen": "web-crisp" },
    {
      "id": "print",
      "longEdge": [3000],
      "sharpen": { "preset": "web-crisp", "for": "glossy", "amount": "high" }
    }
  ]
}
bash
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:

bash
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 файла — форма манифеста плейсхолдеров. Результаты, которые уже были актуальны, тоже перечислены.

Управление конфигурациями ​

bash
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.