## MODIFIED Requirements ### Requirement: Страницы веб-UI Веб-UI SHALL предоставлять страницы: список загрузок с единым окном добавления, **серверными фильтром по группе состояний, поиском и постраничной выдачей (пагинацией)** (`/`), экран ревью одной загрузки (`/review/{id}`) и страницу просмотра одной загрузки (`/download/{id}`) с распознаванием, файлами→раскладкой, историей, блоком информации о торренте и — для сидирующих задач — секцией живой статистики раздачи. Карточки активных (downloading) загрузок в списке SHALL содержать индикатор прогресса. Фильтр, поиск и номер страницы SHALL передаваться GET-параметрами запроса (например `f`, `q`, `page`) и SHALL работать без клиентского JavaScript. Состояние `deleted` SHALL быть скрыто в списке по умолчанию (с переключателем «показать всё»). Механика живого обновления прогресса и наполнение секции раздачи определяются capability `live-status`. #### Scenario: Просмотр одной загрузки - **WHEN** клиент открывает `GET /download/{id}` существующей загрузки - **THEN** отрисовывается страница с её распознаванием, файлами, раскладкой и историей #### Scenario: Прогресс активной загрузки в списке - **WHEN** в списке есть загрузка в состоянии `downloading` - **THEN** её карточка содержит индикатор прогресса (прогресс-бар со скоростью и ETA) #### Scenario: Удалённые скрыты по умолчанию - **WHEN** в списке есть загрузки в состоянии `deleted` и фильтр «показать всё» не включён - **THEN** они не отображаются, но доступны при включённом переключателе #### Scenario: Пагинация списка - **WHEN** загрузок под текущим фильтром больше, чем помещается на одну страницу, и клиент запрашивает `GET /?page=N` - **THEN** возвращается N-я страница результатов и элементы навигации по страницам, сохраняющие текущие фильтр и поисковый запрос #### Scenario: Серверный фильтр и поиск - **WHEN** клиент запрашивает список с параметрами фильтра по состоянию и/или строкой поиска (`GET /?f=review&q=дюна`) - **THEN** сервер возвращает только подходящие загрузки (по группе состояний и совпадению строки в названии/infohash/контексте), отфильтрованные на стороне БД, а не на клиенте ## ADDED Requirements ### Requirement: Порядок списка загрузок Список загрузок SHALL сортироваться по времени добавления торрента в источник (qBittorrent `added_on`), от новых к старым. Если время добавления в источник неизвестно, для сортировки SHALL использоваться время создания загрузки в jellybit (`created_at`). Порядок MUST быть согласован между страницами пагинации. #### Scenario: Сортировка по времени добавления в источник - **WHEN** отрисовывается список загрузок, у которых известно время добавления в qBittorrent - **THEN** загрузки идут от недавно добавленных к более старым по этому времени #### Scenario: Фолбек порядка без времени источника - **WHEN** у загрузки нет сохранённого времени добавления в источник - **THEN** для её позиции в списке используется время создания загрузки в jellybit ### Requirement: Заголовок загрузки из имени раздачи Веб-UI SHALL показывать заголовком загрузки (в карточке списка и в шапке страницы `/download/{id}`) сохранённое отображаемое имя раздачи (`display_name`, переданное в qBittorrent при приёме). Если имя пусто, заголовок SHALL брать распознанное название из плана; если и его нет — сырой источник (`source_ref`), усечённый до одной строки как обычный заголовок. Заголовок MUST NOT занимать несколько строк сырым magnet. #### Scenario: Заголовок из имени раздачи - **WHEN** у загрузки сохранено отображаемое имя раздачи - **THEN** карточка и страница показывают это имя заголовком #### Scenario: Фолбек до распознавания и без имени - **WHEN** отображаемого имени нет, но есть распознанное название - **THEN** заголовком служит распознанное название - **AND** если нет ни того, ни другого — заголовком служит усечённый до одной строки сырой источник, а не многострочный magnet ### 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) без ссылки, страница не ломается ### Requirement: Блок информации о торренте Страница `/download/{id}` SHALL содержать блок «Информация о торренте» с полным исходным источником (magnet/URL/`.torrent`-ссылка) и infohash с кнопкой копирования. Сырой источник MUST выноситься в этот блок, а не в заголовок. #### Scenario: Полный источник в отдельном блоке - **WHEN** клиент открывает `GET /download/{id}` - **THEN** полный источник (magnet) и infohash показаны в блоке «Информация о торренте», а заголовок страницы остаётся человекочитаемым именем