По итогам аудита соответствия спек и кода (11 сабагентов, по одному на capability): Беклог: - новый пункт: гейт авто-раскладки по confidence (спека говорит «вспомогательный сигнал», код делает жёсткий AutoThreshold=0.85) — определиться, что правда - новый пункт: привязка внешних субтитров к серии (спека требует, для сериала связь субтитр→эпизод и пары .idx/.sub в коде не выражены) - новый пункт: раздачи-копии диска DVD/BluRay (VIDEO_TS/BDMV — каталог целиком, не пофайловый разбор) - дополнен существующий баг TVDB /series/: спека metadata-match теперь тоже кодифицирует баг — фикс должен править и требование Спеки (правка на точность, поведение не меняется): - download-tracking, review: «per-download блокировка» → «единая блокировка воркера» (по факту глобальный w.mu, а не per-download) openspec validate --strict — проходит. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.3 KiB
download-tracking Specification
Purpose
Отслеживание скачивания и прямой путь машины состояний загрузки: поллинг
qBittorrent и сопоставление его состояний (downloading → completed; готовность
только когда файлы на месте), таймауты-предохранители (magnet_timeout/
stuck_after), ошибка qBit → failed, усыновление раздач по категории/тегу и
переходы под per-download блокировкой. Сверка уже разложенного с реальностью —
в state-reconciliation.
Requirements
Requirement: Поллинг qBittorrent и сопоставление состояний
Worker SHALL периодически (worker.poll_interval, дефолт 5 с) опрашивать
qBittorrent и сопоставлять его состояния раздачи с состоянием загрузки в БД.
Готовые к раскладке состояния (uploading/stalledUP/pausedUP/stoppedUP/
queuedUP/forcedUP, с учётом различий имён между qBit v4 и v5) SHALL переводить
загрузку в completed. Ещё качающиеся состояния (downloading/stalledDL/
metaDL/…) SHALL оставлять её в downloading.
Scenario: Раздача завершилась
- GIVEN загрузка в
downloading - WHEN qBittorrent сообщает состояние
stalledUPи файлы на месте - THEN загрузка переходит в
completed
Requirement: Готовность только когда файлы на месте
Переходные состояния qBittorrent система SHALL трактовать как «ждём»
(moving/checkingUP/checkingResumeData/allocating): оставаться в
downloading и НЕ объявлять готовность, даже если выставлены флаги UP, пока
qBit не завершит перенос/проверку. Финальные пути файлов система SHALL брать из
API после завершения переноса.
Scenario: Ждём завершения переноса
- GIVEN загрузка, у которой qBittorrent в состоянии
moving - WHEN идёт тик поллинга
- THEN загрузка остаётся в
downloading, готовность не объявляется
Requirement: Таймауты-предохранители downloading
Система SHALL переводить metaDL/forcedMetaDL дольше magnet_timeout (дефолт
24h, редкий предохранитель) в failed (error_code magnet_timeout), а
stalledDL дольше stuck_after — в stuck (error_code
stalled). Возраст система SHALL считать от времени добавления в qBittorrent
(added_on), а не от создания задачи, чтобы базис переживал retry и усыновление.
Долгий metaDL система НЕ SHALL убивать агрессивно (медленные трекеры — норма).
Scenario: Завис на метаданных дольше таймаута
- GIVEN раздача в
metaDLдольшеmagnet_timeoutотadded_on - WHEN идёт тик поллинга
- THEN загрузка переходит в
failedсerror_codemagnet_timeout
Requirement: Ошибка qBittorrent переводит в failed
Состояния error/missingFiles система SHALL трактовать как настоящий провал и
переводить загрузку в failed (error_code qbit_error) — в отличие от
таймаутов-предохранителей, такой провал сверкой не воскрешается.
Scenario: qBit сообщает об ошибке
- GIVEN раздача в состоянии
missingFiles - WHEN идёт тик поллинга
- THEN загрузка переходит в
failedсerror_codeqbit_error
Requirement: Усыновление раздач по категории или тегу
Worker SHALL периодически сверять раздачи qBittorrent с БД и усыновлять те, у
которых наша категория (qbittorrent.category) ИЛИ тег (qbittorrent.tag), а
записи в БД ещё нет, заводя для них загрузку в состоянии downloading. Категория
ставится на добавляемые нами раздачи (push); тег позволяет подхватить уже
существующую раздачу (pull), не трогая её категорию и файлы.
Scenario: Подхват существующей раздачи по тегу
- GIVEN в qBittorrent есть раздача с тегом
qbittorrent.tag, которой нет в БД - WHEN worker сверяет qBittorrent с БД
- THEN для раздачи заводится загрузка в состоянии
downloading
Requirement: Переходы состояний сериализуются воркером
Все переходы состояний загрузки SHALL сериализоваться worker'ом под единой
блокировкой (поллинг-цикл и команды всех транспортов проходят через неё), чтобы
два источника перехода не гонялись за одно состояние. Состояние SHALL быть
персистентным в SQLite; активность загрузки SHALL выводиться только из state,
без отдельного флага.
Scenario: Команды сериализуются
- GIVEN две одновременные команды к одной загрузке из разных транспортов
- WHEN они обрабатываются
- THEN переходы применяются последовательно под блокировкой, без гонки