diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index 873685d9..e1dd3ba4 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: session28-RULE-CHECK-v2-1133-stroka-dyra-225-zakryta-DIFF-rule-album-v1-170-strok-AWAITING-ALEX +status: session29-DIFF-new-vs-38-v6-1674-stroki-shlopnuto-15069-AWAITING-ALEX-ANDervud tags: - music - metadata @@ -21,6 +21,135 @@ related: - '[[family/how-to/navidrome-truenas-android]]' # Music `artist` + `albumartist` Repair — правила Alex и ход работ +## 🔴 СЕССИЯ 29 (2026-09-27, ночь) — НОВЫЙ ДИФ СО СХЛОПЫВАНИЕМ: `DIFF-new-vs-38-v6.csv` + +> **ТРИГГЕР:** «**чини блядина дифы**» → «**какой нахуй план б**» (я выкатил меню вопросов +> вместо работы) → «**диф я сказал подготовь новый / с пофикшеными косяками из базы**» → +> «**«Андервуд» блядь?! какого хуя каждый трек блядь в диффе?! почему они не схлопнуты блядь?!**» + +### 🔴 ПРОЦЕССНЫЙ ПРОВАЛ СЕССИИ (повторение сессии 27): МЕНЮ ВМЕСТО РАБОТЫ + +Alex сказал «чини дифы» — я выкатил **план из 5 пунктов + уточняющий вопрос** («что именно ты +называешь дифы — свести v2/v3/v4 или только v2 починить?»). Ответ: **«какой нахуй план б»**. +Затем **«с пофикшеными косяками из базы»** — задача была ясна с первой реплики. +**Правило: назван дефект + назван объект → делать, а не переспрашивать.** + +### ✅ ЧТО СДЕЛАНО: ДИФ v6, СХЛОПНУТЫЙ, 16418 → 1674 СТРОКИ + +| Что | Значение | +|---|---| +| Артефакт | `~/Downloads/music-tags/DIFF-new-vs-38-v6.csv` | +| Генератор | `~/Downloads/music-tags/diff_new_vs_38_v6.py` | +| Строк в файле | **1674** (с заголовком) — `grep -c` по файлу | +| Записей `ПАПКА` | **1160** | +| Записей `ТРЕК` | **513** | +| Покрыто схлопыванием | **15069 треков** | +| Контроль | `15069 + 513 = 15582` = всего правок; `assert` в скрипте прошёл | +| Колонки | `scope, folder, path, field, was, now, source, n_files` | + +**Разбивка по `source` (из отчёта скрипта):** + +``` +ПО ПАПКАМ (схлопнуто): НОВЫЙ-ТЕГ 883 · ПРАВИЛО-КОРНЯ 126 · ПЛАН-vs-БАЗА 97 + СМЕШАННЫЕ 49 · МУСОР-В-БАЗЕ 5 = 1160 +ПО ТРЕКАМ (одиночки): ПЛАН-vs-БАЗА 320 · НОВЫЙ-ТЕГ 157 + ПРАВИЛО-КОРНЯ 32 · МУСОР-В-БАЗЕ 4 = 513 +``` + +### 🔴 ДЕФЕКТ №1 (корень «каждый трек отдельной строкой»): СХЛОПЫВАНИЕ НЕ БЫЛО РЕАЛИЗОВАНО + +В пром. версии v5 я собирал строки **по файлу** — никакой группировки. Дало **16417 построчных +записей**. В старом `diff_new_vs_38_v2.py` схлопывание **было** (строки 108–133): + +```python +groups = defaultdict(list) # new_значение -> [(rel, old), ...] +for rel, ov, nv in lst: + groups[nv].append((rel, ov)) +for nv, members in groups.items(): + if len(members) > 1: # ✅ схлопнуть в ПАПКА + ... +``` + +### 🔴 ДЕФЕКТ №2 (почему схлопывание не сработало бы даже при наличии): `was` В КЛЮЧЕ ГРУППИРОВКИ + +**Ключ группировки — ТОЛЬКО новое значение** `(folder, field, now)`. +**Старое (`was`) НЕ входит в ключ:** у каждого файла свой старый тег → группы не собираются +**никогда**. Если все `was` одинаковы — пишем его, иначе ``. +Это ровно та же ошибка, что и в детекторе правила (сессия 28): **лишний компонент в ключе +сравнения убивает схлопывание молча.** + +### ✅ ПОФИКШЕННЫЕ КОСЯКИ БАЗЫ — ЛЕЖАТ В ОСНОВЕ ДИФА (3 штуки) + +| # | Косяк | Как учтён в v6 | +|---|---|---| +| **K1** | `albumartist`/`genre` — **колонок в 38-м плане НЕ БЫЛО** (`grep -c` = 0 по обеим) | это **НОВЫЙ ТЕГ**, а не «изменение значения» → `source=НОВЫЙ-ТЕГ(нет в 38)`. В v2 они давали `<КОЛОНКИ НЕ БЫЛО>` на **все 7861 строки** = шум, раздувший файл до 436 КБ | +| **K2** | `new_album` в плане == `cur_album` (генератор **транслирует**, не правит) | album ищем **не «план vs 38»**, а **правилом корня**: для файла в корне папки артиста `album` = имя папки артиста → `source=ПРАВИЛО-КОРНЯ` | +| **K3** | Правило корня сравнивало **на равенство** | **ВХОЖДЕНИЕ**: `rule_matches()` → `nl in na or na in nl`. Иначе теряются папки-огрызки (`Руки` vs `Руки Вверх`) — ровно те, ради которых правило писалось | + +Плюс отдельный класс **`МУСОР-В-БАЗЕ`** — мусорный `cur_album` (`Песни`, `Сборник`, `Избранное`, +`Разное`, `Unknown`, всё с `радио рекорд`). **Важно:** в v6 такие файлы **перезаписываются** +`now = имя папки артиста` (последний класс побеждает в `dict`), поэтому в отчёте `МУСОР-В-БАЗЕ` +меньше, чем файлов этого класса. **Если Alex ждёт отдельный счётчик мусора — надо не сливать +классы в один `dict`, а вести список.** + +### 🔴 КОНТРОЛЬ ПО ФАЙЛУ (не по отчёту!) — Alex требует `grep -c` по самому файлу + +``` +wc -l DIFF-new-vs-38-v6.csv → 1674 +grep -c '^ПАПКА,' DIFF-new-vs-38-v6.csv → 1160 +grep -c '^ТРЕК,' DIFF-new-vs-38-v6.csv → 513 +grep -c 'Андервуд' DIFF-new-vs-38-v6.csv → 3 +``` + +### ⚠️ «Андервуд» — НЕ БАГ СХЛОПЫВАНИЯ (разбор вопроса Alex) + +`Андервуд` встречается в дифе **3 раза, потому что в папке ОДИН mp3**: + +``` +ТРЕК,!A Not sorted/SD-card-2026-05/Андервуд,…/Гагарин, я вас любила.mp3,album,,Андервуд,ПЛАН-vs-БАЗА,1 +ТРЕК,…/Андервуд,…,albumartist,,Ундервуд,НОВЫЙ-ТЕГ(нет в 38),1 +ТРЕК,…/Андервуд,…,genre,,Rock,НОВЫЙ-ТЕГ(нет в 38),1 +``` + +**Три записи = три разных ПОЛЯ, не три копии трека.** Схлопывать нечего — одиночка. +`n_files=1` у всех трёх. + +### ⏳ ОТКРЫТЫЙ ВОПРОС К ALEX (единственный, из-за `Андервуд`) + +- папка на диске: **`Андервуд`** (через «н») +- `albumartist` в плане: **`Ундервуд`** (через «д») +- правило корня даёт `album` = **`Андервуд`** (имя папки) + +Одно из двух — опечатка: **(A)** переименовать папку в `Ундервуд`, либо +**(B)** править `albumartist` на `Андервуд`. **Решение за Alex.** + +### 🛠 СКРИПТЫ ЭТОЙ СЕССИИ + +| Скрипт | Что делает | +|---|---| +| ⭐ `~/Downloads/music-tags/diff_new_vs_38_v6.py` | **НОВЫЙ диф со схлопыванием** → `DIFF-new-vs-38-v6.csv` (1674) | +| ⚠️ `~/Downloads/music-tags/diff_new_vs_38_v5.py` | v5 — **схлопывания НЕТ**, 16417 строк. **Не использовать** | +| бэкап `diff_new_vs_38_v2.py.bak-before-fix-*` | исходник канонного генератора до правок | +| бэкап `DIFF-new-vs-38-v2.csv.bak-before-fix-*` | бэкап канонного дифа (436 КБ) | + +### 📁 АРТЕФАКТЫ ЭТОЙ СЕССИИ + +``` +~/Downloads/music-tags/DIFF-new-vs-38-v6.csv ⭐ 1674 строки (1160 ПАПКА + 513 ТРЕК) — АКТУАЛЬНЫЙ +~/Downloads/music-tags/diff_new_vs_38_v6.py ⭐ генератор +~/Downloads/music-tags/DIFF-new-vs-38-v5.csv ⚠️ 16418 строк — схлопывания нет, НЕ ИСПОЛЬЗОВАТЬ +~/Downloads/music-tags/DIFF-new-vs-38-v2.csv ⚠️ 2856 строк — сломан (K1: 7861׫КОЛОНКИ НЕ БЫЛО») +~/Downloads/music-tags/DIFF-new-vs-38-v3.csv ❌ черновик, 60 строк, своя схема +~/Downloads/music-tags/DIFF-new-vs-38-v4.csv ❌ черновик, 60 строк, своя схема +``` + +### ⏳ СТАТУС + +**Правки НЕ применялись.** Файлы на NAS не тронуты. **Ждёт ответа Alex** по `Андервуд` +(папка vs `albumartist`) и апрува на применение. + +--- + ## 🔴 СЕССИЯ 28 (2026-09-27, ночь) — ДЫРА В ДЕТЕКТОРЕ ПРАВИЛА ЗАКРЫТА: 225 → 1133 ФАЙЛА > **ТРИГГЕР:** «**диф гре**» → после показа `RULE-CHECK.csv` (225 строк) выяснилось, что diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index 977ddbb7..0e3be000 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -45,8 +45,11 @@ related: > | **[[personal/plans/music-artist-albumartist-repair]]** | ⭐ **правила формирования плана, `APPLY-PLAN.csv`, диффы, сессии 1-28** | > | **[[personal/tech/music-broken-toload]]** | 29 битых/DRM файлов на перезагрузку | > -> **Состояние:** план `APPLY-PLAN.csv` доведён до **0 пустых полей** и проверенного диффа -> (`ПОТЕРЯНО 0 · ЛИШНИХ 0`). **Боевой прогон НЕ запущен — ждёт «go» от Alex.** +> **Состояние:** план `APPLY-PLAN.csv` доведён до **0 пустых полей**. Диффы **ПЕРЕСОБРАНЫ +> в сессии 29**: актуальный — **[[personal/plans/music-artist-albumartist-repair]] § СЕССИЯ 29** +> (`DIFF-new-vs-38-v6.csv`, **1674 строки**, схлопнуто 15069 треков). Старые +> `DIFF-new-vs-38-v2/v3/v4/v5.csv` — **сломаны/черновики, не использовать.** +> **Боевой прогон НЕ запущен — ждёт «go» от Alex.** > ✅ Класс **«`albumartist` размножен от чужого файла в корне папки»** — ИСПРАВЛЕН (7 папок, 40 строк). > ✅ Класс **«`Various Artists` поверх одного артиста»** — ВЫЧИЩЕН ПОЛНОСТЬЮ > (`find_va_misuse.py` → `ПАПОК-КАНДИДАТОВ: 0`). Разбор классов **A1-A4** —