Архивация спеки
This commit is contained in:
@@ -25,16 +25,6 @@
|
|||||||
матч в базе), [jellyfin-layout.md](specs/jellyfin-layout.md) (папка
|
матч в базе), [jellyfin-layout.md](specs/jellyfin-layout.md) (папка
|
||||||
сериала с провайдер-id).
|
сериала с провайдер-id).
|
||||||
|
|
||||||
### Название из контекста при добавлении в qBittorrent
|
|
||||||
|
|
||||||
При создании magnet-загрузки передавать в qBittorrent человекочитаемое имя
|
|
||||||
из контекста (если оно есть), чтобы в списке qBit не было безликих
|
|
||||||
`rutracker-topic-6852853`. Небольшая задача с заметной отдачей в
|
|
||||||
повседневной эксплуатации.
|
|
||||||
|
|
||||||
Связано: [architecture.md](specs/architecture.md) → «Транспорты», пакет
|
|
||||||
`ingest`/`qbt`.
|
|
||||||
|
|
||||||
### Рассинхрон состояния с реальностью (удалённый торрент / файлы)
|
### Рассинхрон состояния с реальностью (удалённый торрент / файлы)
|
||||||
|
|
||||||
Состояние jellybit может разойтись с тем, что реально лежит на диске.
|
Состояние jellybit может разойтись с тем, что реально лежит на диске.
|
||||||
|
|||||||
+3
-2
@@ -56,5 +56,6 @@
|
|||||||
- [x] 6.1 `task test` и `task lint` зелёные
|
- [x] 6.1 `task test` и `task lint` зелёные
|
||||||
- [ ] 6.2 Ручная проверка на реальном qBittorrent: имя видно в списке;
|
- [ ] 6.2 Ручная проверка на реальном qBittorrent: имя видно в списке;
|
||||||
подтвердить поведение `rename` для многофайловой раздачи
|
подтвердить поведение `rename` для многофайловой раздачи
|
||||||
- [ ] 6.3 Обновить `docs/specs` (architecture/ingest) и отметить todo
|
- [x] 6.3 При архивации: дельта-спека синкнута в `openspec/specs/ingest`
|
||||||
«Название из контекста» как реализованную при архивации change
|
(новая capability), пункт «Название из контекста» убран из `docs/todo.md`
|
||||||
|
(реализованное переехало в OpenSpec-спеки)
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# ingest Specification
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Приём загрузки: использование контекста для отображаемого имени торрента в
|
||||||
|
qBittorrent. Capability описывает вывод человекочитаемого имени из контекста
|
||||||
|
(через LLM или алгоритмический фолбек) и его передачу в qBittorrent.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Requirement: Отображаемое имя торрента из контекста
|
||||||
|
|
||||||
|
При добавлении загрузки в qBittorrent система SHALL выводить из контекста
|
||||||
|
загрузки человекочитаемое отображаемое имя и передавать его в qBittorrent
|
||||||
|
(параметр `rename` API `/torrents/add`), чтобы задача в списке qBit не
|
||||||
|
показывалась безликим `dn` magnet-ссылки.
|
||||||
|
|
||||||
|
Имя SHALL быть коротким читаемым ярлыком (название, опционально режиссёр и
|
||||||
|
год; для сериала — номер сезона, если он определён), а не куском сырого
|
||||||
|
контекста. Имя SHALL очищаться от управляющих символов и переводов строк и
|
||||||
|
SHALL обрезаться по ограничению длины.
|
||||||
|
|
||||||
|
Вывод имени SHALL выполняться синхронно перед отдачей источника в
|
||||||
|
qBittorrent (параметр `rename` действует только в момент добавления).
|
||||||
|
|
||||||
|
Отображаемое имя SHALL влиять только на отображение в qBittorrent и SHALL
|
||||||
|
NOT влиять на пути файлов на диске, распознавание или раскладку — реальные
|
||||||
|
пути система по-прежнему читает из qBit API.
|
||||||
|
|
||||||
|
#### Scenario: Имя из контекста передаётся в qBittorrent
|
||||||
|
|
||||||
|
- **WHEN** загрузку добавляют с непустым контекстом, из которого удалось
|
||||||
|
получить имя
|
||||||
|
- **THEN** система передаёт это имя в qBittorrent в параметре `rename`
|
||||||
|
- **AND** имя — короткий читаемый ярлык вида «название (режиссёр, год)»,
|
||||||
|
где режиссёр и год опциональны
|
||||||
|
|
||||||
|
#### Scenario: Контекст пуст или имя не получено
|
||||||
|
|
||||||
|
- **WHEN** контекста нет либо ни один способ вывода не дал непустого имени
|
||||||
|
- **THEN** система добавляет загрузку без параметра `rename`
|
||||||
|
- **AND** qBittorrent оставляет собственное имя (из `dn`/торрента)
|
||||||
|
|
||||||
|
### Requirement: Вывод имени через LLM со структурированным выводом
|
||||||
|
|
||||||
|
Система SHALL строить отображаемое имя с помощью LLM (структурированный
|
||||||
|
JSON-вывод), извлекая из контекста тип (movie/series), название, год,
|
||||||
|
режиссёра и (для сериала) номер сезона. Год и режиссёр — опциональные поля.
|
||||||
|
|
||||||
|
Название SHALL быть на русском языке для российского контента и на
|
||||||
|
английском (оригинальном) — для остального.
|
||||||
|
|
||||||
|
Система SHALL предпринять ограниченное число попыток получить от LLM валидный
|
||||||
|
результат (корректный JSON с непустым названием); бюджет попыток —
|
||||||
|
`[llm].max_retries` (по умолчанию 3). Транспортные ретраи провайдера LLM
|
||||||
|
(сетевые сбои, 429, 5xx) в этот счёт не входят.
|
||||||
|
|
||||||
|
Недоступность или ошибка LLM SHALL NOT прерывать приём загрузки: система
|
||||||
|
переходит к алгоритмическому фолбеку.
|
||||||
|
|
||||||
|
#### Scenario: LLM возвращает структурированное имя
|
||||||
|
|
||||||
|
- **WHEN** LLM по контексту возвращает валидный JSON с непустым названием
|
||||||
|
- **THEN** система формирует отображаемое имя из его полей (название, год,
|
||||||
|
для сериала — сезон)
|
||||||
|
|
||||||
|
#### Scenario: Российский контент — название на русском
|
||||||
|
|
||||||
|
- **WHEN** контент распознан как российский
|
||||||
|
- **THEN** в отображаемом имени используется русское название
|
||||||
|
|
||||||
|
#### Scenario: Исчерпан бюджет попыток LLM
|
||||||
|
|
||||||
|
- **WHEN** LLM за отведённые попытки (`[llm].max_retries`) не вернул валидный
|
||||||
|
результат либо недоступен
|
||||||
|
- **THEN** система не прерывает приём и переходит к алгоритмическому фолбеку
|
||||||
|
|
||||||
|
### Requirement: Алгоритмический фолбек вывода имени без сети
|
||||||
|
|
||||||
|
При неудаче LLM система SHALL выводить имя алгоритмически, без сетевых
|
||||||
|
запросов: брать первую содержательную строку контекста (без ссылок, команд
|
||||||
|
бота и UI-мусора), отсекать технические характеристики, очищать и обрезать
|
||||||
|
по длине.
|
||||||
|
|
||||||
|
Если и фолбек не дал непустого имени, система SHALL добавить загрузку без
|
||||||
|
параметра `rename`.
|
||||||
|
|
||||||
|
#### Scenario: Фолбек извлекает имя из контекста
|
||||||
|
|
||||||
|
- **WHEN** LLM недоступен или исчерпал попытки, а контекст содержательный
|
||||||
|
- **THEN** система берёт первую содержательную строку контекста, отсекает
|
||||||
|
технические характеристики и использует результат как отображаемое имя
|
||||||
|
- **AND** при этом не делается ни одного сетевого запроса
|
||||||
|
|
||||||
|
#### Scenario: Фолбек тоже пуст
|
||||||
|
|
||||||
|
- **WHEN** ни LLM, ни алгоритмический фолбек не дали непустого имени
|
||||||
|
- **THEN** система добавляет загрузку без параметра `rename`
|
||||||
Reference in New Issue
Block a user