# Jellybit Jellybit — связующий сервис между qBittorrent и Jellyfin. Принимает magnet-ссылку или `.torrent`-файл вместе с текстовым контекстом, ставит загрузку в qBittorrent, дожидается её завершения, распознаёт содержимое (фильм или сериал, сезоны и серии) и раскладывает готовые файлы по конвенциям библиотеки Jellyfin. Полный замысел, границы домена и типовые сценарии — в [docs/passport.md](docs/passport.md). ## Зачем Arr-стек (prowlarr/radarr/sonarr) плохо ложится на русские трекеры, аниме и ручные раздачи. Jellybit намеренно сокращает путь: одна точка входа → готовая раскладка для Jellyfin, без каталога индексаторов и сложных правил качества. Распознавание делает LLM, которому помогает переданный человеком контекст и (опционально) внешние базы метаданных. ## Как работает 1. Точка входа принимает magnet + контекст (HTTP API, веб-UI, Telegram-бот или CLI). 2. Загрузка ставится в qBittorrent в выделенную категорию. 3. Сервис отслеживает завершение загрузки. 4. По именам файлов, контексту и (опц.) базам метаданных определяется фильм/сериал и нужная раскладка. 5. Файлы **хардлинкаются** в библиотеку Jellyfin — источник остаётся в раздаче, место на диске не дублируется. 6. После раскладки сервис (опц.) просит Jellyfin пересканировать медиатеку, чтобы новые файлы быстрее появились в проигрывателе. При высокой уверенности раскладка выполняется автоматически, иначе — уходит на подтверждение человеку. ## Статус Рабочий прототип с полным сквозным путём: приём magnet → загрузка в qBittorrent → распознавание (LLM + опционально базы метаданных TMDB/TVDB/TVMaze) → раскладка в библиотеку хардлинками, автоматически при уверенном результате либо через подтверждение человеком. Транспорты приёма: REST API, веб-UI, Telegram-бот и CLI (`jellybit add`). Из источников поддержаны magnet и `.torrent`-файл; фетч `.torrent` по обычной ссылке — в планах. Что дальше — [docs/tasks/PLAN.md](docs/tasks/PLAN.md). ## Документация Разработка идёт по **Spec-Driven Development** через [OpenSpec](https://github.com/Fission-AI/OpenSpec): изменение сначала описывается спекой, потом реализуется. - [openspec/specs/](openspec/specs/) — **что система делает**, нормативно: capability-спеки. Изменения (proposal → design → tasks → archive) — в `openspec/changes/`. - [docs/passport.md](docs/passport.md) — зачем и для кого, чем **не** является. - [docs/architecture.md](docs/architecture.md) — как сложено: компоненты, внешние границы, эксплуатация, единые точки, деплой. - [docs/database.md](docs/database.md) — схема хранилища и настройки. - [docs/security.md](docs/security.md) — периметр и модель угроз. - [docs/conventions/](docs/conventions/README.md) — как пишем код: [логи](docs/conventions/logging.md), [ошибки](docs/conventions/errors.md), [конфиг](docs/conventions/config.md), [БД](docs/conventions/database.md), [веб-UI](docs/conventions/web-ui.md). - [docs/adr/](docs/adr/README.md) — журнал решений (почему так), неизменяемый. - [docs/research/](docs/research/README.md) — наблюдения за чужими форматами. - [docs/tasks/](docs/tasks/BACKLOG.md) — задачи и цели. Раскладка документации задана каноном av-dev и проверяется шагом `canon` в `task gate`. ## Стек Go (один статический бинарь), SQLite (`modernc.org/sqlite` + `sqlx`, миграции `goose`), HTTP — `chi` + `html/template` + htmx, конфигурация — TOML, логи — структурированный JSON (`slog`). Подробнее — в [docs/architecture.md](docs/architecture.md). ## Конфигурация Конфигурация — один файл TOML. По умолчанию ищется `config.toml` в рабочей директории; путь переопределяется опцией `--config=path`. Образец со всеми секциями и описанием каждого поля (назначение, диапазон значений, единицы измерения) — [config.example.toml](config.example.toml); скопируй его в `config.toml` и заполни под себя. Конфиг валидируется на старте: при ошибке сервис не стартует. Секреты (пароль qBittorrent, ключи LLM/метабаз, токен Telegram) в репозиторий не коммитятся — их подставляет деплой прямо в файл. Доступ к внешним сервисам (LLM, базы метаданных, Telegram) при необходимости идёт через HTTP-прокси — поле `proxy` в соответствующих секциях. Правила — в [docs/conventions/config.md](docs/conventions/config.md). ## Разработка Нужны Go 1.26 и [Task](https://taskfile.dev). Полный список задач — `task --list`. ```bash cp config.example.toml config.toml # локально: db_path -> ./jellybit.db task setup # golangci-lint + git-хуки lefthook task tidy # go mod tidy task run # go run ./cmd/jellybit --config ./config.toml task test lint # тесты и golangci-lint task build # статический бинарь (linux/amd64) для сервера task image # docker-образ из готового бинаря ``` Отладка распознавания на реальной раздаче (только чтение, без раскладки): ```bash jellybit recognize --dry-run [--context "..."] --config ./config.toml ``` Берёт торрент из qBittorrent по infohash, прогоняет распознавание (LLM + метабазы) и печатает план: тип/название/год, матч в базе, решение авто/review и превью целевых путей — то, что создалось бы при Apply. ## Доставка Рассчитан на домашний медиа-сервер. Артефакты репозитория — статический бинарь (`task build`) и `Dockerfile` (упаковка в `distroless/static`). Образ собирается целиком **локально** на control-хосте (`task image`) и едет на сервер через `docker save`/`load` (роль `app_image` в umbar), поэтому Go-тулчейн и `docker build` на сервере не нужны. В distroless нет shell/curl, поэтому HEALTHCHECK зовёт сам бинарь: `jellybit healthcheck` (GET `/healthz` по порту из конфига, exit 0/1). Контейнер: `user 1000:1000`, порт `8080` на хост, mount `/srv/media` (единая песочница для хардлинков) + том `/config` (ro, `config.toml`, восстановим при деплое) + data-том `/data` (SQLite, бекапить); к qBittorrent — по сети Docker. Конкретная деплой-обвязка (плейбук, секреты) держится в отдельном приватном репозитории и в комплект не входит.