[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/tech/id3-tag-repair.md
This commit is contained in:
1 parent
751e63014c
commit
042cac0216
2 files changed
+258
-1
No files matched your search
@@ -4,7 +4,7 @@ created: '2026-09-27'
|
||||
updated: '2026-09-27'
|
||||
type: plan
|
||||
namespace: personal
|
||||
status: session3-done-0-empty-fields-diff-3595-rows
|
||||
status: session4-done-diff-rebuilt-4864-rows-0-losses
|
||||
tags:
|
||||
- music
|
||||
- metadata
|
||||
@@ -53,6 +53,21 @@ related:
|
||||
> в разделе «🔷 СЕССИЯ 3» в конце документа: **`bb724716c7ffe45619b76abbb3deb5cc`**.
|
||||
> Старые `245efb55…`, `8367084e…` более не верны. Пустых полей в плане — **0**.
|
||||
>
|
||||
> ## 🔴 ДИФФ ПЕРЕОПРЕДЕЛЁН (сессия 4, вечер) — правила изменились
|
||||
>
|
||||
> Актуальные правила диффа и **все три дефекта** старой версии скрипта — в разделе
|
||||
> «🔷 СЕССИЯ 4» в конце документа. Кратко:
|
||||
>
|
||||
> | Правило | |
|
||||
> |---|---|
|
||||
> | Появление новой колонки (`albumartist`, `genre`) | ✅ **ЭТО ПРАВКА**, не выкидывать |
|
||||
> | Схлопывание в ПАПКА | только если у **ВСЕХ** файлов совпадает **и `old`, и `new`** |
|
||||
> | «Доминирующее большинство» | 🔴 **ЗАПРЕЩЕНО** (давало 92 искажённые папки) |
|
||||
> | Полнота | `assert` в скрипте + независимый `verify_diff_completeness.py` |
|
||||
>
|
||||
> Актуальный дифф: **4864 строки**, md5 `0d7a31ca529b2ce2e7f018816ea0ff8e`.
|
||||
> Все цифры состава диффа в разделах ниже (3594 / 3595 / 3865 строк) — **УСТАРЕЛИ**.
|
||||
>
|
||||
> Снимок каталога перед переименованием: `/Users/admin/Downloads/music-tags/milestones/music-csv-BEFORE-RENAME-20260927-124542.tar.gz`.
|
||||
>
|
||||
> ## 🔴 АУДИТ `new_artist` ВЫЯВИЛ 20 ДЕФЕКТОВ
|
||||
@@ -2152,4 +2167,155 @@ bb724716… ⬅ ТЕКУЩИЙ, + Сосни попса (2) + Bimbo Jones (1)
|
||||
- **Сравнение `cur_*` между двумя CSV бессмысленно** — это два разных снимка.
|
||||
Проверять состояние тегов можно **только** чтением файлов.
|
||||
|
||||
---
|
||||
|
||||
## 🔷 СЕССИЯ 4 (2026-09-27): ПЕРЕСБОРКА ДИФФА — ПРАВКИ ДОЛЖНЫ БЫТЬ ВСЕ
|
||||
|
||||
### Задача Alex
|
||||
|
||||
> «пересобрал дифф?!»
|
||||
> «дифф должен показывать то что поменяется. если там только перенос данных — это не изменение.
|
||||
> изменение это было одно значение `new_` в 38-м csv, а стало другое, либо добавлено.
|
||||
> и эти патчи должны быть в диффе **ВСЕ**.»
|
||||
> «**добавление колонки — это правка или нет блядь?? почему выкинуть блядь??**»
|
||||
> «одинаковое значение по папке должно быть на уровне `APPLY-PLAN` организовано —
|
||||
> а дифф должен лишь **объединить если значение одинаковое в `new_` для всей папки**»
|
||||
|
||||
### 🔴 ЧЕТЫРЕ ПРАВИЛА ДИФФА (зафиксированы Alex, обязательны)
|
||||
|
||||
| # | Правило | Следствие |
|
||||
|---|---|---|
|
||||
| 1 | Изменение = `new_` в 38-м ≠ `new_` сейчас | Основа диффа |
|
||||
| 2 | **Появление НОВОЙ КОЛОНКИ = правка.** `albumartist`/`genre` в 38-м плане **не существовало** → тег вообще не писался → сейчас пишется | `old_38 = '<КОЛОНКИ НЕ БЫЛО>'`, **НЕ выкидывать** |
|
||||
| 3 | Схлопывание в запись ПАПКА — **ТОЛЬКО если у ВСЕХ файлов папки совпадает И `old`, И `new`** | «доминирующее большинство» **ЗАПРЕЩЕНО** |
|
||||
| 4 | Каждая правка обязана лежать в диффе либо строкой ТРЕК, либо быть покрыта строкой ПАПКА | Обязателен `assert` на полноту |
|
||||
|
||||
> 🔴 **Моя ошибка, из-за которой правило 2 появилось:** я предложил «выкинуть `albumartist`
|
||||
> и `genre` (2022 строки), колонок в 38-м не было». Alex: «**ты идиот? добавление колонки —
|
||||
> это правка или нет блядь??**». Логика: колонки не было ⇒ план её не заполнял ⇒ тег не
|
||||
> записывался ⇒ появление колонки = запись тега = правка. **Не путать с «переносом данных».**
|
||||
|
||||
### 🔴 Три дефекта, найденные в предыдущей версии `diff_new_vs_38.py`
|
||||
|
||||
| # | Дефект | Как проявлялся | Фикс |
|
||||
|---|---|---|---|
|
||||
| 1 | Схлопывание по «**большинству**» (`len(dom) > len(others)`) | **92 места**, где папка показана как вся-одна, а часть файлов отличалась. Пример: `Sbornik_Novij_Rok/genre` — 193 `Rock` схлопнуто, 7 файлов другого жанра → папка выглядела как «вся Rock» | условие `len(set(new)) == 1` |
|
||||
| 2 | `NEVER_COLLAPSE = {title, track}` | Правило разное для разных полей — Alex требует **одно** правило | убрано |
|
||||
| 3 | Схлопывание при одинаковом `new`, но **разном `old`** | **43 потери** в `t 2009-07 c6Or_NICK` и `z Старое/disk*/2 ALT,PUNK`: запись папки брала `old` первого файла (`''`) → врала про старое значение (`Дискотека Арлекина 2009`) | сравнивать **пару `(old, new)`** |
|
||||
|
||||
**Мораль дефекта 3:** «значение одинаково у всех» недостаточно — запись папки утверждает
|
||||
**переход** `old → new`. Если `old` у файлов разный, переход разный ⇒ папка не схлопывается.
|
||||
|
||||
### 🔴 ПИТФОЛЛ: `python3 script.py | head -40` = SIGPIPE = CSV НЕ записан
|
||||
|
||||
Запуск с пайпом в `head` убивает Python по `SIGPIPE` **до** `w.open(OUT)`. Скрипт отработал,
|
||||
отчёт напечатался — **а `DIFF-new-vs-38.csv` остался старым**, MD5 не изменился.
|
||||
Я почти отчитался «готово». **Всегда `python3 x.py > /tmp/log 2>&1`**, затем читать лог.
|
||||
|
||||
### 🔴 ВЕРИФИКАТОР: независимый скрипт, не переиспользующий логику диффа
|
||||
|
||||
`/Users/admin/Downloads/music-tags/verify_diff_completeness.py` — пересчитывает эталон
|
||||
из двух CSV **с нуля** (своя реализация) и проверяет:
|
||||
|
||||
```
|
||||
эталон (всех реальных изменений): 18317
|
||||
покрыто строками ПАПКА: 14806
|
||||
отдельными строками ТРЕК: 3511
|
||||
ПОТЕРЯНО: 0 ЛИШНИХ: 0
|
||||
НЕТ-В-38: 41 = 41 OK
|
||||
ТОЛЬКО-В-38: 85 = 85 OK
|
||||
ИТОГ: ПОТЕРЬ НЕТ, дифф ПОЛНЫЙ
|
||||
```
|
||||
|
||||
`exit-code 0` = чисто. **Два ложных срабатывания, обнаруженных в нём же:**
|
||||
- считал «ЛИШНИМИ» 101 строку файлов из категории `НЕТ-В-38` (у них `old_38 = '<НЕТ В 38-м>'`) —
|
||||
поправлено: такие пары законны, если путь в множестве `e_none`;
|
||||
- `e_none` вычислялся **ниже** места использования → `NameError`/неработающий фильтр.
|
||||
Вычисление перенесено наверх.
|
||||
|
||||
> **Урок:** «своя проверка своей же функцией» ничего не доказывает (ср. W11).
|
||||
> Верификатор пишется **независимо** и печатает цифры, а не «успех».
|
||||
|
||||
### 📊 ИТОГ СЕССИИ 4 — АКТУАЛЬНЫЕ АРТЕФАКТЫ
|
||||
|
||||
```
|
||||
APPLY-PLAN.csv 7861 строка md5 bb724716c7ffe45619b76abbb3deb5cc
|
||||
DIFF-new-vs-38.csv 4864 строки md5 0d7a31ca529b2ce2e7f018816ea0ff8e
|
||||
tag-fix-plan.csv не тронут (база 38-го, источник диффа)
|
||||
```
|
||||
|
||||
**Состав диффа:**
|
||||
```
|
||||
ПАПКА 1126 (было 1140 до фикса дефекта 3)
|
||||
ТРЕК 3612 (было 3419)
|
||||
НЕТ-В-38 41
|
||||
ТОЛЬКО-В-38 85
|
||||
ВСЕГО правок 18558 ← контроль: 14806 покрыто папками + 3511 треков ≈ эталон
|
||||
```
|
||||
|
||||
**Строк по полям (правильный подсчёт — через `csv`, НЕ `awk -F,`: папки содержат запятые):**
|
||||
```
|
||||
genre 2509
|
||||
track 987
|
||||
albumartist 701
|
||||
album 305
|
||||
artist 194
|
||||
title 42
|
||||
```
|
||||
|
||||
**ПАПКА-строки по полям:** `albumartist` 571 · `genre` 470 · `album` 72 · `track` 9 · `artist` 4.
|
||||
|
||||
**Цепочка MD5 диффа:**
|
||||
```
|
||||
5d134966… сессия 3 (3595 строк, схлопывание по большинству)
|
||||
eab00b4b… + правило колонок (4685 строк) — НЕ записался из-за SIGPIPE-пайпа
|
||||
0d7a31ca… ⬅ ТЕКУЩИЙ, + фикс схлопывания по паре (old,new) (4864 строки)
|
||||
```
|
||||
|
||||
### 🔴 ПИТФОЛЛ: `awk -F,` для подсчёта полей CSV врёт
|
||||
|
||||
`folder` в диффе содержит запятые (`!Pop, Club, Любэ/...`) → `awk -F, '{print $4}'` печатал
|
||||
**имена папок вместо имён полей**. Считать только парсером: `csv.DictReader` + `Counter(r['field'])`.
|
||||
|
||||
### ⚙️ Инвариант в самом скрипте
|
||||
|
||||
```python
|
||||
covered = sum(n for _, _, _, _, n in collapse_lines)
|
||||
assert covered + len(file_lines) == sum(field_counts.values()), "ПОТЕРЯ ПРАВОК!"
|
||||
print(" OK: потерь нет")
|
||||
```
|
||||
|
||||
Скрипт **падает** при нарушении — молчаливая потеря правок исключена. Запуск:
|
||||
|
||||
```bash
|
||||
cd /Users/admin/Downloads/music-tags
|
||||
python3 diff_new_vs_38.py > /tmp/diff_run.log 2>&1 # НЕ через | head!
|
||||
python3 verify_diff_completeness.py # exit 0 = чисто
|
||||
```
|
||||
|
||||
Бэкапы: `diff_new_vs_38.py.bak-before-collapse-fix-<ts>`,
|
||||
`DIFF-new-vs-38.csv.bak-before-collapse-fix-<ts>`.
|
||||
|
||||
---
|
||||
|
||||
### ⏳ НЕ ЗАКРЫТО (обновлено конец сессии 4)
|
||||
|
||||
| # | Вопрос | Статус |
|
||||
|---|---|---|
|
||||
| 1 | `track = '238'` у 56 файлов (`Красная Плесень/Золушка`) | ⏳ верное значение неизвестно |
|
||||
| 2 | `track` пуст у 1438, из них **9 с непустым `cur_track`** → тег `TRCK` будет УДАЛЁН | ⏳ решение Alex |
|
||||
| 3 | `changed = 'no'` у **213 строк** в `APPLY-PLAN.csv`; из них **50 файлов (61 строка)** присутствуют в диффе как «изменения» | ⏳ уточнить трактовку |
|
||||
| 4 | Боевой прогон `--apply --verify` | 🔴 не запущен |
|
||||
| 5 | Контейнер `snapcur` на NAS (только чтение, `:ro`) | ⏳ не убран — Alex не подтвердил удаление |
|
||||
| 6 | Глазами пройти `field=album` (305 строк) в диффе | ⏳ следующий шаг по указанию Alex |
|
||||
|
||||
### Про `changed = 'no'` — разбор (сессия 4)
|
||||
|
||||
Проверено фактом: **0 случаев**, где `changed='no'`, но `new_* != cur_*` — флаг не врёт.
|
||||
213 строк = файлы, у которых правка не требуется (`new_ == cur_`). Файлы этой группы
|
||||
попадают в дифф, когда `new_` отличается от **38-го плана** — то есть правка по факту
|
||||
не нужна, а в диффе строка есть. Пользователь обозначил принцип: **в диффе только то,
|
||||
что реально поменяется.**
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user