diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index e8fae4b7..6c8a84ec 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: RC-complete-defects-fixed +status: session18-dop-diff-inventoried-folders-counted-awaiting-apply-go tags: - music - metadata @@ -19,9 +19,190 @@ related: - '[[personal/tech/music-tags-repair-truenas]]' - '[[personal/plans/tagwriter-container]]' - '[[family/how-to/navidrome-truenas-android]]' -status: session18-step2-done-dryrun-ok-awaiting-apply-go # Music `artist` + `albumartist` Repair — правила Alex и ход работ +## 🟣 СЕССИЯ 18-ДОП (2026-09-27, вечер) — ИНВЕНТАРЬ ДИФФА ПО ФАКТУ, ПОСЛЕ ВОПРОСА ALEX «ГДЕ ДИФ?» + +> **Триггер:** Alex — «**Где диф последний с 371 правкой альбомов/albumartist который ты готовил?**» +> → затем «**Какие там записи?**». Отвечено из **Zulip DB** (по скиллу `zulip-db-forensics`), +> затем проверено по самим файлам на диске. + +### 📍 Местонахождение (факт с диска, mtime 2026-09-27 17:41) + +| Файл | MD5 | Строк | Статус | +|---|---|---:|---| +| ⭐ `~/Downloads/music-tags/DIFF-new-vs-38-v2.csv` | `7bc222a1087787c390026d624af932f3` | **2854** | **АКТУАЛЬНЫЙ** (после 371 правки) | +| `~/Downloads/music-tags/DIFF-new-vs-38.csv` | `4e073fe50c191cc50015d3f15f6a6d28` | 2852 | устаревший (mtime 15:46) — **НЕ трогать** (указание Alex) | +| `~/Downloads/music-tags/milestones/RC-20260927/DIFF-RC-20260927-153717.csv` | — | — | снапшот RC | + +Генератор — `~/Downloads/music-tags/diff_new_vs_38_v2.py` (17:32). + +### 🧩 Состав `DIFF-new-vs-38-v2.csv` (факт, `csv.DictReader`) + +Колонки: `scope, folder, path, field, old_38, new_apply_plan`. +Всего **2854** строки = **2768 правок** + **86 контроль**. +Правки = `ПАПКА` **1501** + `ТРЕК` **1267**. + +| field | строк | что именно | +|---|---:|---| +| `genre` | 911 | снятие `!`-префиксов, `!Radio`→`Radio`, `!Rock&Folk`→`Rock&Folk` | +| `track` | 883 | `#2_04_`→`4`, ведущие нули, суффиксы `-bpm`/`-aQB`/`-1king` | +| `albumartist` | 634 | 🔴 `old_38 = '<КОЛОНКИ НЕ БЫЛО>'` → значение: колонки в 38-м **не существовало** (ср. **D3**) | +| `album` | 164 | скобочный мусор, склейки `Артист - Год`, `Нечетный`→`Нечётный`, заполнение пустых | +| `artist` | 134 | опечатки, регистр, лишние пробелы | +| `title` | 42 | дубли `album == title` | + +Разбивка по scope × field: `ПАПКА` genre 700 · aa 577 · track 76 · album 113 · artist 35 · +`ТРЕК` track 807 · genre 211 · artist 99 · title 42 · albumartist 57 · album 51. + +**Контроль (86):** `ТОЛЬКО-В-38` — файлы `._*.m4a` (AppleDouble) в +`!Pop, Club, Любэ/Infected Mushroom/(2012) Army of Mushrooms/` и не-ID3 форматы. +Не правки, список полноты (ср. D-секцию «служебные секции в хвосте диффа»). + +**Классы правок `album`** (примеры, полный дамп `/tmp/diff_v2_dump.txt`): +`(Авария)`→`Москва пьёт пиво`, `(Философские)`→`Философские`, `(zvezda)`→`zvezda`, +`(Несчастный Случай)`→`Несчастный Случай`, `Бард - Авангард`→`Бард-авангард`, +`Нечетный`→`Нечётный`, `Земфира (2000)`→`Земфира`, `''`→имя подпапки/корня, +сборники `!Retro`/`!Radio` → `Retro` + `Various Artists`. + +> 🔴 **Число 371 ≠ число строк диффа.** `371` — это **строки `APPLY-PLAN.csv`, изменённые +> в сессии 18** (306 + 58 + 12). Дифф показывает **дельту плана против 38-го прогона**: +> 2768 записей, из них `album`+`albumartist` = **798 строк**, `album` = **164**. +> ⚠️ **Не выдавать число ЗАПИСЕЙ диффа за число правок сессии и наоборот** — +> две разные базы счёта (ср. правила счёта в [[personal/tech/id3-tag-repair]] § D-правила). + +### 🛠 Скрипты проверки (созданы в этой сессии, `/tmp/`) + +| Скрипт | Что делает | +|---|---| +| `/tmp/check_diff_v2.py` | заголовок, размер, MD5, разбивка по `scope` | +| `/tmp/check_diff_v2b.py` | матрица `scope × field` (правки/контроль раздельно) | +| `/tmp/dump_diff_v2.py` | полный дамп → `/tmp/diff_v2_dump.txt` (5626 строк до стопа на `head`) | +| `/tmp/dump_diff_v2c.py` | секции `albumartist` / `artist` построчно | + +> 🔴 **ПИТФОЛЛ (повтор D4):** `python3 dump.py 2>&1 | head -160` → `TypeError` в ветке +> контрольной секции **дописался в лог, но файл `/tmp/diff_v2_dump.txt` остался от прошлого +> прогона**. Дамп писать в файл и смотреть файл, а не полагаться на пайп. +> Также: форматирование `None` через `{:12s}` падает — приводить `str(...)` для полей +> контрольной секции (у них `field`/`path` = `None`). + +### 🗂 РАЗБИВКА ДИФФА ПО ПАПКАМ — 633 папки, с числом файлов + +Alex: «**Все папки**» → «**С количеством файлов**». + +Артефакт: **`/tmp/folders_report.txt`** (639 строк, генератор `/tmp/diff_v2_folders2.py`). +Колонки: `FOLDER · fld_edits · files_in_folder · files_changed · album · aa · artist · genre · track · title`. + +**Верхний уровень — ровно 6 папок:** + +``` +!Pop, Club, Любэ +!Retro +!Детские +!Классика +!Рэп +z Другое, Enigma, Ленинград +``` + +Остальные **627 — вложенные** (артист/альбом). `!Rock&Folk` и `!Барды` затронуты +**только через вложенные** — верхнеуровневых строк у них нет. + +**Топ-10 папок по числу файлов:** + +| файлов | edits | папка | +|---:|---:|---| +| 203 | 8 | `!A Not sorted/Sbornik_Novij_Rok` | +| 179 | 44 | `!Retro` | +| 156 | 3 | `!Детские/Детский хит` | +| 97 | 3 | `!Барды/Владимир Высоцкий/Филосовские` | +| 71 | 18 | `!Rock&Folk/Агата Кристи` | +| 69 | 27 | `!Rock&Folk/!rock_o_pop` | +| 69 | 21 | `!Pop, Club, Любэ/!Radio/x 2008-04` | +| 63 | 4 | `!Рэп/НТЛ` | +| 61 | 31 | `!Pop, Club, Любэ/!Radio/z Старое/disk_v2/1 DRIVE` | +| 58 | 29 | `!Pop, Club, Любэ/!Radio/z Старое/disk/1 DRIVE` | + +> 🔴 **ПИТФОЛЛ (счёт файлов по ПАПКА-строке):** ПАПКА-строка диффа разворачивается +> в **ВСЕ файлы папки**, включая те, у которых целевое значение уже совпадает с текущим. +> Это даёт **фантомные файлы**. Сумма по папкам → `8852`, тогда как по плану меняется `7651`, +> а по конкретному полю — см. ниже. **Число файлов из ПАПКА-строки ≠ число реально меняющихся файлов.** + +### 🔴 ОШИБКА СЧЁТА `7963` ПО `genre` — ВСКРЫТА И ПРИЗНАНА + +Alex: «**Чё за нахуй?! В смысле блядь 7963?!!!**» + +**Что я сделал неверно:** в диффе 911 genre-строк, из них **700 — уровня ПАПКА**. +Каждую ПАПКА-строку я развернул во **все файлы папки** и сложил → `7752 + 211 = 7963`. + +**Правильное число по плану — `4359`** (строк `APPLY-PLAN.csv`, где `cur_genre != new_genre`). + +``` +plan rows 7861 +genre differing files 4359 ← правда +albumartist differing 7213 +album differing 2003 +artist differing 381 +track differing 344 +title differing 8 +ANY field differing 7651 +``` + +**Причина расхождения 7752 vs 4359 (3393 фантома):** ПАПКА-строка = «папка целиком меняет genre», +но у части файлов genre **уже равен** целевому — скрипт пишет значение без изменения. +Косвенное подтверждение из плана: `distinct cur_genre` = **156** значений → `new_genre` = **64**; +пустой genre у **2950** файлов (их и заполняют). + +> 🔑 **МЕТОД-УРОК (счёт файлов):** **ПАПКА-строка диффа разворачивается в файлы, которых +> правка не касается.** Для числа «реально меняющихся файлов» считать **по `APPLY-PLAN.csv`** +> (`cur_X != new_X`), а не по диффу. Дифф отвечает на вопрос «что изменилось против 38-го прогона», +> план — «что реально отличается от целевого». **Две разные базы счёта** (ср. §Число 371 ≠ строки диффа). +> Проверочный скрипт: `/tmp/explain_genre.py`. + +### 🧾 Список удаления скобок — ГДЕ ЛЕЖИТ (ответ на «Где список с удалением скобок») + +Alex: «**Где список который ты готовил с удалением скобок**» + +**Два файла, оба mtime 2026-09-27 17:21–17:32:** + +| Файл | MD5 | Строк | Назначение | +|---|---|---:|---| +| ⭐ `~/Downloads/music-tags/ALBUM-FIX-TABLE.csv` | `1597f2716ac5a2881efabaec13755d4c` | **306** | читаемая таблица: `folder, cur_album, new_album, path, title` | +| `~/Downloads/music-tags/ALBUM-FIX-306.csv` | `49ba645462964cc299da7dcffb55ca09` | **306** | + `new_album_was` / `new_album_now` (история уточнения значения) | + +**Факт (`csv.DictReader`):** `cur_album` начинается с `(` — **306 из 306**, значение **целиком +в скобках** — **306 из 306**, уникальных путей **306**, уникальных пар «папка+было→стало» — **17**. + +**17 пар (полный список):** + +| папка | было | стало | +|---|---|---| +| `!Барды/Владимир Высоцкий` | `(Высоцкий)` | `Высоцкий` | +| `!Барды/Владимир Высоцкий/Морские` | `(Морские)` | `Морские` | +| `!Барды/Владимир Высоцкий/Филосовские` | `(Философские)` | `Философские` | +| `!Барды/Владимир Высоцкий/Спортивные` | `(Спортивные)` | `Спортивные` | +| `!Барды/Владимир Высоцкий/Блатные` | `(Блатные)` | `Блатные` | +| `!Барды/Владимир Высоцкий/Разное` | `(Высоцкий - Разное)` | `Высоцкий - Разное` | +| `!Барды/Владимир Высоцкий/Из сказок` | `(Из Сказок)` | `Из Сказок` | +| `!Retro` | `(Авария)` | `Авария` | +| `!Retro` | `(Вилли Токарев)` | `Вилли Токарев` | +| `!Рэп/Птаха - Красивые, богатые (2009)` | `(prod. Shorty)` | `prod. Shorty` | +| `!Рэп/Вилли Токарев` | `(Вилли Токарев)` | `Вилли Токарев` | +| `!Pop, Club, Любэ/Дискотека Авария` | `(Авария)` | `Авария` | +| `!Pop, Club, Любэ/Дискотека Авария/Старые хиты` | `(Авария)` | `Авария` | +| `!Pop, Club, Любэ/Дискотека Авария/Москва пьёт пиво` | `(Авария)` | `Авария` | +| `!Rock&Folk/Несчастный Случай` | `(Несчастный Случай)` | `Несчастный Случай` | +| `!Rock&Folk/NONSONS - zvezda` | `(zvezda)` | `zvezda` | + +> ⚠️ **Важно для читателя списка:** значения в `ALBUM-FIX-TABLE.csv` — **промежуточные** +> (снятие скобок механически). По **финальным правилам** (§ФИНАЛЬНЫЕ РЕШЕНИЯ ПО 306 СТРОКАМ) +> в корне папки артиста `album` = **имя папки артиста**, а в подпапке — **имя подпапки**. +> Поэтому `(Авария)` → `Москва пьёт пиво`, а не `Авария`; `(Высоцкий)` → `Дискотека Авария` +> в одноимённой папке. Отличие `ALBUM-FIX-TABLE.csv` от `ALBUM-FIX-306.csv` фиксирует +> ровно эту правку уточнения (`new_album_was` → `new_album_now`). +> Проверочный скрипт: `/tmp/verify_alb_fix.py`. + +--- + ## 🟣 СЕССИЯ 18 (2026-09-27) — NAVIDROME РАЗВЁРНУТ, ДЕФЕКТ-КЛАСС ВСКРЫТ, ПРАВИЛА ВОССТАНОВЛЕНЫ ИЗ ZULIP, 306 СТРОК ИСПРАВЛЕНЫ, НОВЫЙ ДИФФ ГОТОВ > **СТАТУС СЕССИИ 18 (ФИНАЛ):** Navidrome ✅ развёрнут и работает (см. ниже + diff --git a/personal/tech/id3-tag-repair.md b/personal/tech/id3-tag-repair.md index bb895b96..6ddc5da5 100755 --- a/personal/tech/id3-tag-repair.md +++ b/personal/tech/id3-tag-repair.md @@ -72,8 +72,13 @@ related: > ⚠️ **Обновлён до v2.1.0 (сессия 17):** **A7** (склейка `Родитель - Папка` и технический префикс > в `album`; 🔴 разделитель бывает ТРЁХ видов — `-`/`–`/`—`, матчить все) · **A8** (в контроле -> полноты «ПОТЕРЯНО: 881» = смена ФОРМЫ записи при схлопывании D16, реальных потерь 0 — -> сверять НОВОЕ значение, а не пару `old,new`). Полные формулировки — ниже. +> полноты «ПОТЕРЯНО: 881» = смена ФОРМЫ записи при схлопывании D16, реальных потерь 0 — сверять +> НОВОЕ значение, а не пару `old,new`). Полные формулировки — ниже. + +> ⚠️ **Обновлён (сессия 18-доп):** правила **D24** (число ЗАПИСЕЙ диффа ≠ число ПРАВОК: +> три базы счёта `7861 / 371 / 2768` нельзя смешивать) и **D25** (`albumartist` в диффе почти +> весь — из-за ПОЯВЛЕНИЯ колонки, `old_38='<КОЛОНКИ НЕ БЫЛО>'`, ср. D3). Инвентарь +> актуального диффа — `personal/plans/music-artist-albumartist-repair.md` §СЕССИЯ 18-ДОП. ## 📁 Артефакты проекта @@ -1312,7 +1317,32 @@ Alex дал ответы на все четыре вопроса аудита с отсутствие лучшего источника данных. «Нашёл дефект» без проверки фактов = ложная тревога, которая стоит сессию времени. -## ⚪ Что НЕ является багом +### 🔴 D24 — число ЗАПИСЕЙ диффа ≠ число ПРАВОК; три базы счёта нельзя смешивать + +Три разных числа описывают «сколько изменится», и их нельзя путать в отчёте: + +| База счёта | Число | Что это | +|---|---:|---| +| Строк в `APPLY-PLAN.csv` | 7861 | вся библиотека | +| Строк плана, изменённых в сессии | **371** (306+58+12) | «мои правки этого дня» | +| **Записей в диффе** (`DIFF-new-vs-38-v2.csv`) | **2768** | дельта плана против 38-го прогона | + +Alex спрашивает «где диф с 371 правкой», а в диффе **371 нет вообще**. Если ответить +«371 — это не число правок» — он читает как «не услышал» (см. правила счёта в этом же доке). +**Правильная формулировка:** «371 строка плана изменена в этой сессии; в диффе 2768 записей, +из них `album` 164 и `albumartist` 634». + +### 🔴 D25 — `albumartist` в диффе почти весь — из-за ПОЯВЛЕНИЯ колонки + +В `DIFF-new-vs-38-v2.csv` из 2768 записей `albumartist` = **634**, и у подавляющего большинства +`old_38 = '<КОЛОНКИ НЕ БЫЛО>'`. Это прямое следствие **D3**: колонки в 38-м прогоне не +существовало, значит тег **впервые будет записан** — и это правка, а не перенос данных. +Не путать с реальной сменой значения (`old` = конкретное имя артиста → новое). + +Прочие поля того же диффа (для калибровки масштаба): `genre` 911 · `track` 883 · `album` 164 · +`artist` 134 · `title` 42. Разбивка правок: `ПАПКА` 1501 + `ТРЕК` 1267 = 2768. + +### ⚪ Что НЕ является багом - **Рукописная транслитерация:** `КняZz`, `EMINЭM`, `Flёur`, `изBESTняк`, `Скаzки`, `Глюкоzа`, `Cо`, `WWWаленки`, `Закoлебал` — оригинальное написание 2000-х / реальные псевдонимы.