Skip to content

Repository files navigation

LLAO — Lossless Audio Optimizer

Пережимает lossless-аудио в минимальный по размеру формат без потерь: полным перебором всех поддерживаемых форматов и уровней сжатия, с проверкой результата и переносом тегов.

Из всех кандидатов выбирается файл минимального суммарного размера (аудио + теги). Победитель валидируется побайтово и заменяет исходный файл. Аудио и теги сохраняются без потерь.

Примеры экономии сильно зависят от контента: на обычной музыке в среднем выигрывает OptimFROG или TAK, на шумоподобном материале (студийный шум, эффекты) часто побеждает Monkey's Audio. Оценка по вашей коллекции — llao.exe stats.

Быстрый старт

  1. Скачайте последний релиз llao-v<версия>-win64.zip со страницы Releases и распакуйте в любую папку (установка не требуется). В архиве уже есть все кодеки — интернет при работе не нужен.

  2. Запустите из командной строки:

    llao.exe tools
    llao.exe optimize "D:\Music" --jobs=8

    tools — один раз проверит/скачает утилиты кодеков (при скачивании сверяются контрольные суммы). optimize — пережмёт все аудиофайлы в папке рекурсивно.

    На Linux (под wine) для llao.exe предусмотрена обёртка llao, которая обеспечивает всё, чего wine не даёт из коробки, не требуя правок llao.exe:

    • Синглтон демона. Именованный мьютекс Local\llao-singleton в wine существует в рамках одного wineserver (= одного WINEPREFIX), поэтому все запуски идут в один фиксированный префикс (~/.llao-wine, переопределяется через LLAO_WINEPREFIX или WINEPREFIX) — вторая копия serve отвергается, как в Windows.
    • Инициализация префикса. Свежий префикс прогревается через wineboot -u (как в CI проекта).
    • Шум отладчика подавляется (WINEDEBUG=-all, как в тестах проекта), stderr wine уходит в ${LLAO_LOG:-/tmp/llao-wine.log}.
    • Ширина статус-бара определяется через stty/tput и передаётся через LLAO_STATUS_SIZE (GetConsoleScreenBufferInfo в wine падает).
    ./llao tools
    ./llao optimize "/media/Music" --jobs=8
    ./llao serve

    Если бит исполняемости не сохранился (например, после распаковки zip) — sh llao ....

Ключевые свойства

  • Никаких потерь. Аудио переносится побайтово (декод → эталонный PCM → кодирование → проверка декода против эталона). Теги переносятся полностью: каждый формат тегов хранится отдельно и либо встраивается в целевой формат, либо выносится в ZIP-sidecar рядом с файлом.
  • Полный перебор. Покрываются все форматы и все варианты сжатия; для каждого файла перебор идёт в отдельном процессе параллельно (--jobs).
  • Самодостаточность. Консольные утилиты кодеков скачиваются автоматически (с проверкой SHA-256) и складываются в bin/<id>/; в релизном архиве они уже есть.
  • Windows-native. Единый исполняемый файл llao.exe (C++17, без установки и внешних библиотек). На Linux работает под wine (обёртка llao) или нативно (make TARGET=linux). Все функции — CLI и headless-сервер — в одном бинарнике.
  • Умный порядок перебора. Внутри накапливается статистика (формат, вариант, степень сжатия, тип контента); наиболее вероятные победители перебираются первыми. Таблицу показывает llao.exe stats.
  • Lossy-входы по умолчанию пропускаются (mp3/aac и пр. пережимать в lossless бессмысленно); обработка — только с флагом --allow-lossy.
  • Контроль устаревания конфигов. При скачивании утилиты её вывод --help сверяется с ожиданиями конфига; при расхождении выдаётся предупреждение.
  • Журнал прогона. С --debug каждое событие (запуск, файлы, кандидаты, ошибки) пишется в runs/*.jsonl — удобно для анализа победителей и разбора сбоев.
  • Демон и веб-UI. Подкоманда llao serve — headless-процесс с HTTP-API и веб-интерфейсом: очередь можно пополнять, переупорядочивать, ставить на паузу и перезапускать файлы на лету, не занимая терминал (раздел «Демон и веб-UI»).

Форматы

id Формат Расширение Утилита Примечание
flac FLAC .flac flac.exe уровни -0..-8, встроенный MD5
wavpack WavPack .wv wavpack.exe -h/-hh + -x0..-x6, MD5
monkeys_audio Monkey's Audio .ape mac.exe уровни -c1000..-c5000
alac Apple Lossless .m4a ffmpeg.exe без уровней
tta True Audio .tta ffmpeg.exe без уровней
tak TAK .tak takc.exe -p0..-p4 + уровни E/M (-p4m), CRC+MD5
optimfrog OptimFROG .ofr ofr.exe пресеты 0..10 + max, MD5
la LA .la la.exe только 16 бит, только ID3v1
mpeg4_als MPEG-4 ALS .m4a ffmpeg.exe выключен (кодер удалён из новых ffmpeg)

ffmpeg-форматы (alac/tta/mpeg4_als) делят одну общую сборку bin/ffmpeg/. Полный список параметров — llao.exe variants.

CLI

llao.exe check-formats                        # валидация конфигурации форматов
llao.exe tools [fmt_id ...] [--no-download]   # статус/скачивание утилит в bin/<id>/
llao.exe optimize <файл|папка> [--jobs=N|M.F]  # перебор форматов/параметров (рекурсивно)
                 [--formats flac,wavpack]     # ограничить набор форматов
                 [--report=<файл|папка>]      # итоговый отчёт (или каталог для него)
                 [--no-download] [--dry-run]  # без скачивания / без замены файлов
                 [--allow-lossy]              # обрабатывать и lossy-входы (mp3, aac, …)
                 [--verify=all|winner|none]   # режим проверки кандидатов (по умолчанию all)
                 [--ignore-errors]            # ошибки файлов — помечать skip, не прерывать прогон
                 [--tmp=<path>]               # путь к временной папке (по умолчанию рядом с бинарником)
                 [--debug] [--no-stats]       # журнал runs/*.jsonl / без накопления stats.json
llao.exe restore <файл|папка> [--jobs=N|M.F]   # обратная оптимизация: вернуть в целевой
                 [--to=flac]                  # формат (по умолчанию FLAC), теги возвращаются
                 [--variant=<id>] [--no-download] [--allow-lossy]
llao.exe help <fmt_id> -- <аргументы>         # запустить утилиту кодека (--help и т. п.)
llao.exe serve [опции]                        # headless-движок с HTTP-API и веб-UI
llao.exe stats                                # показать накопленную статистику
llao.exe variants                             # список форматов и вариантов сжатия

--jobs=N — точное число параллельных процессов; --jobs=M.F — множитель числа доступных ядер (например, --jobs=1.5 на 16 ядрах даст 24 процесса). По умолчанию --jobs=2.0, т.е. вдвое больше ядер. Число одновременно запущенных процессов дополнительно ограничивается свободным местом на tmp-диске и доступной памятью (окно адаптируется под размер файлов).

--verify управляет проверкой кандидатов:

  • all (по умолчанию) — каждый кандидат проверяется builtin-проверкой формата (flac -t и т. п.), декодом и побитовым сравнением PCM с эталоном;
  • winner — при отборе кандидаты не проверяются, полностью проверяется только победитель (лучший по размеру), перед заменой файла. Быстрее, но ошибки выявляются позже, только у одного варианта;
  • none — никакой проверки, замена сразу. Максимальная скорость, риск повреждённых файлов — на пользователе.

Ошибка любого файла (битый вход, сбой вариантов, недоступная утилита, не прошедший проверку победитель) по умолчанию прерывает прогон с подробным отчётом: активные процессы (кодеры/декодеры в других потоках) завершаются немедленно, не дожидаясь их окончания. С флагом --ignore-errors такие файлы помечаются skip (исходник не заменяется), а прогон продолжается; --ignore-errors не влияет на restore.

Язык интерфейса

Вывод локализуется через каталоги lang/<код>.json. Язык выбирается в порядке приоритета:

  1. Флаг --lang=ru|en (в любом месте командной строки);
  2. переменная окружения LLAO_LANG=ru|en;
  3. поле "language" в llao.json рядом с llao.exe ("auto" по умолчанию);
  4. автоопределение (LANG, затем язык пользователя Windows).

Пример: в файле llao.json рядом с llao.exe:

{ "language": "ru" }

Демон и веб-UI

Подкоманда llao serve запускает headless-движок без консольного вывода; управление — через HTTP-API и веб-интерфейс. Можно добавлять файлы и папки в уже работающую очередь, переставлять их, перезапускать, чистить завершённые — терминал не нужен. Бинарник один: llao.exe / llao-linux.

Запуск

llao serve [опции]

Опции сессии (дефолты оптимизации):

  • --jobs N|M.F — число потоков или множитель ядер (по умолчанию 2.0);
  • --verify all|winner|none — режим проверки кандидатов (по умолчанию winner);
  • --dry-run — не записывать результат;
  • --no-download — не скачивать кодеки (стартовый гейт только проверяет);
  • --no-stats — не накапливать stats.json;
  • --debug — журнал runs/*.jsonl и отладка;
  • --report PATH — итоговый отчёт при остановке демона (файл или каталог);
  • --restore-to ID — целевой формат режима восстановления (по умолчанию flac; id из formats/*.json — проверяется при старте).

Опции сервера:

  • --bind ADDR — адрес прослушивания (по умолчанию 0.0.0.0 — все интерфейсы, сервис доступен с других компьютеров локальной сети);
  • --port N — порт (по умолчанию 18180); 0 = свободный;
  • --token HEX — явный токен доступа (иначе генерируется и печатается при старте);
  • --no-auth — выключить авторизацию (только локальная отладка);
  • --discovery PATH — переопределить путь к discovery-файлу.

Пример:

llao serve

При старте демон печатает адрес, токен и путь discovery-файла; токен нужен для входа в веб-интерфейс. С другого компьютера открывайте http://<IP-машины>:18180/ и вводите тот же токен. Если порт не наружу, разрешите его в файрволе (Windows: netsh advfirewall firewall add rule name="llao" dir=in action=allow protocol=TCP localport=18180). On Linux: wine llao.exe serve либо нативный ./llao-linux serve.

Одна копия демона на сессию: повторный запуск отвергается (на Windows — именованный мьютекс Local\llao-singleton) — очередь и tmp-каталог не делятся. На Linux под wine тот же мьютекс живёт в одном wineserver — обёртка (./llao serve) гарантирует единый WINEPREFIX, поэтому синглтон работает так же; нативный ./llao-linux serve синглтона не имеет.

Веб-интерфейс

Открыть http://127.0.0.1:<порт>/ и ввести токен. Возможности:

  • добавление папок/файлов (рекурсивно) в работающую очередь — кнопка «+» в строке заголовка открывает модальное окно; поле «Выход» задаёт каталог вывода для обоих режимов: результат пишется в <целевая папка>/<подкаталог пачки>/<файл> (структура сохраняется), исходники не трогаются; пустое поле — замена на месте; флаг «Восстановление» включает режим restore. В optimize при заданной целевой папке пишется лучший кандидат, даже если он не меньше исходника (полная конвертация пачки); если победителя нет вовсе (ни один кандидат не прошёл валидацию) — файл пропускается;
  • статус файлов — цвет фона строки (в очереди / подготовка / работа / готово / остановлен / ошибка); прогресс: для работающих — доля выполненных задач (синий), для готовых — экономия в процентах (зелёный). В режиме восстановления проценты честные: при росте файла убыток показывается серым. Путь результата в колонке «Файл» — относительно целевой папки;
  • массовые операции над выделенными строками: остановка, запуск, перемещение в начало/вверх/вниз/в конец, удаление; перетаскивание строк; сортировка по пути;
  • пауза всей очереди, перезапуск остановленных, очистка только успешно завершённых;
  • статусбар задач в строке заголовка очереди (между «+» и кнопками действий): доля выполненных задач, включая распаковку файлов в WAV. У строк с построенным планом — точное число задач; у строк до подготовки — число вариантов сессии из formats/*.json (через /api/formats) как предварительная оценка, ничего не засчитывая сделанным (после построения плана цифра заменяется на точную). Клик по статусбару включает/выключает автоскролл за обрабатываемыми файлами (следует и вверх при чистке списка или перемещении активных строк); тёмная полоса прокрутки.

Токен и настройка автоскролла запоминаются браузером. В dev-режиме (запуск из каталога с исходниками web/ рядом) демон отдаёт файлы прямо с диска — правки HTML/JS/CSS применяются обновлением страницы без пересборки и без остановки активных задач; в релизе ассеты вшиты в исполняемый файл.

API

Все запросы, кроме GET / и /static/*, требуют заголовок Authorization: Bearer <token>.

Endpoint Назначение
GET /api/state снимок: версия, опции сессии, счётчики, пауза, строки очереди
GET /api/events?since=N событийный лог (log/task/state/…) с last_seq для дельты
GET /api/formats список включённых форматов
POST /rpc JSON-RPC: { "cmd": "...", "args": {...} }

Команды /rpc: ping, stat, add, cancel-file, remove, bulk-cancel, bulk-remove, clear-done, sort, restart, pause/cancel-all, resume, reorder, shutdown, formats, debug.

Discovery-файл — %LOCALAPPDATA%/llao/daemon.json на Windows, $XDG_DATA_HOME/llao/daemon.json на Linux (переопределяется --discovery или env LLAO_DISCOVERY). Содержит port, token, pid, version — позволяет клиентам подключиться к работающему демону.

Персистентность очереди

Рядом с discovery-файлом демон держит queue.json (формат version: 2, порядок строк = порядок очереди). Состояние обновляется при каждом изменении очереди и в конце shutdown, поэтому:

Не запускайте второй экземпляр демона без указания --discovery. Он подхватит ту же боевую очередь ($XDG_DATA_HOME/llao/queue.json) и начнёт обрабатывать её параллельно: несколько процессов одновременно пишут queue.json и выжигают все ядра кодеками, а перезапуск «повисшего» тестового экземпляра возможен только через его собственный discovery-файл. Для любых отладок/проверок изолируйте экземпляр: llao serve --port N --discovery /tmp/llao-test/<id>.jsonqueue.json будет создан рядом с ним и боевая очередь не затрагивается.

  • после корректного рестарта демона ok-строки сохраняются, активные продолжают обработку, остановленные/ошибки — на своих местах с причиной;
  • строки хранят абсолютный корень (root) + относительный от него путь (path): очередь не зависит от рабочей директории демона, и перезапуск из любого каталога распознаёт источники (относительные добавления абсолютизируются уже в момент add); старые строки без root сохраняют прежнее поведение;
  • после аварийного завершения (kill -9, выключение машины) активная обработка не «маскируется»: на следующем старте неоконченные строки помечаются остановлено при перезапуске (обработка не завершена), а queued-строки снова уходят в очередь (справедливо перепроверяются исходники и их sidecar; пропавший файл помечается stopped с причиной);
  • ok-строка восстанавливается с проверкой результата и его sidecar по фактическому пути (out_path).

Запись результата crash-безопасна: кандидат сначала копируется во временный файл рядом (.<имя>.llao-tmp.<ext>), старый файл удаляется, затем временный переименовывается на место — без резервной копии и корректным расширением. Sidecar доставляется той же транзакцией (.<имя>.llao-tmp.tags.zip): если теги встроены в целевой формат, устаревший <имя>.tags.zip удаляется; если кандидату внешний sidecar нужен — он атомарно заменяется. При обрыве между шагами queue.json остаётся с исходной строкой, и следующий запуск честно сообщит о неоконченной работе, а не покажет ложное «готово».

Как это работает (кратко)

  1. Читаются и валидируются конфиги formats/*.json.
  2. tools: для каждого включённого формата находится утилита — кэш bin/<id>/ → PATH → скачивание по URL (SHA-256), распаковка архива или извлечение из установщика через 7-Zip (без установки; скачанный установщик кэшируется и не перекачивается).
  3. Для каждого файла (в отдельном процессе):
    • проба (ffprobe) → декод в эталонный WAV; lossy-входы и файлы с видеопотоком пропускаются (кроме --allow-lossy);
    • извлечение канонического набора тегов (текст, картинки, лирика, cuesheet, ReplayGain, главы);
    • перебор подходящих форматов и вариантов сжатия;
    • для каждого кандидата: кодирование → встроенная проверка → декод → побайтовое сравнение PCM с эталоном → запись тегов (встроенных либо ZIP-sidecar) → валидация тегов;
    • победитель = кандидат минимального размера.
  4. Победитель заменяет исходник (с корректным расширением), при необходимости рядом кладётся sidecar .tags.zip.
  5. Каждый кандидат пополняет локальную статистику stats.json — она определяет порядок перебора в следующих прогонах.
  6. Всё логируется и сводится в отчёт: таблица форматов, экономия, причины исключений.

Восстановление (restore)

restore возвращает файлы в исходный формат (по умолчанию FLAC) с сохранением тегов. Интерфейс аналогичен оптимизатору. Для каждого файла — две фазы: декод исходника → кодирование в целевой формат.

Теги

  • Каждый встреченный формат тегов (ID3v2, RIFF LIST INFO, Vorbis-коммент, APEv2, ID3v1, MP4 ilst) извлекается как отдельная группа.
  • При сжатии одиночная группа конвертируется в нативный тип целевого формата; при нескольких группах каждая встраивается в свой тип, если цель его поддерживает, иначе — в ZIP-sidecar (tags.zip с tags.json и pictures/). Конфликтующие группы не сливаются.
  • При восстановлении (restore) согласованный набор сжимается в одну нативную группу; старые sidecar v1 тоже читаются.
  • Стоимость кандидата — размер файла (+ sidecar); данные не теряются никогда.

Сборка из исходников

  • Целевая ОС: Windows 10+. Кросс-компиляция MinGW-w64; рабочий тулчейн — llvm-mingw: export PATH="$HOME/opt/llvm-mingw-<ver>-…/bin:$PATH" && make.
  • Dev-контейнер с полным окружением (llvm-mingw + wine32/64 + ffmpeg + p7zip): make docker-image docker-build (соберёт llao.exe и llao-linux), make docker-shell — интерактивная оболочка с проектом в /work. Это удобно для прогона wine-зависимой логики (llao.exe, кодеки) без установки чего-либо на хост.
  • Нативная Linux-сборка: make TARGET=linux. Исполняемый файл llao работает нативно; кодеки (Windows-версии) выполняются под wine.
  • Запуск под wine: wine llao.exe … (утилиты кодеков — Windows-версии). На Linux обёртка llao подавляет шум wine (WINEDEBUG=-all).
  • Зависимости для сборки: nlohmann/json и miniz лежат в third_party/.
  • Собирается один бинарник llao (llao.exe / llao-linux): движок (optimize*.cpp, tags_*.cpp — декомпозированные модули), событийный слой (serve_*.cpp, serve_internal.h) и main.cpp (диспетчер подкоманд) линкуются вместе (Makefile). Веб-ассеты вшиваются через tools/embed_assets.py (перегенерируется при правке web/*).
  • Подробности архитектуры и история декомпозиции монолитов — в docs/architecture-audit.md.
  • Схема конфигов форматов — в formats/README.md.

Тесты

  • Нативные (без wine, собираются g++): make test-unit test-daemon-core && ./test-unit && ./test-daemon-core.
  • Интеграционные тесты сервера (нужны собранный llao-linux, ffmpeg в PATH и кодеки в bin/): make TARGET=linux затем make test-daemon (restore → interactions → queue → persist). Каждый тест поднимает свой демон на случайном порту (--no-auth), 18180 не трогает. test-daemon-persist покрывает queue.json, грациозный и аварийный перезапуски очереди и транзакционную доставку sidecar.
  • CI: .github/workflows/ci-linux.yml — собирает linux-бинарник, ставит кодеки нативным llao-linux tools (Windows-версии кодеков выполняются под wine) и гоняет все тесты; bin/ кэшируется по formats/*.json.
  • Релизный прогон (release.yml, тег v*): сборка llao.exe под wine + llvm-mingw, CLI-эталоны кодеков, актуальность версий, перенос тегов и ошибки.

Лицензия

MIT — см. LICENSE. Кодеки в bin/ распространяются под собственными лицензиями авторов (см. README/LICENSE.txt внутри каталогов форматов).

About

Оптимизация lossless-аудио: автоматический перебор форматов и параметров сжатия для минимального размера при полном сохранении качества и тегов. Windows, единый исполняемый файл

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages