# Jellybit

Jellybit — связующий сервис между qBittorrent и Jellyfin. Принимает
magnet-ссылку вместе с текстовым контекстом, ставит загрузку в
qBittorrent, дожидается её завершения, распознаёт содержимое (фильм или
сериал, сезоны и серии) и раскладывает готовые файлы по конвенциям
библиотеки Jellyfin.

Полный замысел и причины — в [BRIEF.md](BRIEF.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` и обычные ссылки — в планах.
См. [дорожную карту](docs/drafts/roadmap.md).

## Документация

Разработка идёт по **Spec-Driven Development** через
[OpenSpec](https://github.com/Fission-AI/OpenSpec): изменение сначала
описывается спекой, потом реализуется.

- [openspec/](openspec/) — OpenSpec: актуальные capability-спеки в
  `openspec/specs/`, изменения (proposal → design → tasks → archive) в
  `openspec/changes/`. Capabilities постепенно переносятся сюда из
  `docs/specs/`.
- [docs/conventions/](docs/conventions/) — конвенции кода (*как* пишем):
  логирование ([logging.md](docs/conventions/logging.md)), конфигурация
  ([config.md](docs/conventions/config.md)), ошибки
  ([errors.md](docs/conventions/errors.md)).
- [docs/specs/](docs/specs/) — спецификации устройства системы
  (архитектурный обзор + ещё не перенесённые в OpenSpec темы). Начать с
  [architecture.md](docs/specs/architecture.md).
- [docs/adr/](docs/adr/) — журнал архитектурных решений (почему так).
- [docs/drafts/](docs/drafts/) — черновики: планы, идеи, нерешённое.

## Стек

Go (один статический бинарь), SQLite (`modernc.org/sqlite` + `sqlx`,
миграции `goose`), HTTP — `chi` + `html/template` + htmx, конфигурация —
TOML, логи — структурированный JSON (`slog`). Подробнее — в
[architecture.md](docs/specs/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 <infohash> --dry-run [--context "..."] --config ./config.toml
```

Берёт торрент из qBittorrent по infohash, прогоняет распознавание (LLM +
метабазы) и печатает план: тип/название/год, матч в базе, решение авто/review
и превью целевых путей — то, что создалось бы при Apply.

## Доставка

Рассчитан на домашний медиа-сервер. Артефакты репозитория — статический
бинарь (`task build`) и `Dockerfile` (упаковка в `distroless/static`). Образ
собирается **на сервере** из доставленного бинаря, поэтому Go-тулчейн на
сервере не нужен. В 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.
Конкретная деплой-обвязка (плейбук, секреты) держится в отдельном приватном
репозитории и в комплект не входит.
