Принимаем .torrent как загруженные байты — через файл-пикер в веб-форме и Telegram-документ, наряду с magnet. Файл несёт полные метаданные: работает там, где magnet не резолвится (закрытые трекеры, без DHT), и даёт максимум контекста для распознавания без сети. - internal/torrent: парсер поверх anacrolix/torrent/metainfo — инфохэш(и) (v1 SHA1 исходных байтов info; v2 BEP52 при наличии) + Context() из имени, дерева файлов, размера, трекеров. Извлечение файлов панико-безопасно (недоверенный вход). - Персистентность байтов: таблица-спутник download_torrent (миграция 0009); пишется в транзакции создания загрузки, только на ветке создания (не при дедупе). Байты живут весь срок строки — нужны для повторного добавления при retry. - ingest: Request.TorrentData/TorrentName, диспетч парсера; source_ref — человекочитаемый референс (имя раздачи/файла), не адрес добавления. - worker: общий sourceAddParts ветвит по source_type в ОБОИХ add-путях — processCatched и Retry (torrent добавляется файлом, не magnet-хешем). - Транспорты: multipart-форма с файл-пикером (деградация без JS) и приём Telegram-документа (скачивание с редактированием токена из ошибок — секрет не в логи; обработка до ветки pending/текста). Разработка по OpenSpec (SDD): change torrent-file-ingest, два чекпоинта ревью (дизайн до кода, код до архива) сабагентами; дельты влиты в спеки, change архивирован. Ручная проверка на живом qBittorrent (7.3) — за деплоем. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.4 KiB
MODIFIED Requirements
Requirement: Ручной повтор зависшей/упавшей загрузки из транспортов
Система SHALL предоставлять пользователю команду повторной попытки (retry)
для задач в failed/stuck из веб-UI и Telegram (не только через REST API).
Retry SHALL переводить задачу обратно в downloading, не вызывая её
немедленного повторного падения по таймауту: базис отсчёта таймаута SHALL
сбрасываться (отсчёт ведётся от факта в qBittorrent, а не от старого
created_at).
Если источник задачи уже жив в qBittorrent, retry SHALL перецепляться к
существующему торренту, а не добавлять источник повторно вслепую; повторный
Add выполняется, только когда раздачи в qBittorrent нет.
Повторный Add при retry система SHALL выполнять по типу источника
(source_type), как и добавление пойманной загрузки (см. download-tracking
«Добавление пойманной загрузки в qBittorrent»): magnet/url — ссылкой; torrent —
сохранёнными байтами .torrent файлом. Для torrent-источника retry БЕЗ живой
раздачи система SHALL добавлять раздачу байтами и SHALL NOT активировать задачу
в downloading, не добавив её (иначе задача повиснет как «нет в qBittorrent»).
Scenario: Retry упавшей magnet-загрузки из веб-UI
- GIVEN задача в
failed, её торрент жив в qBittorrent - WHEN пользователь нажимает retry в веб-UI
- THEN задача возвращается в
downloadingбез повторногоAdd - AND не падает снова на ближайшем тике сверки по таймауту
Scenario: Retry доступен в Telegram
- WHEN для задачи в
failed/stuckпользователь вызывает retry в Telegram-боте - THEN задача возвращается в
downloading
Scenario: Retry без живого источника добавляет источник заново
- GIVEN задача в
failed, раздачи в qBittorrent нет - WHEN пользователь инициирует retry
- THEN источник добавляется в qBittorrent заново — magnet/url ссылкой, torrent сохранёнными байтами файлом
- AND задача переходит в
downloading
Scenario: Retry torrent-загрузки без живого источника
- GIVEN задача с
source_type = torrentвfailed, раздачи в qBittorrent нет, байты.torrentсохранены - WHEN пользователь инициирует retry
- THEN сохранённые байты добавляются в qBittorrent файлом
- AND задача переходит в
downloading(не остаётся без раздачи)