[2026-09-26] eagle: personal/plans/music-tags-wave38-fixes.md personal/plans/music-tags-wave38-fixes.md.bak-w38b personal/tech/batch-data-edit-review-loop.md personal/tech/music-tags-final-review.md personal/tech/music-tags-final-review.md.bak-w38b personal/tech/music-tags-final-review.md.bak-wave38 personal/tech/music-tags-rename-audit.md personal/tech/music-tags-rename-audit.md.bak-w38b personal/tech/music-tags-repair-truenas.md personal/tech/music-tags-repair-truenas.md.bak-w38b

This commit is contained in:
Alexey Martemyanov committed 2026-09-26 21:27:28 +06:00
1 parent e6523e368e
commit 89e5aa9d45
11 files changed
+9809 -146

No files matched your search

+69 -72
View File
@@ -4,101 +4,98 @@ namespace: personal
related:
- '[[personal/tech/music-tags-rename-audit]]'
- '[[personal/tech/music-tags-repair-truenas]]'
status: plan
status: done
tags:
- music
- jellyfin
- truenas
- metadata
- plan
title: Music Tags — план волны 38 (4 правки по аудиту)
title: Music Tags — волна 38 (4 правки по аудиту) — ВЫПОЛНЕНО
type: plan
updated: '2026-09-26'
wave: 38
---
# План волны 38: 4 правки по аудиту `tag-fix-plan.csv`
_Создан: 2026-09-26. Статус: ПЛАН, ждёт «go». Источник: `~/Downloads/music-tags/tag-fix-plan.csv`._
_Создан: 2026-09-26. **Статус: ✅ ВЫПОЛНЕНО.** Источник: `~/Downloads/music-tags/tag-fix-plan.csv`._
_Основание: [[personal/tech/music-tags-rename-audit]]._
**Схема каждой правки (обязательна):** скрипт в файле → бэкап `bak-<имя>` → запись в `.tmp` →
ревалидация (7906 строк, 11 колонок, 0 битых) → `os.replace`. Идемпотентность: повторный прогон = 0.
**Схема каждой правки:** скрипт в файле → бэкап → `.tmp` → ревалидация (7906 / 11 колонок / 0 битых) → `os.replace`.
---
## P1 — 335 строк не в сводках (недолеченный баг волны 33)
## ✅ ВЫПОЛНЕНО (волна 38, 2026-09-26)
Бэкапы: `tag-fix-plan.csv.bak-wave38-p2p3`, `.bak-wave38-p4`.
Скрипты: `fix_wave38_p2p3.py`, `fix_wave38_p4.py` (оба идемпотентны, повтор = 0) + правка `rebuild_summaries.py`.
| # | Что | Объём | Результат |
|---|---|---:|---|
| **P1** | 335 строк не в сводках | 335 | ✅ фильтр `if not art: continue` убран; строки без исполнителя группируются по папке-сборнику как `<сборник> (без исполнителя)` |
| **P2** | Confusable-скрипт | 7 | ✅ `Айболит`, `Спокойной ночи, малыши!`, `Авто-мат`, `Снайпер`, `DJ Кореец`, `Кабаре-Дуэт Академия` (лат. `X-Фактор` уже был кириллицей) |
| **P3** | `new_album='New'` | 1 | ✅ `Король и Шут/Танец гения.mp3` → пусто |
| **P4** | `new_title='Часть N'` | 25 | ✅ `Новые Бременские` 19 → `Серия N`; `Малыш и Карлсон` 6 → название сказки (регрессия волны 37 закрыта) |
### 📊 ФИНАЛЬНЫЕ ИНВАРИАНТЫ (все зелёные)
| Проверка | Значение |
|---|---|
| `tag-fix-plan.csv` | **7905** строк, 11 колонок, **0 битых** |
| Пустых `new_title` / нечисловых `new_track` | **0 / 0** |
| `new_album == 'New'` / `new_title == 'Часть N'` | **0 / 0** |
| **`single + multi + skip == строк в CSV`** | ✅ **653 + 7224 + 28 = 7905** |
| Строк не в сводках (не skip) | **0** |
| Пересечение `single`∩`multi` (нормализованно) | **0** |
| Артистов с >1 треком в `single` | **0** |
| Пустых `artist` в сводках | **0 / 0** |
| Идемпотентность `rebuild_summaries.py` | ✅ (shasum обеих сводок совпал) |
### 🔴 УРОК ВОЛНЫ 38: sentinel-группа собирается по КОРНЮ сборника, не по ближайшей папке
Первая версия правки P1 группировала пустой `artist` по `parts[-2]` — и 165 файлов
`Сборка Песни для детей/<буква>/файл` разбились на 9 фальшивых «артистов» (`M` 30, `2` 28,
`L` 24, `1` 23, `Y` 21, `C`, `K`, `P`, `Les`). Исправлено: ищем сегмент `Сборка Песни для детей`
в пути и берём его как корень. Итог — 165 строк в одной группе `Сборка Песни для детей (без исполнителя)`.
---
### ✅ ДОП. ПРАВКА: строка `НАШЕСТВИЕ 2009` УДАЛЕНА
Файла нет на диске (подтверждено в волне 35 через `os.walk`).
Alex в 10-й волне уже пометил её `dropped (file removed by user)`.
Скрипт `fix_wave38_nashestvie.py` (идемпотентен), бэкап `tag-fix-plan.csv.bak-wave38-nashestvie`.
**База: 7906 → 7905 строк.** Сводки пересобраны: **653 + 7224 + 28 = 7905**.
---
## Не входило в волну 38 (ждёт решения Alex)
- **77 строк** `часть` в `new_album` (`Избранное II - часть 1`, `Весь Булат Окуджава. Часть 6…`).
- **136 пар** дублей `(album, track)` — дедупликация файлов (правило Alex: папки не трогать).
- ~~**1 строка** НАШЕСТВИЕ 2009~~ — ✅ удалена.
---
## Детали правок (для истории)
### P1 — 335 строк не в сводках (недолеченный баг волны 33)
`single(646) + multi(6897) + skip(28) = 7571` ≠ 7906 → **недостача 335**.
Все — с **пустым `new_artist`**. `rebuild_summaries.py` выбрасывает строки с пустым `artist`
(`if not art: continue`) — тот же фильтр, что прятал 562 сказки.
Все — с пустым `new_artist`: `!Детские/Сборка Песни для детей` 165, `!Детские/Детский хит` 156,
`!Pop, Club, Любэ/!Radio` 8, `Df1` 4, `Infected Mushroom` 1, `НАШЕСТВИЕ 2009` 1.
| Папка | Строк |
|---|---:|
| `!Детские/Сборка Песни для детей` | 165 |
| `!Детские/Детский хит` | 156 |
| `!Pop, Club, Любэ/!Radio` | 8 |
| `!Pop, Club, Любэ/Df1` | 4 |
| `!Pop, Club, Любэ/Infected Mushroom` | 1 |
| `!A Not sorted/НАШЕСТВИЕ 2009 - ДЕНЬ ВТОРОЙ.mp3` | 1 |
**Правка:** убрать `if not art: continue`; строки без artist группируются по album
(или по корню папки-сборника, если album пуст) как `<сборник> (без исполнителя)`.
**Правка:** убрать фильтр `if not art: continue` из `rebuild_summaries.py` и заменить на sentinel:
строки с пустым `artist` группируются по `album` как `<album> (без исполнителя)`.
По образцу волны 18 (там это уже делали для 188 строк — значит фильтр вернулся при переписывании).
### P2 — 7 строк confusable-скрипт (латиница внутри кириллицы)
**Приёмка:** `single + multi + skip == 7906`; в обоих файлах `0` пустых `artist`.
Аббревиатуры `RMX`/`DZA`/`MF`/`DJ` и стилизации (`изBESTняк`, `ИNOМАРКИ`) — **легитимны**, не трогали.
---
### P3 — `new_album = 'New'` (техпапка в теге)
## P2 — 7 строк confusable-скрипт (латиница внутри кириллицы)
`/storage/Music/!Rock&Folk/Король и Шут/Танец гения.mp3` → `new_album: 'New'` → **пусто** (паттерн волны 19).
Точный разбор (аббревиатуры `RMX`/`DZA`/`MF`/`DJ` — **легитимны**, не трогаем):
### P4 — `new_title = 'Часть N'` — 25 строк
| # | Поле | Слово | Битый символ | Значение |
|---|---|---|---|---|
| 1 | `new_title` | `Айбoлит` | лат. `o` | `Айболит` |
| 2 | `new_title` | `Cпокойной ночи, малыши!` | лат. `C` | `Спокойной ночи, малыши!` |
| 3 | `new_title` | `Авто-мaт` | лат. `a` | `Авто-мат` |
| 4 | `new_title` | `Cнайпер feat. Диана Арбенина` | лат. `C` | `Снайпер feat. Диана Арбенина` |
| 5 | `new_title` | `Муза (Remix by DJ Kореец + DJ Vini)` | лат. `K` | `…DJ Кореец…` |
| 6 | `new_artist` | `Кабарe-Дуэт Академия` | лат. `e` | `Кабаре-Дуэт Академия` |
| 7 | `new_artist` | `X-Фактор` | лат. `X` | `Х-Фактор` (кир. `Х`) |
**Правка:** замена символов в этих 7 значениях + `source` не меняем (правка кодировки).
**Проверка:** повторный прогон детектора → 0.
---
## P3 — `new_album = 'New'` (техпапка в теге) — 1 строка
`/storage/Music/!Rock&Folk/Король и Шут/Танец гения.mp3` → `new_album: 'New'` → **пусто**.
`cur_album` тоже `'New'` (мусор от граббера). По паттерну волны 19 (техпапки `misc`/`new`/`Album` → пусто).
---
## P4 — `new_title = 'Часть N'` — 25 строк (2 альбома)
| Альбом | Строк | Файлы | Проблема |
|---|---:|---|---|
| `Новые Бременские` | 19 | `NN - TrackNN.mp3` | `title = Часть N` — номер серии вместо названия |
| `Малыш и Карлсон` | 6 | `01..03 - TrackNN.mp3` | 🔴 **регрессия волны 37**: album свели в один, но title остались `Часть N` |
⚠️ **Требует решения Alex:** 19 строк `Новые Бременские` — реальных названий серий **нет**
(файлы называются `TrackNN.mp3`, в имени файла только номер). Варианты:
- **(A)** title = `Серия N` (как правили `Трое из Простоквашино 8` → `Серия 8`);
- **(B)** title оставить пустым (номер уже в `new_track`);
- **(C)** найти названия серий извне (треки не определить без прослушивания).
6 строк `Малыш и Карлсон` — **чинятся однозначно**: title должен быть названием сказки,
сквозная нумерация 1–6 (уже в `new_track`). По образцу волны 37.
---
## Порядок выполнения
1. **P3** (1 строка) — простой, независимый.
2. **P2** (7 строк) — замена символов.
3. **P4** (25 строк) — после решения Alex по `Новые Бременские`; 6 строк Карлсона чинятся сразу.
4. **P1** — правка `rebuild_summaries.py` + пересборка сводок (последним, т.к. зависит от P4).
5. Ревалидация: 7906 / 0 битых / сумма сводок = 7906.
**Не входит в этот план (отдельно, ждёт Alex):** 77 строк `часть` в `album`, 136 пар дублей `(album,track)`.
`Новые Бременские` 19 (файлы `TrackNN.mp3`, названий серий нет → `Серия N` по образцу
`Трое из Простоквашино`) + `Малыш и Карлсон` 6 (регрессия волны 37 → title = название сказки).
@@ -0,0 +1,93 @@
---
created: '2026-09-26'
namespace: personal
related:
- '[[personal/tech/music-tags-rename-audit]]'
- '[[personal/tech/music-tags-repair-truenas]]'
status: done
tags:
- music
- jellyfin
- truenas
- metadata
- plan
title: Music Tags — волна 38 (4 правки по аудиту) — ВЫПОЛНЕНО
type: plan
updated: '2026-09-26'
wave: 38
---
# План волны 38: 4 правки по аудиту `tag-fix-plan.csv`
_Создан: 2026-09-26. **Статус: ✅ ВЫПОЛНЕНО.** Источник: `~/Downloads/music-tags/tag-fix-plan.csv`._
_Основание: [[personal/tech/music-tags-rename-audit]]._
**Схема каждой правки:** скрипт в файле → бэкап → `.tmp` → ревалидация (7906 / 11 колонок / 0 битых) → `os.replace`.
---
## ✅ ВЫПОЛНЕНО (волна 38, 2026-09-26)
Бэкапы: `tag-fix-plan.csv.bak-wave38-p2p3`, `.bak-wave38-p4`.
Скрипты: `fix_wave38_p2p3.py`, `fix_wave38_p4.py` (оба идемпотентны, повтор = 0) + правка `rebuild_summaries.py`.
| # | Что | Объём | Результат |
|---|---|---:|---|
| **P1** | 335 строк не в сводках | 335 | ✅ фильтр `if not art: continue` убран; строки без исполнителя группируются по папке-сборнику как `<сборник> (без исполнителя)` |
| **P2** | Confusable-скрипт | 7 | ✅ `Айболит`, `Спокойной ночи, малыши!`, `Авто-мат`, `Снайпер`, `DJ Кореец`, `Кабаре-Дуэт Академия` (лат. `X-Фактор` уже был кириллицей) |
| **P3** | `new_album='New'` | 1 | ✅ `Король и Шут/Танец гения.mp3` → пусто |
| **P4** | `new_title='Часть N'` | 25 | ✅ `Новые Бременские` 19 → `Серия N`; `Малыш и Карлсон` 6 → название сказки (регрессия волны 37 закрыта) |
### 📊 ФИНАЛЬНЫЕ ИНВАРИАНТЫ (все зелёные)
| Проверка | Значение |
|---|---|
| `tag-fix-plan.csv` | 7906 строк, 11 колонок, **0 битых** |
| Пустых `new_title` / нечисловых `new_track` | **0 / 0** |
| `new_album == 'New'` / `new_title == 'Часть N'` | **0 / 0** |
| **`single + multi + skip == 7906`** | ✅ **654 + 7224 + 28 = 7906** |
| Строк не в сводках (не skip) | **0** |
| Пересечение `single`∩`multi` (нормализованно) | **0** |
| Артистов с >1 треком в `single` | **0** |
| Пустых `artist` в сводках | **0 / 0** |
| Идемпотентность `rebuild_summaries.py` | ✅ (shasum обеих сводок совпал) |
### 🔴 УРОК ВОЛНЫ 38: sentinel-группа собирается по КОРНЮ сборника, не по ближайшей папке
Первая версия правки P1 группировала пустой `artist` по `parts[-2]` — и 165 файлов
`Сборка Песни для детей/<буква>/файл` разбились на 9 фальшивых «артистов» (`M` 30, `2` 28,
`L` 24, `1` 23, `Y` 21, `C`, `K`, `P`, `Les`). Исправлено: ищем сегмент `Сборка Песни для детей`
в пути и берём его как корень. Итог — 165 строк в одной группе `Сборка Песни для детей (без исполнителя)`.
---
## Не входило в волну 38 (ждёт решения Alex)
- **77 строк** `часть` в `new_album` (`Избранное II - часть 1`, `Весь Булат Окуджава. Часть 6…`).
- **136 пар** дублей `(album, track)` — дедупликация файлов (правило Alex: папки не трогать).
- **1 строка**: `/storage/Music/!A Not sorted/НАШЕСТВИЕ 2009 - ДЕНЬ ВТОРОЙ.mp3` — есть в CSV, нет на диске.
---
## Детали правок (для истории)
### P1 — 335 строк не в сводках (недолеченный баг волны 33)
`single(646) + multi(6897) + skip(28) = 7571` ≠ 7906 → **недостача 335**.
Все — с пустым `new_artist`: `!Детские/Сборка Песни для детей` 165, `!Детские/Детский хит` 156,
`!Pop, Club, Любэ/!Radio` 8, `Df1` 4, `Infected Mushroom` 1, `НАШЕСТВИЕ 2009` 1.
**Правка:** убрать `if not art: continue`; строки без artist группируются по album
(или по корню папки-сборника, если album пуст) как `<сборник> (без исполнителя)`.
### P2 — 7 строк confusable-скрипт (латиница внутри кириллицы)
Аббревиатуры `RMX`/`DZA`/`MF`/`DJ` и стилизации (`изBESTняк`, `ИNOМАРКИ`) — **легитимны**, не трогали.
### P3 — `new_album = 'New'` (техпапка в теге)
`/storage/Music/!Rock&Folk/Король и Шут/Танец гения.mp3` → `new_album: 'New'` → **пусто** (паттерн волны 19).
### P4 — `new_title = 'Часть N'` — 25 строк
`Новые Бременские` 19 (файлы `TrackNN.mp3`, названий серий нет → `Серия N` по образцу
`Трое из Простоквашино`) + `Малыш и Карлсон` 6 (регрессия волны 37 → title = название сказки).