[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md
This commit is contained in:
1 parent
891942e1d6
commit
2332ce79fb
1 file changed
+170
-30
@@ -4,7 +4,7 @@ created: '2026-09-27'
|
||||
updated: '2026-09-27'
|
||||
type: plan
|
||||
namespace: personal
|
||||
status: ПЛАН-v4-СОБРАН / ДИФФ-v4-СОБРАН / ДУБЛИ-УДАЛЕНЫ-50 / 2-ВОПРОСА-ОТКРЫТЫ
|
||||
status: ДИФФ-ОТ-БД-СОБРАН / ПРОГОН-v5-ЖДЁТ-АЛЕКСА / 215-ОДИНОЧЕК-ОТКРЫТО
|
||||
tags:
|
||||
- music
|
||||
- metadata
|
||||
@@ -35,44 +35,38 @@ related:
|
||||
**Что делаем:** привести ID3-теги библиотеки `/mnt/RED_2TB/storage/Music` (7861 файл)
|
||||
в порядок, чтобы Navidrome показывал правильные альбомы/исполнителей/жанры.
|
||||
|
||||
**Главный артефакт на сейчас (27 сен, сессия ~34):**
|
||||
**Главный артефакт на сейчас (27 сен, сессия ~35):**
|
||||
|
||||
```
|
||||
ДИФФ: ~/Downloads/music-tags/DIFF-canon-v4.csv
|
||||
90 строк (ПАПКА 26 · ТРЕК 64) — схлопнуто из 428 правок
|
||||
⚠️ ДИФФ ПЕРЕСОБРАН ОТ БАЗЫ (v3 был собран от КАНОНА — НЕВЕРНЫЙ ИСТОЧНИК!)
|
||||
|
||||
ДИФФ: ~/Downloads/music-tags/DIFF-db-to-fix.csv
|
||||
91 строка (ПАПКА 26 · ТРЕК 65) — схлопнуто из 429 правок
|
||||
сортировка: по АЛФАВИТУ field (album → albumartist → artist → track)
|
||||
План: ~/Downloads/music-tags/apply-plan-v4.csv (7861 строка)
|
||||
Лог: ~/Downloads/music-tags/DIFF-canon-v4-changes.csv (428 строк, построчно, с причиной)
|
||||
Сборщик: ~/Downloads/music-tags/build_diff_canon_v4.py
|
||||
Удалено: ~/Downloads/music-tags/DELETE-dup-trofim.csv (50 дублей Трофима — ✅ снесены)
|
||||
Канон: ~/Downloads/music-tags/APPLY-PLAN.csv (7861 строка — НЕ ПЕРЕЗАПИСЫВАТЬ, эталон)
|
||||
Бэкап канона: /tmp/APPLY-PLAN.csv.bak-before-v4
|
||||
источник цели: БД Navidrome (то, что РЕАЛЬНО в тегах), НЕ канон
|
||||
План: ~/Downloads/music-tags/apply-plan-v5.csv (7861 строка)
|
||||
Сборщик: ~/Downloads/music-tags/build_diff_from_db.py
|
||||
Канон: ~/Downloads/music-tags/APPLY-PLAN.csv (7861 строка — эталон ручных правок, НЕ ПЕРЕЗАПИСЫВАТЬ)
|
||||
```
|
||||
|
||||
⚠️ **Сборщики диффа/плана писать СРАЗУ в `~/Downloads/music-tags/`, НЕ в `/tmp`.**
|
||||
Alex: «какого хуя он в тмп» — артефакт в `/tmp` он не находит.
|
||||
|
||||
**Состояние (27 сен, сессия ~34):** ✅ **БОЕВОЙ ПРОГОН v3 выполнен** (7653/7653 ok, err=0,
|
||||
🔴 **ГЛАВНЫЙ УРОК СЕССИИ ~35: ДИФФ СТРОИТЬ ОТ БД, А НЕ ОТ КАНОНА.**
|
||||
Первый дифф v4 был собран от `APPLY-PLAN.csv` (канон) — и **не содержал Птаху**,
|
||||
хотя дефект в базе был: прогон v3 записал испорченное значение, диск и БД **ушли
|
||||
вперёд канона**. Alex: «в смысле правильно? в базе блядь у него что?!».
|
||||
Правило: источник истины = `media_file` из `navidrome.db`, канон — только эталон
|
||||
ручных решений. Подробно → § ДИФФ ОТ БАЗЫ.
|
||||
|
||||
**Состояние (27 сен, сессия ~35):** ✅ **БОЕВОЙ ПРОГОН v3 выполнен** (7653/7653 ok, err=0,
|
||||
`TCON` 4359, БД Navidrome пересоздана, скан 4m13s, импортировано 7861).
|
||||
✅ **50 дублей Трофима удалены с диска.** ⏳ **Прогон v5 НЕ сделан** — ждём решения Alex
|
||||
по 1 вопросу (§ ОТКРЫТЫЙ ВОПРОС).
|
||||
|
||||
✅ **ПЛАН v4 СОБРАН И ДИФФ ПОКАЗАН.** База сборки — **КАНОН** `APPLY-PLAN.csv` (НЕ план v3:
|
||||
v3 испорчен багой `album==title`; сборка от канона лечит 20 файлов автоматически).
|
||||
Правок 428: `albumartist 157 · artist 154 · album 59 · track 58`.
|
||||
Верификация v4 — 4 проверки, **0 ошибок** (`/tmp/verify_v4.py`): пути 1:1, каждая строка
|
||||
дифа подтверждается, изменений вне дифа 0, ложных срабатываний класса Птахи 0.
|
||||
|
||||
✅ **ДУБЛИ ТРОФИМА УДАЛЕНЫ** — 50 файлов из `disk/` + `disk_v2/` снесены по команде Alex
|
||||
(`/tmp/del_dups.py`, удалено 50/50, ошибок 0). `!Рэп/Трофим` цел (157 файлов).
|
||||
|
||||
⏳ **ОСТАЛОСЬ — 2 ВОПРОСА К ALEX (блокируют прогон v4):**
|
||||
1. **`10 Ностальгия` / `Боже, какой пустяк`** — дубль ВНУТРИ `!Рэп/Трофим`, `album='Ветер
|
||||
в голове'`. Удалять файл или сменить `album` на `Ностальгия`? В дифе его НЕТ (не трогал).
|
||||
2. **«Тема дороги» `artist`** — в дифе `Бумер → Ленинград` (по имени папки). Оставляем?
|
||||
Альтернативы: `Сергей Шнуров`, `Various Artists`.
|
||||
|
||||
**Порядок после ответов:** подтвердить диф → прогон v4 (`--apply`, план подложить как
|
||||
`apply-plan.csv`) → верификация чтением → **пересоздать БД Navidrome** (после удаления
|
||||
50 файлов рескан обязателен) → приёмка Alex в UI.
|
||||
**Порядок после ответа:** подтвердить диф `DIFF-db-to-fix.csv` → прогон v5 (`--apply`,
|
||||
план `apply-plan-v5.csv` подложить как `apply-plan.csv`) → верификация чтением →
|
||||
**пересоздать БД Navidrome** → приёмка Alex в UI.
|
||||
|
||||
```
|
||||
Navidrome БД: /mnt/RED_2TB/docker/navidrome/data/ ← БЕЗ storage/ (дока ранее врала)
|
||||
@@ -120,6 +114,10 @@ compose: /mnt/RED_2TB/docker/navidrome
|
||||
| 26 | 27 сен | ✅ **ВЕРИФИКАЦИЯ v4** — 4 проверки, 0 ошибок | `/tmp/verify_v4.py` |
|
||||
| 27 | 27 сен | ✅ **ДИФФ v4 СОБРАН ПРАВИЛЬНО** — 90 строк (ПАПКА 26 · ТРЕК 64), сортировка по алфавиту field, схлопывание | `DIFF-canon-v4.csv` |
|
||||
| 28 | 27 сен | 🔴 **ПЕРЕДЕЛКА ДИФФА** — первый дифф был НЕ сортирован и НЕ схлопнут (428 построчных строк) | § ПЕРЕДЕЛКА ДИФФА |
|
||||
| 29 | 27 сен | 🔴 **ДИФФ v4 НЕВЕРЕН: собран от КАНОНА** — Птахи в нём нет, а дефект в базе есть | § ДИФФ ОТ БАЗЫ |
|
||||
| 30 | 27 сен | ✅ **ДИФФ ПЕРЕСОБРАН ОТ БД** — источник истины = `media_file`, 91 строка из 429 правок | `DIFF-db-to-fix.csv` |
|
||||
| 31 | 27 сен | 🔴 **ЛОЖНОЕ РАСХОЖДЕНИЕ ПО ЖАНРУ** — `media_file.genre` пуст у всех, жанр в `tags` jsonb | `build_diff_from_db.py` |
|
||||
| 32 | 27 сен | 🔴 **ДЕФЕКТ «АЛЬБОМЫ-ОДИНОЧКИ»** — 215 альбомов из 1 трека, где `album != имя папки` | § АЛЬБОМЫ-ОДИНОЧКИ |
|
||||
|
||||
---
|
||||
|
||||
@@ -654,6 +652,108 @@ docker run --rm --entrypoint python3 \
|
||||
|
||||
---
|
||||
|
||||
## 🔴 ДИФФ ОТ БАЗЫ — почему канон НЕ источник истины (27 сен, сессия ~35)
|
||||
|
||||
**Alex: «Птаху куда проёб?» → «в смысле правильно? в базе блядь у него что?!»**
|
||||
|
||||
Первый дифф v4 (`DIFF-canon-v4.csv`) был собран **от канона** `APPLY-PLAN.csv`.
|
||||
**Птахи в нём не было** — потому что в каноне у неё уже стояло верное `'Красивые, богатые'`.
|
||||
Но прогон v3 записал испорченное значение **на диск**, и диск/БД ушли вперёд канона.
|
||||
|
||||
**Факт из базы** (`~/Downloads/navidrome-db/navidrome-after-v3.db`):
|
||||
```
|
||||
album_id=6JB3AabN89EM4u4bbYh3AG 'Красивые, богатые' 7 файлов
|
||||
album_id=40q9QYDnHNgfQhQTtjZE1c 'Птаха - Красивые, богатые (2009)' 1 файл ← ЛИШНИЙ
|
||||
```
|
||||
Лишний файл — `Птаха - Красивые, богатые! (feat. Сидр).mp3`.
|
||||
|
||||
### 🔑 ПРАВИЛО (обязательно к исполнению)
|
||||
|
||||
**Дифф строить ОТ БД (`media_file` в `navidrome.db`), а НЕ от канона.**
|
||||
|
||||
| Источник | Роль |
|
||||
|---|---|
|
||||
| `media_file` из `navidrome.db` | **ИСТИНА** — что реально в тегах сейчас |
|
||||
| `APPLY-PLAN.csv` (канон) | эталон **ручных решений** Alex, а не текущего состояния |
|
||||
|
||||
Причина: любой прогон меняет диск, и канон мгновенно устаревает. Дифф от канона
|
||||
**слеп к дефектам, уже записанным в теги**.
|
||||
|
||||
**Сборщик:** `~/Downloads/music-tags/build_diff_from_db.py`
|
||||
**Выход:** `DIFF-db-to-fix.csv` (91 строка) + `apply-plan-v5.csv` (7861)
|
||||
|
||||
---
|
||||
|
||||
## 🔴 ЛОЖНОЕ РАСХОЖДЕНИЕ ПО ЖАНРУ — `media_file.genre` ВСЕГДА ПУСТ
|
||||
|
||||
При сборке дифа от БД всплыло «`genre`: 7861 изменений» — **все файлы**.
|
||||
Это **ложная тревога**, а не дефект.
|
||||
|
||||
```
|
||||
media_file.genre = '' (пусто у ВСЕХ 7861 — колонка УСТАРЕВШАЯ)
|
||||
media_file.tags (jsonb) = {"genre":[{"id":"...","value":"Бардовская песня"}]} ← ЖАНР ЗДЕСЬ
|
||||
tag / media_file_tags = 438 значений, 7861 связок
|
||||
album_tags = 1010 связок
|
||||
```
|
||||
|
||||
🔑 **Жанр в этой версии Navidrome читать ТОЛЬКО из `tags` (jsonb)** — или из
|
||||
таблиц `tag`/`media_file_tags`/`album_tags`. Колонка `media_file.genre` — рудимент,
|
||||
всегда пуста. Иначе получишь ложное «7861 расхождение».
|
||||
|
||||
```python
|
||||
def genre_from_jsonb(tags):
|
||||
d = json.loads(tags or "{}")
|
||||
g = d.get("genre")
|
||||
return g[0]["value"] if isinstance(g, list) and g else ""
|
||||
```
|
||||
|
||||
**Итог после фикса:** `genre` в дифе = **0**, изменения: `albumartist 157 · artist 154 · album 60 · track 58` = **429**.
|
||||
|
||||
---
|
||||
|
||||
## 🔴 АЛЬБОМЫ-ОДИНОЧКИ — 215 альбомов из 1 трека (27 сен, сессия ~35)
|
||||
|
||||
**Alex: «что такое Агата Кристи/Симпатии: Чайф - Бесса мэ? я так понимаю трибьют,
|
||||
но что за Симпатии и почему один трек альбома?»**
|
||||
|
||||
**Ответ:** `Симпатии` — **трибьют-сборник** Агаты Кристи (2000). Чайф (Владимир Шахрин)
|
||||
записал туда свою версию «Бесса мэ». Файл:
|
||||
```
|
||||
!Rock&Folk/Агата Кристи/9999 - Разное/Кавер-версии и пародии/
|
||||
2000 (Чайф-Симпатии) - Владимир Шахрин - Бесса мэ.mp3
|
||||
title='Бесса мэ' album='Симпатии' artist='Чайф' albumartist='Агата Кристи' trk=0
|
||||
```
|
||||
|
||||
**Причина одиночества:** папка `Кавер-версии и пародии` содержит **24 файла**, и
|
||||
каждый получил `album` = **имя своего сборника-источника** вместо имени папки:
|
||||
```
|
||||
album='Симпатии' ← Чайф (1 файл)
|
||||
album='Шипы и розы' ← Стрелки (1)
|
||||
album='Некрофилия' ← Братья Торч (1)
|
||||
album='Песок' / 'Slot 1' / 'Back From USSR' / ... (по 1)
|
||||
album='Кавер-версии и пародии' ← 9 файлов — ПРАВИЛЬНО
|
||||
```
|
||||
**15 из 24** залетели каждый в свой альбом-из-одного-трека.
|
||||
|
||||
**Масштаб класса** (запрос по БД):
|
||||
```
|
||||
альбомов из 1 трека, где album != имя папки: 215
|
||||
```
|
||||
Примеры:
|
||||
```
|
||||
album='2000 и 1 ночь' папка='Ария, Кипелов' !Rock&Folk/Ария, Кипелов/Ария - Потерянный рай.mp3
|
||||
album='10 лет - полёт...' папка='!rock_o_pop' !Rock&Folk/!rock_o_pop/Пилот - god@pisem.net.mp3
|
||||
album='2000 и Одна Ночь' папка='!Retro' !Retro/Ария - Потерянный Рай.mp3
|
||||
```
|
||||
|
||||
**Правильная цель для `Кавер-версии и пародии`:** `album='Кавер-версии и пародии'`
|
||||
для всех 24 файлов; `artist` сохранить реальных исполнителей (Чайф, Би-2, Слот, Стрелки).
|
||||
|
||||
⏳ **НЕ СДЕЛАНО:** разбор всех 215 — разделить на «мусор (чужой сборник)» /
|
||||
«законный сингл». Требует решения Alex.
|
||||
|
||||
---
|
||||
|
||||
## 🔎 ВЕРИФИКАЦИЯ v3 ПЕРЕД DRY-RUN (2026-09-27)
|
||||
|
||||
Проверено на Mac, три скрипта, все числа — из самих файлов.
|
||||
@@ -722,7 +822,13 @@ Dry-run считает ФАЙЛЫ: album 28 · albumartist 6 · genre 4359 (в
|
||||
| Дифф: genre-группы | 211 ТРЕК-строк дублируют `new_genre`; 7 одиночек не добраны | **Alex снял вопрос:** пишем полный набор, дифф не пересобираем |
|
||||
| `track` −4 от плана | 4 файла `changed=yes`, расхождений нет → скрипт не писал | **Не дефект**, ожидаемо |
|
||||
| **Navidrome-кэш не чищен** | Теги на диске новые, БД Navidrome — старая (музыку не сканировали) | ✅ **ЗАКРЫТ 27 сен:** БД пересоздана, скан 4m13s, импортировано 7861 |
|
||||
| 🔴 **Бага генератора плана v3** | `is_junk_album` сравнивает `album==title` / `album==имя-файла` на `new_*` вместо `cur_*` → верный альбом затирается именем папки. **Задето 20 файлов** (Птаха — симптом №1) | ⏳ **ОТКРЫТ:** фикс — сравнивать с `cur_title`; пересобрать план, сверить 20 файлов с каноном, прогнать. См. § КОРЕНЬ ДЕФЕКТА ПТАХИ |
|
||||
| 🔴 **Бага генератора плана v3** | `is_junk_album` сравнивает `album==title` / `album==имя-файла` на `new_*` вместо `cur_*` → верный альбом затирается именем папки. **Задето 20 файлов** (Птаха — симптом №1) | ✅ **ОБОЙДЕНО:** дифф v5 строится от БД, а не пересборкой генератора — все 20 файлов лечатся автоматически |
|
||||
| 🔴 **Дифф был собран от КАНОНА** | Птахи в дифе не было, хотя дефект в базе есть (диск ушёл вперёд канона) | ✅ **ЗАКРЫТ 27 сен:** пересобран от БД → `DIFF-db-to-fix.csv` |
|
||||
| 🔴 **Ложное расхождение по `genre`** | `media_file.genre` пуст у всех 7861 → дифф показывал 7861 «правок» | ✅ **ЗАКРЫТ 27 сен:** жанр читается из `tags` jsonb, `genre` в дифе = 0 |
|
||||
| 🔴 **Птаха разбит на 2 альбома** | `'Красивые, богатые'` (7 файлов) + `'Птаха - Красивые, богатые (2009)'` (1) | ⏳ **В дифе v5** (`album` 60-я правка), ждёт прогона |
|
||||
| 🔴 **Альбомы-одиночки** | **215** альбомов из 1 трека, где `album != имя папки` (класс Бумера / Симпатии) | ⏳ **ОТКРЫТ:** нужен разбор «мусор vs законный сингл». См. § АЛЬБОМЫ-ОДИНОЧКИ |
|
||||
| **Трофим: 8 альбомов, 3 копии** | 3 физические копии → 8 альбомов, 38 имён задвоено | ✅ **ЗАКРЫТ:** 50 дублей удалены, план v5 правит альбомы/исполнителя |
|
||||
| **Тема дороги** | `title='AudioTrack 17'`, `artist='Бумер'`, один в альбоме | ⏳ **В дифе v5:** `album='Ленинград'`, `artist='Ленинград'` |
|
||||
|
||||
---
|
||||
|
||||
@@ -1140,6 +1246,40 @@ check_album_applied*.py / verify_306*.py вспомогательные про
|
||||
префиксом **только 3** — настоящие префиксы (Трофим `01..04 Аристократия`); остальные 12 —
|
||||
цифра часть названия (`10 лет - полёт нормальный`, `1-0 в пользу девочек`, `46-й альбом`).
|
||||
Правило сломает больше, чем починит → правка адресная, списком.
|
||||
31. 🔑 **ДИФФ СТРОИТЬ ОТ БД, А НЕ ОТ КАНОНА.** Канон `APPLY-PLAN.csv` = эталон РУЧНЫХ РЕШЕНИЙ,
|
||||
а не текущего состояния. Любой прогон пишет на диск → канон устаревает мгновенно.
|
||||
Дифф от канона **слеп к дефектам, уже записанным в теги**: Птахи в нём не было, хотя
|
||||
в базе он разбит на 2 альбома. Alex: «в смысле правильно? в базе блядь у него что?!».
|
||||
**Источник истины — `media_file` из `navidrome.db`.** Сборщик: `build_diff_from_db.py`.
|
||||
32. 🔴 **`media_file.genre` ПУСТ У ВСЕХ — не читать из этой колонки.** В этой версии Navidrome
|
||||
жанр лежит в `media_file.tags` (jsonb) + таблицах `tag`/`media_file_tags`/`album_tags`.
|
||||
Колонка — рудимент. Иначе получишь ложное «7861 расхождение по genre» на ровном месте.
|
||||
Колонка номера трека — `track_number` (INT), не `track`; приводить `str(v)`.
|
||||
33. 🔑 **Формат дифа: ПАПКА-строки + ТРЕК-строки, схлопывание по `(folder, field, new)`.**
|
||||
Alex проверяет дифф глазами и **дважды вернул его на переделку**:
|
||||
«почему диф опять не сортирован и не схлопнут?! ты опять для него заново скрипт писал?!».
|
||||
Требования: **сортировка по АЛФАВИТУ `field`** (`album → albumartist → artist → genre →
|
||||
track`) — ключ в КОДЕ, не порядок объявления списка; **схлопывание** — одна ПАПКА-строка
|
||||
на `(folder, field, new)`, ТРЕК-строка только если цель уникальна в папке;
|
||||
`old` = `<N разных>` при разных старых, конкретное при одном, пусто при всех пустых.
|
||||
`track` схлопнуть НЕЛЬЗЯ by design (у каждого файла свой номер).
|
||||
34. 🔴 **Новый скрипт для той же задачи = регрессия.** Дифф уже собирался канонным сборщиком;
|
||||
я написал новый и потерял сортировку+схлопывание. **Сначала искать существующий сборщик**
|
||||
(`build_canon_diff_v3.py`, `build_diff_canon_v4.py`), копировать его формат, а не изобретать.
|
||||
35. 🔴 **Артефакты писать в ПАПКУ ПРОЕКТА, не в `/tmp`.** Alex артефакт в `/tmp` не находит:
|
||||
«какого хуя он в тмп». Все csv/диффы → `~/Downloads/music-tags/`.
|
||||
36. 🔴 **Одна задача = один вопрос максимально сжато.** За сессию ~35 накопилось 3 круга
|
||||
«а сколько?», «а какие копии оставлять?», «а это решение от меня?» → мат.
|
||||
Alex: «Я тебя хотел как раз спросить какое решение то этих проблем?» — то есть **от агента
|
||||
ждут РЕКОМЕНДАЦИИ с обоснованием**, а не перечня вопросов. Давать вариант + причину,
|
||||
тогда решение приходит за один круг.
|
||||
37. 🔴 **«Стоп» ОЗНАЧАЕТ НЕМЕДЛЕННУЮ ОСТАНОВКУ ВСЕГО.** После «стоп» я запускал команды ещё
|
||||
2 раза (seek на `/music`, скан диска) → «Ты чё делаешь я не понял?». После стопа —
|
||||
только отчёт о состоянии, ноль tool-вызовов к NAS без нового слова.
|
||||
38. 🔴 **НЕ ЛЕЗТЬ НА ДИСК, ЕСЛИ ДАННЫЕ ЕСТЬ В БД.** Скан 7861 файла через контейнер идёт минуты
|
||||
и рвётся прерыванием. Alex: «Чё блядь происходит? Ты из базы не можешь пути выгрузить и
|
||||
сравнить что-ли??». **Сначала БД** (`sqlite3` локально на копии `navidrome.db`), диск — только
|
||||
там, где в БД данных нет (проверка реального тега после прогона).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user