При подтверждённом матче база папки (имя+год) наследуется от живой папки-якоря того же (provider, provider_id) вместо печати заново из выхода LLM — так второй/последующий сезон ложится в ТУ ЖЕ папку, а не заводит рядом почти одинаковую. Отдельная сущность «тайтл» не вводится. - layout: Plan.FolderBase перекрывает базу в папке и именах файлов; TitleFolder разбирает dst_path в папку тайтла и базу (снятие тега). - store: LiveTitleFolders — dst_path живых ссылок того же матча. - worker: resolveFolderBase (живость якоря — по наличию папки на диске, os.Lstat, а не по статусу ссылки в БД) в linkPlan и в превью ревью (превью=применение); рассинхрон нескольких живых папок → review из linking (deferred→review в графе нет). Схема БД не менялась. Change series-folder-convergence заархивирован, требования влиты в openspec/specs/file-layout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.6 KiB
ADDED Requirements
Requirement: Сходимость базы папки при подтверждённом матче
Система SHALL при построении плана раскладки для загрузки с подтверждённым матчем (заданы provider и provider_id) наследовать базу имени (Название (Год) — строку без provider-тега) от существующей на диске папки-якоря того же тайтла, а НЕ печатать её заново из выхода распознавания. Кандидаты
в якорь — целевые пути (dst_path) ссылок со статусом linked/copied/exists
других загрузок (не текущей), чей current recognition имеет тот же
(provider, provider_id).
Истина — папка на диске, а не запись в БД: кандидат SHALL считаться живым
якорем, только если его папка тайтла реально существует на файловой системе.
Статус ссылки в БД недостаточен — при ручном/Jellyfin-переименовании папки
запись file_link какое-то время остаётся linked (загрузка лишь позже уходит
в target_missing по сверке), а dst_path указывает на уже несуществующий путь.
Поэтому кандидат, чья папка тайтла отсутствует на диске, в якоря НЕ берётся; если
живых якорей не осталось, следующая загрузка снова печатает базу из распознавания.
Унаследованная база SHALL применяться и к папке сериала/фильма, и к именам
файлов внутри (episode/movie stem), чтобы серии разных сезонов совпадали по
базе (Fargo (2014) S02E01 рядом с Fargo (2014) S01E01). Provider-тег на папке
([tvdbid-…]) по-прежнему SHALL строиться из текущего (provider, provider_id).
База тайтла извлекается из папки-якоря снятием хвостового provider-тега
[...]; извлечённая база прогоняется через ту же санитизацию, что и печатаемая.
Если папку-якорь нельзя разобрать (сегмент не под корнем библиотеки, база пуста),
кандидат в якоря НЕ берётся (безопасный fallback на печать из распознавания), а
не даёт искажённую базу.
Наследование SHALL происходить только при подтверждённом матче. Нет живого якоря
(первая загрузка тайтла) → база печатается из распознавания, как прежде. Нет
матча (provider пуст / none) → авто-раскладки нет (инвариант), база не
наследуется — папку на ревью выбирает человек. Правило не вводит новых сущностей
и не меняет схему БД: это выборка по существующим file_link → download → recognition(is_current) плюс проверка существования папки на диске. Безопасность
по-прежнему держится на санитизации и проверке пути под библиотекой, а не на
доверии к выходу распознавания.
Разрешение базы SHALL быть единым для авто-раскладки и ручного «Применить», а также для предпросмотра раскладки на ревью — чтобы превью показывало ту же папку/имена, что даст применение (инвариант «превью = применение»). В предпросмотре разрешение выполняется без побочных эффектов (в review из-за рассинхрона переводит только применение, не показ).
Scenario: Второй сезон ложится в папку первого
- GIVEN первый сезон уже разложен в
series/Фарго (2014) [tvdbid-269613]/Season 01/…(ссылки живые) - AND новая загрузка со вторым сезоном имеет матч TVDB
269613, но распознавание дало название «Fargo» и год2017 - WHEN строится план раскладки второго сезона
- THEN база наследуется от живого якоря: папка =
series/Фарго (2014) [tvdbid-269613]/ - AND серия ложится как
Season 02/Фарго (2014) S02E01.mkv(база в имени файла — унаследованная, а не из выхода LLM)
Scenario: Нет живого якоря — печатаем из распознавания
- GIVEN ни у одной загрузки нет живых ссылок с тем же
(provider, provider_id) - WHEN строится план раскладки при подтверждённом матче
- THEN база берётся из распознавания (название+год), как прежде — первая загрузка «печатает» имя папки
Scenario: Нет матча — сходимость не применяется
- GIVEN у загрузки нет подтверждённого матча (
providerпуст /none) - WHEN обрабатывается раскладка
- THEN авто-наследования базы не происходит, загрузка идёт через review (папку выбирает человек)
Scenario: Папка-якорь переименована на диске — печатаем заново
- GIVEN у загрузки-кандидата статус ссылок ещё
linked, но её папка тайтла на диске переименована/удалена (поdst_pathпапки нет) - WHEN строится план раскладки новой загрузки с тем же матчем
- THEN отсутствующая на диске папка в якоря не берётся
- AND при отсутствии других живых якорей база печатается из распознавания
Scenario: Превью на ревью совпадает с применением
- GIVEN есть живой якорь тайтла, а распознавание текущей загрузки дало иную базу
- WHEN на ревью открывается предпросмотр целевой раскладки
- THEN превью показывает папку/имена с унаследованной базой якоря — те же, что даст «Применить»
Requirement: Рассинхрон живых папок тайтла уходит в review
Система MUST NOT молча выбирать якорь при обнаружении нескольких РАЗНЫХ живых целевых папок с одним (provider, provider_id) (рассинхрон, случившийся до внедрения правила сходимости): такая загрузка SHALL уходить в review с явной причиной «рассинхрон папок тайтла», чтобы человек выбрал/свёл папку вручную (по прецеденту коллизии, которая тоже уводит в review из раскладки).
Scenario: Две живые папки одного матча → review
- GIVEN для матча TVDB
269613существуют две разные живые папки (Фарго (2014) …иFargo (2017) …) - WHEN строится план раскладки новой загрузки с этим матчем
- THEN раскладка не выполняется, задача переходит в
reviewс причиной рассинхрона папок тайтла