Files
avandClaude Opus 4.8 5c3ef79496 Раскладка: сходимость папки сериала (второй сезон в ту же папку)
При подтверждённом матче база папки (имя+год) наследуется от живой
папки-якоря того же (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>
2026-07-10 13:50:49 +03:00

86 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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` с причиной рассинхрона папок тайтла