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,120 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Локализованное название кандидата TVDB
|
||||
|
||||
Кандидат TVDB SHALL нести локализованное название в поле `Title` и название на
|
||||
языке оригинала в поле `OriginalTitle`. Язык локализации задаёт та же глобальная
|
||||
настройка `language`, что и для TMDB, — правило единственного источника языка
|
||||
живёт в требовании «Локаль запроса к TMDB» и здесь не переписывается.
|
||||
|
||||
Источник локализованного названия — блок переводов в ответе поиска TVDB (карта
|
||||
«код языка → название»). `OriginalTitle` SHALL брать primary name записи — это
|
||||
название на языке оригинала и ось сравнения при матче.
|
||||
|
||||
Фолбэк SHALL быть тотальным: если перевода на нужный язык нет, его значение
|
||||
пусто после обрезки пробелов или блока переводов нет вовсе, `Title` SHALL быть
|
||||
равен primary name. Пустого `Title` при непустом primary name быть SHALL NOT.
|
||||
В `Title` SHALL попадать значение перевода после обрезки пробелов.
|
||||
|
||||
Ключ языка SHALL искаться регистронезависимо: молчаливый фолбэк из-за регистра
|
||||
ключа неотличим от отсутствия перевода и в эксплуатации не диагностируется. Если
|
||||
условию отвечает несколько ключей, выбор SHALL быть детерминированным — один и
|
||||
тот же ответ провайдера обязан давать один и тот же `Title`.
|
||||
|
||||
Локаль TVDB SHALL влиять только на разбор ответа и SHALL NOT сужать выдачу
|
||||
поиска: параметр языка в запрос поиска не передаётся. Причина — в
|
||||
`docs/research/tvdb-search-translations.md`; здесь заказано поведение, а не
|
||||
устройство чужого API. Тем самым разница с TMDB намеренна: у TMDB локаль едет в
|
||||
запрос, у TVDB читается из ответа.
|
||||
|
||||
Негодная форма блока переводов (блок пришёл не картой, значения не строки) SHALL
|
||||
приводить к тому же тотальному фолбэку, а не проваливать разбор ответа поиска
|
||||
целиком: ответ метабазы — недоверенный вход.
|
||||
|
||||
Подозрение на иную форму ответа SHALL оставлять диагностический след. Признаком
|
||||
служит **отсутствие во всей выдаче хотя бы одного ключа языка ожидаемого вида**,
|
||||
а не неудача разбора блока: `null`, пустая карта и словарь кодов другого вида
|
||||
разбираются без ошибки и потому признаком быть SHALL NOT. Штатное «перевода на
|
||||
этот язык нет» (ключи ожидаемого вида есть, нужного среди них нет) следа
|
||||
оставлять SHALL NOT — иначе сигнал неотличим от рутины. Уровень следа задают
|
||||
конвенции логирования проекта и здесь не нормируются; требуется наблюдаемость,
|
||||
а не конкретный уровень.
|
||||
|
||||
Настоящее требование не изменяет условий подтверждения матча — они заданы
|
||||
требованиями «Подтверждение матча и каноническое имя» и «Безгодовой второй
|
||||
проход сверки как fallback». Заполнение `OriginalTitle` расширяет множество
|
||||
названий кандидата, по которым идёт сравнение, с одного до двух. Исход гейта при
|
||||
этом монотонным SHALL NOT считаться: там, где сильный кандидат был один, их
|
||||
может стать двое, и тогда подтверждённого матча нет, а записи уходят кандидатами
|
||||
в review — по действующему требованию, без исключений для TVDB.
|
||||
|
||||
#### Scenario: Перевод на язык настройки есть
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** поиск возвращает запись с primary name `哪吒之魔童降世` и переводом `rus` = «Нэчжа»
|
||||
- **THEN** `Candidate.Title` = «Нэчжа»
|
||||
- **AND** `Candidate.OriginalTitle` = `哪吒之魔童降世`
|
||||
|
||||
#### Scenario: Перевода на язык настройки нет — фолбэк на оригинал
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** поиск возвращает запись с primary name `Fargo` и переводами без ключа `rus`
|
||||
- **THEN** `Candidate.Title` = `Fargo`
|
||||
- **AND** `Candidate.OriginalTitle` = `Fargo`
|
||||
|
||||
#### Scenario: Перевод есть, но пустой
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** поиск возвращает запись с primary name `Fargo` и переводом `rus` из одних пробелов
|
||||
- **THEN** `Candidate.Title` = `Fargo`
|
||||
- **AND** `Candidate.OriginalTitle` = `Fargo`
|
||||
|
||||
#### Scenario: Язык по умолчанию — английский
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` не задан в конфиге
|
||||
- **WHEN** поиск возвращает запись с переводами `eng` и `rus`
|
||||
- **THEN** `Candidate.Title` берётся из перевода `eng`
|
||||
|
||||
#### Scenario: Ключ перевода в другом регистре
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** поиск возвращает запись с переводом под ключом `RUS` вместо `rus`
|
||||
- **THEN** `Candidate.Title` берётся из этого перевода
|
||||
|
||||
#### Scenario: Блок переводов отсутствует или пришёл негодной формой
|
||||
|
||||
- **GIVEN** TVDB включён с любым значением `language`
|
||||
- **WHEN** поиск возвращает записи без блока переводов либо с блоком, который не разбирается картой
|
||||
- **THEN** `Candidate.Title` = primary name записи
|
||||
- **AND** `Candidate.OriginalTitle` = primary name записи
|
||||
- **AND** разбор ответа поиска не проваливается, кандидаты возвращаются
|
||||
- **AND** остаётся диагностический след о подозрении на иную форму ответа
|
||||
|
||||
#### Scenario: Коды языка не того вида — след остаётся
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** вся выдача поиска несёт блоки переводов с ключами другого вида (например, двухбуквенными), либо `null`, либо пустые
|
||||
- **THEN** `Candidate.Title` = primary name у каждой записи
|
||||
- **AND** остаётся диагностический след о подозрении на иную форму ответа
|
||||
|
||||
#### Scenario: Перевода на язык нет — следа не остаётся
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** выдача несёт блоки переводов с ключами ожидаемого вида, но без нужного языка
|
||||
- **THEN** `Candidate.Title` = primary name у каждой записи
|
||||
- **AND** диагностического следа не остаётся: это штатный исход, а не подозрение
|
||||
|
||||
#### Scenario: Запрос поиска не сужается языком
|
||||
|
||||
- **GIVEN** TVDB включён, глобальный `language` = `ru`
|
||||
- **WHEN** выполняется поиск
|
||||
- **THEN** строка запроса поиска не содержит параметра языка
|
||||
- **AND** строка запроса совпадает с той, что уходит при `language` = `en`
|
||||
|
||||
#### Scenario: Перевод сделал сильных кандидатов двумя — матч не подтверждён
|
||||
|
||||
- **GIVEN** план с `title` «Нэчжа» и `original_title` `Ne Zha`, год известен
|
||||
- **WHEN** поиск TVDB возвращает две записи в пределах года ±1: одну с primary
|
||||
name `哪吒之魔童降世` и переводом `rus` «Нэчжа», другую с primary name `Ne Zha`
|
||||
- **THEN** гейт даёт двух сильных кандидатов вместо одного
|
||||
- **AND** подтверждённого матча нет, обе записи уходят кандидатами в review
|
||||
Reference in New Issue
Block a user