# Skill: jellyfin-music-library-pitfalls — зеркало навыка **Скилл:** `devops/jellyfin-music-library-pitfalls` **Путь:** `~/.hermes/hermes-whale/skills/devops/jellyfin-music-library-pitfalls/SKILL.md` **Создан:** 2026-09-27 **Category:** devops > Зеркало для vault. Полный текст — в SKILL.md скилла. ## Когда применять Любая работа с **музыкальной** библиотекой Jellyfin на удалённом Docker-хосте (TrueNAS/Taiga, Kraken): добавление, отладка неверной навигации, откат. **Сначала читать в vault:** - [[personal/tech/jellyfin-tags-cp1251-unreadable]] — почему Jellyfin не читает теги - [[family/plans/music-jellyfin-lidarr-taiga]] — план проекта ## Ключевые правила (краткая версия) ### 🔴 RULE 0 — бэкап БД ДО создания библиотеки Создание библиотеки + скан пишет тысячи строк в `BaseItems`. **Без бэкапа БД откат невозможен.** ```bash docker stop jellyfin docker cp jellyfin:/config/data/data/jellyfin.db /home/truenas_admin/jellyfin.db.bak-$(date +%Y%m%d-%H%M%S) docker start jellyfin ``` - **WAL чекпоинтится только при остановленном контейнере** — иначе копия битая. - **`SQLiteBackups/` часто ПУСТА** — не полагаться на авто-бэкапы Jellyfin, проверять. - **Снапшоты ZFS часто 0 штук** — проверять `zfs list -t snapshot | wc -l`. ### 🔴 Почему Jellyfin игнорирует ID3-теги — три причины 1. **Баг `jellyfin/jellyfin#18162`** (confirmed, In Progress): имя MusicArtist берётся **из имени ПАПКИ**, перекрывая `ARTIST`/`ALBUMARTIST`. Если имя папки произвольное — Jellyfin идёт в **MusicBrainz** и создаёт артиста оттуда. 2. **Отсутствует `TPE2` (albumartist)** — ключевое поле для иерархии Artist→Album→Track. При пустом `TPE2` Jellyfin уходит в fallback на папки и **перестаёт читать `TPE1` вообще**. 3. **4-уровневая структура** `категория/подпапка/релиз/файл` — Jellyfin читает как `Artist/Album`. **Сигнатура дефекта:** `число MusicArtist == число папок верхнего уровня`. ### 🔬 Диагностика — через `od`, НЕ `ffprobe` `ffprobe` нормализует вывод и **угадывает кодировку** → может показать `album_artist`, которого нет как фрейма `TPE2` (из ID3v1-хвоста или TXXX). ```bash od -A d -t x1z -N 200 "/path/file.mp3" # заголовок + первые фреймы od -A d -t x1z -N 2000 "$F" | grep -oE "[A-Z][A-Z0-9]{3}" | sort -u # все фреймы od -A d -t x1z -N 4000 "$F" | grep "P E 2" # есть ли TPE2 ``` **Питфоллы:** - `-of default=nw=1:nk=1` срезает префиксы `TAG:` → ложный вывод «теги пустые». - `TAG:ID3v1 Comment=` — это **128-байтный ID3v1-хвост**, где CP1251 нормальна. Цитировать как доказательство CP1251 в ID3v2 — **ошибка вывода**. - **Валидация ffprobe ≠ доказательство читаемости для потребителя.** Проверять движком, который читает в проде. Выборка 1% (108 из 7861) ничего не доказывает. ### 🔴 НИКОГДА не использовать `DELETE /Items` — он удаляет ФАЙЛЫ Удаляет медиафайл с диска, не только запись БД. При rw-монтировании **снесёт музыку**. Спасает только `ro`-mount. `POST /Library/Media/Deleted` → **404**; `POST /Library/Media/Updated` → **415** без JSON body. **Для удаления только записей — править SQLite напрямую при остановленном контейнере.** ### Безопасный откат БД Полная процедура (5 шагов) — в SKILL.md. Ключевое: 1. `docker stop` → бэкап текущей БД; 2. распаковка архивной БД + **проверка MD5**; 3. удаление WAL/SHM **при ЗАПУЩЕННОМ контейнере** (`docker exec` не работает на остановленном); 4. `docker cp` БД внутрь + `chown abc:abc` + `docker start`; 5. **проверка `md5sum` ВНУТРИ контейнера** — не по коду возврата. ### Питфоллы (проверено) | # | Питфолл | |---|---| | 1 | `docker exec` НЕ работает на остановленном контейнере → WAL удалять при запущенном | | 2 | `docker cp` может вернуть `container is not running`, **НО копия проходит** → проверять `md5sum` | | 3 | Глоб `jellyfin.db*` не раскрывается в zsh контейнера → ложное «файла нет» | | 4 | `docker stop` возвращает `Exited (137)` (SIGKILL) — не признак поломки данных | | 5 | Хост и контейнер показывают разное время одного файла (PDT vs +07) — это НЕ расхождение | | 6 | **Не запускать скрипт-мутатор «для проверки»** — создаст библиотеку обратно | | 7 | `"a"+"b"` в bash/zsh → пустая строка → HTTP 401. Правильно `"${A}${B}"` | | 8 | Токен/`os.environ` в файле искажается санитайзером → внешняя обёртка + `export`, сверять `md5sum` | ### Выбор движка: Jellyfin vs Navidrome | | Jellyfin | Navidrome | |---|---|---| | Источник имени артиста | **папка перекрывает тег** (баг #18162) | **только тег** | | Структура папок | `Artist/Album/Track` обязательна | **игнорируется полностью** | | Интернет-метаданные | тянет MusicBrainz | **не тянет** | | Пишет в файлы | может | **никогда** | | Видео | ✅ | ❌ | **`ALBUMARTIST` в Navidrome НЕ обязателен** — мейнтейнер `deluan` (#5924): > «when an album artist tag is missing, Navidrome substitutes the track artist in its place» Обход без правки тегов: ```toml PID.Album = "musicbrainz_albumid|albumartist,album,albumversion,releasedate" ``` - **сырое `albumartist`**, НЕ `albumartistid` (тот никогда не пуст); - `folder` в спеку как fallback класть **нельзя**; - смена `PID.Album` → полный рескан + новые ID → **бэкапить `navidrome.db`**. **Lidarr пишет теги сам** при импорте (MusicBrainz + `Write Metadata Tags`) — новые закачки приходят с `Album Artist`. Связка: `navidrome-lidarr-bridge`. ### Ловушка: семантически валидные, но бессмысленные теги ``` artist=Классика в современной обработке ← не артист artist=Классика для детей П.И. Чайковский ← не артист artist=Maria Makarova (organ) ← исполнитель вместо композитора ``` Проходят любую проверку на мойибаке/кодировку. **Если имя артиста в UI удивляет — сначала grep по тегу `artist` реальных файлов**, прежде чем строить версии про внешние базы. ## Связанные заметки - [[personal/tech/jellyfin-tags-cp1251-unreadable]] — полный разбор дефекта - [[family/plans/music-jellyfin-lidarr-taiga]] — план проекта, открытые задачи - [[family/how-to/navidrome-truenas-android]] — план деплоя Navidrome - [[personal/tech/jellyfin-db-schema-mismatch]] — схема БД Jellyfin