[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

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 14:19:15 +06:00
1 parent 463a77ac5d
commit 2520fda13b
6 files changed
+359 -47

No files matched your search

@@ -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 не подтвердил.
+7 -6
View File
@@ -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` БОЛЬШЕ НЕТ
>
+145
View File
@@ -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`.
+8 -5
View File
@@ -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 файл) — **завершена**, описывается ниже как история.
+18 -14
View File
@@ -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:
+11 -20
View File
@@ -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))