[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

+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 — секции полноты в хвосте диффа читаются как мусор