Единый источник полей отображаемого имени и один рендер полного ярлыка на всех путях (старт и «Обновить имя»/авто-перелив). Раньше старт давал полный «Название (режиссёр, год). Сезон 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>
15 KiB
recognition Specification
Purpose
Распознавание: разбор недоверенных сигналов раздачи моделью в структурированный
план (тип фильм/сериал, каноническое название и год, файлы → серии). Capability
описывает пред-парс имени, контракт и провайдер LLM со структурированным
выводом, роли файлов на краях и модель уверенности (решение auto/review). Сверка
с внешними базами метаданных — в metadata-match.
Requirements
Requirement: Контракт LLM на оригинальное и локализованное названия
Промпт распознавания SHALL требовать от модели всегда заполнять и title, и
original_title. Если отдельного оригинального названия нет или контент
российского происхождения, модель SHALL дублировать title в
original_title. При неуверенности в оригинальном названии модель SHALL
дублировать title, а не выдумывать название (защита от ложного авто-матча).
Разбор ответа SHALL оставаться устойчивым к пустому original_title: пустое
значение не отбраковывается и не вызывает correction-ретрай; сверка
gracefully использует доступные названия.
Scenario: Российский фильм — дублирование
- GIVEN раздача российского фильма без отдельного оригинального названия
- WHEN модель возвращает план
- THEN
titleиoriginal_titleзаполнены одинаковым каноническим названием
Scenario: Пустой original_title не ломает разбор
- GIVEN ответ модели с пустым
original_title - WHEN план разбирается
- THEN разбор успешен без correction-ретрая
- AND сверка использует
title(иprovider_hint)
Requirement: Пред-парс имени релиза
Перед вызовом LLM система SHALL выполнять дешёвый пред-парс имени торрента
(go-ptn): извлекать черновые название, год, сезон, серию и качество. Результат
пред-парса SHALL использоваться как вспомогательный сигнал в промпте и как
сторона проверки согласованности при решении auto/review, но НЕ SHALL считаться
итоговым распознаванием.
Scenario: Пред-парс даёт черновые поля
- WHEN на вход распознавания поступает имя релиза
Fargo.S02.2015.WEB-DL.1080p - THEN пред-парс возвращает черновые
title,year,season,quality - AND эти значения передаются в промпт LLM как подсказка
Requirement: Разбор сигналов LLM в структурированный план
Система SHALL передавать LLM недоверенные сигналы (имя торрента, дерево файлов с
размерами, текстовый контекст и накопленные подсказки, пред-парс) и получать
структурированный план в схеме: type (movie|series), title,
original_title, year, provider_hint, files[] и confidence. Каждый
элемент files[] SHALL нести src, role
(main|episode|subtitle|extra|sample|ignore) и, для сериала,
per-file season/episode (отдельного скалярного season быть SHALL NOT — так
выражаются мультисезонные паки и спецвыпуски). План SHALL приниматься только
если каждый files[].src совпадает с реальным файлом торрента.
План MAY дополнительно нести опциональное скалярное поле director (режиссёр).
Это поле НЕ требуется от LLM и НЕ участвует в структурной валидации или гейте
авто-раскладки; его заполняют подтверждённый матч метабазы (авто) или закреплённый
в ревью выбранный источник (через override, см. metadata-match/review) как
недоверенное косметическое значение для вывода отображаемого имени. Как недоверенное
человекочитаемое поле, director SHALL NOT входить в plan-санитайзинг (он чистит
title/original_title/provider_hint); очистка режиссёра применяется при рендере
имени. Пустой director SHALL быть штатным (режиссёр неизвестен).
Scenario: План сериала с per-file нумерацией
- GIVEN сезон-пак из 10 видеофайлов
- WHEN LLM возвращает план
- THEN
type=series, а каждый видеофайл несёт своиseason/episode
Scenario: Несуществующий src отклоняется
- GIVEN ответ LLM, где
files[].srcне совпадает ни с одним файлом торрента - WHEN план разбирается
- THEN такой план не принимается как валидный
Scenario: Режиссёр не требуется от LLM и не влияет на гейт
- GIVEN ответ LLM без поля
director - WHEN план разбирается и оценивается
- THEN разбор успешен,
directorпуст - AND отсутствие режиссёра не влияет на структурную валидацию и решение auto/review
Requirement: Провайдер LLM за абстракцией со структурированным выводом
Доступ к LLM SHALL быть за интерфейсом с выбором реализации по полю [llm].type
(первый тип — openai-compat). Система SHALL запрашивать JSON-режим
(response_format: {"type":"json_object"}), срезать ```-ограждения и
валидировать ответ в Go против схемы плана. При ошибке разбора система SHALL
ретраить до [llm].max_retries, передавая модели саму ошибку и схему. Если после
ретраев ответ не разобран, задача SHALL уходить в review (НЕ в failed) с
причиной «ответ LLM не разобран».
Scenario: Неразобранный ответ уходит в review
- GIVEN LLM, чей ответ не проходит валидацию схемы после всех ретраев
- WHEN завершается распознавание
- THEN задача переходит в
reviewс причиной «ответ LLM не разобран» - AND задача НЕ переходит в
failed
Requirement: Модель уверенности и решение auto/review
Система SHALL раскладывать автоматически (без review) только при выполнении
ВСЕГО: (1) подтверждённый единичный сильный матч в базе (metadata-match) с
provider_id; (2) структурная валидация без предупреждений (фильм — ровно один
основной видеофайл; сериал — число серий бьётся с базой, нумерация S·E
консистентна); (3) согласованность пред-парса и LLM по типу/названию/году. Иначе
задача SHALL уходить в review с явной причиной. Самооценку LLM (confidence)
система SHALL учитывать лишь как вспомогательный сигнал, НЕ как единственный гейт.
Scenario: Нет матча в базе — всегда review
- GIVEN план без подтверждённого матча в базе (база выключена или матча нет)
- WHEN принимается решение auto/review
- THEN задача уходит в
review, авто-раскладка не делается
Scenario: Матч и чистая валидация — авто
- GIVEN подтверждённый единичный матч, чистая структурная валидация и согласованность сигналов
- WHEN принимается решение
- THEN допускается авто-раскладка (при отсутствии
force_review)
Requirement: Роли файлов на краях раздачи
Система SHALL относить семплы, «экстра» и мусор к роли ignore (эвристики размер/
имя + LLM), а внешние субтитры (.srt, .ass, пары VobSub .idx+.sub) —
привязывать к соответствующему видео. Любую неоднозначность нумерации (дыры,
дубли, спорные спецвыпуски) система SHALL эскалировать в review, а не разрешать
молча.
Scenario: Семпл помечается ignore
- GIVEN раздача с файлом
sample.mkvмалого размера - WHEN строится план
- THEN этот файл получает роль
ignoreи в раскладку не попадает
Requirement: Санитайзинг человекочитаемых полей плана
Перед структурной валидацией плана и сверкой с базами система SHALL санитизировать
человекочитаемые поля плана — title, original_title, provider_hint — как
недоверенный вывод LLM. Санитайзинг SHALL: (1) удалять управляющие и zero-width
символы (C0/C1, U+200B и родственные, BOM U+FEFF); (2) сводить внутренние
последовательности пробельных к одиночному пробелу и обрезать края; (3) сворачивать
кирилло-латинские homoglyph-двойники (см. ниже).
Свёртка двойников SHALL работать потокенно (по словам, разделённым не-буквенными символами): токен, все буквы которого принадлежат одному скрипту, система SHALL оставлять без изменений (билингвальность реальна — кириллические названия неприкосновенны); в токене смешанного скрипта система SHALL определять доминирующий скрипт по числу буквенных рун и заменять буквы-меньшинство их визуальными двойниками из доминирующего скрипта по курируемой таблице. При отсутствии доминирующего скрипта (равенство) токен SHALL оставаться без изменений.
Санитайзинг SHALL применяться ТОЛЬКО к перечисленным человекочитаемым полям.
files[].src система SHALL NOT санитизировать — эти значения обязаны совпадать с
реальными файлами торрента, и расхождение (в т.ч. homoglyph) SHALL оставаться
основанием отклонить план, а не поводом «чинить» путь.
Когда санитайзинг реально изменил значение поля, система SHALL логировать это на
уровне Debug (названия не относятся к секретам).
Scenario: Кириллический двойник в англоязычном названии сворачивается
- GIVEN план, где
title=Hаrold and the Purple Crayon(букваав первом слове — кириллическаяU+0430) - WHEN план санитизируется
- THEN первое слово становится
Harold(все буквы латинские) - AND в запрос к базе и в сравнение уходит латинское название
Scenario: Честное кириллическое название не трогается
- GIVEN план российского фильма с
title=Тёмный рыцарь, где все буквы каждого слова кириллические - WHEN план санитизируется
- THEN название остаётся кириллическим без замены букв
Scenario: Токен без доминирующего скрипта не трогается
- GIVEN план, где короткий токен содержит поровну латинских и кириллических букв (доминирующего скрипта нет)
- WHEN план санитизируется
- THEN этот токен остаётся без замены букв (осознанный trade-off: двухбуквенный homoglyph-typo не сворачивается)
Scenario: Zero-width и лишние пробелы вычищаются
- GIVEN план, где
titleсодержит zero-width символ и сдвоенные пробелы - WHEN план санитизируется
- THEN zero-width удалён, внутренние пробелы сведены к одиночным, края обрезаны
Scenario: files[].src не санитизируется
- GIVEN ответ LLM, где
files[].srcсодержит символ-двойник и не совпадает ни с одним реальным файлом торрента - WHEN план обрабатывается
- THEN
files[].srcНЕ изменяется санитайзингом - AND несовпадение src приводит к отклонению плана (эскалация в review), а не к «починке» пути