Единый источник полей отображаемого имени и один рендер полного ярлыка на всех путях (старт и «Обновить имя»/авто-перелив). Раньше старт давал полный «Название (режиссёр, год). Сезон N» но выбрасывал структуру, а перелив по распознаванию — усечённый «Title (Year)». - Слоистое разрешение скаляров имени: override → recognition(+match) → новый базовый слой «контекст» (download.parsed_context, JSON naming.Fields). - naming: публичные Fields/Label/Derive, вынесен единый рендер; удалён FormatTitleYear. Сводка сезонов вынесена в recognize.SeasonSummary. - Режиссёр из метабазы (решение A2): TMDB/TVDB credits через опциональный metadata.DirectorProvider; авто-матч кладёт в plan.Director, ручной выбор кандидата тянет credits и пиннит ovrDirector. Метабаза бьёт контекст. - refreshDisplayNameLocked строит полный ярлык из эффективных полей; инфо-панель ревью показывает загруженного режиссёра. - Миграция 0011_parsed_context + ER-схема. Всё косметика: на пути/раскладку не влияет, приём/вывод имени не валятся (best-effort). Закрывает беклог-задачу «Кнопка „Обновить имя“: полный формат ярлыка». OpenSpec: archive/2026-07-11-field-resolution-display-name (ingest, recognition, metadata-match, review). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9.8 KiB
ingest Specification
ADDED Requirements
Requirement: Сохранение извлечённой из контекста структуры имени
Система SHALL сохранять структуру имени, извлечённую LLM со структурированным
выводом на шаге добавления (тип, название, оригинальное название, год, режиссёр,
сезон), у загрузки (download.parsed_context, JSON), чтобы её поля могли
переиспользоваться при последующем выводе имени без повторного вызова LLM.
Алгоритмический фолбек структуры не даёт (его выход — только строка имени), тогда
parsed_context остаётся пустым — это штатно (нижний слой отсутствует). Сохранённая структура SHALL
быть базовым (наименее доверенным) слоем источника полей имени: её значения
берутся, только если более доверенный слой (распознавание/матч, ручные правки)
соответствующего поля не дал.
Сохранение SHALL быть best-effort и косметическим: неудача записи parsed_context
SHALL NOT проваливать добавление загрузки, а сама структура SHALL влиять только на
отображаемое имя и SHALL NOT влиять на пути файлов, распознавание или раскладку.
Пустая/невыведенная структура (нет контекста и подсказки) SHALL приводить к
пустому parsed_context (нечего сохранять).
Scenario: Извлечённый режиссёр сохраняется у загрузки
- GIVEN контекст загрузки, из которого LLM извлёк режиссёра и год
- WHEN система выводит имя на шаге добавления
- THEN извлечённая структура (в т.ч. режиссёр) сохраняется в
download.parsed_context - AND отображаемое имя формируется как и прежде (полный ярлык)
Scenario: Сбой сохранения структуры не валит добавление
- GIVEN запись
parsed_contextзавершается ошибкой - WHEN идёт шаг добавления загрузки
- THEN загрузка всё равно добавляется в qBittorrent (с
rename, если имя выведено) - AND ошибка логируется, приём/добавление не проваливается
Scenario: Пустой вход — пустая структура
- GIVEN пойманная загрузка без контекста и без подсказки из полей источника
- WHEN выполняется шаг добавления
- THEN структура не выводится,
download.parsed_contextпуст
MODIFIED Requirements
Requirement: Обновление отображаемого имени по распознаванию
Система SHALL уметь обновлять отображаемое имя загрузки после того, как
распознавание дало каноническое название, — переливая уже вычисленное имя (без
нового вызова LLM) в download.display_name и в имя раздачи qBittorrent.
Источником имени SHALL быть эффективные поля имени, разрешённые по слоям
сверху вниз (берётся первый непустой слой): (1) ручные правки override;
(2) распознавание с вложенным подтверждённым матчем (recognition, куда матч
метабазы уже вложил каноничные название/год/режиссёра); (3) сохранённая
структура из контекста (download.parsed_context). Так каждое поле берётся из
самого доверенного доступного источника, а данные из контекста (например
режиссёр) не теряются, если распознавание/матч их не дали. Название из слоя
распознавания SHALL совпадать с тем, что использует раскладка (эффективный
title), чтобы отображаемое имя не расходилось с целевыми путями.
Формат SHALL быть тем же полным детерминированным ярлыком, что и на шаге добавления: «Название (режиссёр, год)», где режиссёр и год опциональны, а для сериала добавляется сводка сезонов («. Сезон N» для одного сезона; «. Сезоны …» для многосезонного пака; отметка спецвыпусков) — согласованная со сводкой сезонов на экране просмотра. Применяются та же очистка от управляющих символов и обрезка по ограничению длины. Пустой источник (нет ни распознавания, ни сохранённой структуры, дающих непустое название) 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устанавливается в полный ярлык «Название (режиссёр, год)» (для сериала — со сводкой сезонов) - AND раздача в qBittorrent переименовывается в то же имя (по infohash своей раздачи)
Scenario: Режиссёр из контекста переживает распознавание без матча
- GIVEN загрузка, где режиссёр был извлечён из контекста, а распознавание прошло без подтверждённого матча (режиссёр из метабазы недоступен)
- WHEN запускается обновление отображаемого имени
- THEN в ярлыке используется режиссёр из сохранённого контекста
- AND название/год берутся из распознавания
Scenario: Режиссёр из матча бьёт контекстного
- GIVEN загрузка, где режиссёр есть и в контексте, и в подтверждённом матче
- WHEN формируется ярлык
- THEN используется режиссёр из матча (более доверенный слой)
Scenario: qBittorrent недоступен — имя у загрузки всё равно обновлено
- GIVEN обновление отображаемого имени с выведенным непустым именем
- WHEN переименование раздачи в qBittorrent завершается ошибкой (недоступен или раздача удалена)
- THEN
download.display_nameвсё равно обновлён - AND ошибка внешнего вызова qBittorrent логируется, операция не проваливается
Scenario: Нет источника имени — обновление ничего не делает
- GIVEN загрузка без распознанного названия и без сохранённой структуры (пустой источник имени)
- WHEN запускается обновление отображаемого имени
- THEN ни
download.display_name, ни имя раздачи не меняются (no-op)