Уточнение деталей архитектуры

This commit is contained in:
2026-06-13 17:42:25 +03:00
parent 5b53f4e8e8
commit 547940ea59
6 changed files with 220 additions and 30 deletions
+53 -14
View File
@@ -26,7 +26,8 @@ qBittorrent, определяет содержимое (фильм или сер
| `ingest` | use-case приёма загрузки, общий для всех транспортов |
| `qbt` | клиент qBittorrent WebUI API |
| `worker` | фоновый цикл: машина состояний, поллинг завершения |
| `recognize` | пред-парс имени + LLM + модель уверенности |
| `recognize` | пред-парс имени + вызов LLM + модель уверенности |
| `llm` | провайдер LLM за интерфейсом (дискриминатор `type`) |
| `metadata` | интерфейс баз метаданных + TMDB/TVDB (опц.) |
| `layout` | конвенции Jellyfin + хардлинкер |
| `store` | SQLite: загрузки, распознавание, ссылки |
@@ -44,10 +45,12 @@ ingest → downloading → completed → recognizing ─┬─ уверенно
- **ingest** — приняли источник + контекст, поставили в qBittorrent
(категория `jellybit`), записали в БД.
- **downloading / completed** — `worker` поллит qBittorrent по категории.
- **downloading / completed** — `worker` поллит qBittorrent по категории
(интервал `worker.poll_interval`, по умолчанию 5 с).
- **recognizing** — `recognize` строит план раскладки и оценку
уверенности (см. [recognition.md](recognition.md)).
- **review** — план уходит человеку (веб-UI / Telegram), ждём решения.
- **review** — план уходит человеку (веб-UI / Telegram), ждём решения;
сценарии — в [review-ux.md](review-ux.md).
- **linking** — `layout` создаёт хардлинки в библиотеке.
- **done** — опционально дёргаем скан библиотеки Jellyfin.
@@ -77,7 +80,10 @@ SQLite, минимум таблиц:
## Конфигурация
TOML, секреты — placeholder'ы; реальный конфиг не коммитим. Пример:
TOML. В репозитории — `config.example.toml` с placeholder'ами; реальный
`config.toml` рендерится при деплое Ansible-шаблоном из переменных umbar
(секреты — в `vars/secrets.yml` под ansible-vault) и не коммитится.
Пример:
```toml
[qbittorrent]
@@ -92,9 +98,11 @@ movies = "/srv/media/movies"
series = "/srv/media/series"
[llm]
provider = "anthropic"
model = "claude-sonnet-4-6" # сложные случаи — claude-opus-4-8
# type — дискриминатор реализации; пока поддерживается "openai-compat"
type = "openai-compat"
base_url = "http://127.0.0.1:1234/v1"
api_key = ""
model = "qwen2.5-32b-instruct"
[metadata.tmdb]
enabled = true
@@ -104,6 +112,9 @@ api_key = ""
enabled = false
api_key = ""
[worker]
poll_interval = "5s" # как часто опрашивать qBittorrent
[recognition]
auto_confidence_threshold = 0.85
@@ -126,20 +137,39 @@ format = "json"
сквозным идентификатором; решения распознавания (почему авто/ревью)
логируются явно.
## Деплой
Jellybit работает в **docker** — в одной среде с qBittorrent и Jellyfin
(они тоже в контейнерах на umbar). Единая среда запуска перевешивает
простоту нативного systemd.
Сборка — дёшево и сердито: статический бинарь собирается здесь; на сервер
во временную build-папку кладутся бинарь + `Dockerfile` (он просто
копирует бинарь в минимальный образ), образ собирается прямо на сервере и
запускается. Go-тулчейн на сервере не нужен — только docker.
Разделение ответственности:
- **jellybit** (этот репозиторий) — производит статический бинарь и
`Dockerfile`.
- **umbar** — оркестрация деплоя: доставка артефактов, `docker build` и
запуск через docker compose (`playbook-jellybit.yml`).
## Раскладка файлов
Хардлинки в `paths.movies` / `paths.series` по конвенциям Jellyfin.
Детали и крайние случаи — в [jellyfin-layout.md](jellyfin-layout.md).
Требование: `downloads` и `media` на одной ФС (иначе хардлинк
невозможен). Если jellybit в docker — смонтировать общего родителя,
чтобы хардлинк работал внутри контейнера.
`/srv/downloads` и `/srv/media` одна ФС (подтверждено), поэтому
хардлинки применимы. Так как jellybit в docker (см. «Деплой»), контейнеру
монтируем общего родителя `/srv` — чтобы внутри оба каталога остались на
одной ФС и хардлинк проходил.
## Предполагаемая структура репозитория
```
cmd/jellybit/ точка входа, сборка зависимостей
internal/
ingest/ qbt/ worker/ recognize/ metadata/
ingest/ qbt/ worker/ recognize/ llm/ metadata/
layout/ store/ httpapi/ tgbot/ config/
migrations/ миграции SQLite
web/templates/ шаблоны веб-UI
@@ -147,9 +177,18 @@ docs/ specs / adr / drafts
config.example.toml
```
## Решённые вопросы
- `/srv/downloads` и `/srv/media` — одна ФС (подтверждено); хардлинки
применимы.
- Детект завершения — поллинг qBittorrent раз в несколько секунд
(`worker.poll_interval`). Webhook — возможная оптимизация на будущее
([drafts/ideas.md](../drafts/ideas.md)).
- Секреты — в переменных umbar; `config.toml` рендерится Ansible-шаблоном
при деплое.
- Форма запуска — **docker**, образ собирается на сервере из готового
бинаря (см. «Деплой»).
## Открытые вопросы
- Подтвердить, что `/srv/downloads` и `/srv/media` — одна ФС.
- Способ детекта завершения: поллинг (старт) или webhook qBittorrent
«run on completion» (позже).
- Где хранить секреты при деплое (шаблонизация из umbar).
Существенных пока нет.