Быстрый приём: сохранение в 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>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
## 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 берёт заголовок из
|
||||
фолбека (распознанное название или усечённый источник)
|
||||
Reference in New Issue
Block a user