[2026-09-27] eagle: family/how-to/navidrome-truenas-android.md personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 18:42:36 +06:00
1 parent 54adfa9a5a
commit 41ae82ab3c
3 files changed
+173 -38

No files matched your search

+44 -29
View File
@@ -1,9 +1,10 @@
# Музыкальный плеер с TrueNAS на Android
**Статус:** ✅ **РАЗВЁРНУТ 2026-09-27** — Navidrome на TrueNAS, работает, скан завершён (8043 трека).
🔴 **2026-09-27 (сессия 22):** теги 306 исправлены ✅, но **rescan НЕ помог** — в БД остались **14 сирот** `album` в скобках. `restart` и **пересоздание контейнера** дают `fullScan=false`, теги не перечитываются. Следующий шаг: **`touch` 306 файлов** (mtime) → полный скан.
**Статус:** ✅ **РАЗВЁРНУТ 2026-09-27** — Navidrome на TrueNAS, работает.
✅ **2026-09-27 (сессия 22):** теги 306 исправлены, **БД Navidrome снесена и построена заново**, скобочных сирот **0**, треков **8167**.
🔴 **Ни `restart`, ни пересоздание контейнера НЕ дают полного скана** (`fullScan=false`) — теги не перечитываются. Рабочий способ: **снести `navidrome.db`** (тогда старт → `fullScan=true`).
**Дата:** 2026-05-10
**Обновлено:** 2026-09-27 (v6 — rescan не помог, 14 сирот, путь через mtime)
**Обновлено:** 2026-09-27 (v7 — БД пересоздана, 0 сирот, треков 8167)
---
@@ -321,23 +322,17 @@ folder="!Pop, Club, Любэ/Дискотека Авария/Москва пьё
**Артефакт для просмотра (запрошен у Alex, ещё не выгружен):** CSV `путь + текущий album + title`
по **306** файлам со «скобочным» album, топ-20 для глазной вычитки.
## 🔴 АКТИВНО (2026-09-27, сессия 22): RESCAN НЕ ПОМОГ — 14 СИРОТ ОСТАЛИСЬ
## ✅ РЕШЕНО (2026-09-27, сессия 22): СНОС БД → ПОЛНЫЙ СКАН → 0 СИРОТ
> **Alex: «Navidrome rescan теперь надо»** → затем **«Просто контейнер пересоздай»**
> **Alex: «Navidrome rescan теперь надо»** → **«Просто контейнер пересоздай»** → **«Сука базу блядь снеси руками!!!»**
**Факт: теги на диске исправлены, но БД Navidrome содержит И СТАРЫЕ, И НОВЫЕ альбомы.**
**Проблема:** теги на диске исправлены, но БД 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 (до и после)
sqlite3 "file:$DB?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE '(%';" # → 14 (до и после попыток rescan)
```
| Что | Значение |
|---|---|
| альбомов в БД | 967 |
| `album` целиком в скобках | **14** ← не вычищены (число НЕ изменилось от rescan) |
| новые значения (`Философские`, `Морские`, `Москва пьёт пиво`, `Старые хиты`, `Дискотека Авария`, `Retro`, `Владимир Высоцкий`, `Несчастный Случай`, `zvezda`) | ✅ присутствуют |
### ❌ ЧТО ПРОБОВАЛОСЬ И НЕ ПОМОГЛО (факт)
| # | Способ | Результат |
@@ -354,27 +349,47 @@ sqlite3 "file:$DB?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE '(%';"
(скан их не помечает missing). Причина: album = пара `(name, album_artist)`; теги сменились →
Navidrome **создал новые** альбомы, **старые не удалил** (быстрый скан файлы не перечитывает).
### 🔑 СЛЕДУЮЩИЙ ШАГ (не сделан): перечитать `mtime` файлов → полный скан
### ✅ СНОС БД — РАБОЧИЙ СПОСОБ (выполнен)
Единственный надёжный путь без пароля: **`touch` 306 файлов** (меняет только `mtime`, содержимое
и аудио не трогает) → Navidrome увидит «изменённые» файлы → перечитает теги → сироты уйдут.
Либо через UI Navidrome: `Скан` с галкой **«Full scan»** (пароль `navi_admin` неизвестен —
спрашивать у Alex, не выдумывать; `curl /rest/ping` → `code 40 Wrong username or password`).
```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
```
**Проверка после:** тот же запрос `LIKE '(%'` → должно стать **0**, в UI не должно быть дублей
«альбом со скобками» + «альбом без скобок».
**Лог на старте:** `Full scan required after migration` → `Scanner: Starting scan fullScan=true` ✅
> 🔴 **ПИТФОЛЛ (важно на будущее): `docker restart` и `docker compose down/up` для Navidrome
> НЕ вызывают полный скан.** В логе всегда `fullScan=false`. Navidrome читает только новые/изменённые
> файлы. Для принудительного перечитывания тегов нужно менять `mtime` файлов или использовать
> UI-скан с `Full scan`. Полный скан на старте — `ND_SCANONSTARTUP` + `ND_SCANONSTARTUP` force
> (**не проверено**, в ENV отсутствует; проверять, если `touch` не поможет).
**Результат (факт по `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`. В доке выше есть устаревшее утверждение «`docker exec navidrome sqlite3`»:
> работает и так, и так, но напрямую по `file:…?mode=ro` быстрее и не требует запуска контейнера.
> В контейнере `tagwriter:v2` sqlite3 **нет** (`python3 -c "import sqlite3"` тоже недоступен —
> только `mutagen`).
> не нужно `docker exec`. Быстрее и не требует запуска контейнера. В контейнере `tagwriter:v2`
> `sqlite3` **нет** (только `mutagen`).
Боевой прогон и приёмка — [[personal/plans/tagwriter-container]] §СЕССИЯ 21.
@@ -4,7 +4,7 @@ created: '2026-09-27'
updated: '2026-09-27'
type: plan
namespace: personal
status: session20-dry-run-306-verified-awaiting-apply-go
status: session20-APPLY-306-done-nav.md-REBUILT
tags:
- music
- metadata
@@ -363,6 +363,89 @@ Alex: «**это 4359 правки?**» про таблицу из 16 пар. **
> 🔴 Ловушка: `/mnt/RED_2TB/Music` **пуста** — первый прогон смонтировал её и дал `к правке: 0`.
> Подробности и два питфолла — `[[personal/plans/tagwriter-container]]` §СЕССИЯ 20.
### ✅ БОЕВОЙ ПРОГОН 306 — ВЫПОЛНЕН И ПРИНЯТ ПО ДИСКУ (сессия 20, вечер)
Alex: «**Гони боевой**» → «**Navidrome rescan теперь надо**» → «**Просто контейнер пересоздай**»
→ «**Сука базу блядь снеси руками!!!**»
| Шаг | Факт |
|---|---|
| Бэкап ДО правки | `/mnt/RED_2TB/storage/Music/backup_before_306/20260927-051727` — **306 файлов + `_MANIFEST.tsv`** (md5 каждого до/после копии, расхождений 0). Скрипт `/tmp/backup_306.py` |
| План на NAS | `/tmp/tw306/apply-plan.csv`, md5 `8bfa52389a80119e713b88d05a91b9ef` = локальному ✅ |
| **APPLY** | ✅ **306/306, ошибок 0** |
| Изменения по полям | `TALB` 306 · `TPE2` 306 · `TCON` 264 · `TIT2`/`TPE1`/`TRCK` **0** (не тронуты) |
| **READ-BACK с диска** (не отчёт скрипта) | ✅ проверено 306 · **совпало 306** · расхождений **0** · **album в скобках — 0** · файлов < 100 КБ — **0** (аудио целое). Скрипт `/tmp/verify_306c.py` (пути внутри контейнера: `/app/data`, `/music`) |
> 🔴 **МЕТОД-УРОК (приёмка):** `--apply` печатает «306/306, 0 ошибок» — это **не приёмка**
> (ср. W2: «40/40 записано» при уничтоженном аудио). Приёмка = **перечитать теги с диска**
> своим скриптом и проверить ЗНАЧЕНИЕ + размер файла. Здесь это дало `совпало 306 / расхождений 0`.
### 🔴 NAVIDROME: СКАН НЕ ПЕРЕЧИТЫВАЕТ ТЕГИ — ЛЕЧИТСЯ ТОЛЬКО СНОСОМ БД
Хронология (каждый шаг **проверен запросом к БД**, а не на словах):
| Действие | Результат |
|---|---|
| `docker restart navidrome` | `fullScan=false`, `Scan completed` за 3 с → **в БД те же 14 альбомов со скобками** |
| `docker compose down && up -d` (пересоздание) | снова `fullScan=false` → **те же 14**. БД лежит на диске (том `/data`), пересоздание контейнера её не трогает |
| ✅ **Снос БД руками** | бэкап `data/navidrome.db.bak-20260927-053238/` → `rm navidrome.db{,-shm,-wal}` → старт → `Full scan required after migration`, **`fullScan=true`** |
**Финал (факт по `sqlite3`):**
```
БЫЛО: album со скобками 14 | треков 7861
СТАЛО: album со скобками 0 | альбомов 837 | треков 8167
```
Целевые значения в БД: `Философские` 1 · `Морские` 1 · `Блатные` 1 · `Спортивные` 1 ·
`Из Сказок` 1 · `Разное` 1 · `Москва пьёт пиво` 1 · `Старые хиты` 1 · `Дискотека Авария` 2 ·
`Красивые, богатые` 1 · `Retro` 1 · `Вилли Токарев` 1 · `Володимир Высоцкий`→`Владимир Высоцкий` 1 ·
`Несчастный Случай` 1 · `zvezda` 1. **Скобочных дублей — 0.**
> 🔴 **МЕТОД-УРОК (Navidrome):** обычный скан (`fullScan=false`) сверяет **`mtime`/путь**, а не теги.
> Массовая правка тегов **не видна** быстрому скану: Navidrome добавляет **новые** альбомы,
> старые **не удаляет** → дубли «скобочный + чистый». Рестарт и пересоздание контейнера
> **не помогают** (`ND_SCANSCHEDULE=1h` тоже). Рабочий способ — **снести `navidrome.db`**,
> тогда старт даёт `fullScan=true` и база строится заново. Перед сносом — копия `navidrome.db*`.
> **Теряется:** плейлисты/избранное/счётчики Navidrome. **Не теряется:** музыка и теги (на диске).
> Проверочные запросы: `sqlite3 "file:.../navidrome.db?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE \"(%\";"`.
### 🗄 ИНФРАСТРУКТУРА (уточнения сессии 20)
| Факт | Значение |
|---|---|
| **Библиотека музыки на NAS** | **`/mnt/RED_2TB/storage/Music`** (100 ГБ, 15 929 mp3). `/mnt/RED_2TB/Music` — **пустая обманка** |
| Карта путей | план хранит `/storage/Music/...`; в контейнере это `/music`; на хосте `/mnt/RED_2TB/storage/Music` |
| Navidrome | контейнер `navidrome`, образ `deluan/navidrome:latest` (0.64.2), порт 4533, `ND_SCANSCHEDULE=1h` |
| Compose Navidrome | `/mnt/RED_2TB/docker/navidrome/docker-compose.yml` (бэкап `.bak-20260927-052857`), том `/mnt/RED_2TB/storage/Music:/music:rw` |
| `tagwriter:v2` | `CSV_IN = "/app/data/apply-plan.csv"` — путь **зашит константой**, CLI-флага для плана нет. Свой план подкладывать под этим именем в отдельную папку |
| Вариант запуска без approval | `docker run --rm -v …:/app/data:ro -v …/Music:/music:rw tagwriter:v2 --dry-run` |
| `/mnt/RED_2TB/IRONWOLF` | пустой второй пул (не библиотека) |
### ❓ ОТКРЫТЫЙ ВОПРОС ALEX (сессия 20, конец)
Alex про песни из `!Детские` — «**А нам все равно, если б я был султан, платы паровоз — какой
albumartist и album? Они же в песнях для детей лежат? Почему они не в одном альбоме для детей
оказались?**»
**Факт:** этих песен в `!Детские` **нет**. Все три — в **`!Retro/Юрий Никулин`** (3 файла):
```
!Retro/Юрий Никулин/Если бы я был султан.mp3
!Retro/Юрий Никулин/Постой паровоз.mp3
!Retro/Юрий Никулин/А нам все равно.mp3
folder = !Retro/Юрий Никулин class = АЛЬБОМ
album = Песни albumartist = Юрий Никулин artist = Юрий Никулин
```
Причина: правило **D21** (файл в папке артиста = один альбом) смотрит только на **путь** и не
учитывает, что песня «детская». `!Детские` (570 файлов) содержит свои альбомы
(`Сборка Песни для детей` 165 · `Детский Хит Сезона` 156), эти три — не там.
**«Храбрый зайчик»** — поиск по плану и по диску (`*зайчик*`, `*заяц*`, `*храбр*`) дал **0**.
Найденные «зайчики» — другие песни (`Мартовский заяц`, `Хор мальчиков-зайчиков`, `Солнечный зайчик` ×2).
⏭ Ждём от Alex точное имя/исполнителя.
---
## 🟣 СЕССИЯ 18 (2026-09-27) — NAVIDROME РАЗВЁРНУТ, ДЕФЕКТ-КЛАСС ВСКРЫТ, ПРАВИЛА ВОССТАНОВЛЕНЫ ИЗ ZULIP, 306 СТРОК ИСПРАВЛЕНЫ, НОВЫЙ ДИФФ ГОТОВ
+45 -8
View File
@@ -4,7 +4,7 @@ created: '2026-09-26'
updated: '2026-09-27'
type: plan
namespace: personal
status: session22-apply-306-done-navidrome-rescan-failed-14-orphans
status: session22-apply-306-done-navidrome-db-rebuilt-0-orphans
tags:
- music
- truenas
@@ -118,15 +118,52 @@ sqlite3 "file:$DB?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE '(%';"
Compose не менялся (бэкап `docker-compose.yml.bak-20260927-052857`), данные/музыка целы.
⚠️ Сироты: `updated_at` 10:09–10:23 UTC, `missing=0` — скан их живыми считает.
**🔑 СЛЕДУЮЩИЙ ШАГ (не сделан): `touch` 306 файлов → полный скан.** `touch` меняет только `mtime`,
содержимое/аудио не трогает; Navidrome увидит «изменённые» файлы и перечитает теги. Альтернатива —
UI-скан с галкой **Full scan** (пароль `navi_admin` неизвестен — **не выдумывать**,
`curl …/rest/ping` → `code 40 Wrong username or password`).
🔑 **РЕШЕНИЕ НАЙДЕНО (Alex): «Сука базу блядь снеси руками!!!» → снос `navidrome.db`.**
Проверка результата — тот же запрос: `LIKE '(%'` должно стать **0**.
| # | Способ | Результат |
|---|---|---|
| 1 | `docker restart navidrome` | ❌ `fullScan=false` → те же **14** |
| 2 | `docker compose down` + `up -d` | ❌ `fullScan=false`, `duration=3.12s` → снова **14** |
| 3 | ✅ **снести `navidrome.db*` → старт** | ✅ `Full scan required after migration`, **`fullScan=true`** → **скобок 0** |
🔴 **ПИТФОЛЛ: `docker restart` и `docker compose down/up` для Navidrome НЕ дают полного скана** —
в логе всегда `fullScan=false`. Смена тегов без смены `mtime` не подхватывается.
**Порядок (выполнен и подтверждён):**
```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 # построит БД заново
```
**Результат (факт по `sqlite3`):**
| Метрика | До сноса | После |
|---|---:|---:|
| `album` со скобками | 14 | **0** |
| треков (`media_file`) | 7861 | **8167** |
| альбомов (`album`) | 967 | **837** |
Все целевые значения присутствуют по одному разу: `Философские` · `Морские` · `Блатные` ·
`Спортивные` · `Из Сказок` · `Разное` · `Москва пьёт пиво` · `Старые хиты` · `Дискотека Авария`(2) ·
`Красивые, богатые` · `Retro` · `Вилли Токарев` · `Владимир Высоцкий` · `Несчастный Случай` · `zvezda`.
⚠️ **Что теряется при сносе БД:** плейлисты, избранное, счётчики проигрываний, оценки, пользователи
Navidrome (пароль задаётся заново в UI). **Что НЕ теряется:** музыка и теги — они на диске.
Поэтому бэкап `navidrome.db.bak-<ts>/` обязателен — единственный путь к откату метаданных Navidrome.
**Проверка после:** `sqlite3 "file:$DB?mode=ro" "SELECT COUNT(*) FROM album WHERE name LIKE '(%';"` → **0**.
⚠️ Сироты: `updated_at` 10:09–10:23 UTC, `missing=0` — скан их живыми считает.
🔴 **ПИТФОЛЛ (главный, метод): НИ `docker restart`, НИ `docker compose down/up` не дают полного
скана Navidrome — в логе всегда `fullScan=false`.** Массовая смена тегов без смены `mtime` не
подхватывается: Navidrome **добавляет** новые альбомы и **не удаляет** старые → дубли
«скобочный + чистый». `ND_SCANSCHEDULE=1h` тоже не помогает. **Единственный рабочий способ —
снести `navidrome.db`** (тогда старт форсирует `fullScan=true`). Альтернатива без сноса —
`touch` 306 файлов (меняет `mtime`, не содержимое) + полный скан; в этой сессии не применялась,
сработал снос БД.
🔴 **Питфолл-нюанс:** Navidrome при скане **пересчитывает album из тегов**, поэтому у 306 файлов
кроме `TALB` сменился и `TPE2` — в БД это **новые** записи альбомов, а не апдейт старых.