[2026-09-26] eagle: personal/tech/id3-tag-repair.md personal/tech/music-tags-final-review.md personal/tech/music-tags-repair-truenas.md
This commit is contained in:
1 parent
d7a3e90631
commit
0aacbc1226
3 files changed
+140
-12
No files matched your search
@@ -72,7 +72,6 @@ TPE1 enc=0 payload = 00 FF FE 53 74 65 65 6C 20 44 65 6C 75 78 65
|
||||
Потеряно 343 файла. → Читать ID3 **напрямую из байтов** (`id3_direct.py`).
|
||||
|
||||
### 4. Ноты из CSV — односторонний канал
|
||||
|
||||
Правки человека в сгенерированной ноте **не возвращаются** в CSV. Следующая перегенерация их
|
||||
**затирает**. Лечится только явным переносом (`apply_manual_decisions.py`, список `DECISIONS`).
|
||||
|
||||
@@ -120,6 +119,28 @@ os.replace(TMP, SRC)
|
||||
Плюс: `album` = год (`2007`) или имя папки-плейсхолдера (`NEW (Remix Album)`, `Unreleased Tracks`)
|
||||
— **это** дефекты (папка протекла в album).
|
||||
|
||||
### 7. 🔴 Неидемпотентный батч-скрипт молча портит данные
|
||||
|
||||
Сквозная нумерация треков по дискам (Диск N = offset от суммы треков предыдущих) в наивной
|
||||
версии читала **текущий** `track` и прибавляла offset. Повторный запуск прибавлял снова:
|
||||
|
||||
```
|
||||
прогон 1: Disc 4 track 10 → 60
|
||||
прогон 2: Disc 4 track 60 → 110 ← ошибки нет, данные уже неверны
|
||||
```
|
||||
|
||||
Три версии скрипта ушли в мусор, дважды откатывались из бэкапа.
|
||||
|
||||
**Правило:** цель считать **от исходного значения** (номер в **имени файла**), не от целевого поля.
|
||||
Обязательная приёмка — **второй прогон = 0 изменений**. Номер писать как `str(int(x))`,
|
||||
иначе вернутся `05` vs `5`. По деталям — `music-tags-repair-truenas.md` §«28-я волна».
|
||||
|
||||
### 8. 🔴 NFC vs NFD в именах папок — `==` ложь при визуально равных строках
|
||||
|
||||
`Принц и нищий (Диск 2)` распался на два альбома: одна папка была precomposed, другая —
|
||||
с combining `U+0306`. Перед сравнением/группировкой всегда
|
||||
`unicodedata.normalize("NFC", name)`.
|
||||
|
||||
## ⚪ Что НЕ является багом
|
||||
|
||||
- **Рукописная транслитерация:** `КняZz`, `EMINЭM`, `Flёur`, `изBESTняк`, `Скаzки`, `Глюкоzа`,
|
||||
|
||||
@@ -18,23 +18,44 @@ related:
|
||||
|
||||
_Обновлено: 2026-09-26. Источник: `~/Downloads/music-tags/tag-fix-plan.csv`._
|
||||
|
||||
> ## ✅ ГОТОВО К АППРУВУ (28-я волна)
|
||||
> **Построчное чтение завершено: 1–7906 (100%).** 27-я волна закрыла блок 6402–7906.
|
||||
> 28-я волна добавила **сквозную нумерацию треков по дискам** (9 многодисковых альбомов).
|
||||
> Все проверки пройдены. Жду слово Alex («go») для бэкапа и применения тегов.
|
||||
> ## ⚠️ 28-я волна ЗАКРЫТА, 29-я В РАБОТЕ — правки по замечаниям Alex
|
||||
> **Построчное чтение завершено: 1–7906 (100%).**
|
||||
> 28-я волна: **сквозная нумерация треков по дискам** (9 многодисковых альбомов) — ✅ ГОТОВО.
|
||||
> 29-я волна: **5 замечаний Alex** (Детский хит капсом, битая строка `яюY`, Benassi 23→2,
|
||||
> Eminem album, формат CSV-сводок) + 597 треков без `new_track` при номере в имени файла.
|
||||
> 👉 **Согласованный план 29-й волны ниже.** Подтверждено Alex, но НЕ применено.
|
||||
> ⚠️ Открытый вопрос: 104 дубля `(album, track)` — копии релизов в разных папках (дедупликация).
|
||||
> Полный контекст волн — `personal/tech/music-tags-repair-truenas.md`.
|
||||
|
||||
## Состав файлов
|
||||
|
||||
| Файл | Строк | Что это |
|
||||
| Файл | Строк | Колонки |
|
||||
|---|---|---|
|
||||
| `tag-fix-plan.csv` | 7906 | Главный план правок, 11 колонок |
|
||||
| `artists-single-track.csv` | 3196 | Разбивка: треки БЕЗ номера (artist/title/album/path) |
|
||||
| `artists-multi-track.csv` | 4694 | Разбивка: треки С номером (artist/album/track/title/path) |
|
||||
| `tag-fix-plan.csv` | 7906 | 11 колонок (главный план) |
|
||||
| `artists-single-track.csv` | 3196 | `artist, title, album, path` ⚠️ см. ниже |
|
||||
| `artists-multi-track.csv` | 4694 | `artist, album, track, title, path` |
|
||||
| | **7890** | = сумма сводок (16 строк отфильтрованы: 15 skip + 1 намеренно пустая) |
|
||||
|
||||
_Сводки пересобраны в 28-й волне после сквозной нумерации (`rebuild_summaries.py`)._
|
||||
### 🔴 Проблема формата сводок (замечание Alex, 29-я волна)
|
||||
|
||||
**Порядок колонок различается** — это дефект генератора `rebuild_summaries.py`:
|
||||
в `single` стоит `title` перед `album`, в `multi` — `album` перед `track/title`.
|
||||
Причина: две разные `writerow()` для одного и того же набора данных.
|
||||
|
||||
**Решение (согласовано):** единый порядок в обоих файлах —
|
||||
`artist, album, track, title, path`. Треки без номера просто имеют пустой `track`.
|
||||
|
||||
### 🔴 «single-track» ≠ «трек без номера» (замечание Alex)
|
||||
|
||||
Метка `single` сейчас означает **пустой `new_track`**, а не отсутствие номера.
|
||||
Из **3197** строк без `new_track` у **597** номер есть прямо в имени файла
|
||||
(`189_3 Doors Down - It's Not My Time.mp3`, `Ace Of Base - Happy Nation.mp3`).
|
||||
Поэтому в «single» попадали нормальные альбомные исполнители — Alex заметил это как «wtf».
|
||||
|
||||
Крупные группы: `Sbornik_Novij_Rok` (199), `Infected Mushroom` (60), `!Radio` (53),
|
||||
Агата Кристи (41), Пилот (30), Savant (29), Захар Май (27), Benassi Bros (25).
|
||||
|
||||
**Решение:** извлечь номер из имени файла (в `track`), треки реально без номера оставить пустыми.
|
||||
|
||||
## Действия
|
||||
|
||||
@@ -86,7 +107,31 @@ _Сводки пересобраны в 28-й волне после сквозн
|
||||
- 46 уникальных альбомов после снятия года — с официальными треклистами не сверялись
|
||||
(кроме Pushnoy, где регрессия найдена и исправлена).
|
||||
|
||||
## Сквозная нумерация по дискам (28-я волна)
|
||||
## 📋 План 29-й волны (согласован Alex 2026-09-26, НЕ применён)
|
||||
|
||||
Пять пунктов из замечаний Alex + находка про `new_track`.
|
||||
|
||||
| # | Что | Объём | Решение |
|
||||
|---|---|---|---|
|
||||
| **A** | `!Детские/Детский хит` — title капсом (`HOLLYWOOD` и т.п.) | 156 строк | привести title к регистру `Как в предложении` (только где `isupper()`) |
|
||||
| **B** | Строка 3260: `new_album='яюY'` — мусор + пустой artist | 1 строка | album из пути (`Sbornik_Novij_Rok`), мусор `яюY` вычистить |
|
||||
| **C** | Benassi разъехался на 23 написания | 23 → 2 | **BENASSI BROS** (feat. Sandy и др.) → `Benassi Bros`; **BENNY BENASSI** → `Benny Benassi` (сольные/`vs.`) |
|
||||
| **D** | Eminem: 15 треков `NEW (Remix Album)` без album | 15 строк | album → **`The Remix Album`** (подтверждено Alex) |
|
||||
| **E** | Формат CSV-сводок: разный порядок колонок | 2 файла | единый `artist, album, track, title, path` в обоих (`rebuild_summaries.py`) |
|
||||
| **F** | 597 треков: номер в имени файла, но `new_track` пуст → падают в «single» | 597 строк | извлечь номер в `track` из имени файла |
|
||||
|
||||
### Пояснение по B (номер сборника)
|
||||
|
||||
В `Sbornik_Novij_Rok` (199 треков) номер в начале файла — это **номер сборника**,
|
||||
не номер трека на альбоме. **Решение Alex: ставить его в `track`** (то есть `track` = позиция
|
||||
в сборнике). Это общий случай для всех `Sbornik_*` папок.
|
||||
|
||||
### Питфолл E (не повторить)
|
||||
|
||||
Порядок колонок в сводках задаётся **двумя разными `writerow()`**. Правку делать **обеими**
|
||||
ветками сразу, иначе рассинхрон вернётся при следующей пересборке.
|
||||
|
||||
## Сквозная нумерация по дискам (28-я волна, ✅ применено)
|
||||
|
||||
9 многодисковых альбомов перенумерованы непрерывно (Диск N = offset от суммы треков
|
||||
предыдущих дисков). Все — без дыр и дублей:
|
||||
|
||||
@@ -3232,7 +3232,6 @@ if cyr < 2: # одна буква — совпадение
|
||||
> 🔴 **Решение Alex:** дубли **НЕ трогать** — разберёт сам. Папки не удалять, не переименовывать.
|
||||
|
||||
## ПЛАН (согласован Alex 2026-09-26)
|
||||
|
||||
| # | Шаг | Статус |
|
||||
|---|---|---|
|
||||
| 1 | **Инвентаризация** read-only: `ffprobe` по 7906 файлам → CSV | ✅ **ГОТОВО (v2)** — `tag_inventory2.py`, `probe_failed`=0 |
|
||||
@@ -3441,6 +3440,69 @@ Alex (третья волна): **«Давай ка дорогой пройди
|
||||
> а чтение глазами каждой строки — «очевидные», которые видны пользователю с первого взгляда.
|
||||
> Все три уровня нужны.
|
||||
|
||||
## 28-я волна — сквозная нумерация по дискам (+ питфолл идемпотентности)
|
||||
|
||||
**Задача (Alex):** «там где ты убрал номера дисков из альбомов (сказки) теперь треки
|
||||
дублированы, нужно для вторых дисков сквозную нумерацию».
|
||||
|
||||
Причина: снятие `(Диск N)` из `album` сводило многодисковые релизы в один альбом, но нумерация
|
||||
треков в каждом диске начиналась с `1` → 179 дублированных пар `(album, track)`.
|
||||
|
||||
**Итог:** 9 альбомов перенумерованы непрерывно (Диск N = offset от суммы треков предыдущих
|
||||
дисков), 119 изменений, без дыр и дублей:
|
||||
|
||||
`Final Fantasy VI` 1–74 · `изBESTняк` 1–40 · `Алиса в Стране Чудес` 1–34 (2 диска + транслит-папка) ·
|
||||
`Трое из Простоквашино` 1–20 · `Лунапарк` 1–19 · `Live Aus Berlin` 1–18 ·
|
||||
`Принц и нищий` 1–14 · `Малыш и Карлсон` 1–6 · `Остров сокровищ` 1–6.
|
||||
|
||||
### 🔴 Питфолл №1: неидемпотентный скрипт silently портит данные
|
||||
|
||||
Три версии скрипта подряд (`fix_disc_tracks.py`, `fix_disc_tracks2.py`, `fix_disc_auto.py`)
|
||||
считали цель от **текущего** поля `track`:
|
||||
|
||||
```
|
||||
прогон 1: Disc 4 track 10 → 60
|
||||
прогон 2: Disc 4 track 60 → 110 ← молчаливая порча, ошибки нет
|
||||
```
|
||||
|
||||
`fix_disc_tracks2.py` был запущен дважды в одной команде → `Disc 4` уехал на `101`.
|
||||
Оба раза откат делался из бэкапа (`cp bak-201011-disc tag-fix-plan.csv`).
|
||||
|
||||
**Фикс (`fix_disc_final.py`):** цель считается **от исходного номера из имени файла**,
|
||||
а не от текущего значения. **Приёмка: второй прогон = 0 изменений** ✓.
|
||||
|
||||
**Общее правило:** любой батч-скрипт обязан (а) считать цель от источника
|
||||
(имя файла), не от целевого поля; (б) писать номер как `str(int(x))`, иначе вернутся `05` vs `5`;
|
||||
(в) при повторном запуске выдавать «nothing to do».
|
||||
|
||||
### 🔴 Питфолл №2: NFC vs NFD в именах папок
|
||||
|
||||
`Принц и нищий (Диск 2)` разъехался на **два альбома**, потому что одна папка использовала
|
||||
precomposed-символ, а другая — combining `U+0306`. Визуально идентичны, `==` даёт `False`.
|
||||
Лечится `unicodedata.normalize("NFC", name)` перед сравнением/группировкой.
|
||||
|
||||
### Открытый вопрос (к Alex)
|
||||
|
||||
**104 пары `(album, track)`** — это НЕ диски, а одни и те же релизы в двух папках
|
||||
(`Город дорог` 15, `Мат без электричества` 13, `Живая коллекция` 13, `Второй Магаданский` 11,
|
||||
`Lies For The Liars` 11, `Ч\Б` 8, `Аристократия помойки 1–4`, `Война и мир`, `СМС`, `MCMXC A.D.`).
|
||||
Решается **дедупликацией файлов**, не нумерацией. Ждём решения Alex.
|
||||
|
||||
## 29-я волна — замечания Alex (согласовано, НЕ применено)
|
||||
|
||||
Приёмка Alex по сводкам выявила 5 пунктов; детальная таблица (A–F) —
|
||||
`personal/tech/music-tags-final-review.md` §«План 29-й волны». Кратко:
|
||||
|
||||
1. `!Детские/Детский хит` — 156 title капсом → привести к регистру.
|
||||
2. Строка 3260 — `new_album='яюY'` (мусор), пустой artist, album брать из пути.
|
||||
3. **Benassi 23 написания → 2:** `Benassi Bros` (feat. Sandy) и `Benny Benassi` (сольные/vs.).
|
||||
4. **Eminem `NEW (Remix Album)`** (15 треков, папка протекла в album) → album `The Remix Album`.
|
||||
5. **Формат сводок:** разный порядок колонок в `artists-single-track.csv` vs `multi` → единый
|
||||
`artist, album, track, title, path`; править **обе** ветки `rebuild_summaries.py`.
|
||||
|
||||
Плюс находка: **597 строк** имеют номер в имени файла, но пустой `new_track` → падали в
|
||||
«single-track». Номер сборника (`Sbornik_*`, 199 строк) **ставится в `track`** — решение Alex.
|
||||
|
||||
## Связанные заметки
|
||||
|
||||
- [[family/how-to/jellyfin-config]] — живой инстанс Jellyfin на Taiga, план добавления Music-библиотеки
|
||||
|
||||
Reference in new issue
Block a user