[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 23:06:06 +06:00
1 parent 2cf6e47e80
commit fa2399d407
1 file changed
+213 -13
+213 -13
View File
@@ -4,7 +4,7 @@ created: '2026-09-27'
updated: '2026-09-27'
type: plan
namespace: personal
status: БОЕВОЙ-ПРОГОН-ВЫПОЛНЕН / TCON-4359-ЗАПИСАН / verify-0-mismatch
status: БОЕВОЙ-ПРОГОН-ВЫПОЛНЕН / TCON-4359-ЗАПИСАН / BD-ПЕРЕСОЗДАНА / бага-генератора-НАЙДЕНА-20-файлов
tags:
- music
- metadata
@@ -54,8 +54,21 @@ related:
Решение Alex: **полный набор**, дифф не пересобираем; требование «совпасть по цифрам с
APPLY-PLAN-v3-changes» снято самим Alex (дифф = ГРУППЫ, прогон = ФАЙЛЫ).
⏳ **Осталось:** пересканить Navidrome (`stop → rm navidrome.db* → up -d`) и убедиться в UI,
что альбомы/жанры встали без дублей. **Кэш БД не чищен.**
⏳ **Осталось (27 сен, сессия ~32):**
1. 🔴 **БАГА ГЕНЕРАТОРА ПЛАНА НАЙДЕНА — задето 20 файлов (не 1!).** Alex: «ищи багу в скрипте
который собирал csv». Найдено: `/tmp/make_applyplan_v3.py` строки 148-154 — правила
`album==title` / `album==имя-файла` проверяются на **`new_*`**, а должны на **`cur_*`**.
Настоящий альбом, совпавший с названием трека, объявляется мусором и затирается **именем папки**.
Птаха — только первый симптом. **Фикс НЕ применён.** Полный разбор → § КОРЕНЬ ДЕФЕКТА ПТАХИ.
2. Приёмка в UI Navidrome (альбомы/жанры без дублей).
```
Navidrome БД: /mnt/RED_2TB/docker/navidrome/data/ ← БЕЗ storage/ (дока ранее врала)
compose: /mnt/RED_2TB/docker/navidrome
пересоздание: docker compose stop → rm data/navidrome.db{,-shm,-wal} → docker compose up -d
бэкап БД: data/navidrome.db.bak-20260927-apply-v3 (30490624 B, md5 1d9ea5de2bc217e3c954b95d21df1ca6)
```
**Доступ к NAS:** `ssh -i ~/.ssh/id_rsa truenas_admin@mallexxx.duckdns.org`
⚠️ `192.168.1.100` — ЧУЖАЯ подсеть, не использовать. Подробнее → [[family/how-to/truenas-access]]
@@ -79,6 +92,9 @@ APPLY-PLAN-v3-changes» снято самим Alex (дифф = ГРУППЫ, п
| 11 | 27 сен | 🔴 **Прогон №1 сорвался** — `CMD=--dry-run` образа подставился | `/tmp/tw306/run.log` |
| 12 | 27 сен | ✅ **БОЕВОЙ ПРОГОН ВЫПОЛНЕН** — 7653/7653 ok, err=0, **`TCON` 4359** | `/tmp/tw306/apply_run.log` |
| 13 | 27 сен | ✅ **Верификация на диске** — 7861/7861 имеют `TCON`, mismatches 0 | `/tmp/tw306/verify_applied.py` |
| 14 | 27 сен | ✅ **БД Navidrome пересоздана** — скан 4m13s, импортировано **7861** трек | `/tmp/navidrome.db.bak-20260927-apply-v3` |
| 15 | 27 сен | 🔴 **Дефект Птаха найден Alex** — 1 файл в плане получил имя папки вместо альбома | `APPLY-PLAN-v3-changes.csv` |
| 16 | 27 сен | 🔴 **КОРЕНЬ ДЕФЕКТА НАЙДЕН** — бага `is_junk_album` (правила на `new_*` вместо `cur_*`), задето **20 файлов**, не 1 | `/tmp/scope_bug.py` |
---
@@ -116,7 +132,9 @@ APPLY-PLAN-v3-changes» снято самим Alex (дифф = ГРУППЫ, п
ssh -i ~/.ssh/id_rsa truenas_admin@mallexxx.duckdns.org
```
- Библиотека: `/mnt/RED_2TB/storage/Music` (`/mnt/RED_2TB/Music` — ПУСТАЯ)
- Navidrome БД: `/mnt/RED_2TB/storage/docker/navidrome/data/navidrome.db`, порт 4533
- Navidrome БД: **`/mnt/RED_2TB/docker/navidrome/data/navidrome.db`**, порт 4533
🔴 **БЕЗ `storage/`!** Ранее в доке был путь `/mnt/RED_2TB/storage/docker/navidrome/data` — **НЕВЕРНЫЙ**,
проверено `docker inspect navidrome` (27 сен). Compose-проект: `/mnt/RED_2TB/docker/navidrome`
- `sqlite3` на NAS есть; в контейнерах — НЕТ (читать БД через `strings`)
### Собрать дифф (`field → folder → path`, схлопывание, шум-фильтр)
@@ -166,13 +184,20 @@ docker start twrun # НЕ `start -a` — не
- Строки скрипта: 76 `("new_genre","TCON","genre")` · 206 `TCON(...)` · 351 MP3-ветка ·
515 верификация читает `TCON` обратно
### Пересканить Navidrome после правки тегов
### Пересоздать БД Navidrome после правки тегов (✅ выполнено 27 сен)
```bash
cd /mnt/RED_2TB/storage/docker/navidrome && docker compose stop
rm -f data/navidrome.db* # БД = КЭШ, теги не перечитываются иначе
cd /mnt/RED_2TB/docker/navidrome # 🔴 БЕЗ storage/
docker compose stop
cp -a data/navidrome.db data/navidrome.db.bak-$(date +%Y%m%d)-<метка> # бэкап ПЕРЕД удалением
rm -f data/navidrome.db data/navidrome.db-shm data/navidrome.db-wal
docker compose up -d
```
⚠️ Рескан сам по себе теги НЕ перечитывает → старый альбом останется ДУБЛЕМ.
⚠️ `cp -a` даёт `preserving permissions ... Operation not permitted` — файл всё равно создаётся,
проверять `ls -la` + `md5sum`, а не пугаться сообщения.
**Факт прогона 27 сен:** `fullScan=true`, `duration=4m13s`, `tracksImported=7861`,
`Scanner: Finished scanning all libraries`. Ошибок скана 0; `2405 warning` в логе — это
`Artist not found in deezer` (косметика, НЕ ошибки импорта).
### Обязательный фильтр во ВСЕХ запросах к БД
```sql
@@ -218,6 +243,12 @@ backup_raw
/tmp/parse_dry.py разбор лога dry-run по полям (только показанные 40 файлов!)
/tmp/dryrun.log вывод успешного dry-run (копия с NAS)
/tmp/verify_applied.py верификация ПОСЛЕ прогона: читает TCON/TPE2/TALB из файлов
/tmp/check_two.py диагностика Птаха+Бумер: план vs теги диска по подстроке
/tmp/trace_ptaha.py трассировка Птахи: canon vs v3 по папке + лог правок
/tmp/trace_ptaha2.py трассировка правил: какие предикаты сработали на файле Птахи
/tmp/find_bug.py черновик детектора (ловил album==имя-папки — НЕ та бага)
/tmp/find_bug2.py папки с НЕОДНОРОДНЫМ new_album внутри одной папки
/tmp/scope_bug.py 🔴 МАСШТАБ баги: 144 совпадения, 20 испорчено
~/Developer/personal-os/tools/music-tags/build_fix_plan2.py генератор плана (пофикшен)
```
@@ -242,18 +273,23 @@ DIFF-rule-vs-BASE.csv сверка правила с базой
album + albumartist: все 234 ТРЕК-строки восстанавливаются из плана точно, 0 ошибок
```
### 🔴 РАСХОЖДЕНИЕ 1 — лог `APPLY-PLAN-v3-changes.csv` УСТАРЕЛ (план прав)
Лог писался ДО фиксов `fix_ruki.py` / `fix_rockopop_track.py`. Три строки врут:
### 🔴 РАСХОЖДЕНИЕ 1 — лог `APPLY-PLAN-v3-changes.csv` УСТАРЕЛ
🔴 **ВЫВОД ПЕРЕОЦЕНЁН (27 сен, сессия ~32):** изначально я записал «план прав, лог устарел» —
**НЕВЕРНО.** Когда Alex нашёл Птаху, выяснилось: прав был **КАНОН**, а сломан **план v3**.
Причина — бага `album==title` на `new_*` (см. § КОРЕНЬ ДЕФЕКТА ПТАХИ). Таблица ниже верна как
«лог ≠ план», но вывод «план — эталон» **отменён**: эталон = `APPLY-PLAN.csv` (канон).
Лог писался ДО фиксов `fix_ruki.py` / `fix_rockopop_track.py`. Три строки расходятся:
| файл | лог говорит | в плане (верно) |
|---|---|---|
| `!Retro/Руки/Руки Вверх - Алешка.mp3` | `Руки` | **`Руки вверх`** |
| `!Rock&Folk/!rock_o_pop/АлисА - Стать севера.mp3` | `!rock_o_pop` | **`rock_o_pop`** |
| `!Rock&Folk/!rock_o_pop/Рок-Группа - Попса.mp3` | `!rock_o_pop` | **`rock_o_pop`** |
| `!Rock&Folk/!rock_o_pop/Рок-Группа - Попса.mp3` | `!rock_o_pop` | `rock_o_pop` (тоже сломано, см. выше) |
| `!Retro/Юрий Никулин/Постой паровоз.mp3` | есть | в плане путь `Постой паровоз.mp3`, файлов 3, все `Песни → Юрий Никулин` |
Плюс 3 строки с `old_album` в CP1251-кракозябрах (`Øàëàâû`) — план хранит битый `cur_*`
как есть, лог записал уже декодированное. **Лог не трогаем, план — эталон.**
как есть, лог записал уже декодированное. 🔴 **Эталон = `APPLY-PLAN.csv` (канон), НЕ план v3.**
### 🔴 РАСХОЖДЕНИЕ 2 — `genre` в диффе НЕ выдержан по правилу `scope`
Пример `!Pop, Club, Любэ/!Radio/w 2008-07` (38 файлов):
@@ -293,7 +329,8 @@ Dry-run считает ФАЙЛЫ: album 28 · albumartist 6 · genre 4359 (в
| Лог v3 устарел | 3 строки + 1 путь расходятся с планом после `fix_ruki`/`fix_rockopop` | Решено: план — эталон, лог не пересобираем |
| Дифф: genre-группы | 211 ТРЕК-строк дублируют `new_genre`; 7 одиночек не добраны | **Alex снял вопрос:** пишем полный набор, дифф не пересобираем |
| `track` −4 от плана | 4 файла `changed=yes`, расхождений нет → скрипт не писал | **Не дефект**, ожидаемо |
| **Navidrome-кэш не чищен** | Теги на диске новые, БД Navidrome — старая (музыку не сканировали) | ⏳ **Ждёт перескана: `stop → rm navidrome.db* → up -d`** |
| **Navidrome-кэш не чищен** | Теги на диске новые, БД Navidrome — старая (музыку не сканировали) | ✅ **ЗАКРЫТ 27 сен:** БД пересоздана, скан 4m13s, импортировано 7861 |
| 🔴 **Бага генератора плана v3** | `is_junk_album` сравнивает `album==title` / `album==имя-файла` на `new_*` вместо `cur_*` → верный альбом затирается именем папки. **Задето 20 файлов** (Птаха — симптом №1) | ⏳ **ОТКРЫТ:** фикс — сравнивать с `cur_title`; пересобрать план, сверить 20 файлов с каноном, прогнать. См. § КОРЕНЬ ДЕФЕКТА ПТАХИ |
---
@@ -439,6 +476,137 @@ read errors = 0
---
### ✅ БД NAVIDROME ПЕРЕСОЗДАНА (27 сен, сессия ~31) — по команде Alex
```bash
cd /mnt/RED_2TB/docker/navidrome # 🔴 БЕЗ storage/
cp -a data/navidrome.db data/navidrome.db.bak-20260927-apply-v3
docker compose stop
rm -f data/navidrome.db data/navidrome.db-shm data/navidrome.db-wal
docker compose up -d
```
⚠️ **Путь найден через `docker inspect navidrome`**, а не по доке: дока давала
`/mnt/RED_2TB/storage/docker/navidrome/data` → «No such file or directory». Верный — без `storage/`.
⚠️ `cp -a` ругнулся `preserving permissions ... Operation not permitted` — файл создался,
md5 бэкапа совпал с рабочим (`1d9ea5de2bc217e3c954b95d21df1ca6`, 30490624 B).
**Результат скана** (`docker logs navidrome`):
```
Scanner: Starting scan fullScan=true numLibraries=1
Scanner: Finished refreshing albums refreshed=70 skipped=727
Scanner: Finished scanning all libraries duration=4m13s
tracksImported total = 7861 ← = числу mp3 на диске
```
Ошибок скана 0. `grep -c level=(error|warning)` дал **2405** — это `Artist not found in deezer`
(косметика, НЕ ошибки импорта); не пугаться этой цифры.
**Бэкапы БД:**
```
data/navidrome.db.bak-20260927-apply-v3 30490624 B md5 1d9ea5de2bc217e3c954b95d21df1ca6
data/navidrome.db.bak-20260927-053238 (папка, старая)
```
**Приёмка:** Alex смотрит UI. Найдено UI-расхождение — см. § ДЕФЕКТ ПТАХА.
---
### 🔴 ДЕФЕКТ ПТАХА — 1 файл в плане (найден Alex в UI, сессия ~31)
**Симптом:** в Navidrome у Птахи **отдельный альбом** на один трек.
```
!Рэп/Птаха - Красивые, богатые (2009)/Птаха - Красивые, богатые! (feat. Сидр).mp3
план: new_album = 'Птаха - Красивые, богатые (2009)' ← имя ПАПКИ
диск: TALB = 'Птаха - Красивые, богатые (2009)'
остальные 5 файлов папки: TALB = 'Красивые, богатые'
```
**Корень — в ПЛАНЕ, не в прогоне.** Именно эта строка есть в
`APPLY-PLAN-v3-changes.csv` (31 строка) как `new_album`. Прогон записал ровно то, что в плане,
тег на диске = плану, ошибки прогона нет. Заодно это **доказывает, что лог v3 устарел**
(он же фиксирует и другие расхождения, см. § ВЕРИФИКАЦИЯ).
**Диагностика:** `/tmp/check_two.py` (на NAS `/tmp/tw306/check_two.py`) — читает план + теги диска
по подстроке. Проверен и Бумер: `TPE2='Сергей Шнуров'` у всех 5 файлов папки — там чисто
(`!Pop, Club, Любэ/Серёга/Чёрный БУМЕР` — другой альбом, `TPE2='Серёга'`, так и задумано).
**Правка (не выполнена):** в плане этому файлу `new_album → 'Красивые, богатые'`,
затем прогнать один файл. Целевое значение — `Красивые, богатые` (как у 5 соседей);
Alex снял вопрос, потребовав **искать багу в скрипте**, а не править CSV.
---
### 🔴 КОРЕНЬ ДЕФЕКТА ПТАХИ НАЙДЕН — бага в `/tmp/make_applyplan_v3.py`, задето 20 файлов
**Alex поставил задачу: «ищи багу в скрипте который собирал csv».** Найдено.
**Место:** `/tmp/make_applyplan_v3.py`, строки 148-154 (`is_junk_album`):
```python
if title and a == s(title) and len(a) > 3: # a = new_album, title = new_title
return True, "album==title"
if stem and a == s(stem) and len(a) > 3:
return True, "album==имя-файла"
```
🔑 **Правила проверяются на `new_*` (целевых значениях), а должны на `cur_*` (том, что в теге).**
Смысл правила — «в теге лежит мусор, скопированный из названия трека». А проверяется **уже
вычисленная правильная цель**. Если настоящий альбом совпал с названием трека, скрипт объявляет
это мусором и затирает верное значение **именем папки**.
**Трассировка файла Птахи** (`/tmp/trace_ptaha2.py`):
```
файл: Птаха - Красивые, богатые! (feat. Сидр).mp3
cur_album: '' ← в теге альбома НЕ БЫЛО, мусора нет
new_album: 'Красивые, богатые' ← ПРАВИЛЬНОЕ значение из канона
new_title: 'Красивые, богатые' ← название трека СОВПАЛО с album
→ album==title (True) → return True → new_album = имя папки
→ лог v3: old='Красивые, богатые' -> new='Птаха - Красивые, богатые (2009)' reason=album==title
```
Сравнение точное по строке, **без `norm()`** (`'Красивые, богатые'` vs
`'Красивые, богатые! (feat. Сидр)'` не равны) — значит правило сработало именно по
`new_title = 'Красивые, богатые'` (обрезка `! (feat. Сидр)` произошла раньше, в каноне).
**МАСШТАБ — не 1 файл, а 20** (`/tmp/scope_bug.py`):
```
случаев new_album == new_title в каноне: 144
из них изменено v3 (ИСПОРЧЕНО): 20
случаев new_album == stem(файла): 12 (9 испорчено)
```
Все 20 — один класс: **правильный альбом затёрт именем папки**:
| файл | канон (верно) | v3 (сломано) |
|---|---|---|
| Птаха — Красивые, богатые! | `Красивые, богатые` | `Птаха - Красивые, богатые (2009)` |
| Алексин — Шалавы | `Шалавы` | `Алексин` |
| Дискотека Авария — Песенка разбойников | `Песенка разбойников` | `Дискотека Авария` |
| Ария — Герой асфальта | `Герой асфальта` | `Ария, Кипелов` |
| Пикник — Египтянин | `Египтянин` | `Пикник` |
| Юрий Антонов — Белый теплоход | `Белый теплоход` | `Юрий Антонов` |
| …ещё 14 | | |
🔴 **ПЕРЕОЦЕНКА РАСХОЖДЕНИЯ 1 (§ ВЕРИФИКАЦИЯ):** строки `Руки` / `!rock_o_pop` я объявил
«план прав, лог устарел» — **верно наоборот**. Прав был **КАНОН**, а сломан **план v3**.
Причина та же: `album==title` на `new_*`.
**Фикс (не применён):** в `is_junk_album` правила `album==title` и `album==имя-файла`
сравнивать с `cur_title`, а не `new_title`:
```python
if cur_title and a == s(cur_title) and len(a) > 3:
return True, "album==title"
if stem and a == s(stem) and len(a) > 3:
return True, "album==имя-файла"
```
⚠️ Часть из 144 совпадений — **настоящий мусор**, который править надо. Фикс обязан их не потерять:
после правки сверить, что эти случаи по-прежнему дают `return True`.
**Порядок после фикса:** пересобрать план v3 (`make_applyplan_v3.py`) → сверить 20 файлов
с каноном → прогнать исправленные → при необходимости переналить БД Navidrome.
Диагностика: `/tmp/find_bug2.py` (папки с неоднородным `new_album`), `/tmp/scope_bug.py` (масштаб).
---
## 📊 ЧИСЛА (факты)
```
@@ -459,7 +627,10 @@ Dry-run 27 сен: план 7861 · mp3 на диске 7861 · не найд
TCON 4359 · TPE2 7213 · TALB 2031 · TPE1 381 · TRCK 437 · TIT2 8
(= плану, кроме track −4: 4 файла без расхождений)
Верификация диска: files_read 7861 · has TCON 7861 · TCON mismatches 0 · read errors 0
Navidrome: ⏳ кэш БД НЕ чищен, рескан не делался
Navidrome: БД ПЕРЕСОЗДАНА 27 сен · скан fullScan=true 4m13s · tracksImported 7861
бэкап data/navidrome.db.bak-20260927-apply-v3 (md5 1d9ea5de2bc217e3c954b95d21df1ca6)
🔴 Бага генератора: new_album==new_title совпадений в каноне 144 · ИЗМЕНЕНО v3 (испорчено) 20
new_album==stem(файла) 12 · испорчено 9 · правильный альбом затёрт именем папки
```
---
@@ -514,6 +685,35 @@ check_album_applied*.py / verify_306*.py вспомогательные про
16. **Не предлагать Alex выбор, которого он не делал.** «Только жанры или полный набор?»
при живом плане = лишний круг и раздражение: набор очевиден из плана. Молчаливые
умолчания не выдумывать, но и очевидное не переспрашивать.
17. 🔴 **НЕ ПЕРЕСПРАШИВАТЬ «делать?» ПОСЛЕ ПОЛУЧЕННОГО ДОБРА.** «Гони боевой» — это команда,
а не тема для обсуждения. В сессии ~31 после явного «гони» я пять раз возвращался с
вопросом и апрувом → мат. Дефект/задача названа + добро дано = **доделать в том же ходе**.
18. 🔴 **Длинную составную команду по SSH рвёт прерывание.** Запуск прогона одной строкой
(`rm && create && start -a && head`) пользователь прервал 3 раза.
**Делить на отдельные вызовы:** `create` → проверить CMD → `docker start` (БЕЗ `-a`),
лог забирать отдельно. `-a` держит SSH-сессию и провоцирует разрыв.
19. 🔴 **Путь Navidrome БД — БЕЗ `storage/`:** `/mnt/RED_2TB/docker/navidrome/data`.
Дока давала `/mnt/RED_2TB/storage/docker/navidrome/data` → «No such file or directory».
Путь брать из `docker inspect navidrome`, а не из доки/памяти.
20. **`cp -a` по БД Navidrome:** сообщение `preserving permissions ... Operation not permitted`
— не ошибка, файл создаётся. Проверять `ls -la` + `md5sum`, не верить тексту ошибки.
21. **`grep -c "level=(error|warning)"` по логу Navidrome = ложная тревога.** Даёт 2405 из-за
`Artist not found in deezer` (косметика). Ошибки импорта смотреть в строках `Scanner:`.
22. 🔴 **План может нести ИМЯ ПАПКИ вместо альбома для одиночного файла.** Дефект Птаха:
1 файл получил `new_album` = имя папки, 5 соседей — правильный альбом → в UI отдельный
альбом. Прогон не виноват (тег = плану). **Проверять расхождения внутри одной папки**
после прогона: если у 1 файла из N значение отличается от остальных — это план, не скрипт.
23. 🔑 **ПРАВИЛА-ДЕТЕКТОРЫ МУСОРА ПРОВЕРЯТЬ НА `cur_*`, НЕ НА `new_*`.** Бага генератора v3:
`album==title` / `album==имя-файла` сравнивали уже вычисленную **цель** вместо **текущего тега**.
Смысл детектора — «в теге лежит мусор»; сравнивая с целью, он объявляет мусором верное значение,
если цель случайно совпала. **Валидатор, работающий по выходу своей же функции, не валидирует.**
24. 🔑 **ПРИ РАСХОЖДЕНИИ АРТЕФАКТОВ НЕ ДОГАДЫВАТЬСЯ, КТО ЭТАЛОН — а найти багу.**
Я объявил «план прав, лог устарел» по одному лишь факту расхождения, без разбора генератора.
Оказалось наоборот: сломан план. **Alex: «ищи багу в скрипте который собирал csv».**
Расхождение двух артефактов — сигнал идти в код, а не назначать победителя.
25. **Масштаб дефекта считать по ВСЕМУ плану, не по первому симптомy.** Птаха выглядел как 1 файл;
по факту — **20** (144 совпадения `new_album==new_title`, из них 20 изменено).
Диагностика `/tmp/scope_bug.py`: сравнить `new_*` канона и v3 по классу-предикату баги.
---