[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/tech/batch-data-edit-review-loop.md personal/tech/whale-soul-answer-format.md
This commit is contained in:
1 parent
204b254cc5
commit
d787749dd5
3 files changed
+334
-1
No files matched your search
@@ -4,7 +4,7 @@ created: '2026-09-27'
|
||||
updated: '2026-09-27'
|
||||
type: plan
|
||||
namespace: personal
|
||||
status: session29okti-CANON-DIFF-v3-BUILT-SORT-FIXED
|
||||
status: session29okti-CANON-DIFF-v3-BUILT-GENRE-GAP-FOUND
|
||||
tags:
|
||||
- music
|
||||
- metadata
|
||||
@@ -21,6 +21,169 @@ related:
|
||||
- '[[family/how-to/navidrome-truenas-android]]'
|
||||
# Music `artist` + `albumartist` Repair — правила Alex и ход работ
|
||||
|
||||
## ✅ СЕССИЯ 29-ОКТИ (2026-09-27, вечер) — КАНОННЫЙ ДИФФ v3 СОБРАН: СОРТИРОВКА ПО `field`, ЖАНРЫ ПРОПУЩЕНЫ ПРОГОНОМ
|
||||
|
||||
> **ТРИГГЕР:** «**диф где блядь**» → «**почему сортировки по филду нету в диффе**» →
|
||||
> «**!Детские/Из чего же.mp3 - где альбом**» → «**Андервуд надо исправить еще**» →
|
||||
> «**\"!rock_o_pop\" - вот это отщепилось**» → «**вот всегда бы ты так отвечал**»
|
||||
|
||||
### 🎯 ГЛАВНЫЙ АРТЕФАКТ СЕССИИ
|
||||
|
||||
```
|
||||
Файл: ~/Downloads/music-tags/DIFF-base-vs-applyplan-v3.csv
|
||||
md5: bdb20c6ae00e7dfab4da2f76653fe084
|
||||
строк: 945 (ПАПКА=711, ТРЕК=234)
|
||||
поля: album 28 · albumartist 6 · genre 427
|
||||
источник правок: /tmp/apply-plan-v3.csv (7861 строк)
|
||||
сборщик: /tmp/build_canon_diff_v3.py
|
||||
```
|
||||
|
||||
**Формат канонного диффа:** `scope,folder,path,field,old,new` (`scope` = ПАПКА | ТРЕК).
|
||||
|
||||
### 🔴 СОРТИРОВКА ПО `field` — ТРЕБОВАНИЕ ALEX, ИСПРАВЛЕНО
|
||||
|
||||
Первый сборщик сортировал по `(folder, field, new)` — `field` был **вторым** ключом, поэтому
|
||||
внутри папки строки ПАПКА/ТРЕК чередовались, а `field` повторялся вразнобой.
|
||||
|
||||
**Фикс:** ключ сортировки `(field, folder, path)` — `field` ПЕРВЫМ.
|
||||
Порядок в файле теперь строго блочный: `album` → `albumartist` → `genre`.
|
||||
|
||||
```python
|
||||
FIELD_ORDER = {f: i for i, f in enumerate(FIELDS)} # title, artist, albumartist, album, genre, track
|
||||
rows.sort(key=lambda r: (FIELD_ORDER[r[3]], r[1], r[2], r[5]))
|
||||
```
|
||||
|
||||
### 🔴🔴 ГЛАВНЫЙ ДЕФЕКТ, ВСКРЫТЫЙ СЕССИЕЙ: ЖАНРЫ НЕ ЗАПИСАНЫ В ТЕГИ ВООБЩЕ
|
||||
|
||||
Вопрос Alex: «**жанры почему пропали есть понимание?**»
|
||||
|
||||
**Факт из базы Navidrome:**
|
||||
```
|
||||
в базе genre пусто: 7861 из 7861 ← ВСЕ файлы
|
||||
в базе genre непусто: 0
|
||||
в плане new_genre есть: 7861 из 7861
|
||||
расхождение: 7861 (не 911!)
|
||||
```
|
||||
|
||||
**Причина — НЕ потеря, а пропуск при прогоне.** В `tagwriter_verify_full.log` из полей
|
||||
присутствуют только `TALB` 29 · `TIT2` 25 · `TPE1` 98 · `TRCK` 102 — **`TCON` (genre)
|
||||
отсутствует полностью**. Боевой прогон 27 сен (7649/7649 ok) `TCON` не записал →
|
||||
Navidrome читает теги и видит пусто.
|
||||
|
||||
🔑 **Это не «правка жанра», а ПОВТОРНАЯ ЗАПИСЬ того, что прогон пропустил.** Все 7861 файл
|
||||
нужно записать заново по `genre`.
|
||||
|
||||
**Ловушка числа 911:** я сначала доложил «genre 911 строк» — Alex-наблюдаемое расхождение.
|
||||
Правильная формулировка: **911 — это число ГРУПП `(папка, жанр)`, а не строк диффа**.
|
||||
Групп 911 → после схлопывания 427 строк (366 ПАПКА + 61 ТРЕК) → покрытие **7861 файл, 100%**.
|
||||
|
||||
### 🔴 ТРЕКИ В БАЗЕ — ОК, ПРАВКИ `N → N/M` БЫЛИ ШУМОМ
|
||||
|
||||
Вопрос Alex: «**с треками в базе navidrome в итоге все ок?**»
|
||||
|
||||
```
|
||||
всего файлов: 7861
|
||||
track_number = '0': 1581 ← нет трека, это НОРМА
|
||||
track_number заполнен: 6280
|
||||
track_number с '/': 0 ← чисто
|
||||
track_number с ведущим 0: 0 ← чисто
|
||||
```
|
||||
|
||||
**Решение Alex: `6` → `6/14` не править.** Обоснование согласовано:
|
||||
1. `track_number` в ID3 — целое; `6` и `6/14` для плеера одно и то же значение
|
||||
2. `TRCK` с `/` ломает часть парсеров и сортировку в Navidrome
|
||||
3. Правилось бы 117 файлов ради нуля изменений по существу
|
||||
|
||||
🔴 **ШУМ-ФИЛЬТР ВШИТ В САМ СБОРЩИК** (строки 99-114 `build_canon_diff_v3.py`), а не в
|
||||
отдельный скрипт — иначе регрессия возвращается при каждой пересборке:
|
||||
|
||||
```python
|
||||
def _is_track_noise(r): # track N -> N/M и N -> 0N == не правка
|
||||
if r[3] != "track": return False
|
||||
old, new = r[4].strip(), r[5].strip()
|
||||
m = re.fullmatch(r"(\d+)/(\d+)", new)
|
||||
if m and old == m.group(1): return True
|
||||
m2 = re.fullmatch(r"0+(\d+)", new)
|
||||
if m2 and old == m2.group(1): return True
|
||||
```
|
||||
|
||||
✅ **Проверка идемпотентности:** повторный прогон сборщика → те же 945 строк.
|
||||
Регрессия убрана, подтверждено 4 способами (фильтр в файле, `track`=0 в диффе, `N/M`=0 в
|
||||
плане, повторный прогон стабилен).
|
||||
|
||||
### 🔴 ПИТФОЛЛ: `track N → N/M` РЕГРЕССИРУЕТ, ЕСЛИ ФИЛЬТР СНАРУЖИ
|
||||
|
||||
Первый раз я убрал 117 шумовых строк **отдельным скриптом** (`/tmp/drop_track_noise.py`).
|
||||
Следующая пересборка диффа вернула их все — сборщик о фильтре не знал.
|
||||
🔑 **Фильтр вывода живёт ВНУТРИ генератора, не в пост-обработчике.**
|
||||
|
||||
### 🔴 ФИКСЫ, ВНЕСЁННЫЕ В ЭТОЙ СЕССИИ
|
||||
|
||||
| # | Что | Было → Стало | Файл |
|
||||
|---|---|---|---|
|
||||
| 1 | Опечатка в имени папки протекла в `album` | `Андервуд` → **`Ундервуд`** | `!A Not sorted/SD-card-2026-05/Андервуд/Гагарин, я вас любила.mp3` |
|
||||
| 2 | Битый `cur_album` дал ПУСТОЙ `new_album` | `Íåèçâåñòíîå íàçâàíèå` → **`Детские`** | `!Детские/Из чего же.mp3` |
|
||||
| 3 | `!` не снесён → папка разъехалась на два значения | `!rock_o_pop` → **`rock_o_pop`** | `Рок-Группа - Попса.mp3`, `АлисА - Стать севера.mp3` (2 файла) |
|
||||
| 4 | Альбом не совпадал с основным | `Руки` → **`Руки вверх`** | `!Retro/Руки/*` (5 файлов) |
|
||||
| 5 | `track N/M` | `7/19` → **`7`** | 117 файлов |
|
||||
| 6 | Сортировка диффа | по `folder` → **по `field`** | сборщик |
|
||||
|
||||
Скрипты: `/tmp/fix_v3_typos.py`, `/tmp/fix_ruki.py`, `/tmp/fix_rockopop_track.py`,
|
||||
`/tmp/drop_track_noise.py`, `/tmp/build_canon_diff_v3.py`.
|
||||
|
||||
**Бэкапы:** `/tmp/apply-plan-v3.csv.bak-20260927-204950`, `-205732`, `-205822`,
|
||||
`/tmp/DIFF-v3.bak-*.csv`, `/tmp/DIFF-v3.bak2-*.csv`.
|
||||
|
||||
### 🔴 РЕШЕНИЕ ALEX: КЛАСС «ПАПКА-МУСОРКА» — ЭТО ПЛАН, НЕ ДЕФЕКТ
|
||||
|
||||
Детектор «файл в корне + мусорный `album`» дал 76 файлов:
|
||||
```
|
||||
44 !Барды/Владимир Высоцкий/Разное album='Разное'
|
||||
14 !Rock&Folk/Пилот/Избранное album='Избранное'
|
||||
10 !Rock&Folk/ДДТ/2003 - песни album='Песни'
|
||||
3 !Rock&Folk/Наутилус Помпилиус/2000 - песни album='песни'
|
||||
3 !Rock&Folk/Rammstein/misc album='misc'
|
||||
1 !A Not sorted/Агата Кристи album='Избранное'
|
||||
```
|
||||
**Alex (дословно):** «**план такой и был что папку Разное мы кидаем как альбом \"Разное\"
|
||||
а не разгребаем**».
|
||||
|
||||
🔑 **Класс ЗАКРЫТ, действий не требуется.** Папка-мусорка остаётся альбомом.
|
||||
**Правило:** перед «фиксом» мусорного `album` — спросить, не план ли это.
|
||||
|
||||
### 🔴 ПИТФОЛЛ ДЕТЕКТОРА: `<NA>`-ЛОГИКА ДАЁТ ТЫСЯЧИ ЛОЖНЫХ СРАБАТЫВАНИЙ
|
||||
|
||||
Мой детектор «правило корня не применилось» (`folder == parent`, `new_album != basename(folder)`)
|
||||
выдал **6161** файл — это шум. Он валится на файлах, где `new_album` **осмысленный**
|
||||
(`Все пройдёт милая`, `Попса`, `Классика`) — правило корня там и **не должно** применяться.
|
||||
|
||||
🔑 **Правило корня нужно только там, где `new_album` мусорный/пустой.**
|
||||
Правильная формулировка детектора: `is_junk(new_album)` — словарь заглушек
|
||||
(`non spécifié`, `null`, `no title`, `album`, `mix`, `misc`, `песни`, `избранное`, `разное`,
|
||||
`[unknown album]`, `неизвестное название`...) + подстроки водяных знаков (`muzmo.ru`,
|
||||
`melody.az`) + `\d{4}` (только год). Скрипт: `/tmp/diag_root_junk.py`.
|
||||
|
||||
### 🔑 ФОРМАТ ОТЧЁТА, ОДОБРЕННЫЙ ALEX
|
||||
|
||||
Alex: «**вот всегда бы ты так отвечал**» — про ответ, который начинался с артефакта.
|
||||
|
||||
Записано в `~/.hermes/hermes-whale/SOUL.md` (строка 82, блок **Verified answer format**,
|
||||
+17 строк, md5 `36c85871b9f94929dedc2a4979e3a119`, бэкап `SOUL.md.bak-20260927-205340`):
|
||||
|
||||
1. **Строка 1 = артефакт.** Путь + `md5` + счёт из САМОГО файла (`wc -l`, `grep -c`). Ничего до него.
|
||||
2. **Что изменено** — список `что → почему`, без хеджинга.
|
||||
3. **Бэкапы.**
|
||||
|
||||
Никогда не открывать «сейчас проверю». Факт-проверка ДО отчёта, числа из файла.
|
||||
**Alex спорит с числом → не повторять цифру**, дать правило счёта + спросить
|
||||
«какое число ждёшь и откуда».
|
||||
|
||||
### 🔴 ПИТФОЛЛ: `grep` ПО ВСЕМУ ФАЙЛУ (повтор из сессии 29-СЕПТИ, но с новым лицом)
|
||||
|
||||
Проверяя фиксы, я грепал `APPLY-PLAN.csv` целиком и получал «опечатки на месте».
|
||||
Верный метод — **по КОЛОНКЕ через CSV-парсер**: `/tmp/check_shortlist_full.py`,
|
||||
`/tmp/check_typo_fixes.py`. Файл один и тот же, опечатки живут в `title`.
|
||||
|
||||
## ✅ СЕССИЯ 29-СЕПТИ (2026-09-27) — ВЕРИФИКАЦИЯ «КОРОТКОГО СПИСКА ОПЕЧАТОК»: ВСЁ УЖЕ СТОИТ, v3 НИЧЕГО НЕ ТЕРЯЕТ
|
||||
|
||||
> **ТРИГГЕР:** «**учел фикс опечаток типа "Муммий тролль" "филосовские" из того же короткого списка??**»
|
||||
|
||||
Reference in new issue
Block a user