[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:39:30 +06:00
1 parent d0a8cd1601
commit 981a59781e
2 files changed
+189 -7

No files matched your search

@@ -4,7 +4,7 @@ created: '2026-09-27'
updated: '2026-09-27'
type: plan
namespace: personal
status: session11-diff-audit-2-cosmetic-defects-sort-order-pending-alex
status: session12-diff-sort-fixed-track-repaired-51-radio-folders-pending-alex
tags:
- music
- metadata
@@ -23,7 +23,7 @@ related:
# Music `artist` + `albumartist` Repair — правила Alex и ход работ
> ## 🟢 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 10 (2026-09-27) — читать это первым
> ## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 10 (2026-09-27) — ИСТОРИЧЕСКОЕ, см. сессию 12 выше
>
> ```
> APPLY-PLAN.csv 7861 строка md5 7679ee01a2aa8f28623b6fb8559d7381
@@ -55,7 +55,110 @@ related:
> ✅ Класс `A2` **дочищен**: `find_va_misuse.py` → 0, `find_va_author.py` → остались только
> 2 защитимые папки (`Блестящие, Фриске`, `Eminem,50cent`).
## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 11 (2026-09-27)
## 🟢 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 12 (2026-09-27) — читать это первым
```
APPLY-PLAN.csv 7861 строка md5 0e6ff0e05bf0353b4c40c8c05e574e9b
DIFF-new-vs-38.csv 4988 строк md5 212d3236bc1eea275890d5ddabf50373
tag-fix-plan.csv 7861 строка md5 203726ebfd2814e2328636df31337238 (НЕ ПРАВИТЬ)
```
**Состав диффа:** `ПАПКА` 1123 (569 папок) · `ТРЕК` 3747 · `НЕТ-В-38` 41 · `ТОЛЬКО-В-38` 85.
Полнота: `ВСЕГО правок 18639 · OK: потерь нет`.
### ✅ ПОЧИНЕНО в сессии 12
**1. Сортировка диффа — вариант «поле первым, `scope` вторым»** (Alex выбрал из 3;
сначала я ошибочно сделал по папке — Alex: «нахуя ты по папке сделал долбоеб!»).
Порядок стал **едиными блоками по полю** без разрыва на стыке секций:
```
1 ПАПКА album 76 ТРЕК album
320 ПАПКА albumartist 884 ТРЕК albumartist
1104 ПАПКА artist 1109 ТРЕК artist
1337 ПАПКА genre 1807 ТРЕК genre
3846 ТРЕК title 3888 ПАПКА track 3897 ТРЕК track
4875 НЕТ-В-38 (41) 4916 ТОЛЬКО-В-38 (85)
```
🔴 **Питфолл, который я не понял с первого раза:** правки ключа `sorted()` внутри двух
раздельных циклов записи (`ПАПКА`, потом `ТРЕК`) **бесполезны** — `scope` в ключе не может
поднять `ТРЕК album` выше `ПАПКА track`. Нужно **сначала слить оба списка в один** и
отсортировать одним ключом:
```python
out_rows = []
for fld, fname, ov, nv, n in collapse_lines:
out_rows.append((fkey(fname), 0, fld, "", "ПАПКА", fld, "", fname, ov, nv))
for fld, rel, fname, ov, nv in file_lines:
out_rows.append((fkey(fname), 1, fld, rel, "ТРЕК", fld, rel, fname, ov, nv))
out_rows.sort(key=lambda x: x[:4])
```
Проверка эффекта — **только по md5 файла и явному списку переходов с номерами строк**.
Первый прогон «починенного» скрипта дал **тот же md5** — правка была пустой.
Alex: «ты проверяешь перед тем как докладывать что сделано!!!»
**2. Потеря `new_track` — 4 строки восстановлены.** Класс дефекта: `cur_track` пустой →
логика заполнения пропускала файл → номер из 38-го плана терялся.
```
old_38=17 → '' !Детские/Сказки/Алиса в Стране Чудес (Диск2)/04 - Песня про ребенка-поросенка.mp3
old_38= 8 → '' !Pop, Club, Любэ/Света/2003 - Зеркала/08 - Жизнь моя.mp3
old_38= 0 → '' !Pop, Club, Любэ/!Radio/t 2009-07 c6Or_NICK/00 Geo da Silva - ....mp3
old_38= 0 → '' !Rock&Folk/ЗАХАР МАЙ/2011 - Казань против диабета/00-intro-family-affair.mp3
```
Детектор потерь: `field == "track" and old_38 непусто and new_apply_plan пусто` в самом DIFF.
Скрипт `/tmp/restore_lost_track.py`. Бэкап `APPLY-PLAN.csv.bak-trackrestore-20260927-143257`.
**3. Косяк `new_track` — «номер ни к чему не привязан» → чинится 1379 строк.**
Alex указал на `Алиса в Стране Чудес (Диск2)/04` = `track 17` при 13 файлах в папке
(ряд 14..26 = нумерация многодискового издания). Правило Alex:
> «если там есть порядковый номер в альбоме — то ты его оставляешь»
> «если он ни к чему не привязан то убираешь»
Реализация (`/tmp/fix_track_bugs.py`, записано с `--apply`):
| Класс | Строк | Действие |
|---|---|---|
| A. Ряд **сдвинут** (диск N многодискового издания), полный без дыр, смещение ≠ 0 | 269 | пересчитать `1..N`: `_offset_` = `min(nums) - 1` |
| B. В папке **нет полного ряда** | 1107 | номер из имени файла (`^\d{1,3}\s*[-._)]?\s+`, 1..300), иначе **пусто** |
| C. Номер есть, но `new` уже верный | 3 | — |
Примеры класса A: `Алиса (Диск2)` 14..26 → 1..13; `Трое из Простоквашино (Диск 2)` 10..20 → 1..11;
`Принц и нищий (Диск 2)` 8..14 → 1..7; `Малыш и Карлсон (Диск 2)` 4..6 → 1..3.
Бэкап `APPLY-PLAN.csv.bak-trackfix-20260927-143611`.
⏳ **ОСТАЛОСЬ 51 папка с рваной нумерацией — ЖДЁТ ПРАВИЛА ALEX.**
Номера не «сдвинуты» (ряд с дырами или дублями), фикс их не тронул:
- `!Radio/t 2009-07 c6Or_NICK` — 43 файла, 1..36 подряд, потом дыры (нет 37-39, 41-47, 49-59) + хвост 60-63;
- `!Radio/x 2008-04` — 59 файлов, номера **дублируются** (`2,2,3,3,5,5,7,7`);
- `!Radio/t 2009-07 c6Or_NICK/base` — 16 файлов, ряд `1,2,3,5,7,8,...` с дырами;
- `!Radio/s 2009-08 Drinkin Summer`, `.../рап` — в папке 1 файл с номером.
Вопрос задан: сбрасывать номер или пересчитывать `1..N` по алфавиту — **ответа нет**.
### 🔴 Метод-уроки сессии 12 (Alex ловил на каждом)
1. **«Готово» = вывод по САМОМУ файлу**, а не логика кода. Проверять `md5` + перечитывать
строки. Если md5 не изменился — правка не применилась.
2. **Не задавать вопросов, если указание уже дано.** Alex: «нахуя я тебе это пишу блядь?
просто так??», «я говорю - ты делаешь блядь!», «так доделывай».
План/апрув нужен только там, где правило действительно не выведено из указаний.
3. **Не останавливаться на середине.** Alex: «ты блядь не останавливаешься пока не доделаешь!»
4. **Структуру файла описывать явным списком переходов** с номерами строк, не агрегатом
`Counter`. Проверка `data == sorted(data)` даёт `True` при нечитаемом порядке.
Первый отчёт сессии 11 («4 блока») и первый отчёт сессии 12 («по папке») — **ошибочны**,
Alex поймал оба.
5. **`НЕТ-В-38` / `ТОЛЬКО-В-38` — не мусор, а проверки полноты:**
- `ТОЛЬКО-В-38` (85) = пути из 38-го плана, которых **нет в `APPLY-PLAN.csv` вообще**
(проверка по basename: `0 из 85`). Состав: `._*.m4a` resource forks macOS 13 шт +
не-ID3 форматы — `.ogg` 40, `.m4a` 26, `.wma` 16, `.flac` 1. Новый план собран только
по mp3 (7861), поэтому эти форматы в него не попали by design.
- `НЕТ-В-38` (41) = есть в новом плане, не было в 38-м.
### 📌 Что делать дальше (порядок приоритета)
1. Получить от Alex правило для 51 папки с рваной нумерацией → применить тем же скриптом.
2. Перегенерировать `DIFF-new-vs-38.csv` (`python3 diff_new_vs_38.py > /tmp/log 2>&1`).
3. Применение плана к файлам — **отдельное «go»** от Alex, через `tagwriter:v2`
(см. `[[personal/plans/tagwriter-container]]`).
## 🔴 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 11 (2026-09-27) — ИСТОРИЧЕСКОЕ
```
APPLY-PLAN.csv 7861 строка md5 7679ee01a2aa8f28623b6fb8559d7381
@@ -72,9 +175,7 @@ tag-fix-plan.csv 7861 строка md5 203726ebfd2814e2328636df31337238
сортировки. Alex: «там album, albumartist, genre, потом снова album!»
2. **Хвост файла** — секции полноты `НЕТ-В-38`/`ТОЛЬКО-В-38` читаются как мусор.
⏳ **ЖДЁТ ОТВЕТА ALEX:** номер варианта починки сортировки (1 — по папке ⭐ / 2 — добавить
`scope` в ключ / 3 — вариант 1 + вынести хвост в отдельный файл). См. «🔷 СЕССИЯ 11» в конце
документа.
✅ **ЗАКРЫТО в сессии 12** — порядок исправлен (блоки полей слиты), см. выше.
📌 **Метод-урок:** проверка «отсортировано?» через `data == sorted(data)` даёт `True`,
хотя порядок нечитаем. Структуру файла описывать **явным списком переходов** с номерами
+82 -1
View File
@@ -29,6 +29,11 @@ related:
> ⚠️ **Обновлён до v1.5.0 (сессия 11):** правила **D9** (порядок блоков: монотонность ≠
> читаемость) и **D10** (служебные секции полноты в хвосте диффа). См. тот же раздел,
> а также `personal/plans/music-artist-albumartist-repair.md` § «🔷 СЕССИЯ 11».
>
> ⚠️ **Обновлён до v1.6.0 (сессия 12):** **D9** — починка порядка (слить `collapse_lines`
> и `file_lines` в один список, `field` первым + `scope` вторым; правка `key=` в двух
> раздельных циклах бесполезна); **D11** (`new_track` теряется при пустом `cur_track`);
> **D12** (`new_track` «ни к чему не привязан» → сдвинутый ряд пересчитать, иначе убрать).
## 📁 Артефакты проекта
@@ -381,7 +386,83 @@ z Другое…/C.Шнуров - Бумер (2003) 19 → Сергей
**Правило:** для отчёта о структуре выводить **явный список границ блоков** (`prev != cur` →
новая строка) с номерами строк, а не агрегат по `Counter`. Монотонность ≠ читаемость.
См. `personal/plans/music-artist-albumartist-repair.md` § «🔷 СЕССИЯ 11».
✅ **РЕШЕНО в сессии 12** — порядок: `field` первым, `scope` вторым. Дифф стал едиными
блоками по полю (`album` ПАПКА+ТРЕК, `albumartist` ПАПКА+ТРЕК, …), стык `track(ПАПКА) →
album(ТРЕК)` исчез.
🔴 **Питфолл починки (стоил 3 итерации):** правка `key=` внутри **двух раздельных циклов
записи** (`for … collapse_lines`, затем `for … file_lines`) **не даёт эффекта** — `scope`
в ключе не может поднять `ТРЕК album` выше `ПАПКА track`, потому что списки пишутся
последовательно. Нужно **слить оба списка в один** и отсортировать одним ключом:
```python
out_rows = []
for fld, fname, ov, nv, n in collapse_lines:
out_rows.append((fkey(fname), 0, fld, "", "ПАПКА", fld, "", fname, ov, nv))
for fld, rel, fname, ov, nv in file_lines:
out_rows.append((fkey(fname), 1, fld, rel, "ТРЕК", fld, rel, fname, ov, nv))
out_rows.sort(key=lambda x: x[:4])
```
**Признак, что правка не применилась:** `md5` файла **не изменился**. Проверять эффект
только по md5 + перечитыванию строк — «правка выглядит правильной» ничего не значит.
### 🔴 D11 — `new_track` теряется, когда `cur_track` пустой
Логика заполнения пропускает файл с пустым текущим тегом → номер из плана 38-го прогона
**не восстанавливается**. Найдено 4 строки:
```
old_38=17 → '' !Детские/Сказки/Алиса в Стране Чудес (Диск2)/04 - Песня про ребенка-поросенка.mp3
old_38= 8 → '' !Pop, Club, Любэ/Света/2003 - Зеркала/08 - Жизнь моя.mp3
old_38= 0 → '' !Pop, Club, Любэ/!Radio/t 2009-07 c6Or_NICK/00 Geo da Silva - ....mp3
old_38= 0 → '' !Rock&Folk/ЗАХАР МАЙ/2011 - Казань против диабета/00-intro-family-affair.mp3
```
**Детектор (гонять после каждой перегенерации плана) — по самому диффу:**
```python
lost = [r for r in rows if r["field"] == "track"
and (r["old_38"] or "").strip() and not (r["new_apply_plan"] or "").strip()]
```
Если `old_38` было числом, а `new_apply_plan` пусто — это **потеря**, а не правка.
Восстановление: перенести значение из `old_38` (`/tmp/restore_lost_track.py`).
### 🔴 D12 — `new_track` «ни к чему не привязан» → пересчитать или убрать
Правило Alex (сессия 12):
> «если там есть порядковый номер в альбоме — то ты его оставляешь»
> «если он ни к чему не привязан то убираешь»
Косяк, на который он указал: `!Детские/Сказки/Алиса в Стране Чудес (Диск2)/04` имел
`track=17` при **13 файлах** в папке (ряд `14..26` — это нумерация **диска 2**
многодискового издания, протёкшая в отдельную папку).
**Классификация по папке** (полный ряд проверять по числу файлов **с номерами**, не по
числу файлов):
| Класс | Признак | Действие | Строк |
|---|---|---|---|
| A. Ряд **сдвинут** | уникальные номера идут подряд без дыр, `len(uni)==n_files`, `uni[0] != 1` | пересчитать `1..N`: `new = old - (min(nums) - 1)` | 269 |
| B. **Полного ряда нет** | есть дыры/дубли/номера вне диапазона | номер из имени файла, иначе **пусто** | 1107 |
| C. Ряд `1..N` корректный | `nums == range(1, len(nums)+1)` | не трогать | 371 папок |
Извлечение номера из имени: `re.match(r"^(\d{1,3})\s*[-._)]?\s+", filename)`, диапазон `1..300`.
Примеры класса A: `Алиса (Диск2)` 14..26 → 1..13 · `Трое из Простоквашино (Диск 2)` 10..20 →
1..11 · `Принц и нищий (Диск 2)` 8..14 → 1..7 · `Малыш и Карлсон (Диск 2)` 4..6 → 1..3.
🔴 **Класс A требует полного ряда без дыр** — иначе пересчёт «съест» пропущенный трек.
Именно поэтому 51 папка `!Radio/*` (рваные ряды, дубли `2,2,3,3,5,5` у `x 2008-04`) **осталась
нетронутой** — для них правило ещё не получено.
**Не путать с `TRCK`-классом «`99` как плейсхолдер»:** `!Рэп/НТЛ` имеет 8 файлов с
`track=99` и 6 с `track=51` (`cur == new`, значение унаследовано). Это не номер трека.
Скрипты: `/tmp/fix_track_bugs.py` (сухой прогон по умолчанию, `--apply` для записи),
`/tmp/restore_lost_track.py`, `/tmp/audit_track_seq.py`, `/tmp/classify_track.py`.
См. `personal/plans/music-artist-albumartist-repair.md` § «🟢 СЕССИЯ 12».
### 🔴 D10 — секции полноты в хвосте диффа читаются как мусор