# Архитектура ## Назначение Jellybit принимает торрент с текстовым контекстом, скачивает его через qBittorrent, определяет содержимое (фильм или сериал с сезонами и сериями) и раскладывает файлы по конвенциям Jellyfin — хардлинками, не трогая исходную раздачу. ## Принципы - **Один статический бинарь.** Доставка — копированием на сервер. См. [ADR-2026-06-13-go-single-binary](../adr/ADR-2026-06-13-go-single-binary.md). - **Источник не трогаем.** В библиотеку кладём хардлинки; qBittorrent продолжает раздачу, место на диске не дублируется. - **Единое ядро, тонкие транспорты.** Вся логика приёма загрузки — в use-case `Ingest`. HTTP API, веб-UI и Telegram — обёртки над ним. - **Опциональные внешние зависимости.** Базы метаданных (TMDB/TVDB) включаются конфигом; без них сервис работает на одном LLM. - **Минимум компонентов.** В духе umbar — без лишних сервисов. ## Компоненты | Пакет | Ответственность | | ----------- | ----------------------------------------------------- | | `ingest` | use-case приёма загрузки, общий для всех транспортов | | `qbt` | клиент qBittorrent WebUI API | | `worker` | фоновый цикл: машина состояний, поллинг завершения | | `recognize` | пред-парс имени + LLM + модель уверенности | | `metadata` | интерфейс баз метаданных + TMDB/TVDB (опц.) | | `layout` | конвенции Jellyfin + хардлинкер | | `store` | SQLite: загрузки, распознавание, ссылки | | `httpapi` | REST + веб-UI (server-rendered, htmx) | | `tgbot` | Telegram-адаптер + парсер сообщений торрент-бота | | `config` | загрузка TOML-конфига | ## Поток и машина состояний ``` ingest → downloading → completed → recognizing ─┬─ уверенно ───────→ linking → done └─ сомнительно → review → linking → done любой шаг при ошибке → failed ``` - **ingest** — приняли источник + контекст, поставили в qBittorrent (категория `jellybit`), записали в БД. - **downloading / completed** — `worker` поллит qBittorrent по категории. - **recognizing** — `recognize` строит план раскладки и оценку уверенности (см. [recognition.md](recognition.md)). - **review** — план уходит человеку (веб-UI / Telegram), ждём решения. - **linking** — `layout` создаёт хардлинки в библиотеке. - **done** — опционально дёргаем скан библиотеки Jellyfin. Состояние персистентно в SQLite — перезапуск сервиса безопасен, `worker` продолжает с того же места. ## Транспорты Все три ведут в один `Ingest(req)`: - **HTTP API + веб-UI** — форма «добавить», список загрузок, экран подтверждения раскладки (server-rendered + htmx, без JS-сборки). - **Telegram-бот** — переслать magnet или сообщение торрент-бота прямо в jellybit; текст становится контекстом распознавания. - **CLI** — `jellybit add --context "..."` для отладки. ## Хранилище SQLite, минимум таблиц: - `download` — источник, контекст, hash торрента, категория, состояние, тайминги. - `recognition` — тип, название, год, сезон, provider-id, оценка уверенности, сырой ответ LLM. - `file_link` — соответствие исходный файл → целевой путь, вид (видео/субтитры), статус. ## Конфигурация TOML, секреты — placeholder'ы; реальный конфиг не коммитим. Пример: ```toml [qbittorrent] url = "http://127.0.0.1:8989" username = "admin" password = "" category = "jellybit" [paths] downloads = "/srv/downloads" movies = "/srv/media/movies" series = "/srv/media/series" [llm] provider = "anthropic" model = "claude-sonnet-4-6" # сложные случаи — claude-opus-4-8 api_key = "" [metadata.tmdb] enabled = true api_key = "" [metadata.tvdb] enabled = false api_key = "" [recognition] auto_confidence_threshold = 0.85 [telegram] enabled = false token = "" allowed_user_ids = [] [http] listen = ":8080" [log] level = "info" format = "json" ``` ## Логирование Структурированный JSON через `log/slog`. Каждая загрузка проходит со сквозным идентификатором; решения распознавания (почему авто/ревью) логируются явно. ## Раскладка файлов Хардлинки в `paths.movies` / `paths.series` по конвенциям Jellyfin. Детали и крайние случаи — в [jellyfin-layout.md](jellyfin-layout.md). Требование: `downloads` и `media` на одной ФС (иначе хардлинк невозможен). Если jellybit в docker — смонтировать общего родителя, чтобы хардлинк работал внутри контейнера. ## Предполагаемая структура репозитория ``` cmd/jellybit/ точка входа, сборка зависимостей internal/ ingest/ qbt/ worker/ recognize/ metadata/ layout/ store/ httpapi/ tgbot/ config/ migrations/ миграции SQLite web/templates/ шаблоны веб-UI docs/ specs / adr / drafts config.example.toml ``` ## Открытые вопросы - Подтвердить, что `/srv/downloads` и `/srv/media` — одна ФС. - Способ детекта завершения: поллинг (старт) или webhook qBittorrent «run on completion» (позже). - Где хранить секреты при деплое (шаблонизация из umbar).