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

4.5 KiB

Сигнал 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-152wrk.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 надо дописать.