Инбокс Tududi разгружен: четыре открытые задачи проекта оформлены файлами в docs/backlog и помечены выполненными в Tududi. - режиссёр в блоке «Распознано как» на странице загрузки (средний); - карточка информации о торренте вылезает за экран на мобильном (средний); - сигнал Jellyfin после отката и удаления файлов (средний); - кнопки выглядят как кнопки: btn-ghost и рассогласование весов (низкий). Каждая задача проверена по коду: все четыре актуальны, ни одну не закрыли прошлые коммиты. В телах — точки правки, развилки и нужные дельты спек. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
4.5 KiB
Markdown
60 lines
4.5 KiB
Markdown
# Сигнал Jellyfin после отката и удаления файлов
|
|
|
|
**Приоритет:** средний
|
|
|
|
Сейчас пересканирование Jellyfin шлётся **только** при входе в `done`. После
|
|
отката (`reverted`) или удаления (`deleted`) хардлинки сняты, а Jellyfin
|
|
продолжает показывать записи с битыми путями до следующего скана по расписанию.
|
|
|
|
Интеграция уже есть целиком, клиент писать не надо:
|
|
|
|
- `internal/jellyfin/jellyfin.go` — клиент, `RefreshLibraries()` (`:76`) →
|
|
`POST /Library/Refresh`. Тесты — `internal/jellyfin/jellyfin_test.go`.
|
|
- Конфиг: `internal/config/config.go:99-107` (`Jellyfin{Enabled,URL,APIKey,Proxy,Timeout}`),
|
|
валидация `:305-308`, пример `config.example.toml:59-64`.
|
|
- Проводка: `cmd/jellybit/serve.go:137-152` → `wrk.SetScanner(jf)`; интерфейс
|
|
`internal/worker/worker.go:171-176`.
|
|
|
|
Точка правки — гейт в `transitionErr` (`internal/worker/worker.go:846-852`):
|
|
`if w.scanner != nil && state == store.StateDone`. Оба пути удаления уже проходят
|
|
через ту же `transition`: `Undo()` (`internal/worker/review.go:535` → `:574`) и
|
|
`Delete()` (`:601` → `:657`). То есть база правки = расширить условие; фоновый
|
|
ctx, `capFileLayout`-скоуп и неблокирующая горутина переиспользуются как есть.
|
|
|
|
## Что учесть
|
|
|
|
1. **`StateDeleted` приходит не только от пользователя.**
|
|
`internal/worker/reconcile.go:30-41` `deriveState()` возвращает `StateDeleted`
|
|
при `!sourcePresent && !targetPresent` (авто-сверка). Скан там формально уместен,
|
|
но это уже не «после удаления файлов нами». Решить: гейтить по состоянию (просто,
|
|
ловит и reconcile) или по факту снятия ссылок (точнее — в `Undo`/`Delete` есть
|
|
счётчик снятого, но тогда триггер уезжает из единого чекпоинта `transitionErr`).
|
|
2. **`StateTargetMissing`** (`reconcile.go:35`) — цель пропала мимо нас. Кандидат
|
|
по той же логике, надо явно решить, входит или нет.
|
|
3. **`Dismiss` идёт мимо чекпоинта:** `internal/worker/worker.go:944` пишет
|
|
состояние напрямую через `w.store.SetDownloadState`, минуя `transitionErr`. Для
|
|
dismiss это корректно (файлы не трогаются), но если вешать скан на `cancelled` —
|
|
не сработает.
|
|
4. `Delete()` при ошибке qBittorrent (`internal/worker/review.go:650-652`)
|
|
возвращается **до** `transition` → ссылки сняты, скана не будет. Идемпотентный
|
|
повтор дожмёт, но окно рассинхрона есть.
|
|
5. Порядок верный: `layouter.Undo` отрабатывает до `transition`, так что скан
|
|
увидит уже снятые ссылки.
|
|
|
|
## Спеки — здесь дыра
|
|
|
|
Про Jellyfin-скан в `openspec/specs/` **нет ни слова** (грепом
|
|
`Library/Refresh|RefreshLibraries|пересканир` — ноль попаданий). Живёт только в
|
|
рукописных доках: `docs/specs/architecture.md:182-196` («Пересканирование
|
|
Jellyfin», прямо сказано «После успешной раскладки (вход в `done`)»),
|
|
`docs/specs/workflow.md:100-102`.
|
|
|
|
→ Задача тянет дельту в `file-layout` (пакет `jellyfin` отнесён к этой capability —
|
|
`architecture.md:44`, код скоупится `capFileLayout`) + правку
|
|
`docs/specs/architecture.md:182` и `docs/specs/workflow.md:100`, где формулировка
|
|
«при входе в done» станет неверной.
|
|
|
|
Тесты: `internal/worker/review_test.go:88` `TestScanner_FiresOnDone` +
|
|
`recordingScanner` (`:81-86`). Негативных тестов «не стреляет на других состояниях»
|
|
нет → расширение безопасно, но тесты на `reverted`/`deleted` надо дописать.
|