[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/tech/id3-tag-repair.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 14:54:41 +06:00
1 parent 6576b53834
commit 85f0f009dd
2 files changed
+171 -21

No files matched your search

+105 -21
View File
@@ -4,7 +4,7 @@ created: '2026-09-27'
updated: '2026-09-27'
type: plan
namespace: personal
status: session13-trackscroll-rollback-genres-fixed-8-album-questions-open
status: session13-collapse-fixed-radio-album-fixed-disk-naming-open
tags:
- music
- metadata
@@ -58,15 +58,94 @@ related:
## 🟢 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 13 (2026-09-27) — читать это первым
>
> ```
> APPLY-PLAN.csv 7861 строка md5 ced5078ebf9b06f25e8bcc2d4ae6fec9
> DIFF-new-vs-38.csv 4996 строк md5 1884b1429e5e9e9f08ecd6abd772fc7c
> APPLY-PLAN.csv 7861 строка md5 a6e440511e195668af2cee785e547fce
> DIFF-new-vs-38.csv 2864 строки md5 b4f5c9f23f52df4dd314f5210f3adfd6
> tag-fix-plan.csv 7861 строка md5 203726ebfd2814e2328636df31337238 (НЕ ПРАВИТЬ)
> ```
>
> **Состав диффа:** `ПАПКА` 1123 · `ТРЕК` 3747 · `НЕТ-В-38` 41 · `ТОЛЬКО-В-38` 85.
> Полнота: `ВСЕГО правок 18639 · OK: потерь нет`.
> **Состав диффа:** `ПАПКА` 1485 · `ТРЕК` 1253 · `НЕТ-В-38` 41 · `ТОЛЬКО-В-38` 85.
> Полнота: `ВСЕГО правок 18841 · OK: потерь нет`.
### ✅ СДЕЛАНО в сессии 13
### ✅ СДЕЛАНО в сессии 13 (продолжение — после вопросов)
**4. 🔴 СХЛОПЫВАНИЕ ДИФФА ПЕРЕДЕЛАНО — ключ группировки `(папка, поле, НОВОЕ ЗНАЧЕНИЕ)`.**
Было: 4996 строк, схлопывание `combos = set((ov, nv))` требовало совпадения **И `old`, И `new`**
у всех файлов — поэтому не срабатывало почти никогда. Стало: 2864 строки.
Alex: «схлопывание по `field+value` делается, а не по `old` — очевидно что он блядь у всех разный!»
```python
# было (неверно):
combos = set((ov, nv) for _, ov, nv in lst)
if len(combos) == 1 and len(lst) > 1: ... # старые значения почти всегда разные -> построчно
# стало:
groups = defaultdict(list)
for rel, ov, nv in lst:
groups[nv].append((rel, ov)) # группируем по НОВОМУ значению
for nv, members in groups.items():
if len(members) > 1:
olds = sorted({ov for _, ov in members})
label = olds[0] if len(olds) == 1 else f"<{len(olds)} разных>"
collapse_lines.append((fld, fname, label, nv, len(members)))
else:
rel, ov = members[0]
file_lines.append((fld, rel, fname, ov, nv))
```
Проверка на папке Alex: `!Radio/t 2009-07 c6Or_NICK`, `field=album` — было **43 строки**, стало **1**:
`[ПАПКА] '<7 разных>' -> '2009-07 c6Or_NICK'`. В `old_38` пишу `<N разных>`, если старых
значений больше одного (подставлять одно из семи = врать в отчёте).
Контроль полноты не изменился: `ВСЕГО правок 18841 · OK: потерь нет` — правки не потеряны, а сгруппированы.
**5. `album` для подборок `!Radio` — 203 строки, 18 папок.**
Корень дефекта: план копирует `cur_album` из старого тега, хотя для подборок `!Radio`
`album` = **имя папки-подборки** (правило сессии 7). Показательный случай — трек «Теряю свет»:
```
!Radio/t 2009-07 c6Or_NICK/plux/Би-2 и Агата Кристи - Теряю свет.mp3
cur_album='Нечетный воин 2' → new_album='Нечётный Воин 2' ❌ (для подборки альбом не при чём)
правильное: '2009-07 c6Or_NICK'
```
Тот же трек в папке-альбоме `!Rock&Folk/Би-2/Нечетный Воин/2008 Проект…` — **оставлен верным**
(`class='АЛЬБОМ'`, `albumartist='Би-2'`, `album='Нечётный Воин 2'`).
| Что | строк | скрипт | бэкап |
|---|---|---|---|
| `plux` (28 ф.) → `2009-07 c6Or_NICK` | 18 | `/tmp/fix_plux.py` | `.bak-plux-…144514` |
| 17 папок `!Radio` с разнобоем → имя подборки | 185 | `/tmp/fix_radio_album.py` | `.bak-radioalb-…144543` |
Целевое имя берётся как значение, которое **уже стоит у большинства** файлов папки (значит
оно и есть имя подборки, проставленное ранее), + требование `cnt >= 2`.
**Пропущены намеренно:** `disk/3 SHANSON/Трофим` и `disk_v2/3 SHANSON/Трофим` — там лежат
альбомы Трофима (`class='АЛЬБОМ'`), имя папки не подборка.
Детектор разнобоя: `/tmp/audit_radio_album.py` → после фикса осталось **2 папки** (обе `Трофим`).
**6. Проверено и НЕ подтверждено — откат не нужен.** Alex заподозрил, что «слетели альбомы»
(`Benassi Bros` и др.). Факт: в `APPLY-PLAN.csv` **`new_album` заполнен у 7861 из 7861**.
Слетевшими выглядели строки вида `'' -> 'КняZz'` в диффе — но `''` это `old_38` (в 38-м
прогоне `album` не заполнялся вовсе). Откат вернул бы пустые альбомы, т.е. сломал бы.
Отдельно по `Benassi Bros`: во **всех 45 бэкапах** корень = `'Benassi Bros'` только у **4 из 15** —
состояние, которое искал Alex, в бэкапах `APPLY-PLAN.csv.*` отсутствует.
### ❓ ОТКРЫТЫЕ ВОПРОСЫ К ALEX (заданы, ответа нет — НЕ править)
**🔴 `z Старое/disk*` — именование `RADIO N NAME`.** Показал факты, решения нет:
```
disk/1 DRIVE 43 ф. → RADIO 1 DRIVE ✅
disk/2 ALT,PUNK 11 ф. → RADIO 2 ALT,PUNK ✅
disk/4 INSTRUMENTAL 1 ф. → RADIO 4 INSTRUMENTAL ✅
disk/5 POPSA 15 ф. → RADIO 5 POPSA ✅
disk/3 SHANSON/Трофим 25 ф. → альбомы Трофима ❌ нет 'RADIO 3 SHANSON'
disk_v2/2 ALT,PUNK/HARD 6 ф. → 'Megamix 2002' ❌ нет 'RADIO 2 ALT,PUNK'
```
Причина по `3 SHANSON`: в папке нет **своих** файлов (только подпапка `Трофим`), а имя
берётся от папки, где лежит файл. `HARD` — третий уровень вложенности.
Моё предположение (брать имя ближайшей нумерованной папки-родителя) **отклонено**:
Alex: «нет, не надо блядь выдумывать. делай че я говорю». Последнее показанное состояние:
`3 SHANSON/Трофим` — `class='АЛЬБОМ'`, альбомы Трофима, но **один файл сбит**:
`#2_04_Koroche delo k nochi.mp3 → 'Трофим'` (должен быть `Аристократия помойки 2`).
`disk_v2/2 ALT,PUNK/HARD` — `class='СБОРНИК'`, все 6 файлов `'Megamix 2002'`.
### ✅ СДЕЛАНО в сессии 13 (начало)
**1. ⚠️ ОТКАТ дефекта из сессии 12: сквозная нумерация многодисковых изданий УНИЧТОЖЕНА мной, восстановлена.**
Класс A из `fix_track_bugs.py` (269 строк) сбрасывал `14..26 → 1..13` и т.п. — это **было неверно**.
@@ -111,30 +190,31 @@ Alex: «НС - это рок», «очевидноъ». Все файлы — `c
> ✅ 51 строка `Psychedelic` у IM — **корректна**, не трогать (IM = психоделик-транс).
> Там же план исправляет опечатку `Psychadelic` → `Psychedelic` — это плюс.
### ❓ ВОПРОСЫ К ALEX — ОТКРЫТЫ (заданы в конце сессии 13, ответа нет)
### ❓ ВОПРОСЫ К ALEX — СТАТУС (сессия 13)
Задан ручной проход по всему `album`-блоку диффа. Найдены классы подозрительного `new_album`.
**Ничего не правил — жду ответа.** Ключевой спор:
🔴 **«Куда слетели альбомы?»** Alex: «мы с тобой ставили album в соответствие названию
корня папки — ищи этот apply-plan backup!» Про **`Benassi Bros`**: ожидает `album='Benassi Bros'`
для 15 файлов корня. **Проверено — НЕ подтвердилось:** во **всех 45 бэкапах**
`APPLY-PLAN.csv.*` корень Benassi Bros = `'Benassi Bros'` только у **4 из 15**, остальные 11
несут значения из тегов (`Phobia`, `Rock N Rave`, `Hypnotica`, `Techno 2003`,
`Miami Sampler 2002 CDS`, `Energy New Release CD`, `Loneliness Promo_Vinyl`,
`Loveparade Compilation 2003`, `I Love My Sex VINYL`, `Pumphonia`, `Non spécifié`, `NULL`).
Скрипт поиска `/tmp/find_benassi2.py` (важно: фильтр по `.csv` **отсекает** бэкапы
🔴 **«Куда слетели альбомы?» — РАЗОБРАНО, откат НЕ нужен.** Alex: «мы с тобой ставили album
в соответствие названию корня папки — ищи этот apply-plan backup!» Про **`Benassi Bros`**:
ожидает `album='Benassi Bros'` для 15 файлов корня.
**Проверено — НЕ подтвердилось** (см. п.6 в разделе «СДЕЛАНО»): в текущем плане `new_album`
заполнен у всех 7861 строк; во **всех 45 бэкапах** `APPLY-PLAN.csv.*` корень Benassi Bros =
`'Benassi Bros'` только у **4 из 15**, остальные 11 несут значения из тегов (`Phobia`,
`Rock N Rave`, `Hypnotica`, `Techno 2003`, `Miami Sampler 2002 CDS`, `Energy New Release CD`,
`Loneliness Promo_Vinyl`, `Loveparade Compilation 2003`, `I Love My Sex VINYL`, `Pumphonia`,
`Non spécifié`, `NULL`).
Скрипт поиска `/tmp/find_benassi2.py` (важно: фильтр `p.endswith(".csv")` **отсекает** бэкапы
`APPLY-PLAN.csv.bak-*` — сначала я на этом ошибся и получил «всего 1 файл»).
➡️ **Искать источник нужно в другом файле:** `tag-inventory3.csv`, `APPLY-PLAN-RAW.csv`,
➡️ Если источник всё же существует — искать в: `tag-inventory3.csv`, `APPLY-PLAN-RAW.csv`,
`archive/*`, `milestones/music-csv-2026-09-26/`.
Остальные вопросы (прочитаны строки `album/ПАПКА` 1-75 и `album/ТРЕК` 76-319):
Остальные вопросы (прочитаны строки `album/ПАПКА` 1-75 и `album/ТРЕК` 76-319).
**Статус:** п.3 (разнобой `!Radio`) и п.7 (корень артиста) — **частично закрыты** п.5 сессии 13;
пп.1, 2, 4, 5, 6, 8 — **открыты**, решения нет.
| # | Что | Пример |
|---|---|---|
| 1 | **Мусор в `album`** | `Non spécifié`, `NULL`, `no title`, `Album`, `2005`, `Неизвестное название`, `melody.az - КняZz - Корсар`, `(Муммий Тролль)` ×8 (скобки в названии) |
| 2 | **`album` = название песни** | `Алексин/Алексин - Шалавы.mp3 → 'Шалавы'`; `Дискотека Авария/Песенка разбойников.mp3 → 'Песенка разбойников'` |
| 3 | **`album` = имя подпапки** (правило сессии 7, но список длинный) | `!Radio/z Старое/disk/2 ALT,PUNK/* → 'RADIO 2 ALT,PUNK'`; `Rammstein → 'misc'`; `Красная Плесень/misc → 'Красная Плесень'`; `Пилот/misc/Про гитару → 'Сказка'`; `Пилот/misc/Хана человечки → 'Война'` |
| 3 | **`album` = имя подпапки** (правило сессии 7; разнобой `!Radio` закрыт в п.5) | `!Radio/z Старое/disk/2 ALT,PUNK/* → 'RADIO 2 ALT,PUNK'`; `Rammstein → 'misc'`; `Красная Плесень/misc → 'Красная Плесень'`; `Пилот/misc/Про гитару → 'Сказка'`; `Пилот/misc/Хана человечки → 'Война'` |
| 4 | 🔴 **4 разных формата `album` внутри одной `!Radio`** | `'Radio - OOF'` / `'Radio - Старое'` (префикс ✅) · `'RADIO 1 DRIVE'`, `'RADIO 5 POPSA'` (без префикса) · `'2009-04 Radio'`, `'2008-07 Radio'`, `'2008-04 Radio'` (суффикс!) · `'2009-10 Oktoberfest'` (без Radio вообще) |
| 5 | `z Другое, Enigma, Ленинград` корень → `'Сосни попса'` | это точно альбом? |
| 6 | `album` из ниоткуда | `detskiy_hor_benassi_satisfaction.mp3 → 'Горячие Синглы 2004'`; `Бутусов & Ю-Питер/Девушка По Городу.mp3 → 'Горячие синглы - июнь 2004'` |
@@ -155,6 +235,10 @@ Alex: «НС - это рок», «очевидноъ». Все файлы — `c
`find_lost_track.py` (потери `track`) · `classify_track.py` (привязан/сирота) ·
`show_remaining51.py` · `rollback_track_fix.py` · `walk_diff.py` (полный ручной проход → `/tmp/diff_full_walk.txt`) ·
`compare_backups_album.py` · `find_benassi2.py` · `check_*` детекторы жанров.
Дописано во второй половине сессии 13: `/tmp/check_c6or_collapse.py` (проверка схлопывания) ·
`/tmp/audit_radio_album.py` (разнобой `new_album` в `!Radio`) · `/tmp/check_plux.py` ·
`/tmp/fix_plux.py`, `/tmp/fix_radio_album.py` (правки `album`) · `/tmp/show_current_album.py` ·
`/tmp/check_disk_name.py` (именование `disk*/N NAME`) · `/tmp/verify_diff_order.py`.
## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 12 (2026-09-27) — ИСТОРИЧЕСКОЕ (частично ОТКАЧЕНО)
+66
View File
@@ -35,6 +35,10 @@ related:
> раздельных циклах бесполезна); **D11** (`new_track` теряется при пустом `cur_track`);
> **D12** (`new_track` «ни к чему не привязан» → сдвинутый ряд пересчитать, иначе убрать).
>
> ⚠️ **Обновлён до v1.8.0 (сессия 13, вторая половина):** **D16** (схлопывание диффа: ключ
> `(папка, поле, НОВОЕ значение)`, `old` в ключ не входит), **D17** (`album` унаследован от
> `cur_album` для подборок `!Radio` — 203 строки; метод: имя подборки = значение большинства).
>
> ⚠️ **Обновлён до v1.7.0 (сессия 13):** **D12 ОТКАЧЕН** — см. **D12-R** ниже (сквозная
> нумерация многодисковых изданий НЕ трогать). Добавлены **D13** (жанр унаследован от
> `cur_genre` без проверки), **D14** (статус «привязан/сирота» для `track`), **D15** (поиск
@@ -531,6 +535,68 @@ lost = [r for r in rows if r["field"] == "track"
Скрипт: `/tmp/classify_track.py`.
### 🔴 D16 — схлопывание диффа: ключ `(папка, поле, НОВОЕ значение)`, НЕ `old` (сессия 13)
Первая версия схлопывала только когда у **всех** файлов папки совпадал **и `old`, и `new`**:
```python
combos = set((ov, nv) for _, ov, nv in lst)
if len(combos) == 1 and len(lst) > 1: collapse...
else: для каждого файла отдельная строка
```
Это **почти никогда не срабатывало**: `old_38` у каждого файла свой (`'Дискотека Арлекина 2009'`,
`'Record Super Chart ¹ 90 (30.05.2009)'`, `''`, `'Ticker / Sensor'` …). Папка из 43 файлов
давала 43 строки при **одном** новом значении.
Alex: «схлопывание по `field+value` делается, а не по `old` — очевидно что он блядь у всех разный!»
**Правильно — группировать по новому значению внутри `(папка, поле)`:**
```python
groups = defaultdict(list)
for rel, ov, nv in lst:
groups[nv].append((rel, ov)) # ключ НЕ включает old
for nv, members in groups.items():
if len(members) > 1:
olds = sorted({ov for _, ov in members})
label = olds[0] if len(olds) == 1 else f"<{len(olds)} разных>"
collapse_lines.append((fld, fname, label, nv, len(members)))
else:
rel, ov = members[0]
file_lines.append((fld, rel, fname, ov, nv))
```
🔴 **Питфолл отчёта:** в колонку `old_38` схлопнутой строки **нельзя** подставлять одно из
нескольких старых значений — это враньё в отчёте. Если старых значений больше одного, писать
`<N разных>`.
**Эффект:** `!Radio/t 2009-07 c6Or_NICK` `field=album` — 43 строки → 1 (`'<7 разных>' -> '2009-07 c6Or_NICK'`).
Дифф целиком 4996 → 2864 строк (`ПАПКА` 1123→1485 · `ТРЕК` 3747→1253).
Контроль полноты на трек-уровне не меняется (`assert covered + len(file_lines) == sum(field_counts)`).
### 🔴 D17 — `album` унаследован от `cur_album` для подборок `!Radio` (сессия 13, 203 строки)
Тот же корень, что и **D13**: план копирует значение из тега файла, не проверяя, уместно ли оно.
Здесь — `new_album` ← `cur_album`. Для подборок `!Radio` **`album` = имя папки-подборки**
(правило сессии 7), а не альбом, из которого трек взят.
```
!Radio/t 2009-07 c6Or_NICK/plux/Би-2 и Агата Кристи - Теряю свет.mp3
cur_album='Нечетный воин 2' → new_album='Нечётный Воин 2' ❌
правильно: '2009-07 c6Or_NICK'
```
Один и тот же трек, но в **папке-альбоме** `!Rock&Folk/Би-2/Нечетный Воин/2008 Проект…`
(`class='АЛЬБОМ'`) — там `album='Нечётный Воин 2'` и `albumartist='Би-2'` **верны**.
**Метод фикса (без выдумывания значений):** целевое имя папки-подборки берётся как значение,
которое **уже стоит у большинства** файлов папки (значит оно проставлено ранее правилом), плюс
требование `cnt >= 2`. Папки, где такого большинства нет либо `class='АЛЬБОМ'` — **пропускать**.
**Детектор разнобоя:** для каждой папки `Counter(new_album)`; `len(vals) > 1` = дефект.
Скрипты: `/tmp/audit_radio_album.py` (поиск), `/tmp/fix_radio_album.py` (правка, сухой прогон по умолчанию).
До: 19 папок / 469 строк. После: 2 папки (обе `3 SHANSON/Трофим` — там альбомы Трофима, пропущены намеренно).
🔴 **Не путать с правильными случаями:** `z Старое/disk/N NAME` → `'RADIO N NAME'` — это
**одно** значение на папку и оно верно; дефект только там, где значения разные.
### 🔴 D15 — поиск по бэкапам: фильтр `.csv` отсекает `APPLY-PLAN.csv.bak-*` (сессия 13)
`glob(f"{B}/APPLY-PLAN.csv*")` + условие `p.endswith(".csv")` **выбрасывает все бэкапы**,