display_name: слоистое разрешение полей + сохранение режиссёра из контекста
Единый источник полей отображаемого имени и один рендер полного ярлыка на всех путях (старт и «Обновить имя»/авто-перелив). Раньше старт давал полный «Название (режиссёр, год). Сезон 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>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
## 1. Хранение контекста (миграция + store)
|
||||
|
||||
- [x] 1.1 Миграция goose (`internal/store/migrations`): `ALTER TABLE download ADD
|
||||
COLUMN parsed_context TEXT NOT NULL DEFAULT ''` (down — дроп колонки)
|
||||
- [x] 1.2 `store.Download`: поле `ParsedContext string \`db:"parsed_context"\``;
|
||||
включить колонку в SELECT/INSERT (`internal/store/download.go`, `list.go`)
|
||||
- [x] 1.3 Метод `SetParsedContext(ctx, id, json string) error` (best-effort
|
||||
апдейт) в store; добавить в интерфейс worker
|
||||
- [x] 1.4 Обновить ER-схему `docs/specs/database.md` (новая колонка)
|
||||
|
||||
## 2. Извлечение из контекста → persistent
|
||||
|
||||
- [x] 2.1 `internal/naming`: экспортировать извлечённую структуру и её JSON
|
||||
(публичный тип полей + метод/функция, возвращающая структуру вместе с именем),
|
||||
не ломая текущий `DeriveName`
|
||||
- [x] 2.2 `worker.go` шаг добавления: сохранять извлечённую структуру в
|
||||
`download.parsed_context` (best-effort; сбой логируется, добавление не валит);
|
||||
пустая структура → пустой `parsed_context`
|
||||
|
||||
## 3. Режиссёр из метабазы (решение A2)
|
||||
|
||||
- [x] 3.1 `metadata.Provider`: метод выборки режиссёра по id (credits) —
|
||||
реализовать в клиентах TMDB и TVDB (best-effort); TVMaze → пусто/не поддержан
|
||||
- [x] 3.2 `recognize.Plan`: поле `Director \`json:"director,omitempty"\``; LLM его
|
||||
не заполняет, в валидации/гейте/plan-санитайзинге не участвует
|
||||
- [x] 3.3 `Recognize`/`matchMetadata`: при подтверждённом авто-матче вкладывать
|
||||
режиссёра в `plan.Director` (как уже вкладываются title/year), best-effort
|
||||
- [x] 3.4 Интерфейс `Recognizer` (worker): метод выборки режиссёра по
|
||||
`(provider, id)` — проброс к metadata-провайдерам (без прямой зависимости
|
||||
worker→metadata)
|
||||
- [x] 3.5 Ручной путь ревью: новое override-поле `ovrDirector`; добавить его в
|
||||
`sourcePins`; `chooseCandidateLocked`/`AddManualSource` best-effort тянут
|
||||
режиссёра выбранного `provider:id` и пишут в `ovrDirector` (пусто → очищает пин)
|
||||
- [x] 3.6 `applyOverrides`: класть `ovrDirector` в `plan.Director`
|
||||
|
||||
## 4. Единый рендер ярлыка + слоистое разрешение
|
||||
|
||||
- [x] 4.1 Вынести логику `seasonSummary` (`httpapi/files.go`) в переиспользуемое
|
||||
место (`recognize` или `naming`); `httpapi` зовёт общий хелпер (поведение
|
||||
карточки не меняется)
|
||||
- [x] 4.2 `internal/naming`: публичная функция рендера полного ярлыка из
|
||||
эффективных полей (title/director/year + **готовая строка сводки сезонов**);
|
||||
удалить/перевести `FormatTitleYear`
|
||||
- [x] 4.3 `worker`: хелпер `effectiveNameFields(download, plan, overrides)` —
|
||||
первый непустой слой override → recognition(+match) → parsed_context на поле;
|
||||
сводка сезонов: `seasonSummary(plan)` при наличии episode-ролей, иначе fallback
|
||||
на контекстный скаляр `Season` → «Сезон N»; director: override → plan → context
|
||||
- [x] 4.4 `refreshDisplayNameLocked`: заменить `FormatTitleYear(...)` на рендер по
|
||||
эффективным полям; сохранить best-effort qBit rename, no-op при пустом источнике
|
||||
- [x] 4.5 Сверить, что старт (`DeriveName`) использует тот же рендер (единый
|
||||
формат ярлыка на всех путях)
|
||||
|
||||
## 5. UI ревью: режиссёр в инфо-части
|
||||
|
||||
- [x] 5.1 `httpapi` инфо-часть выбранного источника: показывать режиссёра из
|
||||
эффективного плана/кандидата, пустой → прочерк (без слома вёрстки)
|
||||
|
||||
## 6. Тесты и проверки
|
||||
|
||||
- [x] 6.1 Юнит-тесты рендера ярлыка: полный формат, опциональность режиссёра/года,
|
||||
сводка сезонов (один/несколько/спецвыпуски), санитайзинг+лимит
|
||||
- [x] 6.2 Тест слоистого разрешения: контекст-режиссёр переживает распознавание без
|
||||
матча; матч-режиссёр бьёт контекстного; override бьёт оба
|
||||
- [x] 6.3 Тест persist `parsed_context` на старте + best-effort (сбой не валит
|
||||
добавление)
|
||||
- [x] 6.4 Тест метабазного режиссёра: авто-матч TMDB/TVDB → `plan.Director`;
|
||||
ручной выбор кандидата тянет режиссёра в `ovrDirector`; недоступность credits →
|
||||
пусто, матч/выбор не падают
|
||||
- [x] 6.5 `task lint` и `task test` зелёные; `openspec validate --strict`
|
||||
- [x] 6.6 Удалить закрытую беклог-задачу
|
||||
`docs/backlog/knopka-obnovit-imya-polnyj-format.md` и строку в индексе беклога
|
||||
Reference in New Issue
Block a user