Files
jellybit/docs/backlog/jellyfin-skan-posle-udaleniya.md
T
avandClaude Opus 4.8 3e470e3ae6 Беклог: перенос четырёх задач из инбокса Tududi
Инбокс Tududi разгружен: четыре открытые задачи проекта оформлены файлами
в docs/backlog и помечены выполненными в Tududi.

- режиссёр в блоке «Распознано как» на странице загрузки (средний);
- карточка информации о торренте вылезает за экран на мобильном (средний);
- сигнал Jellyfin после отката и удаления файлов (средний);
- кнопки выглядят как кнопки: btn-ghost и рассогласование весов (низкий).

Каждая задача проверена по коду: все четыре актуальны, ни одну не закрыли
прошлые коммиты. В телах — точки правки, развилки и нужные дельты спек.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:02:22 +03:00

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` надо дописать.