From 2520fda13b4b6e110972f82f8176b6be2903020c Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sun, 27 Sep 2026 14:19:15 +0600 Subject: [PATCH] [2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md personal/tech/id3-tag-repair.md personal/tech/music-tags-repair-truenas.md personal/tech/scripts/find_aa_hijack.py personal/tech/scripts/find_va_misuse.py --- .../plans/music-artist-albumartist-repair.md | 172 +++++++++++++++++- personal/plans/tagwriter-container.md | 13 +- personal/tech/id3-tag-repair.md | 145 +++++++++++++++ personal/tech/music-tags-repair-truenas.md | 13 +- personal/tech/scripts/find_aa_hijack.py | 32 ++-- personal/tech/scripts/find_va_misuse.py | 31 ++-- 6 files changed, 359 insertions(+), 47 deletions(-) diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index a4bc116c..f0d92b0e 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: session8-done-va-over-single-artist-fixed-5-pending-alex +status: session9-done-va-class-cleared-category-root-rolled-back tags: - music - metadata @@ -23,6 +23,36 @@ related: # Music `artist` + `albumartist` Repair — правила Alex и ход работ +> ## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 9 (2026-09-27) +> +> ``` +> APPLY-PLAN.csv 7861 строка md5 82492a0b2b93dbb6c7444a4040a21a03 +> DIFF-new-vs-38.csv 4946 строк md5 dd67d4fe64bc6365c478a0831cda634c +> tag-fix-plan.csv не тронут (база 38-го, источник истины для диффа) +> ``` +> +> **Пустых полей в плане — 0.** Дифф проверен: `ПОТЕРЯНО 0 · ЛИШНИХ 0 · ПОТЕРЬ НЕТ, дифф ПОЛНЫЙ`. +> +> ✅ **Второй класс `Various Artists` ДОЧИЩЕН полностью** (99 строк): `!Рэп/НТЛ` (63) + +> `!Рэп/НТЛ/2003 - Выход` (17) → `НТЛ`, `Ария, Кипелов` (12) → `Ария`, `Тест Сабвуфера` (4) → +> `Тест Сабвуфера`. Детектор `find_va_misuse.py` теперь даёт **`ПАПОК-КАНДИДАТОВ: 0`**. +> +> ✅ **`z Другое…` корень — ОТКАЧЕНО по требованию Alex.** Было `Various Artists`, стало +> **собственный `artist` файла** (`Красная Плесень` ×2, `ВИА МЕА` ×1). Корень папки-категории +> альбомом **не считается** — см. правило **A4** в `[[personal/tech/id3-tag-repair]]`. +> +> ✅ **`Би-2/Нечётный Воин` — `artist` вытащен из имён файлов** (46 строк): заглушки +> `Нечетный Воин` / `Нечётный воин 2` → реальные исполнители (`Би-2 + Агата кристи`, +> `Лакмус + Александр Ф.Скляр`, …), `albumartist` → `Би-2`. См. раздел «🔷 СЕССИЯ 9». +> +> ⚠️ Все 5 папок из сессии 8 закрыты: `!Рэп/НТЛ`, `Ария, Кипелов`, `Тест Сабвуфера`, +> `z Другое…` корень — решены; правок в ожидании нет. +> +> **Суммарно за сессии 7-9 исправлено 313 строк** по четырём классам (см. A1/A2/A3/A4 в tech-доке). +> +> ⏳ **Открыто:** `album` файлов `Би-2/Нечётный Воин/*` (`Нечетный Воин (CD1)`, `Медленная Звезда`, +> `Нечётный воин 2`) — вопрос задан, ответа нет, **не трогать**. +> > ## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 8 (2026-09-27) > > ``` @@ -40,7 +70,7 @@ related: > > ⏳ **Детектор `find_va_misuse.py` нашёл 5 папок того же класса — ждут решения Alex:** > `!Рэп/НТЛ` (75), `!Rock&Folk/Ария, Кипелов` (8/12), `z Другое…/Тест Сабвуфера` (3/4), -> `z Другое…` корень (2/3). +> `z Другое…` корень (2/3). → **ЗАКРЫТО в сессии 9, см. выше.** > > ## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 7 (2026-09-27) > @@ -2887,6 +2917,144 @@ z Другое… (корень) 2/3 artist='К 2. Повторный прогон `find_va_misuse.py` до пустого результата. 3. Глазами пройти `field=albumartist` в диффе (571 строка папок). +--- + +# 🔷 СЕССИЯ 9 (2026-09-27) — ДОЧИСТКА `Various Artists` + `Би-2/Нечётный Воин` + +Продолжение сессии 8: 5 папок из детектора закрыты, плюс новый разбор проекта «Нечётный Воин». + +## 📊 ФИНАЛЬНОЕ СОСТОЯНИЕ (конец сессии 9) + +``` +APPLY-PLAN.csv 7861 строка md5 82492a0b2b93dbb6c7444a4040a21a03 +DIFF-new-vs-38.csv 4946 строк md5 dd67d4fe64bc6365c478a0831cda634c +tag-fix-plan.csv не тронут (база 38-го, источник истины для диффа) +verify_diff_completeness.py — независимый контроль полноты +``` + +**Пустых полей в плане — 0** (все пять `new_*`). +Дифф: `ПОТЕРЯНО 0 · ЛИШНИХ 0 · НЕТ-В-38 41=41 · ТОЛЬКО-В-38 85=85 · ПОТЕРЬ НЕТ, дифф ПОЛНЫЙ`. +`ВСЕГО правок (трек-уровень): 18558` — не изменилось (правки меняют значения, не число расхождений). + +## ✅ A. ВТОРОЙ КЛАСС `Various Artists` ДОЧИЩЕН ПОЛНОСТЬЮ (99 строк) + +Решение Alex: «Арию ставь в albumartist. исправляй что нашел. `Тест Сабвуфера` — наверное +должен быть `Тест Сабвуфера` а не Various». + +| Папка | Файлов | Было | Стало | Основание | +|---|---:|---|---|---| +| `!Рэп/НТЛ` | 63 | `Various Artists` | **`НТЛ`** | Alex: «исправляй что нашел» | +| `!Рэп/НТЛ/2003 - Выход` | 17 | `Various Artists` | **`НТЛ`** | то же | +| `!Rock&Folk/Ария, Кипелов` | 12 | `Various Artists` | **`Ария`** | Alex: «Арию ставь» | +| `z Другое…/Тест Сабвуфера` | 4 | `Various Artists` | **`Тест Сабвуфера`** | Alex: «наверное должен быть Тест Сабвуфера» | +| `z Другое…` (корень) | 3 | `Various Artists` | см. B | откат | + +**Детектор `find_va_misuse.py` теперь даёт `ПАПОК-КАНДИДАТОВ: 0`** — класс вычищен. + +> ⚠️ **Отмена прежнего противоречия:** в сессии 8 помечено, что `!Рэп/НТЛ` формально попадает +> под правило «треки в корне артиста при наличии подпапок-альбомов → СБОРНИК (кейс НТЛ)». +> Alex решил **в пользу артиста** (`НТЛ`), не сборника. Правило «кейс НТЛ = сборник» из сессии 5 +> **отменено** — детектор был прав, таблица правил устарела. + +## ✅ B. `z Другое…` КОРЕНЬ — ОТКАЧЕНО (3 строки) + +> Alex: «стоп. нет. красную плесень в красную плесень — виа меа в виа меа — не объединять +> корень Другого в альбом» + +``` +ВИА МЕА aa='ВИА МЕА' album='Дерьмо Моржа' +Красная Плесень aa='Красная Плесень' album='Сосни попса' +Красная Плесень aa='Красная Плесень' album='Сосни попса' +``` + +**Правило (зафиксировано):** корень папки-**категории** (`z Другое, Enigma, Ленинград` — +контейнер из подпапок разных исполнителей) **альбомом НЕ считается**. +`albumartist` = **собственный `artist` файла**, НЕ `Various Artists`. +Разные исполнители в корне категории — **не повод** для `Various Artists`. + +## ✅ C. `Би-2/Нечётный Воин` — `artist` вытащен из имён файлов (46 строк) + +> Alex: cur_artist = `Нечётный воин 2` / `Нечетный Воин` → «исправляй артиста значит» + +### Как выявлено + +`new_albumartist` содержал имя ПРОЕКТА вместо артиста: +`Нечетный Воин` (CD1/CD2/Медленная Звезда, 27 файлов) и `Нечётный воин 2` (16 файлов). +А `new_artist` дублировал ту же заглушку — реальные исполнители были **только в имени файла** +в скобках: `02 Все, как он сказал (Би-2 + Агата кристи).mp3`. + +⚠️ **Важно:** это **НЕ порча планом** — в ОРИГИНАЛЕ (`cur_artist`) уже стояло +`Нечётный воин 2`. Проверено сравнением с `tag-fix-plan.csv`: `cur == new` в обоих CSV. +План просто не трогал эти поля. Мой оборот «прочерк» был неверным словом — Alex отреагировал: +«че блядь значит прочерк? почему? они не стояли на оригиналах?» + +### Правка + +| Папка | Файлов | `artist` было | `artist` стало | `aa` | +|---|---:|---|---|---| +| `2005 Нечетный Воин/CD1` | 11 | `Нечетный Воин` | `Би-2 + И.Желанная`, `Серьга`, `Линда`, `Агата Кристи`, `М.Карасёв`, `Brainstorm`, `Неонавт`, `Сети`, `Би-2` ×3 | → `Би-2` | +| `2005 Нечетный Воин/CD2` | 11 | `Нечетный Воин` | `Т.Литвиненко`, `Э.Шклярский`, `Лакмус`, `О.Нестров`, `Н.Борзов`, `О.Петрыкина`, `Лена Т.`, `Би-2 + C.Fereira`, `Би-2` ×3 | → `Би-2` | +| `2005 Медленная Звезда` | 5 | `Нечетный Воин` | `Би-2` (все — скобок в именах нет) | → `Би-2` | +| `2008 Проект Нечетный Воин 2` | 16 | `Нечётный воин 2` | `Би-2 + Агата кристи` ×2, `Лакмус + Александр Ф.Скляр`, `Роман Суслов + David Brown`, `Сергей Мазаев + Татьяна Литвиненко`, `А.Кортнев + И.Богушевская`, `Би-2 + Диана Арбенина` ×2, `Би-2 + Инна Желанная`, `Би-2 + Punk tv`, `Truffele`, `Алексей Романов`, `David Brown`, `Louis Frank`, `А.Севидов, Electro-punk`, `Би-2` (Intro) | → `Би-2` | + +### 🔴 ПИТФОЛЛ: вложенные скобки в имени файла ломают экстрактор + +Регулярка `re.findall(r'\(([^()]*)\)', stem)` берёт **последнюю** группу скобок. Три файла +с вложенными/битыми скобками дали НЕВЕРНЫЙ `artist` — потребовался ручной доп-фикс: + +| Файл | Автоматически | Правильно | +|---|---|---| +| `03 Kao sun road (David Brown (Brazzaville).mp3` | `Brazzaville` | **`David Brown`** | +| `11 Michael (Louis Frank (Esthetic education).mp3` | `Esthetic education` | **`Louis Frank`** | +| `16 Все, как он сказал (А.Севидов, Electro-punk.mp3` | `Би-2` | **`А.Севидов, Electro-punk`** | + +**Мораль:** после массовой экстракции из имён — **всегда глазами просмотреть результат**, +имена с вложенными скобками дают мусор. + +### ⏳ ОТКРЫТО + +`album` у этих файлов остался неоднородным: `Нечетный Воин (CD1)`, `Нечетный Воин (CD2)`, +`Медленная Звезда`, `Нечётный воин 2`. Вопрос Alex задан («оставить или унифицировать?`Нечётный Воин`/`Нечётный воин 2`») — **ответа нет, не трогать**. + +## 🧰 ИНСТРУМЕНТЫ СЕССИИ 9 + +| Файл | Назначение | +|---|---| +| `/tmp/fix_va_rest.py` | дочистка `Various Artists` (НТЛ, Ария, Тест Сабвуфера) | +| `/tmp/rollback_z_drugoe.py` | откат `z Другое…` корня | +| `/tmp/fix_nechetny.py` | `aa` `Нечетный Воин` → `Би-2` | +| `/tmp/fix_nechetny_artist.py` | экстрактор `artist` из скобок имён файлов | +| `/tmp/fix_nested.py` | ручной фикс 3 файлов с вложенными скобками | +| `/tmp/find_va_misuse.py` | детектор `Various Artists` (повторный прогон → 0) | + +### Бэкапы сессии 9 + +``` +APPLY-PLAN.csv.bak-before-va-rest-20260927-141317 +APPLY-PLAN.csv.bak-before-z-drugoe-va-20260927-141339 +APPLY-PLAN.csv.bak-before-z-drugoe-rollback-20260927-141422 +APPLY-PLAN.csv.bak-before-nechetny-20260927-141459 +APPLY-PLAN.csv.bak-before-nechetny-artist-20260927-141705 +APPLY-PLAN.csv.bak-before-nested-fix-20260927-141715 +``` + +## 📊 ИТОГ ТРЁХ СЕССИЙ (7-9): 313 строк по четырём классам + +| Класс | Проявление | Детектор | Объём | +|---|---|---|---| +| **A1** чужое значение `albumartist` из корня папки | `DJ Feel`, `VIP77`, `Бумер`, `КняZz`, `Мика Ньютон`, `Виктор Жуков`, `ДА и Меньшиков` | `find_aa_hijack.py` | 40 | +| **A2** `Various Artists` поверх одного артиста | Агата Кристи 95, НТЛ 80, Бенасси 20, Tiesto 14, Иванушки 8, Магомаев 7, ИМ 5, Ария 12, Тест Сабвуфера 4 | `find_va_misuse.py` | ~250 | +| **A3** имя папки/склейка в `album` | `Rammstein - misc` → `misc`, `Infected Mushroom - Other Track's` → `Other Tracks` | `find_folder_as_album.py` | 5 | +| **A4** корень папки-категории как альбом | `z Другое…` | — | 3 (откат) | + +## ⏭ СЛЕДУЮЩИЙ ШАГ + +1. `album` для `Би-2/Нечётный Воин/*` — оставить или унифицировать (ждёт Alex). +2. Глазами пройти `field=album` в диффе (305 строк, из них ПАПКА — 72) — содержательные решения. +3. Затем `field=albumartist` (571 строка папок). +4. Боевой прогон `--apply --verify` — **не запущен**, ждёт отдельного «go». +5. Контейнер `snapcur` на NAS (только чтение, `:ro`) — **не убран**, Alex не подтвердил. + diff --git a/personal/plans/tagwriter-container.md b/personal/plans/tagwriter-container.md index f1dfb11c..2f595e5a 100755 --- a/personal/plans/tagwriter-container.md +++ b/personal/plans/tagwriter-container.md @@ -20,12 +20,13 @@ related: --- # tagwriter — применение тегов к MP3 (TrueNAS) -> 🔴 **СТАТУС: БОЕВОЙ ПРОГОН НЕ ЗАПУЩЕН.** План правится в сессии 8 -> (дифф против 38-го прогона) — подробности в [[personal/plans/music-artist-albumartist-repair]] § «СЕССИЯ 8». -> Актуальные MD5: `APPLY-PLAN.csv` = `48439e06afeb7bdc28e78be5448f44f7` (7861 стр), -> `DIFF-new-vs-38.csv` = `047755613601bcf31c23ff8f22e69b22` (4945 стр). -> Открыто: `track` (56 файлов с `238` + 9 с пустым при непустом `cur_track`), -> 5 папок с `Various Artists` поверх одного артиста (`!Рэп/НТЛ` и др.). +> 🔴 **СТАТУС: БОЕВОЙ ПРОГОН НЕ ЗАПУЩЕН.** План правится в сессиях 8-9 +> (дифф против 38-го прогона) — подробности в [[personal/plans/music-artist-albumartist-repair]] § «СЕССИЯ 8/9». +> Актуальные MD5: `APPLY-PLAN.csv` = `d1fa36dc0fba72faeff08856369fca46` (7861 стр), +> `DIFF-new-vs-38.csv` = `374ed1fed3abb7d9ec397025c0c88a2b` (4945 стр). +> **Класс `Various Artists` поверх одного артиста — вычищен полностью** (детектор +> `find_va_misuse.py` → `ПАПОК-КАНДИДАТОВ: 0`), корень `z Другое…` откачен на собственный `artist`. +> Открыто: `track` (56 файлов с `238` + 9 с пустым при непустом `cur_track`), контейнер `snapcur` на NAS. > ## 🔴 ПУТИ: `~/milestones` БОЛЬШЕ НЕТ > diff --git a/personal/tech/id3-tag-repair.md b/personal/tech/id3-tag-repair.md index 92ea7c0a..e78faf81 100755 --- a/personal/tech/id3-tag-repair.md +++ b/personal/tech/id3-tag-repair.md @@ -232,6 +232,151 @@ assert covered + len(file_lines) == sum(field_counts.values()), "ПОТЕРЯ П Никаких третьих форм. «Одинаковое значение по папке» должно быть организовано **в плане**, дифф лишь объединяет — так формулирует Alex. +### 🔴 D7 — рассинхрон MD5 после «успешного» прогона = скрипт умер на SIGPIPE + +Отдельная проверка к D4. Симптом: отчёт напечатан полностью, `exit=0` на вид, но +`md5` выходного CSV **тот же, что до запуска**, и `wc -l` не изменился. Значит запись +не произошла. Всегда сверять **до/после**: + +```bash +md5 out.csv && python3 gen.py > /tmp/run.log 2>&1 && md5 out.csv # MD5 обязан смениться +``` + +### 🔴 D8 — состояния файла и плана расходятся: колонка `changed` + +`APPLY-PLAN.csv` содержит колонку `changed` (`yes` 7648 / `no` 213). Дифф её **не отражает** — +он сравнивает `new_*` плана с `new_*` 38-го прогона, а не с тегами на диске. Поэтому 50 файлов +(`changed=no`, 61 строка) попадают в дифф как «изменения», хотя на диске ничего не поменяется. +Флаг при этом честный: проверка «`changed=no`, но `new_* != cur_*`» даёт **0**. Класс шире: +«появление колонки» (D3) — тоже не изменение на диске, а правка плана. + +### 🔴 D9 — `folder` из CSV содержит запятую, `awk -F,` врёт + +Продолжение D6 для любого анализа, а не только диффа: `!Pop, Club, Любэ/...` рвётся по +запятой, `$1`/`$4` уезжают в фрагмент имени папки. `scope` считается правильно (`ПАПКА`/`ТРЕК` +первое поле), а `field` — нет. Только `csv.DictReader`. + +## 🔴 Классы порчи `albumartist` (сессия 7, 2026-09-27) + +Три класса, найденных сплошным поиском по `APPLY-PLAN.csv`. Все три — из-за того, что правило +выводилось из **имени папки**, а не из данных файла. + +### 🔴 A1 — значение от СЛУЧАЙНОГО файла корня размножается на всю папку + +`albumartist` папки брался от первого/последнего файла и присваивался всем. Если в корне +папки лежит один чужой трек из сборника — его `albumartist` уезжает на весь альбом: + +``` +!Pop, Club, Любэ/The Prodigy DJ Feel → должно The Prodigy (4 файла, 1 из них реально DJ Feel) +!Pop, Club, Любэ/Банд Эрос VIP77 → Банд'Эрос (7, 1 реально VIP77) +!Pop, Club, Любэ/Жуки Виктор Жуков → Жуки (12, 1 реально Рома Жуков) +!Pop, Club, Любэ/Дискотека Авария ДА и Меньшиков → Дискотека Авария +!Pop, Club, Любэ/Потап & Каменских Мика Ньютон → Потап и Настя Каменских +!Rock&Folk/Король и Шут КняZz → Король и Шут (4, 1 реально КняZz) +z Другое/Ленинград Бумер → Ленинград (+ Сергей Шнуров) +``` + +**Правило:** `albumartist` трека = **его собственный `artist`**, если альбом не сборник. +Значение из чужого файла не размножается. Миноритарные артисты в корне получают **своё имя** +(`КняZz`, `Рома Жуков`, `Мика Ньютон`, `VIP77`, `Бумер`, `DJ Feel` — остаются у своего 1 файла). + +Детектор: `personal/tech/scripts/find_aa_hijack.py` — находит папки, где `albumartist` совпадает +с `artist` **миноритарного** файла, но не с топ-артистом и не с именем папки. 40 строк исправлено. + +Ложные срабатывания детектора (НЕ порча): `Re Sfaction` (решение Alex), `S Is Here` (решение +Alex), `ДДТ/2008 L'Echoppe` (сборник ДДТ, 10 файлов = дуэт Шевчук & Казански), `Сборка/L` +(сборник), `z Другое` (корень-категория). + +### 🔴 A2 — `Various Artists` там, где исполнитель ОДИН + +Правило «разные `artist` → Various Artists» срабатывало на разнобое написания одного имени +(`Иванушки` / `Иванушки Int.` / `Иванушки International`) и на фитах. Затронуто: + +``` +!Rock&Folk/Агата Кристи 95 файлов → Агата Кристи (вкл. Кавер-версии 24, Проекты 12) +!Рэп/НТЛ + 2003 - Выход 80 → НТЛ +!Pop, Club, Любэ/Benassi Bros (2 папки) 20 → Benassi Bros +!Pop, Club, Любэ/Dj Tiesto 14 → Tiesto (DJ-микс: aa = автор компиляции) +Ария, Кипелов 12 → Ария +!Retro/Иванушки 8 → Иванушки +!Retro/Муслим Магомаев 7 → Муслим Магомаев +Infected Mushroom (2 папки) 5 → Infected Mushroom +Тест Сабвуфера 4 → Тест Сабвуфера +``` + +**Правило:** `Various Artists` ставить ТОЛЬКО когда исполнители **действительно разные**. +Один артист (в т.ч. с вариациями написания) → ставить **его**. + +Детектор: `personal/tech/scripts/find_va_misuse.py` — папки с `aa='Various Artists'`, где +>50% файлов имеют одного `artist`. Итог: `ПАПОК-КАНДИДАТОВ: 0` (класс вычищен). + +### 🔴 A3 — имя подпапки уезжает в `album`/`albumartist` склейкой + +``` +❌ new_album = 'Rammstein - misc' (я придумал сам, Alex не просил) +✅ new_album = 'misc' +``` + +`misc` / `base` / `plux` / `рап` — имя **подпапки**, оно идёт в `album` как есть, но **никогда** +не в `albumartist`. Склейка `Родитель - Папка` запрещена в обоих полях. + +Реакция Alex: «**albumartist = Ленинград, Rammstein, какой misc нахуй? это альбом - misc**». + +### 🔴 A4 — корень папки-КАТЕГОРИИ не считается альбомом + +`z Другое, Enigma, Ленинград` — категория-солянка. Сначала файлы-сироты в её корне получили +`album` = имя категории (мусор), затем `albumartist = Various Artists` — тоже неверно. + +**Правило:** для корня категории `albumartist` = **собственный `artist` файла**, даже если +файлов несколько и они разные. Не `Various Artists`, не имя папки. + +``` +ВИА МЕА aa='ВИА МЕА' album='Дерьмо Моржа' +Красная Плесень aa='Красная Плесень' album='Сосни попса' +``` + +Реакция Alex (со «стоп»): «**красную плесень в красную плесень - виа меа в виа меа - +не объединять корень Другого в альбом**». + +### 🔴 A5 — имя ПРОЕКТА вместо артиста + вложенные скобки в имени файла (сессия 9) + +Класс: `artist` (и `albumartist`) = **название музыкального проекта**, а реальные исполнители +треков лежат **только в имени файла в скобках**. План такие поля не трогал — в ОРИГИНАЛЕ +(`cur_artist`) уже стояло имя проекта, поэтому в диффе это выглядело как «не изменение». + +``` +Папка: !Rock&Folk/Би-2/Нечетный Воин/* (проект Шнурова/Самойлова «Нечётный Воин») + cur_artist = 'Нечетный Воин' ← реальный тег файла + new_artist = 'Нечетный Воин' ← план не менял + файл: 02 Все, как он сказал (Би-2 + Агата кристи).mp3 ← артист ТОЛЬКО здесь +``` + +**Правка:** `artist` извлекается из **скобок имени файла** (правило Alex: «artist — есть в скобках +в имени файла → брать из скобок»), `albumartist` → `Би-2` (владелец проекта). + +``` +Нечетный Воин → Би-2 + И.Желанная | Серьга | Линда | Агата Кристи | Т.Литвиненко | Лакмус +Нечётный воин 2 → Би-2 + Агата кристи | Лакмус + Александр Ф.Скляр | Роман Суслов + David Brown +Би-2 (скобок нет) → Би-2 +``` + +🔴 **Питфолл экстрактора: вложенные скобки.** Регулярка `re.findall(r'\(([^()]*)\)', stem)` +берёт **последнюю** группу. Три файла получили мусор вместо артиста: + +| Файл | Автоматика дала | Правильно | +|---|---|---| +| `03 Kao sun road (David Brown (Brazzaville).mp3` | `Brazzaville` | **`David Brown`** | +| `11 Michael (Louis Frank (Esthetic education).mp3` | `Esthetic education` | **`Louis Frank`** | +| `16 Все, как он сказал (А.Севидов, Electro-punk.mp3` | `Би-2` | **`А.Севидов, Electro-punk`** | + +**Мораль:** после массовой экстракции `artist` из имён файлов **обязателен глазной проход** +по результату — вложенные/битые скобки дают неверного артиста без всякой ошибки. + +⚠️ **Не путать с порчей:** `cur == new` в обоих CSV (`APPLY-PLAN` и `tag-fix-plan`) — значит +план **не портил**, а просто не заполнял. Оборот «прочерк» неверен, если `cur_artist` непуст: +Alex отреагировал «че блядь значит прочерк? почему? они не стояли на оригиналах?». +**Проверять первоисточник (`cur_*` в обоих CSV) до слова «порча».** + ## 🖊 Слой ЗАПИСИ — ID3v1 vs ID3v2 (добавлено 2026-09-27) Зеркало правил W1–W15 скилла. Полные формулировки — в `id3-tag-repair/SKILL.md`. diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index 3195ed5b..a121faba 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -23,23 +23,26 @@ related: # Music Tags Repair — TrueNAS (7906 → 7905 файлов) -> ## 🔴 АКТУАЛЬНОЕ СОСТОЯНИЕ (2026-09-27, конец сессии 6) — ЧИТАЙ ЭТО ПЕРВЫМ +> ## 🔴 АКТУАЛЬНОЕ СОСТОЯНИЕ (2026-09-27, конец сессии 9) — ЧИТАЙ ЭТО ПЕРВЫМ > > **Этот файл — исторический лог волны 38 (завершена). Актуальная работа идёт здесь:** > > | Документ | О чём | > |---|---| > | **[[personal/plans/tagwriter-container]]** | ⭐ скрипт-писатель `apply_tags_v2.py` (566 стр): логика полей, ID3v1-снос, тесты, 18 питфоллов, запуск | -> | **[[personal/plans/music-artist-albumartist-repair]]** | ⭐ **правила формирования плана, `APPLY-PLAN.csv`, дифф `DIFF-new-vs-38.csv`, сессии 1-6** | +> | **[[personal/plans/music-artist-albumartist-repair]]** | ⭐ **правила формирования плана, `APPLY-PLAN.csv`, дифф `DIFF-new-vs-38.csv`, сессии 1-9** | > | **[[personal/tech/music-broken-toload]]** | 29 битых/DRM файлов на перезагрузку | > > **Состояние:** план `APPLY-PLAN.csv` доведён до **0 пустых полей** и проверенного диффа > (`ПОТЕРЯНО 0 · ЛИШНИХ 0`). **Боевой прогон НЕ запущен — ждёт «go» от Alex.** -> 🔴 Открыт класс дефекта **«`albumartist` размножен от чужого файла в корне папки»** (7 папок) — -> см. сессию 6 в [[personal/plans/music-artist-albumartist-repair]]. +> ✅ Класс **«`albumartist` размножен от чужого файла в корне папки»** — ИСПРАВЛЕН (7 папок, 40 строк). +> ✅ Класс **«`Various Artists` поверх одного артиста»** — ВЫЧИЩЕН ПОЛНОСТЬЮ +> (`find_va_misuse.py` → `ПАПОК-КАНДИДАТОВ: 0`). Разбор классов **A1-A4** — +> в [[personal/tech/id3-tag-repair]] § «Классы порчи `albumartist`». +> ✅ **`Би-2/Нечётный Воин`** — `artist` вытащен из имён файлов (46 строк), `albumartist` → `Би-2`. > > Боевой вход: `/Users/admin/Downloads/music-tags/APPLY-PLAN.csv` (**7861 строка**, -> md5 `c8d43cc56d82cd35a67a0efe8173cd4b`). Рабочая папка — **`~/Downloads/music-tags/`** +> md5 `82492a0b2b93dbb6c7444a4040a21a03`). Рабочая папка — **`~/Downloads/music-tags/`** > (`~/milestones/` больше НЕ существует). Скрипты: `~/Automation/music-tags/`. > > ⚠️ Волна 38 (TIT2/TPE1/TALB/TRCK, 7041 файл) — **завершена**, описывается ниже как история. diff --git a/personal/tech/scripts/find_aa_hijack.py b/personal/tech/scripts/find_aa_hijack.py index 4750863a..00544148 100755 --- a/personal/tech/scripts/find_aa_hijack.py +++ b/personal/tech/scripts/find_aa_hijack.py @@ -1,26 +1,29 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""Детектор класса «albumartist размножен от чужого файла в корне папки». +"""Detector A1 — albumartist hijacked from a random ROOT file of the folder. -Сессия 6, 2026-09-27. Нашёл 7 испорченных папок из 11 подозреваемых -(The Prodigy->DJ Feel, Банд Эрос->VIP77, Дискотека Авария, Жуки, -Потап & Каменских, Король и Шут, Ленинград). +Rule (Alex): a track's albumartist = its OWN artist, unless the album is a genuine +compilation. A value taken from one stray file in the root must NOT be propagated. -ПРИЗНАК ПОРЧИ: - для папки, где new_albumartist ОДИН на всю папку: - он не совпадает с top-артистом папки - И не совпадает с именем папки - И совпадает с МИНОРИТАРНЫМ артистом папки - -> ПОДОЗРЕВАЕМЫЙ +Finds folders where the folder-level albumartist equals the `artist` of a MINORITY +file, but is neither the top artist nor the folder name. -ЗАПУСК: python3 find_aa_hijack.py -Не меняет файлы. Только печатает. +Benign hits to review manually (NOT corruption): + - eponymous compilation (ДДТ/2008 L'Echoppe — files are a duet credit) + - letter sub-folder of a set (Сборка Песни для детей/L) + - category root (z Другое, Enigma, Ленинград) + - any folder the user explicitly decided (Re Sfaction, S Is Here) + +Usage: + python3 find_aa_hijack.py [путь_к_APPLY-PLAN.csv] """ +import sys + import csv import collections import os -P = '/Users/admin/Downloads/music-tags/APPLY-PLAN.csv' +P = sys.argv[1] if len(sys.argv) > 1 else '/Users/admin/Downloads/music-tags/APPLY-PLAN.csv' rows = list(csv.DictReader(open(P, encoding='utf-8-sig', newline=''))) @@ -36,13 +39,14 @@ suspects = [] for folder, rs in sorted(by_folder.items()): aa_vals = collections.Counter(s(r['new_albumartist']) for r in rs if s(r['new_albumartist'])) if len(aa_vals) != 1: - continue # смешанные папки — отдельный случай + continue # mixed folder — separate case aa = next(iter(aa_vals)) art = collections.Counter(s(r['new_artist']) for r in rs if s(r['new_artist'])) if not art: continue top_artist, top_n = art.most_common(1)[0] folder_name = os.path.basename(folder) + # albumartist matches a MINORITY artist, not the top one, not the folder name if aa != top_artist and aa != folder_name and aa in art: minority = art[aa] if minority < top_n: diff --git a/personal/tech/scripts/find_va_misuse.py b/personal/tech/scripts/find_va_misuse.py index 0c89bda9..dae9a4c6 100644 --- a/personal/tech/scripts/find_va_misuse.py +++ b/personal/tech/scripts/find_va_misuse.py @@ -1,31 +1,22 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""Детектор: aa='Various Artists' у папки, где БОЛЬШИНСТВО файлов имеет одного artist. +"""Detector A2 — 'Various Artists' on a folder that has only ONE artist. -Класс дефекта (сессия 8, 2026-09-27): - Папка-АЛЬБОМ помечена 'Various Artists', хотя исполнитель один. - Причины ложного срабатывания критерия «разные artist = сборник»: - 1. разнобой написания одного исполнителя - ('Иванушки' / 'Иванушки Int.' / 'Иванушки International'); - 2. несколько чужих файлов в корне папки артиста - (7 чужих треков среди 52 треков 'Агата Кристи'). +Rule (Alex): Various Artists ONLY when the performers are genuinely different. +One artist — including spelling variants (Иванушки / Иванушки Int. / Иванушки +International) and featured credits — gets THAT artist. For a DJ-mix compilation the +albumartist is the DJ who compiled it, not Various Artists. -Правило Alex: «Папка с 1 уникальным артистом → АЛЬБОМ, albumartist = artist». +Iterate until it reports zero candidates: the classes are nested — fixing one reveals +the next (e.g. Агата Кристи → 9999 - Разное/Проекты). -Запуск: +Usage: python3 find_va_misuse.py [путь_к_APPLY-PLAN.csv] - -Выход: список папок-кандидатов с частотой доминирующего артиста. -⚠️ Кандидат ≠ дефект. Каждый случай подтверждать у Alex (правило сессии 7: - «не отвечать за Alex»). Спорный случай: '!Рэп/НТЛ' — формально попадает - под правило сборника из сессии 5 (треки в корне при наличии подпапок-альбомов). - -Контроль после правок: гонять детектор, пока он не вернёт пусто или только -подтверждённые Alex спорные случаи (класс выявляется итеративно). """ +import sys + import csv import collections -import sys P = sys.argv[1] if len(sys.argv) > 1 else '/Users/admin/Downloads/music-tags/APPLY-PLAN.csv' rows = list(csv.DictReader(open(P, encoding='utf-8-sig', newline=''))) @@ -49,7 +40,7 @@ for folder, rs in sorted(by.items()): continue top, n = art.most_common(1)[0] total = len(rs) - # доминирует один артист: >50% файлов + # one artist dominates — folder is NOT a genuine compilation if n / total > 0.5 and n >= 2: suspects.append((n, total, top, folder))