From 694a9f4bef23b5579af0e6beec682c6c3b4d3868 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 28 Jun 2026 12:27:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=90=D1=80=D1=85=D0=B8=D0=B2=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D1=81=D0=BF=D0=B5=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/todo.md | 10 -- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/ingest/spec.md | 0 .../2026-06-28-add-qbt-display-name}/tasks.md | 5 +- openspec/specs/ingest/spec.md | 98 +++++++++++++++++++ 7 files changed, 101 insertions(+), 12 deletions(-) rename openspec/changes/{add-qbt-display-name => archive/2026-06-28-add-qbt-display-name}/.openspec.yaml (100%) rename openspec/changes/{add-qbt-display-name => archive/2026-06-28-add-qbt-display-name}/design.md (100%) rename openspec/changes/{add-qbt-display-name => archive/2026-06-28-add-qbt-display-name}/proposal.md (100%) rename openspec/changes/{add-qbt-display-name => archive/2026-06-28-add-qbt-display-name}/specs/ingest/spec.md (100%) rename openspec/changes/{add-qbt-display-name => archive/2026-06-28-add-qbt-display-name}/tasks.md (93%) create mode 100644 openspec/specs/ingest/spec.md diff --git a/docs/todo.md b/docs/todo.md index b2a7917..09f1458 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -25,16 +25,6 @@ матч в базе), [jellyfin-layout.md](specs/jellyfin-layout.md) (папка сериала с провайдер-id). -### Название из контекста при добавлении в qBittorrent - -При создании magnet-загрузки передавать в qBittorrent человекочитаемое имя -из контекста (если оно есть), чтобы в списке qBit не было безликих -`rutracker-topic-6852853`. Небольшая задача с заметной отдачей в -повседневной эксплуатации. - -Связано: [architecture.md](specs/architecture.md) → «Транспорты», пакет -`ingest`/`qbt`. - ### Рассинхрон состояния с реальностью (удалённый торрент / файлы) Состояние jellybit может разойтись с тем, что реально лежит на диске. diff --git a/openspec/changes/add-qbt-display-name/.openspec.yaml b/openspec/changes/archive/2026-06-28-add-qbt-display-name/.openspec.yaml similarity index 100% rename from openspec/changes/add-qbt-display-name/.openspec.yaml rename to openspec/changes/archive/2026-06-28-add-qbt-display-name/.openspec.yaml diff --git a/openspec/changes/add-qbt-display-name/design.md b/openspec/changes/archive/2026-06-28-add-qbt-display-name/design.md similarity index 100% rename from openspec/changes/add-qbt-display-name/design.md rename to openspec/changes/archive/2026-06-28-add-qbt-display-name/design.md diff --git a/openspec/changes/add-qbt-display-name/proposal.md b/openspec/changes/archive/2026-06-28-add-qbt-display-name/proposal.md similarity index 100% rename from openspec/changes/add-qbt-display-name/proposal.md rename to openspec/changes/archive/2026-06-28-add-qbt-display-name/proposal.md diff --git a/openspec/changes/add-qbt-display-name/specs/ingest/spec.md b/openspec/changes/archive/2026-06-28-add-qbt-display-name/specs/ingest/spec.md similarity index 100% rename from openspec/changes/add-qbt-display-name/specs/ingest/spec.md rename to openspec/changes/archive/2026-06-28-add-qbt-display-name/specs/ingest/spec.md diff --git a/openspec/changes/add-qbt-display-name/tasks.md b/openspec/changes/archive/2026-06-28-add-qbt-display-name/tasks.md similarity index 93% rename from openspec/changes/add-qbt-display-name/tasks.md rename to openspec/changes/archive/2026-06-28-add-qbt-display-name/tasks.md index 64088ac..e121ccd 100644 --- a/openspec/changes/add-qbt-display-name/tasks.md +++ b/openspec/changes/archive/2026-06-28-add-qbt-display-name/tasks.md @@ -56,5 +56,6 @@ - [x] 6.1 `task test` и `task lint` зелёные - [ ] 6.2 Ручная проверка на реальном qBittorrent: имя видно в списке; подтвердить поведение `rename` для многофайловой раздачи -- [ ] 6.3 Обновить `docs/specs` (architecture/ingest) и отметить todo - «Название из контекста» как реализованную при архивации change +- [x] 6.3 При архивации: дельта-спека синкнута в `openspec/specs/ingest` + (новая capability), пункт «Название из контекста» убран из `docs/todo.md` + (реализованное переехало в OpenSpec-спеки) diff --git a/openspec/specs/ingest/spec.md b/openspec/specs/ingest/spec.md new file mode 100644 index 0000000..462dabe --- /dev/null +++ b/openspec/specs/ingest/spec.md @@ -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`