Files
jellybit/docs/specs/jellyfin-layout.md
T
avandClaude Opus 4.8 512567c8ba Рефакторинг границ capabilities: цепочка загрузка→матч→ревью→раскладка (openspec)
Привёл набор capabilities в OpenSpec к цепочке обработки, чтобы имя capability
отвечало одному поведению. Чисто по спекам, код и поведение системы не меняются.

Change refactor-capability-boundaries (архивирован):
- recognition разделён на recognition (разбор LLM) + metadata-match (сверка с базами)
- review выделен из web-ui + мигрирован из docs/specs/review-ux.md
- новые capability из docs/specs: file-layout, download-tracking, notifications
- identity очищен до инфра-id; приём (инфохэши, дедуп, ядро приёма) — в ingest
- уведомление о рассинхроне перенесено из state-reconciliation в notifications
- дубль владения путём и безопасного undo оставлен в state-reconciliation

Итог: 11 capabilities, openspec validate --strict проходит (+37/−11 требований).
Источник истины по мигрированным темам переехал в openspec/specs (шапки в docs).
Снят пункт беклога «Пересмотр набора capabilities».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 21:17:51 +03:00

7.0 KiB

Конвенции раскладки Jellyfin

Источник истины переехал в OpenSpecopenspec/specs/file-layout/ (имена, хардлинки, коллизия, copy-fallback). Владение путём (superseded) и безопасный undo (nlink<=1) — в openspec/specs/state-reconciliation/. Этот файл — справочный нарратив; при расхождении верна спека OpenSpec.

Целевые имена и структура, в которые jellybit раскладывает файлы хардлинками. Источники: Movies, Shows.

Фильмы

movies/
  Дюна Часть вторая (2024) [tmdbid-693134]/
    Дюна Часть вторая (2024).mkv
    Дюна Часть вторая (2024).ru.srt
  • Папка и файл — Название (Год).
  • provider-id в имени папки ([tmdbid-...]) добавляется при работе с базой — снимает неоднозначность для русских названий, которые Jellyfin иначе может опознать неверно.
  • Внешние субтитры — Имя.<lang>[.flag].srt (флаги forced/sdh/ default/hi), напр. …ru.forced.srt; база имени совпадает с именем видеофайла. Пары VobSub — .idx + .sub.

Сериалы

series/
  Название (2024) [tvdbid-123456]/
    Season 01/
      Название (2024) S01E01.mkv
      Название (2024) S01E02.mkv
  • provider-id — на папке сериала.
  • Сезоны — Season 01, файлы — ... SxxEyy.

Сопоставление источник → цель

Источник берём по пути из qBittorrent (save_path + относительное имя файла из /torrents/files, которое уже содержит корневую папку многофайловой раздачи; это уже хост-путь, path_map — фолбэк). Для каждого распознанного файла (не каталога) создаётся хардлинк в paths.movies/paths.series; целевые каталоги — mkdir (0755, 1000:1000). Исходный файл остаётся на месте (раздача продолжается), inode общий — диск не дублируется.

Целевое имя строится из распознанных полей и санитизируется (без разделителей пути, .., управляющих символов); финальный путь обязан быть строго под библиотекой. Существующую цель не перезаписываем (тот же inode → готово; другой файл → коллизия → review). Инварианты и undo — в architecture.md → «Раскладка файлов».

Владение целевым путём

Целевой путь принадлежит одной загрузке. Когда новая раскладка успешно ложится на путь, который раньше занимала другая загрузка (путь к этому моменту свободен — иначе была бы коллизия → review, чужой файл не перезаписываем), владение переходит к новой загрузке: прежние записи file_link на этот путь помечаются статусом superseded и перестают считаться целью прежней загрузки. Это нужно сверке с реальностью: иначе повторная закачка того же фильма (например, в другом качестве) по тому же пути ложно «воскрешала» бы удалённую задачу — см. workflow.md → «Сверка с реальностью». superseded-ссылки не считаются целью при сверке и не снимаются в Undo.

Желательно: целевой и исходный каталоги — на одной ФС/одном mount'е (внутри контейнера это обеспечивает единая песочница /srv/media), тогда работает дешёвый хардлинк. Если хардлинк невозможен (разные ФС или ФС без поддержки жёстких ссылок), layout не падает, а копирует файл с предупреждением в лог — см. architecture.md → «Раскладка файлов».

Безопасный undo (не снимать последнюю копию)

Undo снимает лишний хардлинк, а не единственный файл. Перед удалением батча layout проверяет каждую цель: если исходный файл уже не существует или у цели не осталось других жёстких ссылок (nlink <= 1), это — последняя копия данных, и весь Undo отклоняется целиком (ошибка ErrLastCopy), не сняв ни одной ссылки (частичный откат тоже стёр бы часть данных). Так нарушенный инвариант «источник неприкосновенен» (источник удалён вручную) не приводит к потере данных. Отсутствующую цель Undo пропускает как уже снятую (идемпотентность). Связь с состояниями рассинхрона — workflow.md → «Сверка с реальностью».

Крайние случаи

  • Многофайловый фильм (части) — стэкинг по точному токену Jellyfin (… - part1/cd1); точный формат уточнить при реализации.
  • РедакцииИмя (Год) [edition-Director's Cut] либо отдельные версии в папке фильма.
  • Двойная серия в одном файле — … SxxEyy-Eyy.
  • СпецвыпускиSeason 00.
  • Сезон-пак — серии в один Season xx; смешанный пак — по per-file сезонам.
  • Несколько аудиодорожек — обычно внутри mkv, не наша забота.
  • Аниме с абсолютной нумерацией — пересчёт в S·E, отдельная проработка (backlog.md).