## MODIFIED Requirements

### Requirement: Вывод имени через LLM со структурированным выводом

Система SHALL строить отображаемое имя с помощью LLM (структурированный
JSON-вывод), извлекая из контекста тип (movie/series), название, год,
режиссёра и (для сериала) номер сезона. Год и режиссёр — опциональные поля.

Если и контекст, и подсказка из полей источника (`dn` magnet / имя `.torrent`)
пусты, система SHALL считать имя не выведенным и SHALL NOT вызывать LLM (выводить
имя не из чего — вызов на пустом входе способен лишь галлюцинировать). В этом
случае загрузка добавляется без `rename`, а `download.display_name` остаётся
пустым.

Название 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** система не прерывает приём и переходит к алгоритмическому фолбеку

#### Scenario: Пустой вход — LLM не вызывается

- **GIVEN** пойманная загрузка без пользовательского контекста и без подсказки
  из полей источника (голый magnet без `dn`)
- **WHEN** система выводит отображаемое имя на шаге добавления
- **THEN** LLM не вызывается, имя считается не выведенным
- **AND** загрузка добавляется без `rename`, а `download.display_name` пуст
  (заголовок в веб-UI берётся из фолбека)

## ADDED Requirements

### Requirement: Обновление отображаемого имени по распознаванию

Система SHALL уметь обновлять отображаемое имя загрузки после того, как
распознавание дало каноническое название, — переливая уже вычисленное имя (без
нового вызова LLM) в `download.display_name` и в имя раздачи qBittorrent.

Источником имени SHALL быть **эффективное** распознанное название: пины
`title`/`year` (если матч подтверждён), иначе `recognition.title`/`year` — то
же название, что использует раскладка. Формат SHALL быть коротким детерминированным
ярлыком `Title (Year)` (год опционален), с той же очисткой от управляющих
символов и обрезкой по длине, что и вывод имени на шаге добавления. Пустой
источник (нет распознавания или пустое название) SHALL приводить к отсутствию
изменений (no-op).

Переименование раздачи в qBittorrent SHALL адресоваться по infohash своей
раздачи и SHALL быть best-effort: сбой (раздача удалена, qBittorrent недоступен)
SHALL NOT проваливать обновление — `download.display_name` обновляется в любом
случае, ошибка внешнего вызова логируется. Как и на шаге добавления,
отображаемое имя SHALL влиять только на отображение и SHALL NOT влиять на пути
файлов, распознавание или раскладку.

Обновление имени SHALL иметь две точки входа: **авто** — при подтверждённом
матче (см. capability `review`); **ручную** — по явному действию пользователя.
Ручное действие SHALL перезаписывать текущее имя всегда; авто SHALL перезаписывать,
когда выведенное имя непусто.

#### Scenario: Перелив имени в загрузку и раздачу

- **GIVEN** загрузка с распознанным непустым каноническим названием
- **WHEN** запускается обновление отображаемого имени
- **THEN** `download.display_name` устанавливается в `Title (Year)`
- **AND** раздача в qBittorrent переименовывается в то же имя (по infohash своей
  раздачи)

#### Scenario: qBittorrent недоступен — имя у загрузки всё равно обновлено

- **GIVEN** обновление отображаемого имени с выведенным непустым именем
- **WHEN** переименование раздачи в qBittorrent завершается ошибкой (недоступен
  или раздача удалена)
- **THEN** `download.display_name` всё равно обновлён
- **AND** ошибка внешнего вызова qBittorrent логируется, операция не проваливается

#### Scenario: Нет распознавания — обновление ничего не делает

- **GIVEN** загрузка без распознанного названия (пустой источник имени)
- **WHEN** запускается обновление отображаемого имени
- **THEN** ни `download.display_name`, ни имя раздачи не меняются (no-op)
