757 lines
55 KiB
Markdown
Executable File
757 lines
55 KiB
Markdown
Executable File
# Музыкальный плеер с TrueNAS на Android
|
||
|
||
**Статус:** ✅ **РАЗВЁРНУТ 2026-09-27** — Navidrome на TrueNAS, работает.
|
||
✅ **2026-09-27 (сессия 22):** теги 306 исправлены, **БД Navidrome снесена и построена заново**, скобочных сирот **0**, треков **8167**.
|
||
✅ **2026-09-27 (сессия 29-кватер):** `.ndignore` дополнен 2 бэкап-папками → живая музыка **7861** (= плану), бэкапы помечены `missing=1`.
|
||
🔴 **Ни `restart`, ни пересоздание контейнера НЕ дают полного скана** (`fullScan=false`) — теги не перечитываются. Рабочий способ: **снести `navidrome.db`** (тогда старт → `fullScan=true`).
|
||
🔑 **Любой запрос к `media_file` — ТОЛЬКО с `missing=0 AND path NOT LIKE 'backup%'`** (иначе всплывают бэкапы → ложные «дефекты»).
|
||
**Дата:** 2026-05-10
|
||
**Обновлено:** 2026-09-27 (v8 — `.ndignore` дополнен, живая музыка 7861; найден корень дефекта `NON_ARTIST` в `build_fix_plan2.py`)
|
||
|
||
---
|
||
|
||
# 🚀 v4: NAVIDROME РАЗВЁРНУТ (2026-09-27)
|
||
|
||
> ✅ **Живой сервис: `https://music.mallexxx.duckdns.org`** — HTTP/2, сертификат Let's Encrypt.
|
||
> Navidrome **v0.64.2**, поднят за **12 минут** от нуля.
|
||
> **Скан завершён: 8043 трека** (цель 7861 + бэкап-папки исключены — расхождение объяснено ниже).
|
||
|
||
## Команда Alex и решение
|
||
|
||
Alex: «**Ставь navidrome**» → затем **«Монтируй в rw на всякий»** → **«Поехали»**.
|
||
Изменение против исходного плана: **музыка монтируется `rw`, не `ro`** (явное решение Alex).
|
||
|
||
## Факты живого контейнера
|
||
|
||
| Параметр | Значение |
|
||
|---|---|
|
||
| Контейнер | `navidrome` (образ `deluan/navidrome:latest`, v0.64.2) |
|
||
| Compose | `/mnt/RED_2TB/docker/navidrome/docker-compose.yml` |
|
||
| Порт | `4533:4533` |
|
||
| Сеть | `caddy_default` |
|
||
| Данные | `/mnt/RED_2TB/docker/navidrome/data/` (владелец 950:950) |
|
||
| Музыка | `/mnt/RED_2TB/storage/Music` → `/music` (**`rw`**) |
|
||
| Домен | `music.mallexxx.duckdns.org` → `reverse_proxy navidrome:4533` |
|
||
| PUID/PGID | 950/950, `TZ=Asia/Novosibirsk` |
|
||
| ENV | `ND_MUSICFOLDER=/music`, `ND_DATAFOLDER=/data`, `ND_SCANSCHEDULE=1h`, `ND_SESSIONTIMEOUT=24h` |
|
||
| Логин/пароль | **НЕТ по умолчанию** — Navidrome при первом входе требует создать админа (форма «Create admin») |
|
||
|
||
## ✅ `.ndignore` — механизм исключения бэкап-папок ПОДТВЕРЖДЁН
|
||
|
||
**Файл `/mnt/RED_2TB/storage/Music/.ndignore`** (43 байта):
|
||
```
|
||
backup
|
||
backup_conv40
|
||
backup_fix
|
||
backup_raw
|
||
```
|
||
|
||
**Формат подтверждён НЕ докой, а строками в бинаре** (`docker exec navidrome strings /app/navidrome`):
|
||
```
|
||
Scanner: Error opening .ndignore file
|
||
Scanner: Error reading .ndignore file
|
||
Scanner: Ignoring entry matching .ndignore
|
||
Scanner: .ndignore file is empty, ignoring everything
|
||
Ignoring change in folder matching .ndignore pattern
|
||
Scanner: Error marking DB analysis pending
|
||
```
|
||
> 🔑 **Приём проверки**: `strings <бинарник> | grep -i ndignore` — быстрее и надёжнее,
|
||
> чем искать доку. `strings`-вывод подтверждает, что фича вкомпилирована в эту сборку.
|
||
> ⚠️ **Пустой `.ndignore` = «игнорировать ВСЁ»** (не «не игнорировать ничего») — сообщение
|
||
> `Scanner: .ndignore file is empty, ignoring everything`. **Не создавать пустым.**
|
||
|
||
## 🔴 ПРИЁМКА В UI ВСКРЫЛА ДЕФЕКТ ПЛАНА: `album` со скобочным мусором
|
||
|
||
Navidrome поднялся и просканировал **8043 трека**. Приёмка в UI дала сразу два результата:
|
||
|
||
**✅ Главный — движок работает правильно.** Артисты и альбомы строятся **по ТЕГАМ**, папки игнорируются
|
||
(`!Rock&Folk/ДДТ`, `!Рэп/НТЛ`, `z Другое/Красная Плесень` — разложены по тегам). **Багу Jellyfin #18162
|
||
здесь места нет** — главная причина, по которой выбирали Navidrome, **подтверждена на живых данных**.
|
||
|
||
**🔴 Побочный — вскрыт дефект входного плана.** Alex, открыв UI:
|
||
|
||
> «Откуда альбомы в скобках взялись? Типа `Дискотека Авария` — для корня, а `(Авария)` — откуда??»
|
||
|
||
**Ответ (факт, `ffprobe` внутри контейнера):** `TAG:album=(Авария)` — **это реальный тег в файле**,
|
||
не подстановка Navidrome. Мусор вписал mp3-граббер (`comment=This File from http://rmp.ru`).
|
||
Механика: план `APPLY-PLAN.csv` заполнял `new_album` **копией `cur_album`** при любом непустом
|
||
значении → мусор из диска прошёл в целевое значение и был записан обратно.
|
||
|
||
| | |
|
||
|---|---:|
|
||
| `new_album == cur_album` (оставлено как есть) | **6202 из 7861 (79%)** |
|
||
| Реальный скобочный мусор | **306 строк / 15 папок** |
|
||
|
||
**Итог:** 306 строк исправлены (скобочный мусор → 0), затем **ещё 58 строк по 3 дефектам**
|
||
(склейка артист+год, чужой `albumartist`, `album`=название песни), затем **+12** (`Шнуров`→`Ленинград`,
|
||
`NONSONS - zvezda`→`zvezda`). **Всего 371 строка.** Дифф **`DIFF-new-vs-38-v2.csv` перегенерирован**
|
||
(164 album-строки, полнота 18/18 + 6/6 проверена).
|
||
Полный разбор — [[personal/plans/music-artist-albumartist-repair]] §СЕССИЯ 18.
|
||
|
||
> 🔴 **ПРИЁМКА NAVDROME ДАЛА ДВОЙНОЙ РЕЗУЛЬТАТ — оба важны:**
|
||
> 1. **Движок подтверждён на живых данных** — теги читаются, папки игнорируются, бага Jellyfin нет.
|
||
> 2. **Вскрыт дефект ВХОДНОГО ПЛАНА**, который не видел ни один валидатор и ни один дифф:
|
||
> мусор, унаследованный из источника, присутствует в **обеих** версиях плана → дифф показывает
|
||
> «совпало». **Открывать данные в UI-потребителе = самый эффективный способ найти мусор в тегах,
|
||
> который не ловится сверкой файлов.**
|
||
|
||
## 🔴 УРОК: разброс значений в папке ≠ дефект (метод-урок сессии 18)
|
||
|
||
При проверке «один ли `album`/`albumartist` в папке» получено **96 папок с разными `album`
|
||
и 7 с разным `albumartist`**. **Реальных дефектов — 2 папки**, остальное легитимно:
|
||
|
||
| Что выглядит подозрительно | Почему это НОРМА |
|
||
|---|---|
|
||
| `!Retro`: `De Luxe collection`, `Опиум`, `Просвистела`, `Retro` | сборник, в нём реальные альбомы |
|
||
| `Пилот/2006 Уходящее лето` → `albumartist='Илья Чёрт'` | лидер группы, верно |
|
||
| `ГрОб` → `'Гражданская Оборона'` | верно |
|
||
| `Benassi Bros` → `'Benny Benassi'` | верно |
|
||
| `album == title` (`Малыш и Карлсон`, `Ария/Вавилон`) | одноимённые альбомы |
|
||
|
||
**ПРАВИЛО: прежде чем объявлять класс дефектом — проверять, не является ли совпадение/разброс
|
||
следствием правила Alex или легитимной структуры данных.** Детекторы «по форме» дали
|
||
**7899 ложных срабатываний** против **26 реальных** (ср. сессия 17: `album == title` → 149, реальных 0).
|
||
|
||
## 🔴 КАК ИСКАТЬ ПРАВИЛА: база Zulip, НЕ переспрашивать Alex
|
||
|
||
Правила `album` (`!Radio` формат, `misc` = подпапка, корень артиста = один альбом) **обсуждались
|
||
только в Zulip** — ни в скилле, ни в доке, ни в комментариях скриптов. Alex: «**Читай базу зулип
|
||
как мы его делали**». Ключевые сообщения: **127513–127593, 128410** (фильтр `up.full_name='Alex'`).
|
||
|
||
```bash
|
||
docker exec zulip-database-1 psql -U zulip zulip -c "SELECT m.id, m.date_sent, LEFT(m.content,3000) \
|
||
FROM zerver_message m JOIN zerver_recipient r ON m.recipient_id=r.id \
|
||
JOIN zerver_stream s ON r.type_id=s.id JOIN zerver_userprofile up ON m.sender_id=up.id \
|
||
WHERE r.type=2 AND up.full_name='Alex' AND m.date_sent >= '<T1>' AND m.date_sent <= '<T2>' \
|
||
ORDER BY m.date_sent;"
|
||
```
|
||
|
||
> 🔑 **Правило: правила предметной области ищутся в Zulip DB ПЕРВЫМ шагом.**
|
||
> Переспрашивать Alex то, что уже обсуждалось — прямой путь к «Ебаный ты мудак блядь».
|
||
|
||
> 🔑 **Приёмка в UI — единственный способ поймать дефект такого класса.** Ни валидатор плана
|
||
> («пустых 0»), ни дифф (`new_album` плана vs базы 38-го прогона — мусор стоял в обоих → «совпало»)
|
||
> его не видят. **Смотреть глазами в UI после деплоя — обязательно.**
|
||
|
||
## ПЛАН ДЕПЛОЯ (выполнен по шагам)
|
||
|
||
### Шаг 1 — каталог `/mnt/RED_2TB/docker/navidrome/`
|
||
|
||
🔴 **ПИТФОЛЛ: `/mnt/RED_2TB/docker` — root-owned (`drwxr-xr-x root root`), `truenas_admin` не пишет.**
|
||
`mkdir` через ssh → `Permission denied`. Обход — **`midclt call filesystem.mkdir`** (`truenas_admin` = FULL_ADMIN).
|
||
|
||
🔴 **ПИТФОЛЛ: `filesystem.mkdir` НЕ создаёт промежуточные каталоги.** Попытка сразу создать
|
||
`.../navidrome/data` → `FileNotFoundError: [Errno 2] No such file or directory`.
|
||
**Порядок: сначала `<svc>`, потом `<svc>/data`.**
|
||
|
||
```bash
|
||
# 1. создать каталог через midclt (от root)
|
||
ssh truenas_admin@mallexxx.duckdns.org \
|
||
"midclt call filesystem.mkdir '{\"path\":\"/mnt/RED_2TB/docker/navidrome\",\"options\":{\"mode\":\"755\"}}'"
|
||
# 2. отдать владение 950:950 полным dacl-массивом (шаблон «эталон рабочего OWNER-ACL»)
|
||
ssh truenas_admin@mallexxx.duckdns.org \
|
||
"midclt call filesystem.setacl '{\"path\":\"/mnt/RED_2TB/docker/navidrome\",\"uid\":950,\"gid\":950,\"acltype\":\"NFS4\",\"dacl\":[...],\"options\":{\"recursive\":true,\"traverse\":true}}'"
|
||
# 3. проверить, что пишет
|
||
ssh truenas_admin@mallexxx.duckdns.org \
|
||
"mkdir -p /mnt/RED_2TB/docker/navidrome/data && touch /mnt/RED_2TB/docker/navidrome/.wtest && echo WRITE_OK && rm -f /mnt/RED_2TB/docker/navidrome/.wtest"
|
||
```
|
||
- `midclt` возвращает **job id** (асинхронно) — проверять `midclt call core.get_jobs`.
|
||
- Без `setacl` каталог останется `root root` и дальше писать не получится.
|
||
|
||
### Шаг 2 — compose
|
||
|
||
```yaml
|
||
services:
|
||
navidrome:
|
||
image: deluan/navidrome:latest
|
||
container_name: navidrome
|
||
restart: unless-stopped
|
||
environment:
|
||
- ND_MUSICFOLDER=/music
|
||
- ND_DATAFOLDER=/data
|
||
- ND_SCANSCHEDULE=1h
|
||
- ND_LOGLEVEL=info
|
||
- ND_SESSIONTIMEOUT=24h
|
||
- ND_ENABLETRANSCODINGCONFIG=false
|
||
- ND_BASEURL=
|
||
- PUID=950
|
||
- PGID=950
|
||
- TZ=Asia/Novosibirsk
|
||
volumes:
|
||
- /mnt/RED_2TB/storage/Music:/music:rw # 🔴 rw по решению Alex (в плане было ro)
|
||
- /mnt/RED_2TB/docker/navidrome/data:/data
|
||
ports:
|
||
- "4533:4533"
|
||
networks:
|
||
- caddy_default
|
||
|
||
networks:
|
||
caddy_default:
|
||
external: true
|
||
```
|
||
|
||
⚠️ **Питфолл лимита ресурсов (наблюдение, не ошибка):** в `navidrome.db` файлы принадлежат
|
||
`root:950` — контейнер стартует как **root**, `PUID/PGID` в образе `deluan/navidrome`
|
||
на NFSv4 ACL влияют не так, как в linuxserver-образах. **Чтение/запись работают фактом**
|
||
(скан 8043 файла прошёл), но при будущих проблемах с правами смотреть сюда.
|
||
|
||
### Шаг 3 — запуск (не `up`)
|
||
|
||
```bash
|
||
ssh truenas_admin@mallexxx.duckdns.org \
|
||
"cd /mnt/RED_2TB/docker/navidrome && docker compose pull && docker compose create && docker compose start"
|
||
```
|
||
✅ `docker compose up` в этой среде блокируется апрувом — **`pull/create/start` работает**.
|
||
В логе немедленно: `----> Navidrome server is ready!` (`startupTime=2.35s`).
|
||
|
||
### Шаг 4 — Caddy
|
||
|
||
**Caddyfile 88 строк** (root-owned). Диф ровно 4 строки в **конец** файла:
|
||
```caddy
|
||
music.mallexxx.duckdns.org {
|
||
reverse_proxy navidrome:4533
|
||
}
|
||
```
|
||
|
||
```bash
|
||
# 1. вытащить текущий
|
||
ssh truenas_admin@mallexxx.duckdns.org "docker exec caddy cat /etc/caddy/Caddyfile" > /tmp/caddyfile-current.txt
|
||
# 2. добавить блок ЛОКАЛЬНЫМ скриптом-файлом (write_file + python3 /tmp/x.py) — НЕ sed
|
||
# 3. сверить диф: diff /tmp/caddyfile-current.txt /tmp/caddyfile-new.txt → только добавленные 4 строки
|
||
# 4. залить ЦЕЛИКОМ через alpine
|
||
cat /tmp/caddyfile-new.txt | ssh truenas_admin@mallexxx.duckdns.org \
|
||
"docker run --rm -i -v /:/host:rw alpine:latest sh -c 'cat > /host/mnt/RED_2TB/docker/caddy/Caddyfile && echo WRITE_OK'"
|
||
# 5. validate → reload
|
||
ssh truenas_admin@mallexxx.duckdns.org "docker exec caddy wc -l /etc/caddy/Caddyfile && docker exec caddy caddy validate --config /etc/caddy/Caddyfile 2>&1 | tail -3 && docker exec caddy caddy reload --config /etc/caddy/Caddyfile"
|
||
```
|
||
|
||
**Результат:** `Valid configuration`, ID подтверждён на `music.mallexxx.duckdns.org`.
|
||
|
||
⚠️ **TLS-сертификат выпускается НЕ сразу (≈10 сек).** Сразу после reload `curl -I https://music...`
|
||
даёт **`exit=35` (TLS handshake failure)** — это НОРМАЛЬНО, не поломка. Проверять прогресс:
|
||
```bash
|
||
ssh truenas_admin@mallexxx.duckdns.org "docker logs caddy --tail 25 2>&1 | grep -iE 'acme|certificate|error'"
|
||
```
|
||
Ждать строк `certificate obtained successfully` (challenge `tls-alpn-01`). После — `HTTP/2 405` на HEAD-запрос.
|
||
|
||
### Шаг 5 — `.ndignore`
|
||
|
||
```bash
|
||
scp /tmp/ndignore.txt truenas_admin@mallexxx.duckdns.org:/mnt/RED_2TB/storage/Music/.ndignore
|
||
ssh truenas_admin@mallexxx.duckdns.org "docker restart navidrome"
|
||
```
|
||
|
||
## 🔴 ПОДТВЕРЖДЕНИЕ: Navidrome читает ТЕГИ, а не папки
|
||
|
||
**Выборка из лога скана — артист/альбом берутся из тегов:**
|
||
```
|
||
folder="!Rock&Folk/ДДТ/2004 - ДДТ - Город без окон (Выход)" tracksImported=14
|
||
folder="!Рэп/НТЛ/2003 - Выход" tracksImported=17
|
||
folder="z Другое, Enigma, Ленинград/Красная Плесень/misc" tracksImported=28
|
||
folder="!Pop, Club, Любэ/Дискотека Авария/Москва пьёт пиво" tracksImported=19
|
||
```
|
||
Ни одного случая «артист = имя папки» (`!Rock&Folk`, `z Другое, Enigma, Ленинград`) — **баг Jellyfin #18162 здесь отсутствует**. Это и есть цель перехода.
|
||
|
||
## 🔴 НАХОДКА: `album` в скобках — мусор В ТЕГАХ, не подстановка Navidrome
|
||
|
||
**Вопрос Alex:** «Откуда альбомы в скобках взялись? Типа `Дискотека Авария` — для корня,
|
||
а `(Авария)` — откуда??»
|
||
|
||
**Проверено фактом (`ffprobe` по живому файлу):**
|
||
```
|
||
/music/!Pop, Club, Любэ/Дискотека Авария/Смелей вперёд.mp3
|
||
TAG:title=Смелей вперед
|
||
TAG:artist=Дискотека Авария
|
||
TAG:album=(Авария) ← 🔴 реальный тег в файле
|
||
TAG:genre=Disco
|
||
TAG:comment=This File from http://rmp.ru ← маркер mp3-граббера
|
||
TAG:album_artist=Дискотека Авария
|
||
```
|
||
**`(Авария)` — не фантазия Navidrome. Он честно показал то, что записано в файле.**
|
||
Причина — **mp3-граббер** (`rmp.ru`): часть файлов в папке артиста получила `album='(Авария)'`
|
||
вместо реального названия альбома. Папка `Дискотека Авария` содержит треки **минимум трёх
|
||
разных альбомов** (`Четверо парней`, `Москва пьёт пиво`, `Старые хиты`) **сваленных в корень
|
||
без подпапок** — различить их можно ТОЛЬКО по тегам.
|
||
|
||
### Масштаб (БД Navidrome, `media_file`)
|
||
|
||
| Что | Треков |
|
||
|---|---:|
|
||
| `album` начинается со скобки | **306** |
|
||
| `album` содержит скобку | 695 |
|
||
| `album_artist = 'Various Artists'` | 1225 |
|
||
| `album` пустой | **0** |
|
||
| `media_file` всего | **8043** |
|
||
|
||
### 🔴 КОРЕНЬ `(Авария)`: не граббер, а ДЕФЕКТ-КЛАСС ПЛАНА (уточнено, сессия 18)
|
||
|
||
**Первая версия этой заметки сказала «виноват mp3-граббер». Уточнение: граббер — источник
|
||
мусора в тегах, но ЧИНИТЬ его должен был план — и не починил.**
|
||
|
||
Полный разбор — [[personal/plans/music-artist-albumartist-repair]] §СЕССИЯ 18. Кратко:
|
||
|
||
| | Факт |
|
||
|---|---:|
|
||
| `new_album == cur_album` (оставлено как есть) | **6202 из 7861 (79%)** |
|
||
| из них `album` в скобках | **306** |
|
||
| Источник скобочного мусора в тегах | mp3-граббер `rmp.ru` |
|
||
|
||
**Механика дефекта:** план копировал `cur_album` в `new_album` при **непустом** значении —
|
||
без проверки смысла. Валидатор считал ПУСТЫЕ, а не мусорные. Дифф планов мусор не поймал,
|
||
потому что стоял в обеих версиях («совпало»).
|
||
|
||
> 🔴 **Правило для будущих ревизий: дифф двух планов ≠ валидатор значений.**
|
||
> Нужен отдельный валидатор **смысла** (паттерны: `^\(.+\)$`, заглушки, спам-URL) — в пайплайне его нет.
|
||
|
||
**Полный список оставленных значений:** `~/Downloads/music-tags/REPORT-new-album-equals-cur.csv`
|
||
(6202 строки, группировка папка → значение → count).
|
||
|
||
### ⚠️ КОНФЛИКТ ПРАВИЛА: `album = имя папки` ломает эту категорию
|
||
|
||
Правило Alex (v8 плана ремонта): «`album` для сборников и unsorted → **ИМЯ ПАПКИ**».
|
||
**Для таких папок правило неверно:** `Дискотека Авария` — папка ОДНОГО артиста с несколькими
|
||
альбомами в корне. Применить «имя папки» → все три альбома сольются в один `Дискотека Авария`,
|
||
а `albumartist` станет `Имя артиста` (не сборник). **Нужно решение Alex.**
|
||
|
||
**Возможные подходы (не выбраны):**
|
||
| # | Вариант | Оценка |
|
||
|---|---|---|
|
||
| A | Восстановить реальные названия альбомов по внешним источникам (MusicBrainz) | точно, но много ручной работы |
|
||
| B | Разложить файлы по подпапкам-альбомам и `album` = имя подпапки | меняет структуру на диске |
|
||
| C | Оставить `album` = имя папки артиста | три альбома сливаются в один ❌ |
|
||
| D | Обнулить `album` у таких файлов | Navidrome покажет по имени файла — мусор ❌ |
|
||
|
||
**Артефакт для просмотра (запрошен у Alex, ещё не выгружен):** CSV `путь + текущий album + title`
|
||
по **306** файлам со «скобочным» album, топ-20 для глазной вычитки.
|
||
|
||
## ✅ РЕШЕНО (2026-09-27, сессия 22): СНОС БД → ПОЛНЫЙ СКАН → 0 СИРОТ
|
||
|
||
> **Alex: «Navidrome rescan теперь надо»** → **«Просто контейнер пересоздай»** → **«Сука базу блядь снеси руками!!!»**
|
||
|
||
**Проблема:** теги на диске исправлены, но БД Navidrome содержала И СТАРЫЕ, И НОВЫЕ альбомы — **14 сирот**.
|
||
|
||
```bash
|
||
DB=/mnt/RED_2TB/docker/navidrome/data/navidrome.db
|
||
sqlite3 "file:$DB?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE '(%';" # → 14 (до и после попыток rescan)
|
||
```
|
||
|
||
### ❌ ЧТО ПРОБОВАЛОСЬ И НЕ ПОМОГЛО (факт)
|
||
|
||
| # | Способ | Результат |
|
||
|---|---|---|
|
||
| 1 | `docker restart navidrome` | ❌ лог: `fullScan=false` → теги не перечитаны, всё те же **14** |
|
||
| 2 | `docker compose down` + `up -d` (**пересоздание контейнера**) | ❌ лог: `fullScan=false`, `duration=3.12s` → снова **14** |
|
||
| 3 | Проверка БД после обоих | `LIKE '(%'` = **14** (число не двинулось) |
|
||
|
||
⚠️ **Пересоздание контейнера БД НЕ чистит** — `navidrome.db` лежит на диске и монтируется томом
|
||
(`/mnt/RED_2TB/docker/navidrome/data:/data`), `down` его не удаляет. Compose-файл не менялся,
|
||
бэкап `docker-compose.yml.bak-20260927-052857`. Данные/музыка не пострадали.
|
||
|
||
⚠️ **`updated_at` у 14 сирот = 10:09–10:23 UTC, `missing=0`** — они считаются живыми записями
|
||
(скан их не помечает missing). Причина: album = пара `(name, album_artist)`; теги сменились →
|
||
Navidrome **создал новые** альбомы, **старые не удалил** (быстрый скан файлы не перечитывает).
|
||
|
||
### ✅ СНОС БД — РАБОЧИЙ СПОСОБ (выполнен)
|
||
|
||
```bash
|
||
D=/mnt/RED_2TB/docker/navidrome/data
|
||
mkdir -p "$D/navidrome.db.bak-$(date +%Y%m%d-%H%M%S)" # копия ПЕРЕД сносом (обязательно)
|
||
cp "$D"/navidrome.db* "$D/navidrome.db.bak-<ts>"/
|
||
cd /mnt/RED_2TB/docker/navidrome && docker compose stop
|
||
rm -v "$D/navidrome.db" "$D/navidrome.db-shm" "$D/navidrome.db-wal"
|
||
docker compose up -d # старт → Full scan required after migration
|
||
```
|
||
|
||
**Лог на старте:** `Full scan required after migration` → `Scanner: Starting scan fullScan=true` ✅
|
||
|
||
**Результат (факт по `sqlite3`):**
|
||
|
||
| Метрика | До сноса | После |
|
||
|---|---:|---:|
|
||
| `album` целиком в скобках | **14** | **0** ✅ |
|
||
| треков (`media_file`) | 7861 | **8167** |
|
||
| альбомов (`album`) | 967 | **837** |
|
||
|
||
Все целевые значения присутствуют по одному разу: `Философские` · `Морские` · `Блатные` ·
|
||
`Спортивные` · `Из Сказок` · `Разное` · `Москва пьёт пиво` · `Старые хиты` · `Дискотека Авария`(2) ·
|
||
`Красивые, богатые` · `Retro` · `Вилли Токарев` · `Владимир Высоцкий` · `Несчастный Случай` · `zvezda`.
|
||
|
||
**Проверка после:** `sqlite3 "file:$DB?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE '(%';"` → **0**.
|
||
|
||
⚠️ **Что теряется при сносе БД:** плейлисты, избранное, счётчики проигрываний, оценки, пользователи
|
||
Navidrome (пароль задаётся заново в UI). **Что НЕ теряется:** музыка и теги — они на диске.
|
||
**Бэкап `navidrome.db.bak-<ts>/` обязателен** — единственный путь к откату метаданных Navidrome.
|
||
|
||
> 🔴 **ПИТФОЛЛ (главный, метод): НИ `docker restart`, НИ `docker compose down/up` не дают полного
|
||
> скана Navidrome** — в логе всегда `fullScan=false` (`duration≈3s`). Массовая смена тегов без смены
|
||
> `mtime` не подхватывается: Navidrome **добавляет** новые альбомы и **не удаляет** старые → дубли
|
||
> «скобочный + чистый». `ND_SCANSCHEDULE=1h` тоже не помогает. **Единственный проверенный способ —
|
||
> снести `navidrome.db`** (старт форсирует `fullScan=true`). Альтернатива без сноса — `touch` файлов
|
||
> (меняет `mtime`, не содержимое) + полный скан; в этой сессии не применялась.
|
||
|
||
> 🔴 **ВАЖНО на будущее: `sqlite3` ЕСТЬ на NAS напрямую (`/usr/bin/sqlite3`, 3.40.1)** —
|
||
> не нужно `docker exec`. Быстрее и не требует запуска контейнера. В контейнере `tagwriter:v2`
|
||
> `sqlite3` **нет** (только `mutagen`).
|
||
|
||
Боевой прогон и приёмка — [[personal/plans/tagwriter-container]] §СЕССИЯ 21.
|
||
|
||
## ✅ ВЕРИФИКАЦИЯ ТЕГОВ ЧЕРЕЗ БД NAVidrome (метод сессии 24, 2026-09-27)
|
||
|
||
> 🔴 **Для UI истина = БД Navidrome, а не read-back по диску.** Alex: «**Ты блядь база хотел
|
||
> скачать вроде**» — скрипт `run_tagwriter.sh readback` (читает диск) прерван как лишний.
|
||
|
||
**Забрать БД локально (обязательно с WAL и SHM — иначе читается устаревший снимок):**
|
||
|
||
```bash
|
||
mkdir -p ~/Downloads/navidrome-db && cd ~/Downloads/navidrome-db
|
||
for f in navidrome.db navidrome.db-wal navidrome.db-shm; do
|
||
scp "truenas_admin@mallexxx.duckdns.org:/mnt/RED_2TB/docker/navidrome/data/$f" .
|
||
done
|
||
cp navidrome.db navidrome-merged.db # копия ДО открытия — открытие WAL модифицирует файл
|
||
sqlite3 navidrome-merged.db "SELECT count(*) FROM album; SELECT count(*) FROM media_file;"
|
||
# → 837 / 8167
|
||
```
|
||
|
||
**Ключи сверки:** `media_file.path` — **относительный** (`!Pop, Club, Любэ/...`), без префикса
|
||
`/storage/Music/`, который есть в плане. Поля: `media_file.album`, `media_file.album_artist`.
|
||
`sqlite3` на Mac есть (`/usr/bin/sqlite3`), `docker exec` не нужен.
|
||
|
||
**Результат сессии 24:** 7861 трек сверено, расхождений `album` **50** + `albumartist` **9**
|
||
(все — дефект самого плана, а не применения). Разбор → [[personal/plans/music-artist-albumartist-repair]] §СЕССИЯ 24.
|
||
|
||
### ✅ ИСПРАВЛЕНО 2026-09-27 (сессия 29-кватер): `.ndignore` дополнен, бэкапы ушли из БД
|
||
|
||
**Причина, почему бэкапы были в БД:** `.ndignore` создан **03:49**, а две бэкап-папки появились
|
||
**позже** — `backup_before_306` в **05:17**, `backup_album_root_20260927-album-root-229` в **06:19**.
|
||
Файл не обновлялся после создания новых бэкапов. Остальные 4 папки были в списке с самого начала.
|
||
|
||
**Было (43 байта, 4 строки):**
|
||
```
|
||
backup
|
||
backup_conv40
|
||
backup_fix
|
||
backup_raw
|
||
```
|
||
|
||
**Стало (103 байта, 6 строк)** — дописаны 2 отсутствовавшие:
|
||
```
|
||
backup
|
||
backup_album_root_20260927-album-root-229
|
||
backup_before_306
|
||
backup_conv40
|
||
backup_fix
|
||
backup_raw
|
||
```
|
||
|
||
**Порядок правки (боевой конфиг на NAS):**
|
||
```bash
|
||
# 1. бэкап файла
|
||
ssh truenas_admin@mallexxx.duckdns.org \
|
||
"cp /mnt/RED_2TB/storage/Music/.ndignore /mnt/RED_2TB/storage/Music/.ndignore.bak-\$(date +%Y%m%d-%H%M%S)"
|
||
# 2. перечислить ВСЕ бэкап-папки и сверить со списком (2 отсутствовали)
|
||
ssh truenas_admin@mallexxx.duckdns.org "cd /mnt/RED_2TB/storage/Music && ls -d */ | grep -i backup"
|
||
# 3. новый файл собрать ЛОКАЛЬНО (write_file) → scp → cp НА МЕСТО (НЕ sed!)
|
||
scp /tmp/ndignore-new truenas_admin@mallexxx.duckdns.org:/tmp/ndignore-new
|
||
ssh truenas_admin@mallexxx.duckdns.org "cp /tmp/ndignore-new /mnt/RED_2TB/storage/Music/.ndignore"
|
||
# 4. рестарт + скан
|
||
ssh truenas_admin@mallexxx.duckdns.org "docker restart navidrome && sleep 20 && docker logs --tail 25 navidrome | grep -i 'scan completed'"
|
||
```
|
||
|
||
**Результат (факт из `sqlite3` на NAS):**
|
||
|
||
| Метрика | До | После |
|
||
|---|---:|---:|
|
||
| `media_file` всего | 8167 | **8396** |
|
||
| из них `missing=1` (игнор) | — | **535** |
|
||
| **живая музыка** (`missing=0 AND path NOT LIKE 'backup%'`) | 7861 | **7861** ✅ |
|
||
|
||
Лог скана: `Scanner: Finished scanning all libraries duration=5.19s` → `Scan completed`.
|
||
Игнорируемые файлы Navidrome **помечает `missing=1`, НЕ удаляет** — записи остаются в БД.
|
||
|
||
> 🔴 **ПРАВИЛО (закреплено в скилле `truenas-administration` Area 3, RULE 0):**
|
||
> **любой новый `backup*` под `/mnt/RED_2TB/storage/Music/` обязан быть дописан в `.ndignore`
|
||
> В ТОМ ЖЕ ПРОГОНЕ, что его создал.** Скрипты бэкапа (`backup_306.py` и подобные) должны
|
||
> сами обновлять `.ndignore` после создания папки.
|
||
|
||
### 🔴 ПИТФОЛЛ (главный, метод): ЗАПРОС К `media_file` БЕЗ ФИЛЬТРА БЭКАПА ДАЁТ ЛОЖНЫЕ ВЫВОДЫ
|
||
|
||
**Эта ошибка дала неверный отчёт «скобки не исправлены».** Alex спросил, почему `(zvezda)`,
|
||
`(Несчастный Случай)`, `(Авария)` до сих пор в базе, если скобки правили. Запрос был сделан
|
||
**без фильтра** — и вытащил 306 файлов бэкапа (`backup_before_306/20260927-051727/...`),
|
||
которые и содержали старые скобочные значения. Я выдал бэкап за живое дерево.
|
||
|
||
**В живом дереве этих альбомов со скобками НЕТ — правка отработала.** Проверка после фикса:
|
||
`SELECT DISTINCT album FROM media_file WHERE missing=0 AND path NOT LIKE 'backup%' AND (album LIKE '(%' OR album LIKE '%)')`
|
||
→ **0 альбомов-обёрток**. Оставшиеся 17 значений вида «название (уточнение)»
|
||
(`Hey Baby (Single)`, `Wider (Domestic RMX)`, `Outcasts (DEMOS)`) — **осмысленные части
|
||
названий, правки не требуют** (скобка в КОНЦЕ = уточнение, не обёртка вокруг всего имени).
|
||
|
||
> 🔑 **ОБЯЗАТЕЛЬНЫЙ ФИЛЬТР для любого запроса к `media_file`:**
|
||
> `WHERE missing=0 AND path NOT LIKE 'backup%'`
|
||
> Без него всплывают файлы бэкапов → ложные «дефекты», которых нет.
|
||
|
||
### 🔴 ДВА КЛАССА СКОБОК — НЕ СМЕШИВАТЬ
|
||
|
||
| Класс | Пример | Правка |
|
||
|---|---|---|
|
||
| Скобка **вокруг всего** названия | `(Авария)`, `(zvezda)`, `(Несчастный Случай)`, `(Высоцкий)`, `(Морские)` | ✅ правится → `Авария`, `zvezda`, … |
|
||
| Скобка **в конце** как уточнение | `Бумер (2003)`, `Муза (EP)`, `Война (Акустика)`, `Outcasts (DEMOS)` | ❌ НЕ правится — часть названия |
|
||
|
||
## Остаточные дефекты после деплоя
|
||
|
||
| # | Что | Статус |
|
||
|---|---|---|
|
||
| 1 | `album` в скобках, 306 файлов | ✅ **ИСПРАВЛЕНО** (скобок 0) + 58 строк по 3 дефектам |
|
||
| 2 | Семантика `artist` в `!Классика` | 🔴 не начато (см. ниже) |
|
||
| 3 | `TPE2`/`albumartist` — ремонт по 7861 файлу | 🟡 разброс 7→2 папки; ⏳ 2 папки ждут решения |
|
||
| 4 | Symfonium на Android | ⏳ не установлен |
|
||
| 5 | Lidarr | ⏳ не начат (отложен) |
|
||
| 6 | ⏭ Перегенерация `DIFF-new-vs-38-v2.csv` | ✅ **заменено** — актуальный диф `DIFF-new-vs-38-v6.csv` (1674 строки); v2/v3/v4/v5 сломаны/черновики |
|
||
| 7 | Валидатор **смысла** `new_album` | 🔴 в пайплайне отсутствует — **корень найден, см. ниже** |
|
||
| 8 | ✅ **`.ndignore` — 2 бэкап-папки не были в списке** | ✅ **ИСПРАВЛЕНО** — `backup_before_306`, `backup_album_root_…-229` дописаны; живая музыка **7861** |
|
||
|
||
## 🔴 КОРЕНЬ ДЕФЕКТА «мусор в `album`» НАЙДЕН (сессия 29-кватер): хардкод `NON_ARTIST` в генераторе плана
|
||
|
||
**Генератор плана найден через **Zulip DB** (не по локальному поиску):**
|
||
`~/Developer/personal-os/tools/music-tags/build_fix_plan2.py` (29690 байт).
|
||
|
||
**Цепочка генерации:**
|
||
```
|
||
tag_inventory2.py → tag-inventory3.csv (7861 строк — съём тегов с диска)
|
||
build_fix_plan2.py → APPLY-PLAN-FINAL.csv (7861 строк — план правок)
|
||
→ APPLY-PLAN.csv (боевой канон)
|
||
```
|
||
Скрипт вызывался: `python3 build_fix_plan2.py INVENTORY.csv OUT.csv [HOST_ROOT]`.
|
||
|
||
**Строки 692-697 — место дефекта:**
|
||
```python
|
||
if not new_al and d_album:
|
||
new_al = d_album
|
||
if new_al and norm(new_al) in NON_ARTIST and d_album:
|
||
new_al = d_album # 🔴 ЗДЕСЬ
|
||
```
|
||
|
||
**Два независимых провала:**
|
||
|
||
1. **`norm()` = только `strip().lower()`** (строки 258-259) — не убирает пунктуацию.
|
||
2. **`norm(new_al) in NON_ARTIST`** — сравнение **ВСЕЙ СТРОКИ** с элементами множества
|
||
(точное совпадение), а не поиск слова.
|
||
3. **`NON_ARTIST`** (строки 28-33) содержит `избранное`, `сборник`, `сборка`, `разное`, `детские`,
|
||
`хиты`, `лучшее` — **но НЕ содержит `песни`**.
|
||
|
||
| `cur_album` | `norm()` | `in NON_ARTIST` | Причина провала |
|
||
|---|---|---|---|
|
||
| `Песни` (18 файлов: Никулин, Руки вверх, Little Big) | `песни` | ✗ | слова `песни` нет в списке |
|
||
| `Храбрый Зайчик. Песни для детей` (1: Миансарова) | `храбрый зайчик. песни для детей` | ✗ | фраза, а в списке одиночные слова |
|
||
| `песни` строчными (3: Наутилус) | `песни` | ✗ | + регистр: даже будь `Песни` в списке, не совпало бы |
|
||
| `Избранное` | `избранное` | ✅ | есть в списке — потому и отработало |
|
||
|
||
**Всего пропущено 44 файла в 5 значениях:** `Песни` 18 · `Избранное` 16 ·
|
||
`Супер ДискотЭка Радио Рекорд` 6 · `песни` 3 · `Храбрый Зайчик. Песни для детей` 1.
|
||
|
||
> 🔑 **ДЕФЕКТ НЕ В КОРОТКОМ СПИСКЕ — ДЕФЕКТ В ТОЧНОМ СРАВНЕНИИ.**
|
||
> Даже если бы `песни` добавили в `NON_ARTIST`, `Храбрый Зайчик. Песни для детей` всё равно
|
||
> прошёл бы: это не слово из списка, а целая фраза. **Проверка обязана быть по ВХОЖДЕНИЮ
|
||
> слова, а не по равенству строки** — тот же принцип, что и в правиле корня
|
||
> ([[personal/plans/music-artist-albumartist-repair]] § СЕССИЯ 28: `nleaf in nartist`).
|
||
> **Это 4-е повторение одного метод-урока:** лишняя строгость ключа сравнения убивает детектор молча.
|
||
|
||
**Диагностика:** `/tmp/find_junk_miss.py` → 5 значений / 44 файла, все помечены `НЕТ В JUNK`.
|
||
|
||
**Статус:** фикс **не внесён** — ждёт решения Alex (править только album-детектор
|
||
или сразу все подобные по тому же принципу вхождения).
|
||
|
||
## ⚠️ Расхождение счётчика: 8043 в Navidrome vs 7861 mp3 на диске
|
||
|
||
**Гипотеза требует проверки фактом** (не подтверждена): `.ndignore` применён ПОСЛЕ первого
|
||
старта — сканирование началось сразу при запуске контейнера, до создания файла. Возможно,
|
||
часть файлов бэкап-папок успела проиндексироваться.
|
||
|
||
**Как проверить (следующая сессия):**
|
||
```bash
|
||
# есть ли в БД пути из бэкап-папок
|
||
ssh truenas_admin@mallexxx.duckdns.org "docker exec navidrome sh -c \
|
||
'echo \"SELECT COUNT(*) FROM media_file WHERE path LIKE \\\"backup%\\\";\" | sqlite3 /data/navidrome.db'"
|
||
```
|
||
Если `> 0` — `Скан` → очистить БД-записи (перескан после `.ndignore` их не удаляет автоматически;
|
||
Navidrome помечает missing только при полном рескане с `ND_SCANONSTARTUP`).
|
||
|
||
## Питфоллы (выучено на живом деплое)
|
||
|
||
- **`/mnt/RED_2TB/docker` root-owned** → `mkdir` через ssh запрещён, нужен `midclt call filesystem.mkdir`.
|
||
- **`filesystem.mkdir` не создаёт промежуточные каталоги** → создавать по одному уровню.
|
||
- **`filesystem.setacl` возвращает job id асинхронно** → проверять `core.get_jobs`, не считать успехом.
|
||
- **`docker compose up` блокируется апрувом** → `pull` + `create` + `start`.
|
||
- **TLS-сертификат выпускается ~10 сек** → `curl exit=35` сразу после reload = норма.
|
||
- **`.ndignore` пустой = игнорировать ВСЁ** (проверено строкой в бинаре).
|
||
- **`strings <бинарь> | grep ndignore`** — быстрый способ подтвердить фичу без чтения доки.
|
||
- **`docker exec navidrome sqlite3`** есть внутри контейнера — БД читается напрямую (в отличие от Jellyfin).
|
||
- 🔴 **`docker restart` и `docker compose down/up` НЕ вызывают полный скан Navidrome** — в логе всегда
|
||
`fullScan=false` (`duration≈3s`). Смена тегов без смены `mtime` файлов **не подхватывается**.
|
||
Для принудительного перечитывания — `touch` файлов либо UI-скан с галкой **Full scan**.
|
||
- 🔴 **Пересоздание контейнера не чистит БД** — `navidrome.db` в томе на диске; после `down/up`
|
||
данные и число сирот те же. Пересоздавать контейнер ради «чистого скана» бессмысленно.
|
||
- **Navidrome не имеет дефолтного логина** — первый вход = форма «Create admin», это не поломка.
|
||
|
||
---
|
||
|
||
## 🔴 ИСПРАВЛЕНО 2026-09-27: Navidrome ≠ Jellyfin по поведению движка
|
||
|
||
> **Прежнее утверждение этой заметки: «Navidrome раскладывает библиотеку по тегам ТАК ЖЕ,
|
||
> как Jellyfin» — НЕВЕРНО.** Проверено по официальной доке и GitHub Navidrome.
|
||
> Разница принципиальная, и она как раз в нашу пользу.
|
||
|
||
| | Jellyfin (баг [#18162](https://github.com/jellyfin/jellyfin/issues/18162)) | Navidrome |
|
||
|---|---|---|
|
||
| Источник имени артиста | **имя папки** перекрывает тег | **только тег** |
|
||
| Структура папок | требуется `Artist/Album/Track` | **игнорируется полностью** |
|
||
| Наша структура `категория/подпапка/файл` | ломает навигацию | **работает как есть** |
|
||
| Интернет-метаданные артистов | тянет из MusicBrainz → левые артисты | **не тянет** |
|
||
| Пишет в файлы | может | **никогда** (by design) |
|
||
|
||
**Официальная дока Navidrome (Tagging Guidelines), прямая цитата:**
|
||
> «Navidrome organizes your music library **entirely** based on the metadata tags in your audio
|
||
> files. Unlike some music players, **it does not use folder names or file names to group tracks**.»
|
||
|
||
**GitHub Navidrome #3355:**
|
||
> «Being tag-based means **it does not care for the folder structure**... You can even put your
|
||
> **whole library in just one folder**, and as long as the files are tagged properly, Navidrome
|
||
> will organize your library correctly.»
|
||
|
||
**Почему это важно:** именно баг Jellyfin #18162 сорвал приёмку Music-библиотеки 2026-09-27
|
||
(навигация по папкам: `Letuchuj korabl`, `Vinni pooh`). **Navidrome этого бага не имеет.**
|
||
Разбор — [[personal/tech/jellyfin-tags-cp1251-unreadable]].
|
||
|
||
## Задача
|
||
Поднять стриминг музыки с TrueNAS (`/mnt/RED_2TB/storage/Music/`) на Android-устройства.
|
||
|
||
## Варианты
|
||
|
||
### ✅ Вариант 1 — Navidrome + Symfonium (рекомендован, ОСНОВНОЙ)
|
||
- **Сервер:** [Navidrome](https://www.navidrome.org/) — лёгкий музыкальный сервер, Docker-образ
|
||
- **Плеер Android:** [Symfonium](https://symfonium.app/) — Subsonic API, офлайн-кэш, теги, обложки
|
||
- Работает через интернет по порту, можно за nginx reverse proxy
|
||
- Docker уже есть: `/mnt/RED_2TB/docker/`
|
||
- **Ключевое преимущество:** читает теги, **игнорирует папки** — наша структура категорий не мешает
|
||
|
||
### Вариант 2 — Jellyfin + Finamp
|
||
- **Сервер:** Jellyfin (Docker) — универсален (видео, фото, музыка)
|
||
- **Плеер Android:** [Finamp](https://github.com/jmshrv/finamp) — опенсорс
|
||
- ⚠️ 🔴 **ПРОВАЛЕНО 2026-09-27:** музыкальная библиотека построена по папкам, не по тегам
|
||
(баг #18162). Приёмка Alex не пройдена, библиотека удалена, БД восстановлена.
|
||
**Пока не исправлено в upstream — для музыки не годится.**
|
||
|
||
### Вариант 3 — SMB напрямую
|
||
- Без сервера, VLC или Solid Explorer играют по SMB
|
||
- Только для домашней сети — по интернету медленно и небезопасно
|
||
- ✅ Запасной вариант: не зависит ни от тегов, ни от движка
|
||
|
||
## ✅ Блокер снят (был: «теги массово битые»)
|
||
|
||
> ~~🔴 **Общий блокер для ВСЕХ вариантов (найдено 2026-09-26):** Теги в `Music/` массово битые —
|
||
> `mojibake` 2759, `lost` 662, `empty` 1026, `partial` 2920, `clean` только 537.~~
|
||
|
||
**АКТУАЛЬНО 2026-09-27:** ремонт тегов (волна 38) выполнен — `artist`/`title`/`album`
|
||
заполнены, мойибаке нет, теги в **UTF-16 с BOM**. Цифры выше относятся к состоянию
|
||
**ДО** волны 38 и устарели.
|
||
|
||
⚠️ **Но выявлен новый пробел:** в файлах **отсутствует `TPE2` (albumartist)** — проверено
|
||
`154/154`. Его **не было в плане правок** (`tag-fix-plan.csv` не содержит колонки `album_artist`).
|
||
|
||
### ✅ `ALBUMARTIST` для Navidrome — НЕ обязателен (проверено 2026-09-27)
|
||
|
||
**Не блокирует запуск.** Мейнтейнер Navidrome `deluan` (обсуждение #5924):
|
||
> «when an album artist tag is missing, Navidrome substitutes the **track artist** in its place»
|
||
|
||
| Ситуация | Результат |
|
||
|---|---|
|
||
| `ALBUMARTIST` у всех треков | 1 альбом ✅ |
|
||
| Нет `ALBUMARTIST`, сольный альбом | 1 альбом ✅ |
|
||
| Нет `ALBUMARTIST`, 4 фита | **5 альбомов** вместо 1 ❌ |
|
||
| Нет `ALBUMARTIST`, сборник 18 треков | **до 18 альбомов** ❌ |
|
||
|
||
**Обход без правки файлов — конфиг:**
|
||
```toml
|
||
PID.Album = "musicbrainz_albumid|albumartist,album,albumversion,releasedate"
|
||
```
|
||
> 🔑 Сырое `albumartist`, **НЕ** `albumartistid` (тот никогда не пуст). `folder` в спеку
|
||
> класть нельзя. ⚠️ Смена `PID.Album` → полный рескан + новые ID → **бэкапить `navidrome.db`**.
|
||
|
||
### ✅ Новые закачки тегирует Lidarr сам
|
||
|
||
Lidarr матчит по MusicBrainz release groups и пишет полные теги, **включая `Album Artist`**
|
||
(действие `Write Metadata Tags`). Связка [`navidrome-lidarr-bridge`](https://github.com/danielbanariba/navidrome-lidarr-bridge)
|
||
— закачанное Lidarr'ом подхватывается Navidrome автоматически.
|
||
|
||
> 🆕 **2026-09-28 — план деплоя Lidarr к Navidrome составлен, ждёт «go» Alex.**
|
||
> Актуальный runbook Taiga — [[family/how-to/arr-stack-taiga]].
|
||
> Ключевое для этой связки:
|
||
> - **Root folder Lidarr = `/storage/Music`** — та же папка, что читает Navidrome.
|
||
> - 🔴 **`Write Metadata Tags` в Lidarr ОБЯЗАТЕЛЕН.** Без него Lidarr не пишет `Album Artist`,
|
||
> и новые закачки развалятся на альбомы-одиночки в Navidrome (при отсутствии ALBUMARTIST
|
||
> Navidrome подставляет track artist — см. таблицу выше).
|
||
> - 🔴 **Существующие 7861 файл в Lidarr НЕ импортировать.** Импорт как «Existing Albums»
|
||
> заставит Lidarr перезаписать теги из MusicBrainz — правки 50 сессий ремонта тегов уедут.
|
||
> Lidarr отвечает только за новые закачки; старая библиотека остаётся как есть.
|
||
|
||
### ⚠️ Что реально стоит починить до запуска: СЕМАНТИКА `artist`
|
||
|
||
Файлы `!Классика` содержат **семантически мусорные** `artist` — формально валидные теги:
|
||
```
|
||
artist=Классика в современной обработке ← это не артист
|
||
artist=Классика для детей П.И. Чайковский ← это не артист
|
||
artist=Maria Makarova (organ) ← исполнитель вместо композитора
|
||
```
|
||
**Navidrome покажет их как артистов** — надо смотреть и чистить.
|
||
|
||
Детали — [[personal/tech/jellyfin-tags-cp1251-unreadable]] §ИНВЕНТАРИЗАЦИЯ.
|
||
|
||
## Следующие шаги
|
||
- [x] ~~Сначала: ремонт тегов 7906 файлов~~ → ✅ выполнен (волна 38)
|
||
- [x] ~~Поднять Navidrome в Docker на TrueNAS~~ → ✅ **ВЫПОЛНЕНО 2026-09-27** (см. v4 выше)
|
||
- [x] ~~Пробросить порт / настроить Caddy subdomain~~ → ✅ `music.mallexxx.duckdns.org`,
|
||
сертификат Let's Encrypt выпущен
|
||
- [x] ~~Исключить бэкап-папки~~ → ✅ `.ndignore` (формат подтверждён строками в бинаре)
|
||
- [x] ~~Выгрузить CSV по «скобочному» `album`~~ → ✅ **ВЫПОЛНЕНО**:
|
||
`~/Downloads/music-tags/REPORT-new-album-equals-cur.csv` (6202 строки) +
|
||
`REPORT-album-kept-all-folders.csv` (558 папок с классификацией kind).
|
||
**Уточнено: реальный мусор — 9 папок / 265 треков** (только значения-огрызки в скобках).
|
||
- [x] ~~Решение Alex по правке `album` для мусорных папок~~ → ✅ **ВСЕ РЕШЕНИЯ ПОЛУЧЕНЫ И ПРИМЕНЕНЫ**
|
||
- файл в **корне папки артиста** → `album` = имя папки артиста (`Дискотека Авария`)
|
||
- файл в **подпапке-альбоме** → `album` = имя подпапки (`Москва пьёт пиво`)
|
||
- **папка-сборник** (`!Retro`) → `album='Retro'`
|
||
- правило корня подтверждено из Zulip (сообщение 128410): «то что в корне лежит мы считаем
|
||
одним альбомом». **306 строк исправлены, мусора 0 — проверено по файлу.**
|
||
- 3 спорные папки закрыты (Alex «Да»): `Филосовские`→`Философские`, `Разное`→`Разное`,
|
||
`Из сказок`→`Из Сказок`
|
||
- `Птаха` → `album='Красивые, богатые'` (без артиста и года)
|
||
Разбор — [[personal/plans/music-artist-albumartist-repair]] §СЕССИЯ 18.
|
||
- [ ] ⚠️ **Проверить `albumartist='Various Artists'` для 5 файлов `!Retro`** — `album=Retro`
|
||
проставлен, но `albumartist` не менялся.
|
||
- [ ] 🔴 **Alex вычитывает `DIFF-new-vs-38-v2.csv`** — секция `album` (+12 записей против старого).
|
||
- [ ] 🔴 **Валидатор смысла `new_album`** — паттерн «значение целиком в скобках» `^\(.+\)$`,
|
||
заглушки (`нет альбома`, `NULL`, `no title`, `Non spécifié`), спам-URL. В пайплайне ОТСУТСТВУЕТ:
|
||
это и есть причина дефекта (дифф двух планов мусор не ловит — он стоит в обоих).
|
||
- [ ] ⚠️ **Проверить, не проиндексировались ли бэкап-папки** (8043 трека vs 7861 на диске,
|
||
т.е. +182) — запрос к БД Navidrome. ⚠️ **Скан завершён, проверка НЕ выполнена.**
|
||
`.ndignore` создан ПОСЛЕ первого скана → нужен рескан или проверка БД.
|
||
- [ ] 🔴 **АКТИВНО (2026-09-27): ремонт `artist` + `albumartist` по 7861 файлу** —
|
||
правила Alex зафиксированы, идёт ручная разбивка по группам.
|
||
План — [[personal/plans/music-artist-albumartist-repair]]
|
||
- `albumartist`: сборник → `Various Artists`, детские → `Детские песни`, сказки → `Сказки`
|
||
- `artist`: из скобок в имени файла, иначе искать в интернете
|
||
- мойибаке **492 файла** — расшифровывать CP1251→UTF-8, не маскировать
|
||
- [ ] **Аудит семантики `artist`** — убрать «Классика в современной обработке» и подобное
|
||
(поглощён задачей выше)
|
||
- [ ] Собрать `TPE2` по плану выше (нужен **НОВЫЙ** план правок с колонкой `album_artist` —
|
||
в `tag-fix-plan.csv` её не было)
|
||
- [ ] **Создать админа Navidrome при первом входе** (дефолтного логина НЕТ — форма «Create admin»)
|
||
— ⏳ Alex увидел форму, подтвердил, что это ожидаемое поведение
|
||
- [ ] Установить Symfonium на Android, подключить к `https://music.mallexxx.duckdns.org`
|
||
|
||
## Связанные заметки
|
||
- [[personal/tech/jellyfin-tags-cp1251-unreadable]] — 🔴 почему Jellyfin провалился (баг #18162), сравнение с Navidrome
|
||
- [[personal/tech/music-tags-repair-truenas]] — ремонт тегов (волна 38)
|
||
- [[family/plans/music-jellyfin-lidarr-taiga]] — план Music → Jellyfin + Lidarr (Jellyfin-ветка откачена)
|
||
- [[family/how-to/jellyfin-config]] — Jellyfin как альтернативный музыкальный сервер (Вариант 2)
|