feat: add local image enhancement and lossless mod assets
CI / frontend (push) Failing after 26s
CI / backend (push) Failing after 1m37s

This commit is contained in:
mrleo1nid committed 2026-10-25 21:41:45 +03:00
1 parent cc71050362
commit f7f7f5b1b3
29 files changed
+1495 -10

No files matched your search

+159
View File
@@ -0,0 +1,159 @@
# Улучшение графики локальной нейросетью (дизайн)
Клиент у себя поднимает генератор изображений (SwarmUI; также ComfyUI и A1111), браузер ходит к нему
напрямую — как к `local-translator` ([LOCALIZATION.md](LOCALIZATION.md)). Сервер библиотеки о генераторе
не знает; он получает только готовые файлы через уже существующую замену ассетов в моде
(`PUT /api/versions/{id}/asset`, [MODDING.md](MODDING.md)).
Два режима поверх одного движка задач:
| Режим | Что делает | Где хранится результат | Видят другие |
|---|---|---|---|
| **На лету** | плеер подменяет картинку при показе на улучшенную | IndexedDB браузера, на сервер ничего не уходит | нет |
| **В исходниках** | редактор «Улучшение графики» массово обрабатывает файлы мод-форка | блобы мода (через замену ассета) | да, как обычный мод |
## Схема
```
Браузер ──► SwarmUI / ComfyUI / A1111 (127.0.0.1, разрешён CORS для сайта)
│
├─ IndexedDB: кэш результатов key = версия|путь|хэш оригинала|хэш пресета
├─ плеер: engine.fileUrl() → кэш, если есть улучшение
└─ редактор: результат → PUT /asset (мод-форк), по одному файлу
```
## Модули клиента (`frontend/src/imager/`)
* `backends/` — интерфейс `ImageBackend { health(), models(), process(png, preset, signal) → Blob }`
и адаптеры `swarm.ts` (основной: `GetNewSession`, `GenerateText2Image` с `initimage`,
`refinerupscale`/`refinerupscalemethod`, `ListT2IParams`), `comfy.ts`, `a1111.ts`.
* `presets.ts` — пресет = бэкенд + режим (`upscale` | `img2img`) + параметры (модель, апскейлер,
множитель, creativity, prompt/negative, steps, cfg). Хранится в localStorage, экспорт в JSON.
`hash(preset)` входит в ключ кэша.
* `queue.ts` — очередь задач: параллелизм 1 (GPU), отмена, повтор, возобновление после
перезагрузки (журнал в IndexedDB), прогресс.
* `cache.ts` — IndexedDB: `{key, blob, scale}`; очистка по версии.
* `ImagerSetup.tsx` — страница настройки, как `TranslatorSetup` (проверка связи, CORS-подсказка).
* `ImageEnhance.tsx` — редактор массовой обработки (ниже).
## Масштаб изображения (важно)
Плеер рисует `<img>` в натуральном размере (`renderer.ts`), поэтому ×2 апскейл без поправки покажется
вдвое больше. Решение: у каждой улучшенной картинки хранится `scale` (отношение новой ширины к исходной).
* на лету — `scale` лежит в записи кэша;
* в исходниках — `game.json → assetScale: { "images/bg room.webp": 2 }` (проверяется `mods.validate`);
* рендерер: после загрузки `width = naturalWidth / scale` для `<img>` без явного размера;
размеры из `def.size`, ATL и масок масштабом не затрагиваются.
Это единственное изменение плеера и схемы PGVN; без него замена файла на файл другого размера ломает вёрстку.
## Режим «на лету»
Хук в `Engine.fileUrl()`: если для `(версия, логический путь)` в кэше есть результат — возвращается
`blob:` URL (создаётся один раз, освобождается в `destroy`). Предзагрузка ближайших картинок уже
использует `fileUrl`, поэтому работает без изменений. Фоновая обработка: при показе картинки без улучшения
в очередь ставится её задача (опция «улучшать по ходу игры»), результат подхватывается при следующем показе.
Переключатель «оригинал / улучшенное» в меню плеера для сравнения.
## Режим «в исходниках» (редактор)
Страница доступна для `kind=mod` с `versions.fork`:
1. Список изображений версии (фильтр по папке/размеру/уже обработанные), миниатюры.
2. Выбор пресета, предпросмотр «до/после» на одном файле (слайдер).
3. Запуск пачки: для каждого файла — скачать оригинал → бэкенд → превью в списке → при «Применить»
загрузка в мод. Загрузка последовательная (каждая замена меняет `revision`; клиент берёт
актуальную из ответа). Для скорости позже — один пакетный эндпоинт `POST …/assets` с одной ревизией.
4. Откат — обычная история ревизий; «вернуть оригинал» = восстановление блоба из родителя.
## Что нужно поменять на сервере
* `converter/assets._image`: не пересжимать результат повторно в WebP q88 (потеря качества после
апскейла). Для замены из этого редактора принимать PNG/WebP без перекодирования лосслесс-веткой
(параметр `lossless=1` в `/asset`).
* лимит `WEBP_MAX = 16383`: при апскейле ×4 больших фонов результат уйдёт в PNG — предупреждать в UI.
* `assetScale` в `game.json` и его проверка в `mods.validate`.
* пакетный эндпоинт замены ассетов (вторая очередь работ).
## Риски и открытые вопросы
* **CORS**: проверить, какие заголовки отдают SwarmUI/ComfyUI/A1111 при запросах с чужого origin;
страница настройки должна объяснять флаг (`--enable-cors-header`, `--cors-allow-origins`, настройка Swarm).
Mixed content: сайт по https → локальный http; Chrome разрешает для `127.0.0.1`, Safari нет.
* **Качество**: img2img на персонажах с альфой — нейросеть не сохраняет прозрачность. Для спрайтов
режим «только апскейлер» (модель ESRGAN-типа) с отдельной обработкой альфа-канала (апскейл
маски обычным ресайзом, цвета — моделью). Это должно быть в первой версии, иначе спрайты получат ореол.
* **Анимации** (animated WebP) и видео — вне первой версии.
* **Права на результаты**: производные изображения остаются в моде пользователя, в публичные версии
не попадают без `versions.publish`.
## Этапы
1. Каркас: `imager/` (клиент SwarmUI, пресеты, очередь, кэш), `ImagerSetup`.
2. `assetScale` в схеме, рендерере и валидаторе; режим «на лету» в плеере.
3. Редактор массовой обработки + загрузка в мод + лосслесс-замена.
4. Адаптеры ComfyUI и A1111, обработка альфы, пакетный эндпоинт.
## Реализация (25 октября 2026)
Добавлены все четыре группы функций:
* `/imager` — настройка локального генератора, проверка API из браузера, список моделей и
апскейлеров, пресеты с импортом/экспортом JSON. Последний сохранённый пресет используется плеером.
* `frontend/src/imager/` — адаптеры SwarmUI, ComfyUI и A1111, общая очередь, журнал и кэш
IndexedDB. Ключ включает ID версии, логический путь, хэш исходного блоба и SHA-256 параметров
пресета (включая адрес генератора и фиксированный seed). Переименование пресета кэш не меняет.
* Плеер PGVN: Настройки → Графика → «Показывать улучшенные изображения» и отдельно
«Улучшать по ходу игры». Переключатели сохраняются по версии. По умолчанию оба выключены.
Завершённые задания подхватываются при следующем показе изображения; URL освобождаются при закрытии.
* `/versions/:versionId/enhance` — выбор картинок, фильтры пути/размера файла/готовности,
выбор пресета, сравнение слайдером, обработка выбранных файлов и отдельное применение.
Открывается из редактора мода или его карточки. Применять можно только в доступный для
редактирования черновик. Ошибки ревизий не удаляют локальные результаты.
* `PUT /api/versions/{id}/asset?lossless=1&scale=2` сохраняет проверенные статичные PNG/WebP
без повторного сжатия. `scale` — полный масштаб относительно исходных игровых координат,
поэтому повторный апскейл умножает прежний `assetScale`. Файл и метаданные пишутся одной ревизией.
Обычная ручная замена без `scale` сбрасывает прежний масштаб этого файла.
* `POST /api/versions/{id}/assets`: JSON `{revision, assets: [{path, filename, data, scale}]}`,
где `data` — base64 без префикса data URL. Максимум 32 картинки / 128 МБ запроса; редактор
дополнительно ограничивает группу 80 МБ исходных блобов. Каждый пакет атомарен и создаёт одну
ревизию. При ошибке файлы версии и её ревизия не меняются.
* `POST /api/versions/{id}/asset/restore`: `{revision, path}` возвращает файл **из замороженной
базовой ревизии форка**, вместе с прежним масштабом. Для отката пачки также доступна история мода.
* Альфа-канал масштабируется отдельно в браузере и возвращается в итоговый PNG. Img2img
прозрачных изображений отклоняется с предложением выбрать апскейлер. Анимации отклоняются,
а не превращаются незаметно в первый кадр. Изменение пропорций генератором также отклоняется.
Отмена активного задания отбрасывает его результат, но ждёт завершения запроса перед стартом
следующего: глобальный interrupt генератора мог бы остановить чужую работу. После перезагрузки
незавершённые задания видны в журнале и запускаются кнопкой «Повторить». При наличии Web Locks
очередь сериализует GPU-запросы также между вкладками этого сайта. При очистке кэша нужно закрыть
плеер, чтобы освободить уже загруженные в память blob-URL.
Текущие адаптеры рассчитаны на локальные API без обязательной авторизации. Прямые обращения
генератора не проксируются через сервер библиотеки. Для SwarmUI настройка CORS называется
`Network.AccessControlAllowOrigin`; для ComfyUI — `--enable-cors-header ORIGIN`,
для A1111 — `--api --cors-allow-origins ORIGIN`. Страница настройки проверяет доступ к API,
а скачивание результата проверяется первым пробным заданием. Реальный SwarmUI на
`127.0.0.1:7801` при разработке не отвечал: GPU-генерация и CORS установленного сервиса пока
не подтверждены. Протоколы адаптеров покрыты тестовыми HTTP-ответами.
Проверки: `frontend/tests/imager.test.mjs`, `frontend/tests/imager-browser.test.mjs`
(опциональный Playwright/Chromium, как существующие браузерные тесты),
`backend/tests/test_image_enhance.py`. Браузерный тест проверяет реальные пиксели альфы,
геометрию `<img>`, сохранность IndexedDB после перезагрузки, освобождение URL и отсутствие
загрузок на сервер в локальном режиме.
Результат проверки реализации: 9 клиентских тестов imager, 13 серверных тестов
imager/mods и отдельный тест Chromium проходят; production-сборка и Ruff проходят.
Интеграционный тест с PostgreSQL пропущен без `TEST_POSTGRES`. В полном клиентском
наборе отдельно воспроизводится сбой изменённого теста интерпретатора Ren'Py
`interpreted instances can own arguments without exposing host function properties`
(`frontend/tests/renpy-runtime.test.mjs`, неизвестное имя `list`); этот тест не импортирует imager.
Первичная документация протоколов:
[SwarmUI API](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/API.md),
[параметры SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/APIRoutes/T2IAPI.md),
[ComfyUI API](https://github.com/comfyanonymous/ComfyUI/blob/master/script_examples/basic_api_example.py),
[A1111 API](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/api/models.py).