diff --git a/personal/plans/music-tags-review/music-tags-review.md b/personal/plans/music-tags-review/music-tags-review.md index 0b01e964..53241cdf 100755 --- a/personal/plans/music-tags-review/music-tags-review.md +++ b/personal/plans/music-tags-review/music-tags-review.md @@ -11,6 +11,13 @@ updated: '2026-09-26' **К правке:** 6773 файлов · **без изменений:** 1133 +> **✅ 21-я волна (2026-09-26): CSV ГОТОВ. 7907 строк, 0 битых. Ждём «go» на применение.** +> Разобраны блоки `Pushnoy.ru`/`Александр Пушной` (13 строк cur_artist) и `Infected Mushroom` +> (7 реальных дефектов: обрезки `Deeply Disturbed (Kobi Shalti-`, `elation station (beat hackers`; +> title = имя файла `Infected Mushoom - Saeed/Red Filter`; `Eyal Shechter` → title `Raash`; +> `U R So Fucked (GANDI Remix)`). 14 файлов `._*` (AppleDouble) помечены `skip`. +> Детали — [[personal/tech/music-tags-repair-truenas|tech/music-tags-repair-truenas]], 21-я волна. + > [!warning] Ничего не изменено > Это план. Ставь галочки, что одобряешь. После «go» — бэкап в `Music/backup/` и применение. diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index ecb1812d..0a7e411e 100755 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -51,8 +51,12 @@ related: > 🔴 **ТРЕТИЙ УРОК: нет данных → СПРАШИВАТЬ, не оставлять молча пустым.** `NSK.mp3` (2 копии) > — artist пустой, вопрос Alex'у не задан → Alex: «Почему ты не спросил меня что с ним делать?!». > Открытый вопрос теперь вынесен явно (см. ниже). -> 📊 **Сводные CSV (20-я волна):** `artists-single-track.csv` **777** + `artists-multi-track.csv` -> **7129** = **7906 — все строки покрыты.** +> 📊 **Сводные CSV (ПЕРЕСОБРАНЫ в 21-й волне):** `artists-single-track.csv` **3222** + +> `artists-multi-track.csv` **4669** = **7891 строк** (скрипт `rebuild_summaries.py` строит по +> признаку `new_track` и ФИЛЬТРУЕТ `action=skip` (14 AppleDouble) + `source=dropped` (надгробие) — +> отсюда 7891, а не 7906). **Старые 777+7129 — устарели**, не использовать. Считались грубым +> `awk`/`jq` (не парсят кавычки CSV) → правильный счётчик только `csv.DictReader`: **6009 строк +> с непустым `new_track`**. > 🖐 **190 строк без artist (было 188 — +2 из-за `Перец` не считается, см. разбор):** 186 > `!Детские/Сказки/*` (artist не существует), `Голубой щенок/01 - Хоттабыч!`, > `НАШЕСТВИЕ` (надгробие), **`NSK.mp3` ×2**. Живой разбор `NSK.mp3` на NAS: ID3v2.3 заголовок @@ -60,7 +64,9 @@ related: > Итог: **0 битых, 7907 строк, ручных правок ~641.** > ⏭ Осталось: **решение по `NSK.mp3`** (A/B/C ниже) → бэкап `Music/backup/` → применение → > приёмка в UI → Music-библиотека Jellyfin. Плюс merge-конфликт в `1-needs-decision.md`. -> **Бэкапы:** `...bak-20260926-185339` (последний), `bak-20260926-184122`, `bak-20260926-164432` (самый ранний). +> **Бэкапы (актуально на 21-ю волну):** 27 файлов `tag-fix-plan.csv.bak-20260926-HHMMSS` + +> `bak-194111-wave21` (сделан перед пересборкой сводок). Самый ранний — `...164432`, самый +> свежий — `...193421`. Все в `~/Downloads/music-tags/`. > > --- > @@ -403,36 +409,48 @@ file size 475954 Б ≈ 29 секунд ← валидный MP3, коро - Питфолл `sed`: `\r\n`-split в Python даёт сдвиг номеров строк после правок — брать строки по **тексту**, не по номеру (`grep -n`, потом `sed -n` для просмотра). -### 📊 6. Сводные CSV пересобраны после правок +### 📊 6. Сводные CSV пересобраны (21-я волна, актуальное) -`python3 ~/Downloads/music-tags/build_summaries.py` (read-only исходника, пишет 2 файла): +`python3 ~/Downloads/music-tags/rebuild_summaries.py` (read-only исходника, пишет 2 файла). +Ключевое отличие от старого `build_summaries.py`: строит по признаку **наличия `new_track`** +и **фильтрует `action=skip` + `source=dropped`**. | Файл | Строк | Колонки | |---|---:|---| -| `artists-single-track.csv` | **777** | `artist, title, album, path` | -| `artists-multi-track.csv` | **7129** | `artist, album, track, title, path` | +| `artists-single-track.csv` | **3222** | `artist, title, album, path` | +| `artists-multi-track.csv` | **4669** | `artist, album, track, title, path` | -**777 + 7129 = 7906 — покрыты ВСЕ строки** (проверка `TOTAL COVERED` встроена в скрипт). -Пустой artist → sentinel **`(без исполнителя)`**; для сказок ключ `(без исполнителя) — `. +**3222 + 4669 = 7891** = 7906 − 14 AppleDouble − 1 надгробие. Пустой artist → sentinel +**`(без исполнителя)`**. Сортировка: artist → album → track (числа первыми, потом строки). -### 📊 СОСТОЯНИЕ НА КОНЕЦ ДВАДЦАТОЙ ВОЛНЫ +> 🔴 **ПИТФОЛЛ СЧЁТЧИКОВ:** `awk -F','` / `jq -R split(",")` **НЕ парсят кавычки CSV** → дают +> 4669 вместо реальных **6009** строк с `new_track`. Для любых подсчётов по этому файлу — +> **только `csv.DictReader`** (как в `rebuild_summaries.py`). +> 🔴 Старые цифры **777 + 7129** (метод «группировка по исполнителю») больше не действительны — +> читать файлы с этим скриптом. + +### 📊 СОСТОЯНИЕ НА КОНЕЦ ДВАДЦАТЬ ПЕРВОЙ ВОЛНЫ | Показатель | Значение | |---|---:| | Строк в CSV | 7906 данных + заголовок = **7907** | | **Битых строк** | **0** | | CRLF-потерь | **0** | -| Ручных правок | **~641** | +| AppleDouble `._*` (skip) | **14** | +| Надгробие (dropped) | **1** (`НАШЕСТВИЕ 2009`) | | Строк без artist | **190** (186 сказки + 4 прочие) | -| Сводные CSV | ✅ 777 + 7129 = 7906 | +| Сводные CSV | ✅ 3222 + 4669 = 7891 | +| Бэкапов | **27** | | Применение на NAS | **НЕ начато (ждём «go»)** | -**Бэкапы:** `bak-20260926-185339` (последний, 20-я волна), `bak-20260926-184122`, -`bak-20260926-164432` (самый ранний). +**Бэкапы (21-я волна):** 27 файлов: `tag-fix-plan.csv.bak-20260926-HHMMSS` (самый свежий +`...193421`, самый ранний `...164432`) + `bak-194111-wave21` (перед пересборкой сводок). -**Рабочие скрипты `~/Downloads/music-tags/`:** -`diag_fields.py` (гистограмма полей), `build_summaries.py` (2 сводные CSV), -`list_noartist.py` (**новый, 20-я волна** — полный список строк без artist по папкам), +**Рабочие скрипты `~/Downloads/music-tags/` (все read-only по исходнику):** +`diag_fields.py` (гистограмма полей), **`rebuild_summaries.py`** (21-я волна — 2 сводные CSV, +строит по `new_track` + фильтрует skip/dropped; **заменяет `build_summaries.py`**), +`build_summaries.py` (старый, 20-я волна — даёт устаревшие 777+7129, не использовать), +`list_noartist.py` (список строк без artist по папкам), `probe_nsk.py` (живой разбор ID3 — переиспользован для дампа кадров), `repair_broken.py` / `repair_min.py` (черновики, **не применялись** — регрессия починена вручную).