22 KiB
TODO
Конкретные задачи на будущее, ранжированные по приоритету. Это не план
реализации (он — в drafts/roadmap.md) и не свалка
идей (drafts/ideas.md): сюда попадает то, что уже решили
сделать, но ещё не сделали. Принятое и реализованное переезжает в
docs/specs/docs/adr.
Приоритет — грубая оценка «ценность / стоимость», не обязательство к порядку.
Высокий
Проблема второго сезона
Если первый сезон сериала уже разложен, а мы добавляем второй/третий/…,
распознавание должно привязать новый сезон к тому же названию и папке,
а не завести рядом почти одинаковую вторую папку. Ключ — стабильный
provider_id: один и тот же [tvdbid-…] → одна папка сериала, новые
Season NN доливаются внутрь. Нужно: при матче учитывать уже существующие
в библиотеке сериалы (или прошлые распознавания с тем же провайдер-id) и
склонять LLM/выбор кандидата к согласованности с ними.
Связано: recognition.md (модель уверенности, матч в базе), jellyfin-layout.md (папка сериала с провайдер-id).
Название из контекста при добавлении в qBittorrent
При создании magnet-загрузки передавать в qBittorrent человекочитаемое имя
из контекста (если оно есть), чтобы в списке qBit не было безликих
rutracker-topic-6852853. Небольшая задача с заметной отдачей в
повседневной эксплуатации.
Связано: architecture.md → «Транспорты», пакет
ingest/qbt.
Рассинхрон состояния с реальностью (удалённый торрент / файлы)
Состояние jellybit может разойтись с тем, что реально лежит на диске. Несколько сценариев разной остроты:
- Жёсткий — удалён источник. Раздачу удаляют (вручную или авто по
достижении seed limit), и qBittorrent стирает скачанные файлы. Тогда
хардлинк в библиотеке становится последней ссылкой на inode, и
обычный
undo(unlinkцели + чистка пустых каталогов) сотрёт единственную копию насовсем — прямая потеря данных. Инвариант «источник неприкосновенен» молчаливо перестаёт держаться: источника уже нет. - Мягкий — удалена цель. Файлы убрали из библиотеки Jellyfin (вручную
или из самого Jellyfin), а jellybit по-прежнему числит загрузку в
done. Состояние врёт: ссылок уже нет, а сервис думает, что всё разложено.
Нужно продумать сверку записанного состояния (file_link, состояние
загрузки) с фактом на ФС:
- как
workerреагирует на исчезновение раздачи из qBittorrent (состояние/пометка загрузки); - как
undoзащищается, когда источник недоступен — например, отказываться удалять, если у целевого файла счётчик ссылок == 1 (нет второй копии) или исходный путь не существует, и явно об этом сообщать. Откат снимает лишний хардлинк, а не последнюю копию файла; - как ловить пропажу целевых файлов и отражать её в состоянии (напр. периодическая сверка или проверка при показе — «разложено, но файлов нет»), чтобы можно было осознанно перепривязать/переразложить.
Связано: ADR-2026-06-13-hardlinks,
architecture.md → «Раскладка файлов» (undo,
инвариант источника), workflow.md (done → reverted).
Наблюдаемость: метрики и учёт стоимости LLM
Сейчас единственное окно в систему — slog. Нет быстрых ответов на
вопросы «сколько задач висит в review», «сколько токенов и денег съело
распознавание», «какова медиана времени ingest → done». Нужны метрики:
эндпоинт /metrics (Prometheus-формат) со счётчиками загрузок по
состояниям, длительностями стадий и расходом LLM (токены/стоимость на
задачу).
Расход LLM уже снимается с провода — llm.openai парсит usage
(prompt/completion/total tokens и cost, который отдаёт шлюз) в
llm.Response.Usage и пишет в лог. Не хватает только персистентности и
отображения: сохранять usage у попытки распознавания (recognition) и
показывать в карточке загрузки + агрегатом в /metrics. Где провайдер не
шлёт cost — считать из токенов по таблице «модель → цена» в конфиге.
Отдельный LLM-прокси (LiteLLM и т.п.) для этого не нужен и противоречит
принципам «один бинарь» / «минимум компонентов»: подсчёт токенов уже в коде,
а роль мульти-модельного шлюза играет используемый OpenAI-совместимый
эндпоинт (он и возвращает cost); разные модели подключаются сменой
[llm].model или новым типом провайдера за интерфейсом llm.Provider.
Связано: architecture.md → «Логирование»,
recognition.md (провайдер LLM, [llm].type),
пакеты worker, llm, httpapi.
Ретеншн и очистка БД
Терминальные задачи (done/cancelled/failed/reverted), их попытки
recognition с сырыми ответами LLM и metadata_candidate копятся вечно —
со временем БД и список загрузок распухают и становятся нечитаемыми. Нужна
авточистка старше N дней (с настройкой в [storage] или [worker]) и/или
ручное удаление. Маленькая задача, но без неё интерфейс деградирует по мере
эксплуатации.
Связано: architecture.md → «Хранилище» (таблицы
download/recognition/metadata_candidate/file_link), пакет store.
Eval-харнес распознавания (корпус кейсов + метрика точности)
Распознавание — ядро продукта, но смена модели или правка промпта сейчас
вслепую: регрессий не видно. Нужен корпус размеченных кейсов (русские
релизы, аниме, сезон-паки, репаки, спецвыпуски) и прогон распознавания по
нему с метрикой точности (тип/название/год/нумерация). Тогда можно
сравнивать LLM-провайдеры и версии промпта по числам, а не на ощупь.
Прогон — отдельной командой (jellybit eval или тестом), на фикстурах, без
реального qBittorrent.
Связано: recognition.md (конвейер, модель
уверенности), пакет recognize.
Средний
Машина состояний на go-библиотеке
Сейчас FSM реализована вручную в worker. Выбрать подходящую go-библиотеку
для описания воркфлоу/машины состояний и перевести переходы на неё — ради
декларативности, проверяемости переходов и единого места правды. Кандидаты
для оценки: looplab/fsm, qmuntal/stateless (и аналоги). Граф и переходы
уже формализованы — переносим один в один.
Связано: workflow.md (текущий граф состояний).
Привязка уведомлений к источнику в ботах (мульти-бот)
Уведомления и запросы подтверждения должен получать тот, кто прислал загрузку: автор сообщения о новой раздаче — адресат пингов и ревью по ней. Транспортов-ботов может быть несколько (Telegram, в перспективе Matrix и др.); каждый адресует «своему» отправителю. Веб-интерфейс остаётся единым для всех и точкой правды по функциональности (боты — тонкие адаптеры над тем же ядром). Нужно: хранить у загрузки источник/транспорт и идентификатор отправителя, маршрутизировать пинги по нему.
Связано: review-ux.md (разделение труда транспортов, веб = точные правки), architecture.md → «Транспорты».
Раздачи с докачиванием (слияние при повторном добавлении)
Свежий сериал часто раздают по мере выхода: торрент содержит 5 эпизодов из 10. Позже его перезаливают целиком (или добавляют недостающие серии), и пользователь повторно добавляет тот же торрент. Нужно распознать, что это та же раздача/сезон, и повторить раскладку с слиянием: доложить недостающие хардлинки, не дублируя уже разложенное и не перезаписывая существующее (инвариант «существующее не трогаем»). Перекликается с «Проблемой второго сезона», но здесь доливаются эпизоды внутри одного сезона, а не новый сезон. Нужно продумать: как опознать повторное добавление (хеш торрента / провайдер-id + сезон), как сверять состав файлов и доливать только новые.
Связано: «Проблема второго сезона», jellyfin-layout.md (раскладка, идемпотентность), workflow.md (повторный прогон загрузки).
Улучшения UI клиентов: показывать матч с записью метабазы
Во всех транспортах (веб, Telegram) показывать, с какой именно записью метабазы (TMDB/TVDB) сматчилась загрузка: название, год, провайдер-id, ссылку. Сейчас результат распознавания непрозрачен — пользователь не видит, к чему привязались, и не может быстро поймать ошибочный матч.
Связано: review-ux.md, recognition.md (матч в базе), architecture.md → «Транспорты».
Добавление торрентов файлом/ссылкой — «единое окно»
Поддержать источники помимо magnet: .torrent-файл и URL (отдаём их в
qBittorrent, без исходящих запросов на пользовательский URL — SSRF
исключён). Идеал — одно поле «единого окна»: кидаем туда текст или файл, а
сервис сам разбирает, что это (magnet / ссылка / .torrent / сообщение
бота), и заводит загрузку.
Связано: architecture.md → «Транспорты»
(source_type = magnet|torrent|url уже в схеме), пакет ingest (сейчас
поддержан только magnet).
Бэкап SQLite
architecture.md требует «бекапить data-том», но как — не описано. Без
понятной стратегии сбой или редеплой стирают всё in-flight состояние.
Зафиксировать решение и реализовать: периодический VACUUM INTO в
/data/backups по расписанию (с ротацией) либо потоковая репликация
(litestream). Лучше сделать, пока БД маленькая.
Связано: architecture.md → «Деплой» (data-том,
«бекапить-и-не-терять»), пакет store.
Версии/качество одного тайтла (репаки, апгрейд 1080p → 2160p)
Фильм уже разложен, позже добавили раздачу лучшего качества — сейчас это просто новая задача, упирающаяся в «коллизию цели → review», без понятия «это та же вещь, заменить версию». Нужно осознанно обработать апгрейд качества: распознать тот же тайтл, предложить замену существующей раскладки либо сосуществование версий (Jellyfin поддерживает несколько версий одного фильма). Близко к «докачиванию», но про качество, а не про эпизоды.
Связано: «Раздачи с докачиванием», jellyfin-layout.md (never-overwrite, коллизия), architecture.md → «Идентификация торрента» (репаки = разные infohash → разные задачи).
Глубокий healthcheck и статус зависимостей
/healthz проверяет только сам сервис. Если qBittorrent, LLM или метабаза
недоступны — узнаёшь лишь по застрявшим задачам. Нужна readiness-проверка
ключевых зависимостей и отражение их состояния в UI (бейдж «qBittorrent
недоступен»), чтобы причина простоя была видна сразу.
Связано: architecture.md → «Деплой» (healthcheck),
пакеты qbt, llm, metadata, httpapi.
Обучение на правках человека (few-shot из прошлых ревью)
Когда человек поправил матч, тип или нумерацию — сохранять это как пример и
подмешивать похожие в будущие промпты. Системно повышает точность на «твоих»
трекерах и форматах имён без смены модели. Развитие идеи многоступенчатой
верификации, но дешевле: учимся на уже собранных hint/override.
Связано: recognition.md (конвейер, промпт),
«Многоступенчатая верификация»,
architecture.md → «Хранилище» (hint, override).
Список загрузок: фильтр, поиск, пагинация
Прямое следствие роста БД (см. «Ретеншн»): плоский список загрузок со временем становится непригоден. Нужны фильтр по состоянию, поиск по названию и пагинация. Естественно ложится на экран расширенной информации.
Связано: «Расширенная информация о загрузке в web-UI»,
пакет httpapi.
Низкий
Многоступенчатая верификация привязки (тема для размышления)
Идея: несколько раз извлекать данные из раздачи и контекста разными промптами, искать в метабазах, затем сводить результаты в общий вердикт (голосование/консенсус) — выше точность ценой нескольких вызовов LLM и запросов к базам. Требует проработки: когда включать, как мерджить расхождения, стоимость/латентность.
Связано: recognition.md (конвейер и модель уверенности).
Расширенная информация о загрузке в web-UI
Экран просмотра деталей одной загрузки: исходный контекст и magnet, лог переходов состояний, распознанные данные и матч в метабазе (см. «показывать матч»), целевые пути и созданные хардлинки. Помогает разбираться, когда что-то пошло не так, без чтения логов сервера.
Связано: review-ux.md, пакет httpapi.
Выбор из нескольких находок метабазы в Telegram
Когда распознавание даёт несколько подходящих кандидатов в метабазе, предлагать их в Telegram списком (кнопки) для ручного выбора, а не молча брать первый/лучший. Веб остаётся точкой точных правок, бот — быстрый выбор из готового короткого списка.
Связано: review-ux.md (боты — быстрые действия, веб — точные правки), recognition.md (кандидаты матча).
Проверка свободного места перед copy-fallback
Когда хардлинк невозможен (EXDEV/ENOTSUP/…), layout копирует файл,
дублируя место на диске. На забитом диске это упрётся в полку посреди
раскладки. Перед копированием проверять доступное место и при нехватке
внятно уходить в failed с понятной причиной, а не падать на полпути.
Связано: architecture.md → «Раскладка файлов»
(фолбэк-копирование), пакет layout.
Кэш метабаз (и опционально LLM)
Повторные и ретраящиеся прогоны распознавания бьют TMDB/TVDB/TVMaze одним и тем же запросом. Кэш ответов с TTL экономит лимиты API и ускоряет «Распознать заново»/«Уточнить». При желании — кэш ответов LLM по хешу входа (но он менее полезен, т.к. вход меняется подсказками).
Связано: recognition.md (сверка с базой), пакеты
metadata, llm.
Современный Web-UI как PWA
Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, отзывчивое, удобное с телефона). Текущий server-rendered UI функционален, поэтому это улучшение, а не блокер; большой объём работы.
Связано: review-ux.md (веб = точные правки),
пакет httpapi.