diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index 8f0b37b1..4b25ac1c 100755 --- a/personal/plans/music-artist-albumartist-repair.md +++ b/personal/plans/music-artist-albumartist-repair.md @@ -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 по классу-предикату баги. ---