## ADDED Requirements ### 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** в предпросмотре присутствует место под режиссёра, показанное пустым (или прочерком), не ломая вёрстку ## MODIFIED Requirements ### Requirement: Превью раскладки через единую логику именования Превью целевых путей раскладки в веб-UI SHALL вычисляться той же логикой именования, что и реальная раскладка (`internal/naming`/`internal/layout`), а не дублировать правила в шаблоне. На экране ревью превью SHALL строиться **для каждого источника в списке** (нейронка, кандидат базы, добавленный вручную) — эфемерно на сервере, без записи сохранённого матча. Показанные для источника пути MUST совпадать с теми, что создались бы при выборе этого источника и применении. #### Scenario: Превью совпадает с реальной раскладкой - **WHEN** на экране ревью отображается превью целевых путей для источника - **THEN** эти пути идентичны тем, что создаст применение при выборе этого источника (те же правила имён, спецвыпусков, мультифайла, запрещённых символов, тега провайдера и коллизий) #### Scenario: Переключение источника не тянет чужие поля - **GIVEN** активен кандидат с запиненными название/год, затем выбран источник без собственных названия/года (нейронка или ручной кандидат) - **WHEN** строится превью и затем выполняется применение выбранного источника - **THEN** и превью, и применение используют название/год этого источника (из плана распознавания), без унаследованных от прежнего кандидата ### Requirement: Матч с записью метабазы ссылкой Веб-UI SHALL показывать подтверждённый матч с записью метабазы (TMDB/TVDB/IMDb) как ссылку на эту запись — на странице просмотра `/download/{id}` (блок распознавания) и в едином списке источников совпадения экрана ревью (у активного источника-кандидата). Ссылка SHALL открываться в новой вкладке с `rel="noopener"`. Рядом со ссылкой SHALL быть видны провайдер, идентификатор записи и (при наличии) год. #### Scenario: Матч виден ссылкой на странице просмотра - **WHEN** у загрузки подтверждён матч с записью метабазы и известен URL записи - **THEN** в блоке распознавания на `/download/{id}` матч показан ссылкой на запись с провайдером и id #### Scenario: URL записи неизвестен - **WHEN** матч подтверждён (например, id задан вручную), но канонический URL записи построить нельзя - **THEN** матч показывается текстом (провайдер и id) без ссылки, строка списка не ломается