metadata: TVDB отдаёт локализованное название и оригинал
- локаль из [general].language применяется при разборе ответа /search, а в запрос не уходит: параметр language у TVDB — фильтр выдачи, а не селектор перевода (ADR-2026-08-07) - Title берётся из блока translations с тотальным фолбэком на primary name, OriginalTitle — из primary name; форма ответа сверена по документации и живым прогоном не подтверждена (docs/research) - неожиданная форма ответа даёт WARN: признак — отсутствие во всей выдаче ключей языка ожидаемого вида, а не неудача разбора блока
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
## 1. Разведка формы ответа
|
||||
|
||||
- [x] 1.1 Записать `docs/research/tvdb-search-translations.md`: форма `SearchResult`
|
||||
(`name`, `translations`, `name_translated`, `primary_language`), смысл
|
||||
параметра `language` у `/search`, версия swagger, честный провенанс
|
||||
«сверено по документации, живым прогоном не подтверждено»
|
||||
- [x] 1.2 Добавить запись в перечень `docs/research/README.md` → «Записи»
|
||||
|
||||
## 2. Клиент TVDB
|
||||
|
||||
- [x] 2.1 `TVDBConfig.Language` — абстрактный код `ru`|`en`, комментарий по образцу
|
||||
`TMDBConfig.Language`
|
||||
- [x] 2.2 `tvdbLocale(lang string) string` — тотальный `switch`, `ru` → `rus`,
|
||||
default → `eng`; имя ровно как у соседа `tmdbLocale`; комментарий называет
|
||||
`config.validate` источником множества
|
||||
- [x] 2.3 `NewTVDB` кладёт выведенный код в поле клиента
|
||||
- [x] 2.4 `tvdbSearchResp` объявляет `translations` как `json.RawMessage` и
|
||||
раскладывает в `map[string]string` отдельно, с гашением ошибки: негодная
|
||||
форма косметического поля не должна ронять разбор всей выдачи
|
||||
- [x] 2.5 `Search` заполняет `Title` из перевода (ключ ищется `strings.EqualFold`,
|
||||
выбор среди совпавших детерминирован, значение — после `strings.TrimSpace`)
|
||||
с фолбэком на `name`, и `OriginalTitle` из `name`
|
||||
- [x] 2.6 Убедиться, что параметр `language` в строку запроса `/search` НЕ попал
|
||||
- [x] 2.7 Сигнал на операцию, когда во всей выдаче не встретилось ни одного ключа
|
||||
языка ожидаемого вида: подозрение на иную форму ответа отличается от
|
||||
штатного «перевода нет». Уровень `WARN` — `DEBUG` в проде выключен
|
||||
(правка по находке 1 ревью кода)
|
||||
- [x] 2.8 Дописать третий маппер (`metadata.tvdbLocale`) в перечень потребителей
|
||||
кода языка в комментарии `internal/config/config.go` → `validate`
|
||||
|
||||
## 3. Проброс из точки входа
|
||||
|
||||
- [x] 3.1 `cmd/jellybit/serve.go` — `metadataProviders` передаёт
|
||||
`Language: cfg.ContentLanguage()` в `TVDBConfig`
|
||||
|
||||
## 4. Тесты
|
||||
|
||||
- [x] 4.1 Стенд `fakeTVDB` в `tvdb_test.go` отдаёт запись с блоком `translations`
|
||||
- [x] 4.2 Табличный тест: перевод есть → `Title` локализован; перевода на нужный
|
||||
язык нет; перевод из одних пробелов; ключ в другом регистре; блока нет;
|
||||
блок пришёл не картой. Во всех случаях, кроме первого, `Title` = primary
|
||||
name; во всех без исключения `OriginalTitle` = primary name
|
||||
- [x] 4.3 Тест на язык по умолчанию (`Language` не задан → берётся `eng`)
|
||||
- [x] 4.4 Тест: строка запроса `/search` не содержит `language` ни при `ru`, ни при
|
||||
пустом языке, и совпадает в обоих случаях
|
||||
- [x] 4.5 Тест: негодный блок переводов не проваливает `Search` — кандидаты
|
||||
возвращаются
|
||||
- [x] 4.6 Тест: на один `Search` уходит ровно один HTTP-запрос к `/search`
|
||||
(счётчик на стенде) — расход лимита ключа не растёт
|
||||
- [x] 4.8 Тест на сигнал о неожиданной форме ответа: буфер логгера, 8 случаев,
|
||||
включая штатный «перевода нет» с ожиданием отсутствия следа
|
||||
- [x] 4.9 Тест на детерминизм выбора ключа перевода (200 прогонов на случай)
|
||||
- [x] 4.10 Тест `TestMatchMetadata_TranslationMakesTwoStrong` в
|
||||
`internal/recognize/metadata_test.go`: два кандидата совпадают с планом
|
||||
разными осями — подтверждённого матча нет, обе записи уходят в review
|
||||
- [x] 4.7 Интеграционный тест в `integration_test.go` за `TVDB_API_KEY` печатает
|
||||
`Title` и `OriginalTitle`. **Пишется, но не запускается** — живых обращений
|
||||
к TVDB в этой задаче нет
|
||||
|
||||
## 5. Учёт нерешённого
|
||||
|
||||
- [x] 5.1 Записать вопрос человеку в `docs/tasks/items/tvdb-title-locale.md`
|
||||
(раздел «Вопросы» + тег `question`) через скилл `av-dev-pm:tasks`: форма
|
||||
ответа TVDB не сверена живым API, нужен ручной прогон под ключом; плюс
|
||||
расхождение с критерием приёмки A1 про параметр языка в запросе; плюс
|
||||
двустороннее движение границы авто/review. **Сделано иначе, чем
|
||||
написано**: раздел «Вопросы» у задачи в спринте краснит `tasks.py check`
|
||||
и через него шаг `canon` гейта, а закрытие задачи стёрло бы вопрос вместе
|
||||
с файлом. Вопрос вынесен отдельной задачей-разведкой
|
||||
`docs/tasks/items/tvdb-search-response-live-check.md`
|
||||
|
||||
## 6. Гейт
|
||||
|
||||
- [x] 6.1 `openspec validate --strict tvdb-title-locale`
|
||||
- [x] 6.2 `task gate` зелёный
|
||||
|
||||
## Критерии приёмки задачи
|
||||
|
||||
Пришли из `docs/tasks/items/tvdb-title-locale.md`; переписывать и занижать их
|
||||
нельзя, исход по каждому идёт в доклад.
|
||||
|
||||
- [ ] A1 Запрос поиска TVDB содержит параметр языка, выведенный из
|
||||
`[general].language` (оракул: тест на `httptest`-сервере в `tvdb_test.go`).
|
||||
**Отменён решением 1 design.md**: параметр — фильтр выдачи, а не селектор
|
||||
перевода. Вместо него проверяется обратное — параметра в запросе нет
|
||||
(задача 4.4). Расхождение вынесено вопросом человеку
|
||||
- [x] A2 При наличии перевода `Candidate.Title` приходит на языке настройки, при
|
||||
отсутствии — равен primary name (оракул: табличный тест 4.2)
|
||||
- [x] A3 `Candidate.OriginalTitle` у TVDB непуст и равен primary name (оракул: тот
|
||||
же тест 4.2; интеграционный прогон за `TVDB_API_KEY` печатает оба поля —
|
||||
**написан, но не запущен**, живых обращений к метабазе в задаче нет)
|
||||
- [x] A4 Дельта-спека `metadata-match` заказывает локаль TVDB и фолбэк на оригинал
|
||||
(оракул: `openspec validate --strict`)
|
||||
|
||||
## Приёмочные критерии от рубрики (ревью дизайна, проход `rubric`)
|
||||
|
||||
Рубрика порождена до чтения кода и дизайна. Пункты 1, 5, 7 закрываются **условно**:
|
||||
они опираются на форму ответа `/search`, взятую из публичной документации, — стенд
|
||||
`httptest` проверит поведение кода на предполагаемой форме, а не истинность
|
||||
предположения.
|
||||
|
||||
- [x] R1 Фолбэк `Title` тотален по всем формам отсутствия перевода: блока нет; блок
|
||||
пуст; ключа нет; значение пустое. Пустого `Title` при непустом `name` нет ни
|
||||
на одной ветви. Оракул — строка табличного теста на каждую форму *(условно)*
|
||||
- [x] R2 Строка запроса `/search` не содержит параметра языка ни при каком значении
|
||||
настройки: утверждение об **отсутствии** ключа плюс равенство URL при `ru` и `en`
|
||||
- [x] R3 `OriginalTitle` = primary name всегда, включая случай найденного перевода;
|
||||
значение настройки языка его не смещает
|
||||
- [x] R4 Язык имеет один источник и один вывод диалекта: ни константы
|
||||
`tvdbDefaultLanguage`, ни фолбэка в конструкторе, ни второго места перевода
|
||||
`ru`/`en` → `rus`/`eng`
|
||||
- [x] R5 Разбор устойчив к форме, а не только к содержимому: лишние поля,
|
||||
отсутствующие поля, `translations: null`, `translations` не картой, усечённое
|
||||
тело — не паникуют и не проваливают `Search`; худший исход — фолбэк по R1.
|
||||
Негодный ответ поиска **целиком** по-прежнему остаётся ошибкой *(условно)*
|
||||
- [x] R6 Пустота значения проверяется **после** `TrimSpace`, а не до
|
||||
- [x] R7 Ключ в неожиданной форме (иной регистр) не даёт молчаливого фолбэка: поиск
|
||||
ключа регистронезависимый, исход зафиксирован тестом *(условно)*
|
||||
- [x] R8 Число обращений к TVDB на одну сверку не растёт: один `Search` — один
|
||||
HTTP-запрос, проверяется счётчиком на стенде
|
||||
- [x] R9 Результат — функция ответа провайдера, а не порядка проходов: исход не
|
||||
зависит от того, каким проходом пришёл кандидат; заполнение `OriginalTitle`
|
||||
не заставляет следующий заход перебора искать по подменённому значению; на
|
||||
фолбэке `Title == OriginalTitle` дедупликация ключей перебора не схлопывает
|
||||
перебор до нуля запросов
|
||||
- [x] R10 Секрет и контекст ведут себя как у остальных клиентов: ключ TVDB не
|
||||
попадает в лог, в текст ошибки разбора и в сообщение с URL; `context`
|
||||
доходит до запроса и отменяет его; таймаут из конфига
|
||||
- [x] R11 У ответа есть предел (`maxBody` в `rawGet`), у выдачи — прежний потолок
|
||||
кандидатов; второго канала без предела не заводится
|
||||
- [x] R12 Факт фолбэка наблюдаем, но не шумен: один чекпоинт на операцию,
|
||||
отсутствие перевода — не `ERROR`, недоступность опциональной метабазы
|
||||
состояние загрузки не двигает
|
||||
Reference in New Issue
Block a user