From 85f0f009dd712971ee6e943c80f3a4a30dc65c04 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sun, 27 Sep 2026 14:54:41 +0600 Subject: [PATCH] [2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/tech/id3-tag-repair.md --- .../plans/music-artist-albumartist-repair.md | 126 +++++++++++++++--- personal/tech/id3-tag-repair.md | 66 +++++++++ 2 files changed, 171 insertions(+), 21 deletions(-) diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index 8fac08b3..e4ae7870 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: 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` пишу ``, если старых +значений больше одного (подставлять одно из семи = врать в отчёте). +Контроль полноты не изменился: `ВСЕГО правок 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) — ИСТОРИЧЕСКОЕ (частично ОТКАЧЕНО) diff --git a/personal/tech/id3-tag-repair.md b/personal/tech/id3-tag-repair.md index 2b2d0dc5..7e7ff5c7 100755 --- a/personal/tech/id3-tag-repair.md +++ b/personal/tech/id3-tag-repair.md @@ -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` схлопнутой строки **нельзя** подставлять одно из +нескольких старых значений — это враньё в отчёте. Если старых значений больше одного, писать +``. + +**Эффект:** `!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")` **выбрасывает все бэкапы**,