Files
avandClaude Opus 4.8 0d263270cb Быстрый приём: сохранение в catched, добавление в qBittorrent — шаг worker'а
Приём (Ingest) стал быстрым: синхронно только парс magnet, синтез контекста из
полей ссылки, атомарный дедуп и запись загрузки в новое состояние `catched` —
ответ клиенту сразу. Медленный вывод имени (LLM) и добавление в qBittorrent
вынесены в асинхронный шаг машины состояний, который двигает worker.

- store: состояние `catched` (нетерминальное, активная группа); атомарный
  переход PromoteCatched (catched → downloading + display_name) с гардом
  state='catched' (ре-валидация после сетевых вызовов вне блокировки)
- ingest: убраны namer/qbt из пути приёма; пишем `catched`, отвечаем сразу
- worker.processCatched: вне w.mu выводит имя и qbt.Add, под w.mu — короткий
  переход; сбой add оставляет catched (ретрай тиком); предохранитель
  catch_timeout → failed(qbit_add)+notify; catched исключён из проверок пропажи
- config: worker.catch_timeout (дефолт 10m)
- веб-UI: бейдж catched, активная группа, самозавершающийся htmx-поллинг
  карточки/страницы до перехода в downloading; Telegram-текст без сырого catched
- OpenSpec: дельты ingest/download-tracking/web-ui влиты в спеки, change
  заархивирован

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

82 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## MODIFIED Requirements
### Requirement: Приём источника и заведение загрузки
Приём SHALL быть единым **быстрым** use-case, общим для всех транспортов (HTTP,
Telegram, CLI): по источнику (Ф1 — magnet) и текстовому контексту система SHALL
синхронно извлечь инфохэши, синтезировать контекст из полей ссылки (без сети),
дедуплицировать по активной задаче и при отсутствии дубля завести загрузку
(`download` в состоянии **`catched`** + записи `download_infohash`), после чего
**сразу вернуть ответ** транспорту. Заведение загрузки и запись её хешей SHALL
выполняться атомарно (см. «Атомарность возврата загрузки в активное
состояние»).
Синхронный путь приёма SHALL NOT обращаться к qBittorrent и SHALL NOT выводить
отображаемое имя (потенциально медленный LLM): и добавление источника в
qBittorrent, и вывод имени выполняются отдельным асинхронным шагом машины
состояний (worker) — см. `download-tracking` «Добавление пойманной загрузки в
qBittorrent».
`catched` — нетерминальное активное состояние: оно участвует в инварианте «не
более одной активной загрузки на infohash» наравне с прочими активными.
#### Scenario: Быстрый приём magnet
- **GIVEN** валидная magnet-ссылка и контекст
- **WHEN** вызывается приём
- **THEN** создаётся `download` в состоянии `catched` с записями
`download_infohash`
- **AND** ответ транспорту отдан без обращения к qBittorrent и без вывода имени
#### Scenario: Дубль по активной задаче на быстром пути
- **GIVEN** уже есть активная (в т.ч. `catched`) загрузка с тем же infohash
- **WHEN** вызывается приём
- **THEN** новая загрузка не создаётся, возвращается существующая
### Requirement: Отображаемое имя торрента из контекста
На шаге добавления пойманной загрузки в qBittorrent (worker) система SHALL
выводить из контекста загрузки человекочитаемое отображаемое имя и передавать
его в qBittorrent (параметр `rename` API `/torrents/add`), чтобы задача в списке
qBit не показывалась безликим `dn` magnet-ссылки. Это же имя система SHALL
**сохранять у загрузки** (`download.display_name`) для последующего показа
заголовком в веб-UI.
Имя SHALL быть коротким читаемым ярлыком (название, опционально режиссёр и
год; для сериала — номер сезона, если он определён), а не куском сырого
контекста. Имя SHALL очищаться от управляющих символов и переводов строк и
SHALL обрезаться по ограничению длины.
Вывод имени SHALL выполняться на шаге добавления, непосредственно перед вызовом
`add` (параметр `rename` действует только в момент добавления), а НЕ в
синхронном пути ответа приёма. В состоянии `catched` (до добавления)
`download.display_name` ещё пуст — веб-UI берёт заголовок из фолбека.
Отображаемое имя SHALL влиять только на отображение (в qBittorrent и как
заголовок в веб-UI) и SHALL NOT влиять на пути файлов на диске, распознавание
или раскладку — реальные пути система по-прежнему читает из qBit API.
#### Scenario: Имя из контекста передаётся в qBittorrent
- **WHEN** на шаге добавления получен непустой контекст, из которого удалось
вывести имя
- **THEN** система передаёт это имя в qBittorrent в параметре `rename`
- **AND** имя — короткий читаемый ярлык вида «название (режиссёр, год)»,
где режиссёр и год опциональны
#### Scenario: Имя сохраняется у загрузки
- **WHEN** на шаге добавления выведено непустое отображаемое имя
- **THEN** система сохраняет его в `download.display_name` (обновлением записи
загрузки)
- **AND** веб-UI использует его заголовком карточки и страницы загрузки
#### Scenario: Контекст пуст или имя не получено
- **WHEN** контекста нет либо ни один способ вывода не дал непустого имени
- **THEN** система добавляет загрузку без параметра `rename`
- **AND** qBittorrent оставляет собственное имя (из `dn`/торрента)
- **AND** `download.display_name` остаётся пустым, а веб-UI берёт заголовок из
фолбека (распознанное название или усечённый источник)