Единый беклог задач вместо todo.md и ideas.md (docs)

Слил docs/todo.md и docs/drafts/ideas.md в docs/backlog.md: единый список
будущих задач по приоритетам (Высокий/Средний/Низкий), спекулятивные пункты
помечены _(идея)_. Реализованное из ideas.md (повторное распознавание,
нотификации) не переносил.

Добавил задачи: переработка ревью (выбор источника совпадения с
предпросмотром), главная как список карточек вместо таблицы, отдельная
страница просмотра загрузки (поднял из «Расширенной информации»), скрытие
deleted-загрузок по умолчанию.

Ссылки на drafts/ideas.md из docs/specs/* перенаправлены на backlog.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
av
2026-06-30 15:31:27 +03:00
co-authored by Claude Opus 4.8
parent 70d8758646
commit d190072647
6 changed files with 126 additions and 73 deletions
+121 -26
View File
@@ -1,13 +1,13 @@
# TODO # Беклог
Конкретные задачи на будущее, ранжированные по приоритету. Это не план Единый список будущих задач по проекту: то, что уже решили сделать, и
реализации (он — в [drafts/roadmap.md](drafts/roadmap.md)) и не свалка идеи, которые ещё надо обдумать. Это не план реализации (он — в
идей ([drafts/ideas.md](drafts/ideas.md)): сюда попадает то, что уже решили [drafts/roadmap.md](drafts/roadmap.md)) и не источник истины: принятое и
сделать, но ещё не сделали. Принятое и реализованное переезжает в реализованное переезжает в `docs/specs`/`docs/adr`.
`docs/specs`/`docs/adr`.
Приоритет — грубая оценка «ценность / стоимость», не обязательство к Приоритет — грубая оценка «ценность / стоимость», не обязательство к
порядку. порядку. Спекулятивные пункты (ещё без решения «делаем») помечены
_(идея)_ — их сперва надо проработать.
## Высокий ## Высокий
@@ -45,6 +45,32 @@ qBittorrent с файлами), подтверждение осознаннос
[architecture.md](specs/architecture.md) → «Раскладка файлов», [architecture.md](specs/architecture.md) → «Раскладка файлов»,
[workflow.md](specs/workflow.md). [workflow.md](specs/workflow.md).
### Ревью: выбор источника совпадения и предпросмотр
Переработать страницу ревью так, чтобы показывать **все** совпавшие
результаты по метабазам списком и дать выбрать из них. Принцип: совпадение
есть **всегда** — мы лишь выбираем источник. Поэтому матч нейронки — это
отдельная строка в том же списке (наравне с кандидатами TMDB/TVDB), а не
особый режим.
Возможности экрана:
- список кандидатов из баз + строка «распознано нейронкой»;
- выбрать один кандидат, переключиться на другой, отменить матч с базой в
пользу нейронки;
- добавить кандидат вручную (по id/url базы), когда автопоиск промахнулся;
- при выборе/переключении — **предпросмотр полей** (название, режиссёр,
год) и **предпросмотр раскладки** (целевые пути) до применения.
Развивает «показывать матч с записью метабазы» (web-сторона), пересекается
с «Расширенной информацией о загрузке» (детальный экран) и быстрым выбором
в Telegram. Веб остаётся точкой точных правок.
Связано: [review-ux.md](specs/review-ux.md) (выбор кандидата, «без базы»,
переключатель типа), [recognition.md](specs/recognition.md) (кандидаты
матча, провайдер-id), [«Улучшения UI: показывать матч»](#улучшения-ui-показывать-матч-с-записью-метабазы),
пакет `httpapi`.
### Наблюдаемость: метрики и учёт стоимости LLM ### Наблюдаемость: метрики и учёт стоимости LLM
Сейчас единственное окно в систему — `slog`. Нет быстрых ответов на Сейчас единственное окно в систему — `slog`. Нет быстрых ответов на
@@ -98,6 +124,33 @@ qBittorrent с файлами), подтверждение осознаннос
## Средний ## Средний
### Главная: список загрузок вместо таблицы
Переделать главную страницу из таблицы в **список** карточек. Для каждой
загрузки: название (как распознали при добавлении в qBittorrent), `infohash`
с кнопкой быстрого копирования, статус и кнопки действий. Контекст (исходное
сообщение/magnet) спрятать под спойлер или вынести на отдельный детальный
экран (см. «Расширенная информация о загрузке»). Естественно сочетается с
фильтром/поиском/пагинацией по мере роста БД.
Связано: [«Расширенная информация о загрузке в web-UI»](#расширенная-информация-о-загрузке-в-web-ui),
[«Список загрузок: фильтр, поиск, пагинация»](#список-загрузок-фильтр-поиск-пагинация),
[review-ux.md](specs/review-ux.md), пакет `httpapi`.
### Расширенная информация о загрузке в web-UI
Отдельная страница просмотра одной загрузки — целиком отображение того, что
лежит в БД по задаче: актуальный статус (текущее состояние + лог переходов),
исходный контекст и magnet, распознанные данные и матч в метабазе (см.
«показывать матч»), а также **точная раскладка, если есть** — целевые пути и
созданные хардлинки. Помогает разбираться, когда что-то пошло не так, без
чтения логов сервера. Сюда же выносится контекст с карточки в списке
загрузок.
Связано: [«Главная: список загрузок»](#главная-список-загрузок-вместо-таблицы),
[review-ux.md](specs/review-ux.md), [architecture.md](specs/architecture.md)
→ «Хранилище» (`download`/`recognition`/`file_link`), пакет `httpapi`.
### Машина состояний на go-библиотеке ### Машина состояний на go-библиотеке
Сейчас FSM реализована вручную в `worker`. Выбрать подходящую go-библиотеку Сейчас FSM реализована вручную в `worker`. Выбрать подходящую go-библиотеку
@@ -139,16 +192,29 @@ qBittorrent с файлами), подтверждение осознаннос
[jellyfin-layout.md](specs/jellyfin-layout.md) (раскладка, идемпотентность), [jellyfin-layout.md](specs/jellyfin-layout.md) (раскладка, идемпотентность),
[workflow.md](specs/workflow.md) (повторный прогон загрузки). [workflow.md](specs/workflow.md) (повторный прогон загрузки).
### Улучшения UI клиентов: показывать матч с записью метабазы ### Улучшения UI: показывать матч с записью метабазы
Во всех транспортах (веб, Telegram) показывать, **с какой именно записью** Во всех транспортах (веб, Telegram) показывать, **с какой именно записью**
метабазы (TMDB/TVDB) сматчилась загрузка: название, год, провайдер-id, метабазы (TMDB/TVDB) сматчилась загрузка: название, год, провайдер-id,
ссылку. Сейчас результат распознавания непрозрачен — пользователь не видит, ссылку. Сейчас результат распознавания непрозрачен — пользователь не видит,
к чему привязались, и не может быстро поймать ошибочный матч. к чему привязались, и не может быстро поймать ошибочный матч. На web-стороне
развивается в полноценный выбор источника — см. [«Ревью: выбор источника
совпадения»](#ревью-выбор-источника-совпадения-и-предпросмотр).
Связано: [review-ux.md](specs/review-ux.md), [recognition.md](specs/recognition.md) Связано: [review-ux.md](specs/review-ux.md), [recognition.md](specs/recognition.md)
(матч в базе), [architecture.md](specs/architecture.md) → «Транспорты». (матч в базе), [architecture.md](specs/architecture.md) → «Транспорты».
### Аниме с абсолютной нумерацией
Релизы аниме часто нумеруют серии сквозным числом (`#137`) без сезонов, а
Jellyfin ждёт `SxxEyy`. Нужен пересчёт абсолютной нумерации в сезон/серию —
надёжнее всего через TVDB (там есть absolute order). Отдельный крайний
случай распознавания; на стороне ревью — веб-хелпер «absolute → S·E».
Связано: [recognition.md](specs/recognition.md) (конвейер, сезон-паки),
[jellyfin-layout.md](specs/jellyfin-layout.md) (нумерация серий),
[review-ux.md](specs/review-ux.md) (крайние сценарии).
### Добавление торрентов файлом/ссылкой — «единое окно» ### Добавление торрентов файлом/ссылкой — «единое окно»
Поддержать источники помимо magnet: `.torrent`-файл и URL (отдаём их в Поддержать источники помимо magnet: `.torrent`-файл и URL (отдаём их в
@@ -204,24 +270,31 @@ qBittorrent, без исходящих запросов на пользоват
верификации, но дешевле: учимся на уже собранных `hint`/`override`. верификации, но дешевле: учимся на уже собранных `hint`/`override`.
Связано: [recognition.md](specs/recognition.md) (конвейер, промпт), Связано: [recognition.md](specs/recognition.md) (конвейер, промпт),
[«Многоступенчатая верификация»](#многоступенчатая-верификация-привязки-тема-для-размышления), [«Многоступенчатая верификация»](#многоступенчатая-верификация-привязки-идея),
[architecture.md](specs/architecture.md) → «Хранилище» (`hint`, `override`). [architecture.md](specs/architecture.md) → «Хранилище» (`hint`, `override`).
### Список загрузок: фильтр, поиск, пагинация ### Список загрузок: фильтр, поиск, пагинация
Прямое следствие роста БД (см. «Ретеншн»): плоский список загрузок со Прямое следствие роста БД (см. «Ретеншн»): плоский список загрузок со
временем становится непригоден. Нужны фильтр по состоянию, поиск по временем становится непригоден. Нужны фильтр по состоянию, поиск по
названию и пагинация. Естественно ложится на экран расширенной информации. названию и пагинация. Естественно ложится на список-карточки главной и на
экран расширенной информации.
Связано: [«Расширенная информация о загрузке в web-UI»](#расширенная-информация-о-загрузке-в-web-ui), Частный случай как разумный дефолт: на главной **по умолчанию скрывать
загрузки в статусе `deleted`** (терминальные, удалённые из источника и
цели — шум в ленте), с переключателем/фильтром «показать всё». Маленькая
часть, может приехать раньше полноценного фильтра.
Связано: [«Главная: список загрузок»](#главная-список-загрузок-вместо-таблицы),
[«Расширенная информация о загрузке в web-UI»](#расширенная-информация-о-загрузке-в-web-ui),
пакет `httpapi`. пакет `httpapi`.
## Низкий ## Низкий
### Многоступенчатая верификация привязки (тема для размышления) ### Многоступенчатая верификация привязки _(идея)_
Идея: несколько раз извлекать данные из раздачи и контекста разными Несколько раз извлекать данные из раздачи и контекста разными промптами,
промптами, искать в метабазах, затем сводить результаты в общий вердикт искать в метабазах, затем сводить результаты в общий вердикт
(голосование/консенсус) — выше точность ценой нескольких вызовов LLM и (голосование/консенсус) — выше точность ценой нескольких вызовов LLM и
запросов к базам. Требует проработки: когда включать, как мерджить запросов к базам. Требует проработки: когда включать, как мерджить
расхождения, стоимость/латентность. расхождения, стоимость/латентность.
@@ -229,21 +302,14 @@ qBittorrent, без исходящих запросов на пользоват
Связано: [recognition.md](specs/recognition.md) (конвейер и модель Связано: [recognition.md](specs/recognition.md) (конвейер и модель
уверенности). уверенности).
### Расширенная информация о загрузке в web-UI
Экран просмотра деталей одной загрузки: исходный контекст и magnet, лог
переходов состояний, распознанные данные и матч в метабазе (см. «показывать
матч»), целевые пути и созданные хардлинки. Помогает разбираться, когда
что-то пошло не так, без чтения логов сервера.
Связано: [review-ux.md](specs/review-ux.md), пакет `httpapi`.
### Выбор из нескольких находок метабазы в Telegram ### Выбор из нескольких находок метабазы в Telegram
Когда распознавание даёт несколько подходящих кандидатов в метабазе, Когда распознавание даёт несколько подходящих кандидатов в метабазе,
предлагать их в Telegram списком (кнопки) для ручного выбора, а не молча предлагать их в Telegram списком (кнопки) для ручного выбора, а не молча
брать первый/лучший. Веб остаётся точкой точных правок, бот — быстрый выбор брать первый/лучший. Веб остаётся точкой точных правок (полный выбор
из готового короткого списка. источника — см. [«Ревью: выбор источника
совпадения»](#ревью-выбор-источника-совпадения-и-предпросмотр)), бот —
быстрый выбор из готового короткого списка.
Связано: [review-ux.md](specs/review-ux.md) (боты — быстрые действия, веб — Связано: [review-ux.md](specs/review-ux.md) (боты — быстрые действия, веб —
точные правки), [recognition.md](specs/recognition.md) (кандидаты матча). точные правки), [recognition.md](specs/recognition.md) (кандидаты матча).
@@ -268,6 +334,35 @@ qBittorrent, без исходящих запросов на пользоват
Связано: [recognition.md](specs/recognition.md) (сверка с базой), пакеты Связано: [recognition.md](specs/recognition.md) (сверка с базой), пакеты
`metadata`, `llm`. `metadata`, `llm`.
### guessit как сервис-спутник _(идея)_
`go-ptn` слабее питоновского `guessit`. Если точности пред-парса не
хватит — завернуть `guessit` в крошечный HTTP-сервис (один файл,
поставляется рядом с бинарём jellybit) и спрашивать его на шаге
пред-парса. Сохраняет «доставку копированием»: два файла вместо одного.
Связано: [recognition.md](specs/recognition.md) → «На будущее» (пред-парс).
### Завершение загрузки через webhook _(идея)_
Сейчас завершение ловим поллингом qBittorrent раз в несколько секунд.
Альтернатива: «Run external program on torrent completion» в qBittorrent
дёргает эндпоинт jellybit. Реагирует быстрее, но связывает нас с конфигом
qBittorrent. Решим по опыту эксплуатации.
Связано: [architecture.md](specs/architecture.md) → «Отслеживание загрузки»,
пакет `worker`.
### Авторизация веб-UI (на будущее)
Решено для v1: без авторизации в доверенной LAN, опц. allowlist подсетей
(`http.trusted_subnets`) — как умеет qBittorrent. Если понадобится защита:
токен/Basic в самом приложении или вынос за reverse-proxy с
аутентификацией.
Связано: [architecture.md](specs/architecture.md) → «Транспорты» (доступ к
веб-UI), пакет `httpapi`.
### Современный Web-UI как PWA ### Современный Web-UI как PWA
Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое,
-42
View File
@@ -1,42 +0,0 @@
# Идеи и нерешённое
Свалка мыслей на будущее. Ни к чему не обязывает; принятое переезжает в
specs/adr.
## guessit как сервис-спутник
`go-ptn` слабее питоновского `guessit`. Если точности пред-парса не
хватит — завернуть `guessit` в крошечный HTTP-сервис (один файл,
поставляется рядом с бинарём jellybit) и спрашивать его на шаге
пред-парса. Сохраняет «доставку копированием»: два файла вместо одного.
## Аниме с абсолютной нумерацией
Релизы аниме часто нумеруют серии сквозным числом (`#137`) без сезонов, а
Jellyfin ждёт `SxxEyy`. Нужен пересчёт абсолютной нумерации в
сезон/серию — надёжнее всего через TVDB (там есть absolute order).
Отдельный крайний случай распознавания.
## Завершение загрузки через webhook
Сейчас принято — поллинг qBittorrent раз в несколько секунд.
Альтернатива: «Run external program on torrent completion» в qBittorrent
дёргает эндпоинт jellybit. Реагирует быстрее, но связывает нас с конфигом
qBittorrent. Решим по опыту эксплуатации.
## Нотификации о готовности
Когда раскладка завершена (или нужен review) — уведомить: Telegram,
возможно ntfy/Apprise. Естественно ложится на Telegram-транспорт.
## Доступ к веб-UI
Решено для v1: без авторизации в доверенной LAN, опц. allowlist подсетей
(`http.trusted_subnets`) — как умеет qBittorrent. На будущее, если
понадобится защита: токен/Basic в самом приложении или вынос за
reverse-proxy с аутентификацией.
## Повторный прогон распознавания
Возможность переоткрыть загрузку, поправить контекст и перераспознать без
перекачивания — полезно, когда LLM ошибся, а файлы уже скачаны.
+1 -1
View File
@@ -61,7 +61,7 @@ reject / defer / undo) — команды к `worker`:
(server-rendered). В v1 **без авторизации** (доверенная LAN). Поле (server-rendered). В v1 **без авторизации** (доверенная LAN). Поле
`http.trusted_subnets` зарезервировано, но **пока не применяется**: `http.trusted_subnets` зарезервировано, но **пока не применяется**:
деплой только в локальную сеть без доступа из интернета, поэтому деплой только в локальную сеть без доступа из интернета, поэтому
allowlist-middleware и авторизацию отложили — [drafts/ideas.md](../drafts/ideas.md). allowlist-middleware и авторизацию отложили — [backlog.md](../backlog.md#авторизация-веб-ui-на-будущее).
- **Telegram-бот** — переслать magnet/сообщение бота; текст становится - **Telegram-бот** — переслать magnet/сообщение бота; текст становится
контекстом. Доступ — по `telegram.allowed_user_ids` (пусто = запрет контекстом. Доступ — по `telegram.allowed_user_ids` (пусто = запрет
всем, fail-closed). Бот же шлёт **пинги** о входе в review/готовности. всем, fail-closed). Бот же шлёт **пинги** о входе в review/готовности.
+1 -1
View File
@@ -94,4 +94,4 @@ inode общий — диск не дублируется.
сезонам. сезонам.
- **Несколько аудиодорожек** — обычно внутри mkv, не наша забота. - **Несколько аудиодорожек** — обычно внутри mkv, не наша забота.
- **Аниме с абсолютной нумерацией** — пересчёт в S·E, отдельная проработка - **Аниме с абсолютной нумерацией** — пересчёт в S·E, отдельная проработка
([drafts/ideas.md](../drafts/ideas.md)). ([backlog.md](../backlog.md#аниме-с-абсолютной-нумерацией)).
+2 -2
View File
@@ -129,10 +129,10 @@ notes пояснения, неоднозначности
00`), двойные серии (`SxxEyy-Eyy`) — через per-file season/episode; 00`), двойные серии (`SxxEyy-Eyy`) — через per-file season/episode;
любая неоднозначность → review. любая неоднозначность → review.
- Аниме с абсолютной нумерацией — отдельный крайний случай, см. - Аниме с абсолютной нумерацией — отдельный крайний случай, см.
[drafts/ideas.md](../drafts/ideas.md). [backlog.md](../backlog.md#аниме-с-абсолютной-нумерацией).
## На будущее ## На будущее
`go-ptn` слабее питоновского `guessit`. Если точности пред-парса не `go-ptn` слабее питоновского `guessit`. Если точности пред-парса не
хватит — завернуть `guessit` лёгким сервисом-спутником (один файл рядом с хватит — завернуть `guessit` лёгким сервисом-спутником (один файл рядом с
бинарём). См. [drafts/ideas.md](../drafts/ideas.md). бинарём). См. [backlog.md](../backlog.md#guessit-как-сервис-спутник-идея).
+1 -1
View File
@@ -109,7 +109,7 @@ Telegram = одобрить / подсказать / выбрать кандид
provider-id и каноническое имя). provider-id и каноническое имя).
- **База пустая (рус/аниме)** → «без базы» или ручной id/url. Аниме с - **База пустая (рус/аниме)** → «без базы» или ручной id/url. Аниме с
абсолютной нумерацией → веб-хелпер «absolute → S·E» (см. абсолютной нумерацией → веб-хелпер «absolute → S·E» (см.
[drafts/ideas.md](../drafts/ideas.md)). [backlog.md](../backlog.md#аниме-с-абсолютной-нумерацией)).
- **Не тот тип (movie↔series)** → переключатель пересобирает форму плана. - **Не тот тип (movie↔series)** → переключатель пересобирает форму плана.
- **Мусор (sample/extra/дубли дорожек)** → роль «игнор». - **Мусор (sample/extra/дубли дорожек)** → роль «игнор».
- **Полный провал** (LLM ничего не вытащил) → веб-«ручной режим»: выбрать - **Полный провал** (LLM ничего не вытащил) → веб-«ручной режим»: выбрать