[2026-09-28] eagle: personal/plans/tagwriter-container.md personal/tech/music-tags-repair-truenas.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-28 00:37:14 +06:00
1 parent 8ad2c77435
commit 85d2856bc0
2 files changed
+197 -1

No files matched your search

+1 -1
View File
@@ -494,7 +494,7 @@ docker start -a twdry; docker rm -f twdry
| Что | Правильно | Дока говорила |
|---|---|---|
| Образ | **`tagwriter:latest`** | ❌ `tagwriter:v2` (устаревший) |
| Скрипт | **монтируется** через `--entrypoint python3 /app/data/apply_tags_v2.py` | ❌ «нужен `docker cp`» |
| Скрипт | **`/app/apply_tags_v2.py`** (В ОБРАЗЕ), монтируется папка данных | ❌ «нужен `docker cp`» |
| Музыка (боевой) | `/music` **без** `:ro` | ✅ верно |
| Музыка (сухой) | `/music:ro` | ✅ верно |
| `save(v1=0)` | ⚠️ ID3v1-комментарий **уцелел** в тесте | ❌ «сносит ID3v1 всегда» |
+196
View File
@@ -51,6 +51,202 @@ related:
> «с нуля», теряя сортировку, схлопывание и правило `album`.
> **Сборщик живёт в папке проекта (`~/Downloads/music-tags/`), НЕ в `/tmp`.**
> ## ✅ СЕССИЯ 23 (2026-09-27): ВОЛНА v6 — СБОРНИКИ + ТРОФИМ + ПРОВЕРКА БД. ПРИМЕНЕНО 282/282
>
> **Триггер:** Alex: «поехали применять APPLY-PLAN-v3-changes… потом dry run который должен
> совпасть по цифрам» → далее «делай диф от текущей бд версии».
>
> ### 🔴 ГЛАВНЫЙ УРОК: ПРАВИЛО СБОРНИКОВ ДЕЙСТВУЕТ И БЕЗ `!` В ИМЕНИ ПАПКИ
>
> Первый сборщик этой сессии (`build_diff_from_db.py`) правил папки **только по хардкоду трёх
> путей** (`!Рэп/Трофим/`, `!Рэп/Птаха…`, `Тема дороги.mp3`). Правила сборников в нём
> **не было вообще** — отсюда дифф на 91 запись, где 26 строк — Трофим.
>
> **Alex (несколько раз, пока не дошло):** правило `album` = имя папки-владельца действует
> для ВСЕХ сборников, включая папки без `!` (`Infected Mushroom/Unreleased/Remixes, Mixes`,
> `Other Track's`). Я сузил его до папок с `!` — это была ошибка.
>
> **Корневая модель тегов (Alex):**
> ```
> album = имя папки-владельца (папка релиза), БЕЗ ведущих '!', БЕЗ года, БЕЗ артиста
> albumartist= владелец папки; для СБОРНИКОВ = 'Various Artists' (уже так, НЕ трогать)
> artist = реальный исполнитель трека -- НЕ ТРОГАТЬ (кроме явных указаний)
> track = номер из имени файла, если в теге пусто
> ```
> 🔴 **Алекс про сборники, дословно:** «для сборников типа rock_o_pop **artist = 'Various Artists'**!
> АЛЬБОМ БЛЯДЬ rock_o_pop, не albumartist!» — то есть `album` = имя папки, `albumartist` = `Various Artists`.
>
> ### 🔴 ПРОХОД v3 БЫЛ ПО ФАЙЛАМ, А НЕ ПО ПАПКАМ — 18 ПАПОК ИЗ 23 ЗАКРЫТЫ ЧАСТИЧНО
>
> Замер по `APPLY-PLAN-v3-changes.csv` против плана:
> ```
> !Pop, Club, Любэ/!ZabuGor 1 из 15 ЧАСТИЧНЫЙ
> !Pop, Club, Любэ/Green Apelsin 1 из 38 ЧАСТИЧНЫЙ
> !Pop, Club, Любэ/Алексин 1 из 14 ЧАСТИЧНЫЙ
> !Retro/Забугр 3 из 32 ЧАСТИЧНЫЙ
> !Rock&Folk/!rock_o_pop 2 из 57 ЧАСТИЧНЫЙ
> !Rock&Folk/Ария, Кипелов 3 из 12 ЧАСТИЧНЫЙ
> !Рэп/Птаха - Красивые… 1 из 8 ЧАСТИЧНЫЙ
> ```
> **Причина:** правило срабатывало только если `album == title` или `album == имя файла`.
> Файлы с ЧУЖИМ альбомом в теге проскакивали мимо. Полных проходов — 6 (все мелкие: `Little Big`,
> `Руки вверх`, `Хабиб`, `Юрий Антонов`, `Юрий Никулин`, `Серьга`).
> 🔴 **Правило проверки: после любой волны сверять «сколько файлов правилось» против
> «сколько файлов в папке». Расхождение = незакрытый проход, а не норма.**
>
> ### АРТЕФАКТЫ v6 (файлы в `~/Downloads/music-tags/`)
>
> | Файл | Что | md5 / число |
> |---|---|---|
> | `build_diff_v6.py` | сборщик (правило сборников + Трофим + Птаха + Ленинград + Т9) | — |
> | `DIFF-v6.csv` | 103 строки, 554 правки, **282 файла** | — |
> | `apply-plan-v6.csv` | 7861 строка | `60819ea387be6f98c3fdcb9da601e9c8` |
>
> **Разложение 282 файлов:** `album` 183 · `artist` 155 · `albumartist` 158 · `track` 58.
>
> ### 🔴 RED FLAG: ПУТЬ В ПЛАНЕ ОБЯЗАН НАЧИНАТЬСЯ С `/storage/Music/`
>
> Первый dry-run дал **`к обработке: 0`, 282 MISSING, «путь вне /music»**.
> Причина: в моём сборщике `path` писался **без префикса** `/storage/Music/`.
> Канонный `APPLY-PLAN.csv` префикс имеет; `apply_tags_v2.py` через `map_path()` снимает его,
> и без префикса файл уходит в MISSING.
> **Фикс:** `plan_rows.append({"path": "/storage/Music/" + rel, ...})`.
> 🔴 **Любой новый сборщик плана ОБЯЗАН писать путь с префиксом `/storage/Music/`.**
>
> ### БОЕВОЙ ПРОГОН v6 — ФАКТ
>
> ```bash
> ssh … 'docker create --name twapply6 --entrypoint python3 \
> -v /tmp/tw306:/app/data \
> -v /mnt/RED_2TB/storage/Music:/music \
> tagwriter:latest /app/apply_tags_v2.py --apply --verify'
> ```
> ```
> строк в плане: 7861 | не найдено на диске: 0 | к обработке: 282
> === APPLY: 282 файлов === ok=282 err=0
> TPE1 155 · TPE2 158 · TALB 183 · TRCK 58 · TIT2 0 · TCON 0
> === VERIFY: совпало 282, MISMATCH 0 ===
> ```
> **Приёмка с диска (mutagen, `verify_applied_v6.py`):** 282/282 совпало, расхождений 0.
> Точечная проверка 11 файлов (`spot_check_v6.py`) — все классы верны.
>
> ### 🔴 ДЕФЕКТ, НАЙДЕННЫЙ ПРИЁМКОЙ: `albumartist` у Т9 остался мусорным
>
> `Т9 - Вдох-Выдох`: я правил `album`/`artist`, но НЕ `albumartist` → в теге остался
> `TPE2 = 'Вдох-Выдох'` (мусор из старого artist). Дописан в сборщик, пересобран план
> (`albumartist` 157 → 158), повторный прогон — `aa='Т9'` ✅.
> 🔴 **Правило: правя `artist`, всегда проверять `albumartist` — он часто дублирует старое
> значение artist и остаётся мусором.** Детектор: `check_aa_stale.py` (ищет файлы, где artist
> менялся, а albumartist не менялся и равен старому artist). В библиотеке найден ровно **1**.
>
> ### КОНТЕКСТ ДЛЯ ВОССТАНОВЛЕНИЯ (Alex): Сирано — реальный альбом Т9 «Вдох-выдох» (2008)
>
> `!Рэп/03. Т9 - Вдох-Выдох (Ода Нашей Любви).mp3` (файл в КОРНЕ `!Рэп`):
> `album='Вдох-выдох'`, `artist='Т9'`, `albumartist='Т9'`. Подтверждено веб-поиском (Discogs 2008).
>
> ### СНОС БД 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
> ```
> **До сноса:** `media_file` 7861 · `album` 727. **После полного скана:** `media_file` 7811 · `album` 718.
> ⚠️ `media_file` 7811 ≠ 7861 — часть файлов Navidrome не импортировал (не-mp3 / битые;
> при dry-run: `mp3 на диске: 7811, пропущено не-mp3: 562`). Для проверок считать по 7811.
>
> ### ✅ ЧТО ПОДТВЕРЖДЕНО В БД ПОСЛЕ ПЕРЕСБОРА
>
> ```
> Трофим: 11 альбомов, aa='Сергей Трофимов' (Аристократия помойки 1-4, Война и мир,
> Я рождаюсь вновь, Бард-авангард, Я скучаю по тебе, Ветер в голове, Ностальгия,
> Следующая остановка) -- ВЕРНО
> rock_o_pop: 57 треков ОДНИМ альбомом -- ВЕРНО
> ZabuGor: 2 альбома (13 + 2) -- второй = файл в корне, album='Radio'
> !Retro: 2 альбома (157 + 1) -- ОДИН ФАЙЛ ОТВАЛИЛСЯ
> !Классика: 2 альбома (34 + 1) -- ОДИН ФАЙЛ ОТВАЛИЛСЯ
> ```
>
> ### 🔴 ОТКРЫТЫЙ КЛАСС ДЕФЕКТА (не закрыт): ПАПКИ БЕЗ `!`, СОСТОЯЩИЕ ИЗ ОДИНОЧЕК
>
> `Infected Mushroom` (51 папка, 244 файла) — в БД распался на ~35 альбомов-одиночек:
> `Becoming Insane (Chemical Trolls Remix)`, `Birthday (DJ Goni Peles Remix)`, `CDS`, `Promo`,
> `Elation Station`, `Magnet`, `Deck Wizards…`, `Unreleased Tracks` ×29 и др.
> В моём правиле их не было: я обрабатывал только папки со `!` и `Разное`.
>
> ### 🔴 ПРАВИЛО ALEX ДЛЯ ОДИНОЧЕК (получено в конце сессии, дословно разобрано)
>
> ```
> папка с 1 файлом -> album = имя РОДИТЕЛЬСКОЙ папки (Remixes, Mixes)
> папка с 2+ файлами -> album = имя папки БЕЗ года и исполнителя
> ('(2001) Infected Mushroom - Wider (Domestic RMX)'
> -> 'Wider (Domestic RMX)')
> albumartist -> 'Infected Mushroom'
> artist -> как в треке, НЕ трогать
> ```
> **Исключения, названные Alex:**
> - `2016 - Liquid Smoke (Single)` → `album='Liquid Smoke'` (сингл, без «WEB»)
> - `2016 - Nutmeg (Single)` → `album='Nutmeg'`
> - `Unreleased/Other/… Other Track's` (23 файла) → `album='Other Tracks'` (**без апострофа**)
> - `Unreleased/Other/… Compilations Tracks` (12) и `Unreleased/Albums/*` — **уже верны, не трогать**
> - **ПАПКИ-АРТИСТЫ С 1 ФАЙЛОМ НЕ ТРОГАТЬ** (`Little Big`, `Хабиб`, `Юрий Антонов`,
> `Андрей Миронов`, `Агата Кристи` в SD-card) — Alex: «не надо ломать то что уже сделали».
> Правило «1 файл → родитель» к ним НЕ применять.
>
> ### ❌ `!A Not sorted/SD-card-2026-05` — Alex: УДАЛИТЬ ПАПКУ
>
> 3 файла, 3 разных артиста (`Пуля` — Агата Кристи, `Гагарин, я вас любила` — Андервуд,
> `Генералы песчаных карьеров` — Несчастный случай). **Дублей в дереве не найдено** —
> это 3 разные песни. Alex сказал «снеси папку, там только дубли вроде».
> ⚠️ **Требует подтверждения перед удалением** (расхождение: дублей нет).
>
> ### 📁 АРТЕФАКТЫ СЕССИИ 23 (все в `~/Downloads/music-tags/`)
>
> ```
> build_diff_v6.py сборщик v6 (правило сборников + адресные)
> DIFF-v6.csv 103 строки, 554 правки, 282 файла
> apply-plan-v6.csv 7861 строка, md5 60819ea387be6f98c3fdcb9da601e9c8
> verify_applied_v6.py приёмка с диска (mutagen), 282/282
> spot_check_v6.py точечная проверка 11 классов
> check_aa_stale.py детектор устаревшего albumartist
> check_db_v6.py проверка БД Navidrome (альбомы/artists)
> db_final_rule.py список одиночек по правилу Alex
> db_find_single_albums.py папки, где album == title
> sanity/ diag_single_track.py, db_classB.py, db_singles.py, db_im_struct.py
> NAS: /tmp/tw306/apply-plan.csv, dryrun-v6.log, verify_v6.py, spot_v6.py, check_aa.py
> ```
>
> ### 🔴 ПИТФОЛЛ: НЕ ЛОПАТИТЬ ДИСК, ЕСЛИ ДАННЫЕ ЕСТЬ В БД
>
> Alex: «**Ты базу проверяешь или ты назуя тотфайлуху пошел лопатить?!**»
> Я пошёл обходить `/music` через `os.walk` для проверки альбомов — медленно (таймаут 180 s)
> и не нужно: **Navidrome читает из своей БД**, все проверки группировки делать SQL-запросом
> к копии `navidrome.db`. Диск трогать только когда данные в БД отсутствуют.
>
> ### 🔴 ПИТФОЛЛ: `backup*/` ВНУТРИ `/music` ЛОВИТ `os.walk`
>
> Первый скан `Infected Mushroom` нашёл `backup_fix/!Pop, Club, Любэ/Infected Mushroom`
> (бэкап!) и выдал его за живое дерево. **Любой обход диска ОБЯЗАН отсеивать `backup*`.**
>
> ### 🔴 ПИТФОЛЛ: `--plan` НЕ СУЩЕСТВУЕТ, `CSV_IN` — КОНСТАНТА
>
> Запуск `apply_tags_v2.py --plan /app/data/apply-plan.csv` → `unrecognized arguments`.
> Путь зашит: `CSV_IN = "/app/data/apply-plan.csv"`. Свой план подкладывать **под этим именем**
> в отдельной папке данных (`/tmp/tw306:/app/data`).
>
> ### 🔴 ПИТФОЛЛ: `--entrypoint python3` + путь к скрипту
>
> Рабочая форма (проверена):
> ```bash
> docker create --name twX --entrypoint python3 \
> -v /tmp/tw306:/app/data:ro -v /mnt/RED_2TB/storage/Music:/music:ro \
> tagwriter:latest /app/apply_tags_v2.py --dry-run
> ```
> ⚠️ В доке `personal/plans/tagwriter-container` было `/app/data/apply_tags_v2.py` — **неверно**,
> скрипт лежит в `/app/apply_tags_v2.py` (в образе). Dока исправлена ниже.
> ## 🔴 ДЕФЕКТЫ `build_fix_plan2.py` (найдены 27 сен, не исправлены в боевом файле)
>
> | # | Дефект | Где | Последствие |