Files
jellybit/docs/tasks/items/metadata-title-sanitize.md
T
av 397f8aa2c3 tasks: два дефекта из ревью tvdb-title-locale взяты в спринт
- metadata-title-sanitize: название из метабазы подставляется в план мимо
  санитизации и уезжает в имя каталога дословно
- long-title-to-review: имя длиннее ~237 байт роняет раскладку в failed
  вместо отправки на ревью
2026-08-07 17:03:59 +03:00

5.8 KiB
Raw Blame History

🐞 Санитизировать название из метабазы перед подстановкой в план

  • Тип: fix
  • Категория: Ядро продукта
  • Зачем: plan.Title = match.Title подставляется ПОСЛЕ sanitizePlan — название из TMDB/TVDB уезжает в имя каталога Jellyfin дословно, с невидимыми символами и гомоглифами, и авто-раскладка это пропускает
  • Теги: sprint:2026-08-06

Название, пришедшее из метабазы, подставляется в план после того, как план прошёл санитизацию: plan.Title = match.Title стоит ниже sanitizePlan. Значение уезжает в имя каталога библиотеки, не сверенное ни с чем — гейт авто-раскладки сверяет normalize(c.Title) || normalize(c.OriginalTitle) и проходит по второму полю, а в путь идёт первое.

layout.sanitizeComponent ниже по потоку снимает /\:*?"<>| и байты < 0x20, но категорию Cf (невидимые управляющие) не трогает и гомоглифы не сворачивает. Итог — два визуально неотличимых каталога в медиатеке.

Класс пре-существующий, и это важно для рамок: тот же путь у TMDB, задача tvdb-title-locale его только распространила на второго провайдера. Инвариант «целевой путь строго под библиотекой» держится — проверено на Dune/../../etc, " .. ", "..." и на 400 символах, выхода из песочницы нет. Поэтому major, а не critical.

Провенанс: ревью изменения tvdb-title-locale, находка 2 — отчёт триажа.

Воспроизведение

Падающий тест на реальном Recognizer (оракул добыт триажом, не рассуждением). Подать кандидата метабазы с названием и посмотреть на plan.Title и решение гейта:

Вход Что происходит
три символа ZWSP (U+200B) auto=true, в план уезжают невидимки; sanitizeTitle дал бы пустую строку
Dune<RLO>gnp.mkv (U+202E) auto=true, имя каталога переворачивается при отображении
Dune\nHACK auto=true, перевод строки доезжает до плана
Dunа с кириллической а auto=true, каталог визуально неотличим от латинского Dune

Во всех четырёх случаях значение в плане отличается от того, что дал бы санитайзер, и ни одно не остановлено гейтом.

Затрагивает

  • internal/recognize/recognize.go — порядок подстановки match.Title и match.OriginalTitle относительно sanitizePlan;
  • internal/layout — состав sanitizeComponent, если решим закрывать категорию Cf и гомоглифы здесь, а не на подстановке (выбор места — часть задачи);
  • поведение обоих провайдеров, TMDB и TVDB: правка меняет уже работающий TMDB, и это её главный риск;
  • openspec/specs/metadata-match/spec.md либо file-layout — чьим требованием станет «название из метабазы санитизируется перед попаданием в путь».

Критерии приёмки

  • Все четыре входа из «Воспроизведения» дают либо санитизированный plan.Title, либо уход в review — но не авто-раскладку с исходным значением. Оракул: тот самый падающий тест из отчёта триажа, перенесённый в дерево.
  • Название, схлопывающееся санитизацией в пустую строку, не порождает каталог с пустым именем и не роняет раскладку. Оракул: табличный тест на границе, случай «три ZWSP».
  • Поведение TMDB на нормальных названиях не изменилось. Оракул: существующие тесты internal/recognize и internal/metadata зелёные без правок ожиданий.
  • Место санитизации названо требованием спеки, а не только кодом. Оракул: openspec validate --strict на дельте.

Рамки

Гомоглифы сворачивать не обязательно — достаточно сделать значение в пути предсказуемым и сверяемым; полноценная нормализация Unicode это отдельный разговор. Инвариант «целевой путь строго под библиотекой» уже держится, ломать его правкой нельзя. Правка задевает работающий TMDB — регресс на нём дороже самого дефекта.