From d77aec354846d8db67e4726a24b788016100d9f3 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sun, 27 Sep 2026 20:13:53 +0600 Subject: [PATCH] [2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/tech/batch-data-edit-review-loop.md personal/tech/music-tags-repair-truenas.md --- .../plans/music-artist-albumartist-repair.md | 113 +++++++++++++++++- personal/tech/batch-data-edit-review-loop.md | 53 ++++++++ personal/tech/music-tags-repair-truenas.md | 18 ++- 3 files changed, 182 insertions(+), 2 deletions(-) diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index 709517cb..576ae0ae 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: session29bis-DIFF-rule-album-v1-170-DIFF-rule-vs-BASE-13-rashozhdeniy-AWAITING-ALEX +status: session29ter-APPLY-PLAN-=-RC-RC-verify-7861-rashozhdeniy-genre-7861-track-1699-pokrytie-polnoe-AWAITING-ALEX tags: - music - metadata @@ -288,6 +288,117 @@ leaf='!Классика' artist='Фредерик Шопен' aa='Various --- +## 🔴 СЕССИЯ 29-ТЕР (2026-09-27) — ПЕРЕОСМЫСЛЕНИЕ: `APPLY-PLAN.csv` = RC, СВЕРКА `new_*` vs БД + +> **ТРИГГЕР:** «**стоп блядь. полный назад. возвращаемся блядь к исходной.**» → +> «**APPLY-PLAN.csv — это RC. Канон. источник истины для прогона на 7496 файлов. так?**» → +> «**cur в нем очевидно не то что сейчас, а то что было до прогона. так?**» → +> «**теперь сними разницу с базы navidrome и этого списка. должно быть 0 различий +> с new_ полем. так?**» + +### 🔑 ГЛАВНОЕ ОПРЕДЕЛЕНИЕ СЕССИИ (принято Alex, вернуться при любом сомнении) + +**`APPLY-PLAN.csv` = RC (release candidate). Канон. Источник истины.** +Прогон — на **7496 файлов**. + +| Поле в плане | Что означает | +|---|---| +| `new_*` | **канон** — то, что ДОЛЖНО быть в файлах | +| `cur_*` | **снимок ДО прогона**, НЕ текущее состояние | + +**🔴 `cur_*` после прогона УСТАРЕЛ.** Сверять `cur_*` с базой после прогона = сверять +«до» с «после» — они обязаны расходиться. Именно поэтому `new_album == cur_album` +в ~100% строк: генератор писал `cur` как есть, а не правил. + +**Отсюда корректная проверка (и ответ на вопрос Alex «должно быть 0 различий?» — ДА):** +сверять `new_title/new_artist/new_albumartist/new_album/new_genre/new_track` из плана +против `title/artist/album_artist/album/genre/track_number` в БД Navidrome. Различий — 0. +**`cur_*` в сверке НЕ участвует.** + +### 📊 ФАКТ: RC-СВЕРКА ДАЛА 7861 РАЗЛИЧИЕ ИЗ 7861 — НОЛЬ НЕ СОШЁЛСЯ + +Скрипт `/tmp/rc_verify.py` → `~/Downloads/music-tags/RC-VERIFY.csv` + +| Поле | Файлов с различием | Характер | +|---|---:|---| +| 🔴 `genre` | **7861 (ВСЕ)** | БД=`''` → ПЛАН=`Classical` и т.п. | +| 🔴 `track` | **1699** | БД=`0` → ПЛАН=`''` | +| `album` | 50 | БД=значение → ПЛАН=другое | +| `albumartist` | 9 | БД=значение → ПЛАН=другое | +| ✅ `title` | **0** | совпало полностью | +| ✅ `artist` | **0** | совпало полностью | + +``` +СОВПАЛО ПОЛНОСТЬЮ (все 6 полей): 0 +ФАЙЛОВ С РАЗЛИЧИЯМИ: 7861 +НЕТ В БАЗЕ: 0 +``` + +**Вывод:** по `title`/`artist` прогон сошёлся. По `genre` (все 7861) и `track` (1699) — НЕТ. +Две гипотезы, требуют проверки: либо прогон `genre` не писал (а в плане он появился +ПОСЛЕ прогона), либо база не перечитала теги. + +### 🔴 ПИТФОЛЛ: ИМЯ КОЛОНКИ В БД — `track_number`, НЕ `track` + +`sqlite3 … "SELECT track FROM media_file"` → `OperationalError: no such column: track`. +Правильные имена: `title`, `artist`, `album_artist`, `album`, `genre`, **`track_number`**. +`track_number` возвращается как **int** → `str(v).strip()`, иначе `AttributeError`. +`track_number = 0` в Навидроме означает «номер не проставлен» — сравнивать с пустой +строкой из плана нужно с учётом этого, иначе 1699 ложных расхождений. + +### ✅ ВОПРОС ALEX: «ПОЧЕМУ ЧЁРНОГО КОТА НЕТ В CSV — ТОЛЬКО БРАВО?» + +**Ответ: каждый трек ЕСТЬ. Покрытие полное.** + +Скрипт `/tmp/gap_db_plan.py` (двусторонний gap-анализ план ↔ БД): + +| Метрика | Значение | +|---|---:| +| БД без `backup` | **7861** | +| PLAN строк / уникальных путей | 7861 / **7861** | +| **ЕСТЬ В БД, НЕТ В ПЛАНЕ** | **0** | +| **ЕСТЬ В ПЛАНЕ, НЕТ В БД** | **0** | + +Артефакты: `GAP-db-not-in-plan.csv`, `GAP-plan-not-in-db.csv` (оба пусты). + +**«Чёрных котов» в библиотеке ТРИ, и все три в плане:** + +| Путь | class | `cur_album` | `new_album` | +|---|---|---|---| +| `!Retro/БРАВО - ЧЕРНЫЙ КОТ.mp3` | СБОРНИК | — | `Retro` | +| `!Rock&Folk/Браво/Чёрный кот.mp3` | АЛЬБОМ | `[muzmo.ru]` | **`Браво`** (правит) | +| `!Retro/Тамара Миансарова/Чёрный кот.mp3` | АЛЬБОМ | `Храбрый Зайчик. Песни для детей` | **`Храбрый Зайчик. Песни для детей`** (НЕ правит ⛔) | + +**🔑 ПИТФОЛЛ ПОИСКА: «Черный кот» vs «Чёрный кот».** Все три файла пишутся через **«ё»**. +`grep -ci "черный кот"` даёт **0** и создаёт ложное впечатление, что файла нет. +**Правило: искать и через `ё`, и через `е`, и без гласной вообще** (по «рный кот»). +Строк с «Браво» в плане — **6**, не 1; «только Браво» — ошибка поиска, не дефект CSV. + +**🔴 ДЕФЕКТ ПЛАНА, вскрытый этим вопросом (Миансарова):** +`cur_album = Храбрый Зайчик. Песни для детей` (мусор), план его **не правит** (`new == cur`), +хотя правило корня требует `Тамара Миансарова`. Это ровно тот класс, из-за которого +диффа «план vs база» мусор не ловит: **`new_album == cur_album` → попадает в «нет изменений»**. +**Ждёт решения Alex:** править `new_album` Миансаровой на `Тамара Миансарова`? + +### 🛠 СКРИПТЫ ЭТОГО БЛОКА + +``` +/tmp/rc_verify.py ⭐ RC-сверка: new_* плана vs БД → RC-VERIFY.csv +/tmp/gap_db_plan.py ⭐ двусторонний gap план↔БД → GAP-*.csv (оба пусты = покрытие полное) +/tmp/why_13.py 🔍 диагностика 13 расхождений правила +~/Downloads/music-tags/RC-VERIFY.csv 7861 расхождение (genre 7861 · track 1699 · album 50 · aa 9) +~/Downloads/music-tags/GAP-db-not-in-plan.csv ПУСТ +~/Downloads/music-tags/GAP-plan-not-in-db.csv ПУСТ +``` + +### ⏳ СТАТУС БЛОКА + +**Правок не было.** БД читалась `mode=ro`. Файлы на NAS не тронуты. +**Открыто:** (1) причина 7861 расхождения по `genre` и 1699 по `track` — прогон или база; +(2) дефект №4 (развёртывание ПАПКА-записи); (3) дефект №5 (`nartist in nleaf`); (4) Миансарова. + +--- + ## 🔴 СЕССИЯ 28 (2026-09-27, ночь) — ДЫРА В ДЕТЕКТОРЕ ПРАВИЛА ЗАКРЫТА: 225 → 1133 ФАЙЛА > **ТРИГГЕР:** «**диф гре**» → после показа `RULE-CHECK.csv` (225 строк) выяснилось, что diff --git a/personal/tech/batch-data-edit-review-loop.md b/personal/tech/batch-data-edit-review-loop.md index f10ecbed..87157587 100755 --- a/personal/tech/batch-data-edit-review-loop.md +++ b/personal/tech/batch-data-edit-review-loop.md @@ -361,6 +361,59 @@ album = 'misc' (имя подпапки — как есть) Худший вариант — **«готово» при отсутствии изменений**. Перед словом «готово» обязательна команда, доказывающая факт изменения (`md5` до/после, счётчик). +### 29. 🔴🔴 ИСКАТЬ ГОТОВЫЙ СКРИПТ ПРЕЖДЕ ЧЕМ ПИСАТЬ НОВЫЙ — `grep -l` обязателен + +Скрипты копятся в `/tmp/*.py` и рабочей папке проекта, и **нужный почти всегда уже есть** — +вместе с выстраданными правилами (списки исключений, нормализация, схлопывание). + +Кейс сессии 29-бис: написаны `diff_new_vs_38_v5.py` / `_v6.py` **вместо** существующих +`/tmp/make_diff_rule.py` + `/tmp/rule_check_v2.py`, которые делали ровно эту задачу и **уже +содержали исключение сборников** (`Sbornik_*` / `Сборник*`). Переписывание с нуля: +потеряно схлопывание (**16417 строк вместо 1674**) и повторно сломан `Sbornik_Novij_Rok` +(имя папки ушло в `album` на **200 файлов**). Alex: «**какого хуя ты по кругу мне предлагаешь +обратно то это менять?!**». + +**Правило: перед написанием любого скрипта —** +```bash +grep -l "<ключевое понятие>" /tmp/*.py ~/Downloads/<проект>/*.py +``` + +### 30. 🔴 `ё` и `е` — РАЗНЫЕ СИМВОЛЫ: `grep` с `е` НЕ НАХОДИТ значение с `ё` + +`grep -ci "черный кот"` вернул **0**, хотя в датасете лежали три файла `Чёрный кот.mp3`. +Из-за поиска сделан вывод «строки нет в плане вообще» — **ответ пользователю был неверным +не по данным, а по поиску**. + +**Протокол поиска кириллического значения:** сначала с `ё`, затем с `е`, затем по фрагменту +**без спорной гласной** (`"рный кот"`), затем `grep -i` по всему полю. +Относится к любому поиску имён/текста — артисты, альбомы, заголовки, папки. + +### 31. 🔴🔑 СНАЧАЛА УСТАНОВИТЬ, ЧТО ЕСТЬ КАНОН — диффать не тот набор колонок + +Диф между неверной парой колонок даёт **уверенный, правдоподобный и полностью неверный** отчёт. +Канонная рамка этого проекта (слова Alex, подтверждать при любом сомнении): + +**`APPLY-PLAN.csv` = RC. Канон. Источник истины. Прогон на 7496 файлов.** + +| Поле | Смысл | +|---|---| +| `new_*` | **канон** — что ДОЛЖНО быть в файлах | +| `cur_*` | **снимок ДО прогона**; после прогона УСТАРЕЛ | + +Отсюда верная проверка — `new_*` плана vs живая БД, ожидание **0 различий**. +Диффать `cur_*` против БД бессмысленно: различия гарантированы и ничего не доказывают. +**В каждом отчёте называть пару полей, которые сравниваешь.** Факт этой сессии: +RC-сверка (`/tmp/rc_verify.py` → `RC-VERIFY.csv`) дала **7861 расхождение из 7861** — +`title`/`artist` сошлись ✅, но `genre` разошёлся на **ВСЕХ 7861** и `track` на 1699. + +### 32. 🔴 Колонка `track` в БД Navidrome называется `track_number` + +`SELECT track FROM media_file` → `OperationalError: no such column: track`. +Верные имена: `title`, `artist`, `album_artist`, `album`, `genre`, **`track_number`**. +Возвращается **int** → `str(v).strip()`, иначе `AttributeError`. +`track_number = 0` = «номер не проставлен» — сравнивать с пустым значением из плана +с учётом этого, иначе 1699 ложных расхождений. + ## Связанное - [[personal/tech/music-tags-repair-truenas]] — волновой лог канонического инстанса diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index c37505fc..5f6708b6 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -1,7 +1,7 @@ --- title: Music Tags Repair — TrueNAS 7906 → 7905 файлов created: '2026-09-26' -updated: '2026-09-26' +updated: '2026-09-27' type: tech namespace: personal wave: 38 @@ -63,6 +63,22 @@ related: > 13 расхождений → дефект №4 (мой: ПАПКА-запись разворачивалась на всю папку из БД) + > дефект №5 (правило: `nartist in nleaf` ловит `50 Cent` внутри папки `Eminem,50cent`). > Полный разбор — [[personal/plans/music-artist-albumartist-repair]] § СЕССИЯ 29-БИС. +> +> 🔑🔑 **ОПРЕДЕЛЕНИЕ СЕССИИ 29-ТЕР (главное, читать при любом сомнении):** +> **`APPLY-PLAN.csv` = RC. Канон. Источник истины. Прогон на 7496 файлов.** +> `new_*` = канон (что ДОЛЖНО быть). **`cur_*` = снимок ДО прогона, после прогона УСТАРЕЛ.** +> Сверять `cur_*` с базой после прогона НЕЛЬЗЯ — они обязаны расходиться. +> Корректная проверка: `new_*` плана vs БД → должно быть **0 различий**. +> **ФАКТ: RC-сверка (`/tmp/rc_verify.py` → `RC-VERIFY.csv`) дала 7861 расхождение из 7861** +> — `title`/`artist` сошлись ✅, но 🔴 **`genre` разошёлся на ВСЕХ 7861** (БД пусто) и +> `track` на 1699 (БД `0`). Причина не выяснена: прогон не писал `genre`, либо база не +> перечитала теги. Разбор — § СЕССИЯ 29-ТЕР. +> ✅ **Покрытие план↔БД ПОЛНОЕ:** 7861 = 7861, `GAP-db-not-in-plan.csv` и +> `GAP-plan-not-in-db.csv` **оба пусты** (`/tmp/gap_db_plan.py`). +> 🔑 **ПИТФОЛЛ: «Чёрный кот» пишется через Ё** — `grep "черный кот"` даёт 0 и создаёт +> ложное «файла нет». Правило: искать и через `ё`, и через `е`, и без гласной. +> 🔴 **Питфолл БД:** колонка — **`track_number`**, не `track`; возвращается **int**; +> `0` = «номер не проставлен». > ✅ Класс **«`albumartist` размножен от чужого файла в корне папки»** — ИСПРАВЛЕН (7 папок, 40 строк). > ✅ Класс **«`Various Artists` поверх одного артиста»** — ВЫЧИЩЕН ПОЛНОСТЬЮ > (`find_va_misuse.py` → `ПАПОК-КАНДИДАТОВ: 0`). Разбор классов **A1-A4** —