[2026-09-27] eagle: family/how-to/navidrome-truenas-android.md personal/plans/music-artist-albumartist-repair.md personal/tech/music-tags-repair-truenas.md personal/tech/skill-truenas-administration.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 20:29:04 +06:00
1 parent d77aec3548
commit eac340006e
4 files changed
+342 -12

No files matched your search

+133 -9
View File
@@ -2,9 +2,11 @@
**Статус:** ✅ **РАЗВЁРНУТ 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 (v7 — БД пересоздана, 0 сирот, треков 8167)
**Обновлено:** 2026-09-27 (v8 — `.ndignore` дополнен, живая музыка 7861; найден корень дефекта `NON_ARTIST` в `build_fix_plan2.py`)
---
@@ -417,13 +419,83 @@ sqlite3 navidrome-merged.db "SELECT count(*) FROM album; SELECT count(*) FROM me
**Результат сессии 24:** 7861 трек сверено, расхождений `album` **50** + `albumartist` **9**
(все — дефект самого плана, а не применения). Разбор → [[personal/plans/music-artist-albumartist-repair]] §СЕССИЯ 24.
### 🔴 ПИТФОЛЛ: бэкап-папки ВНУТРИ `/music` попадают в БД
### ✅ ИСПРАВЛЕНО 2026-09-27 (сессия 29-кватер): `.ndignore` дополнен, бэкапы ушли из БД
`backup_before_306/20260927-051727/` и родственные проиндексированы Navidrome —
**306 «лишних» треков** в БД, которых нет в плане. `.ndignore` их **не покрывает** (в файле
перечислены только `backup`, `backup_conv40`, `backup_fix`, `backup_raw`).
**Держать бэкапы музыки ВНЕ `/music`** либо дописывать каждую новую бэкап-папку в `.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)` | ❌ НЕ правится — часть названия |
## Остаточные дефекты после деплоя
@@ -434,8 +506,60 @@ sqlite3 navidrome-merged.db "SELECT count(*) FROM album; SELECT count(*) FROM me
| 3 | `TPE2`/`albumartist` — ремонт по 7861 файлу | 🟡 разброс 7→2 папки; ⏳ 2 папки ждут решения |
| 4 | Symfonium на Android | ⏳ не установлен |
| 5 | Lidarr | ⏳ не начат (отложен) |
| 6 | ⏭ Перегенерация `DIFF-new-vs-38-v2.csv` | 🔴 план изменился после генерации |
| 7 | Валидатор **смысла** `new_album` | 🔴 в пайплайне отсутствует |
| 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 на диске