From 322bd8aa5bf4c789cb0b9f3710bf07e00c1bd173 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Fri, 3 Jul 2026 10:34:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B0=D1=80=D1=85=D0=B8=D0=B2?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20review-source-selection:?= =?UTF-8?q?=20=D0=B4=D0=B5=D0=BB=D1=8C=D1=82=D0=B0=20web-ui=20=D0=B2=D0=BB?= =?UTF-8?q?=D0=B8=D1=82=D0=B0=20=D0=B2=20=D1=81=D0=BF=D0=B5=D0=BA=D0=B8=20?= =?UTF-8?q?(openspec)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Влил 3 ADDED (единый список источников, ручное добавление по id/URL, предпросмотр полей до фиксации) и 2 MODIFIED (превью для каждого источника; матч ссылкой в списке источников) требования в openspec/specs/web-ui. Change перенесён в changes/archive. Убрал реализованный пункт из беклога, перецелил ссылки на review-ux.md. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/backlog.md | 35 +---- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/web-ui/spec.md | 0 .../tasks.md | 0 openspec/specs/web-ui/spec.md | 128 ++++++++++++++++-- 7 files changed, 121 insertions(+), 42 deletions(-) rename openspec/changes/{review-source-selection => archive/2026-07-03-review-source-selection}/.openspec.yaml (100%) rename openspec/changes/{review-source-selection => archive/2026-07-03-review-source-selection}/design.md (100%) rename openspec/changes/{review-source-selection => archive/2026-07-03-review-source-selection}/proposal.md (100%) rename openspec/changes/{review-source-selection => archive/2026-07-03-review-source-selection}/specs/web-ui/spec.md (100%) rename openspec/changes/{review-source-selection => archive/2026-07-03-review-source-selection}/tasks.md (100%) diff --git a/docs/backlog.md b/docs/backlog.md index 569ccef..66c68e4 100644 --- a/docs/backlog.md +++ b/docs/backlog.md @@ -95,32 +95,6 @@ qBittorrent — осознанный выход за инвариант «ист [architecture.md](specs/architecture.md) → «Раскладка файлов», [workflow.md](specs/workflow.md). -### Ревью: выбор источника совпадения и предпросмотр - -Переработать страницу ревью так, чтобы показывать **все** совпавшие -результаты по метабазам списком и дать выбрать из них. Принцип: совпадение -есть **всегда** — мы лишь выбираем источник. Поэтому матч нейронки — это -отдельная строка в том же списке (наравне с кандидатами TMDB/TVDB), а не -особый режим. - -Возможности экрана: - -- список кандидатов из баз + строка «распознано нейронкой»; -- выбрать один кандидат, переключиться на другой, отменить матч с базой в - пользу нейронки; -- добавить кандидат вручную (по id/url базы), когда автопоиск промахнулся; -- при выборе/переключении — **предпросмотр полей** (название, режиссёр, - год) и **предпросмотр раскладки** (целевые пути) до применения. - -Развивает уже реализованный показ матча в вебе (страница загрузки и экран -ревью — провайдер, id, ссылка на запись) и детальный экран `/download/{id}`; -пересекается с быстрым выбором в Telegram. Веб остаётся точкой точных правок. - -Связано: [review-ux.md](specs/review-ux.md) (выбор кандидата, «без базы», -переключатель типа), [recognition.md](specs/recognition.md) (кандидаты -матча, провайдер-id), [«Улучшения UI: показывать матч»](#улучшения-ui-показывать-матч-с-записью-метабазы), -пакет `httpapi`. - ### Ретеншн и очистка БД Терминальные задачи (`done`/`cancelled`/`failed`/`reverted`), их попытки @@ -233,7 +207,7 @@ LLM, и **сверку с внешними базами** — а «поиск в Связано: [recognition.md](specs/recognition.md) (модель уверенности), [ADR-2026-06-13-auto-link-requires-db-match](adr/ADR-2026-06-13-auto-link-requires-db-match.md), -[«Ревью: выбор источника совпадения»](#ревью-выбор-источника-совпадения-и-предпросмотр), +[review-ux.md](specs/review-ux.md) (выбор источника в ревью, реализовано), [«Пересмотр набора capabilities»](#пересмотр-набора-capabilities-и-рефакторинг-спек). ### История переходов загрузки @@ -283,8 +257,8 @@ Web-сторона реализована: страница загрузки `/d загрузка — провайдер, id и ссылку на запись. Осталось довести то же в **Telegram**: в уведомлениях/подтверждениях показывать запись матча (название, год, провайдер-id, ссылку), чтобы ошибочную привязку было видно и из бота. -Полный выбор источника в вебе — см. [«Ревью: выбор источника -совпадения»](#ревью-выбор-источника-совпадения-и-предпросмотр). +Полный выбор источника в вебе уже реализован — см. +[review-ux.md](specs/review-ux.md). Связано: [review-ux.md](specs/review-ux.md), [recognition.md](specs/recognition.md) (матч в базе), [architecture.md](specs/architecture.md) → «Транспорты». @@ -388,8 +362,7 @@ qBittorrent, без исходящих запросов на пользоват Когда распознавание даёт несколько подходящих кандидатов в метабазе, предлагать их в Telegram списком (кнопки) для ручного выбора, а не молча брать первый/лучший. Веб остаётся точкой точных правок (полный выбор -источника — см. [«Ревью: выбор источника -совпадения»](#ревью-выбор-источника-совпадения-и-предпросмотр)), бот — +источника уже реализован — см. [review-ux.md](specs/review-ux.md)), бот — быстрый выбор из готового короткого списка. Связано: [review-ux.md](specs/review-ux.md) (боты — быстрые действия, веб — diff --git a/openspec/changes/review-source-selection/.openspec.yaml b/openspec/changes/archive/2026-07-03-review-source-selection/.openspec.yaml similarity index 100% rename from openspec/changes/review-source-selection/.openspec.yaml rename to openspec/changes/archive/2026-07-03-review-source-selection/.openspec.yaml diff --git a/openspec/changes/review-source-selection/design.md b/openspec/changes/archive/2026-07-03-review-source-selection/design.md similarity index 100% rename from openspec/changes/review-source-selection/design.md rename to openspec/changes/archive/2026-07-03-review-source-selection/design.md diff --git a/openspec/changes/review-source-selection/proposal.md b/openspec/changes/archive/2026-07-03-review-source-selection/proposal.md similarity index 100% rename from openspec/changes/review-source-selection/proposal.md rename to openspec/changes/archive/2026-07-03-review-source-selection/proposal.md diff --git a/openspec/changes/review-source-selection/specs/web-ui/spec.md b/openspec/changes/archive/2026-07-03-review-source-selection/specs/web-ui/spec.md similarity index 100% rename from openspec/changes/review-source-selection/specs/web-ui/spec.md rename to openspec/changes/archive/2026-07-03-review-source-selection/specs/web-ui/spec.md diff --git a/openspec/changes/review-source-selection/tasks.md b/openspec/changes/archive/2026-07-03-review-source-selection/tasks.md similarity index 100% rename from openspec/changes/review-source-selection/tasks.md rename to openspec/changes/archive/2026-07-03-review-source-selection/tasks.md diff --git a/openspec/specs/web-ui/spec.md b/openspec/specs/web-ui/spec.md index b0fece5..321c146 100644 --- a/openspec/specs/web-ui/spec.md +++ b/openspec/specs/web-ui/spec.md @@ -178,9 +178,10 @@ jellybit (`created_at`). Порядок MUST быть согласован ме Веб-UI SHALL показывать подтверждённый матч с записью метабазы (TMDB/TVDB/IMDb) как ссылку на эту запись — на странице просмотра `/download/{id}` (блок -распознавания) и в блоке «Источник совпадения» экрана ревью. Ссылка SHALL -открываться в новой вкладке с `rel="noopener"`. Рядом со ссылкой SHALL быть -видны провайдер, идентификатор записи и (при наличии) год. +распознавания) и в едином списке источников совпадения экрана ревью (у +активного источника-кандидата). Ссылка SHALL открываться в новой вкладке с +`rel="noopener"`. Рядом со ссылкой SHALL быть видны провайдер, идентификатор +записи и (при наличии) год. #### Scenario: Матч виден ссылкой на странице просмотра @@ -192,8 +193,8 @@ jellybit (`created_at`). Порядок MUST быть согласован ме - **WHEN** матч подтверждён (например, id задан вручную), но канонический URL записи построить нельзя -- **THEN** матч показывается текстом (провайдер и id) без ссылки, страница не - ломается +- **THEN** матч показывается текстом (провайдер и id) без ссылки, строка списка + не ломается ### Requirement: Блок информации о торренте @@ -227,16 +228,27 @@ jellybit (`created_at`). Порядок MUST быть согласован ме ### Requirement: Превью раскладки через единую логику именования Превью целевых путей раскладки в веб-UI SHALL вычисляться той же логикой -именования, что и реальная раскладка (`internal/naming`), а не дублировать -правила в шаблоне. Показанные пути MUST совпадать с теми, что создались бы при +именования, что и реальная раскладка (`internal/naming`/`internal/layout`), а +не дублировать правила в шаблоне. На экране ревью превью SHALL строиться **для +каждого источника в списке** (нейронка, кандидат базы, добавленный вручную) — +эфемерно на сервере, без записи сохранённого матча. Показанные для источника +пути MUST совпадать с теми, что создались бы при выборе этого источника и применении. #### Scenario: Превью совпадает с реальной раскладкой -- **WHEN** на экране ревью отображается превью целевых путей для текущей - догадки -- **THEN** эти пути идентичны тем, что создаст применение (те же правила имён, - спецвыпусков, мультифайла, запрещённых символов и коллизий) +- **WHEN** на экране ревью отображается превью целевых путей для источника +- **THEN** эти пути идентичны тем, что создаст применение при выборе этого + источника (те же правила имён, спецвыпусков, мультифайла, запрещённых + символов, тега провайдера и коллизий) + +#### Scenario: Переключение источника не тянет чужие поля + +- **GIVEN** активен кандидат с запиненными название/год, затем выбран + источник без собственных названия/года (нейронка или ручной кандидат) +- **WHEN** строится превью и затем выполняется применение выбранного источника +- **THEN** и превью, и применение используют название/год этого источника + (из плана распознавания), без унаследованных от прежнего кандидата ### Requirement: Клиентские взаимодействия без сборки @@ -256,3 +268,97 @@ jellybit (`created_at`). Порядок MUST быть согласован ме - **WHEN** пользователь раскрывает спойлер переданного контекста - **THEN** контекст показывается нативным `
`, без скриптов +### Requirement: Единый список источников совпадения на ревью + +Экран ревью (`/review/{id}`) SHALL показывать совпавшие источники **единым +списком**, в котором распознавание нейронкой (без базы) — такая же строка, +как кандидаты метабаз (TMDB/TVDB/TVMaze), а не отдельный режим сверху. +Ровно один источник в списке SHALL быть отмечен активным (эффективный +матч). Экран SHALL позволять как операции над этим списком: выбрать +кандидата базы, переключиться на другого кандидата и снять матч с базой +обратно на нейронку («без базы»). Смена активного источника SHALL +выполняться через раундтрип на сервер (форма/htmx), без клиентского +пересчёта доменного состояния. Список источников SHALL показываться только +при наличии плана распознавания. + +#### Scenario: Нейронка — строка в общем списке + +- **GIVEN** загрузка в `review` с распознаванием нейронкой и одним или + несколькими кандидатами метабаз +- **WHEN** пользователь открывает `GET /review/{id}` +- **THEN** источники показаны единым списком, где строка «распознано + нейронкой» стоит наравне с кандидатами баз +- **AND** активным отмечен ровно один источник (текущий эффективный матч) + +#### Scenario: Переключение между кандидатами + +- **GIVEN** на экране ревью выбран один кандидат метабазы +- **WHEN** пользователь выбирает другого кандидата из списка +- **THEN** активным становится выбранный кандидат, прочие — неактивны + +#### Scenario: Снятие матча в пользу нейронки + +- **GIVEN** на экране ревью активен кандидат метабазы с названием «Fargo» +- **WHEN** пользователь выбирает строку «распознано нейронкой» +- **THEN** матч с базой снимается (источник — нейронка, «без базы»), тег + папки провайдера не проставляется +- **AND** поля источника — из распознавания нейронкой, без унаследованных + от прежнего кандидата название/год + +### Requirement: Ручное добавление источника по id или URL + +Когда автопоиск по базам промахнулся, экран ревью SHALL позволять добавить +источник вручную — по идентификатору записи метабазы или, где применимо, по +её URL. Ввод SHALL разбираться и валидироваться в пару +`(provider, provider_id)` на входной границе (`internal/httpapi`); допустимые +провайдеры — `tmdb`, `tvdb`, `imdb`. Добавленный источник SHALL появляться в +списке как выбираемая строка; при совпадении `provider:id` с уже присутствующим +источником новая строка NOT создаётся, а выбирается существующая. +Некорректный ввод SHALL отклоняться с сообщением, не меняя текущий активный +источник. + +#### Scenario: Добавление кандидата по URL TMDB + +- **GIVEN** загрузка в `review`, где нужной записи нет среди автокандидатов +- **WHEN** пользователь вводит URL записи TMDB и подтверждает добавление +- **THEN** из URL извлекаются провайдер и id, источник добавляется в список + выбираемой строкой + +#### Scenario: Дубль id выбирает существующую строку + +- **GIVEN** в списке уже есть кандидат с данным `provider:id` +- **WHEN** пользователь добавляет вручную тот же `provider:id` +- **THEN** новая строка не создаётся, активным становится существующий + кандидат + +#### Scenario: Некорректный ввод отклонён + +- **WHEN** пользователь вводит нераспознаваемый id/URL +- **THEN** экран показывает сообщение об ошибке и не меняет текущий активный + источник + +### Requirement: Предпросмотр полей источника до фиксации выбора + +Экран ревью SHALL показывать для рассматриваемого источника (нейронка, +кандидат базы или добавленный вручную) **поля** результата — тип, название, +год, с зарезервированным местом под режиссёра. Показ полей источника +MUST NOT менять сохранённый матч загрузки и MUST NOT создавать хардлинки: +сохранённый матч меняется только явным выбором источника, а раскладка — +только действием «Применить». Совпадение целевых путей предпросмотра с +результатом применения регулируется требованием «Превью раскладки через +единую логику именования». + +#### Scenario: Предпросмотр полей без фиксации выбора + +- **GIVEN** список источников на экране ревью +- **WHEN** пользователь рассматривает источник, ещё не выбрав его активным +- **THEN** показаны поля результата (тип, название, год) для этого источника +- **AND** сохранённый матч загрузки не меняется, хардлинки не создаются + +#### Scenario: Зарезервированное место под режиссёра + +- **GIVEN** режиссёр из метабазы пока не загружается +- **WHEN** отображается предпросмотр полей источника +- **THEN** в предпросмотре присутствует место под режиссёра, показанное + пустым (или прочерком), не ломая вёрстку +