[2026-09-27] eagle: family/how-to/navidrome-truenas-android.md personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 17:51:51 +06:00
1 parent d520e6331d
commit 9f62092089
3 files changed
+157 -25

No files matched your search

+10 -2
View File
@@ -79,10 +79,18 @@ Navidrome поднялся и просканировал **8043 трека**. П
| Реальный скобочный мусор | **306 строк / 15 папок** |
**Итог:** 306 строк исправлены (скобочный мусор → 0), затем **ещё 58 строк по 3 дефектам**
(склейка артист+год, чужой `albumartist`, `album`=название песни) → сгенерирован новый дифф
`DIFF-new-vs-38-v2.csv` (⏭ устарел после правки 58 строк, требует перегенерации).
(склейка артист+год, чужой `albumartist`, `album`=название песни), затем **+12** (`Шнуров`→`Ленинград`,
`NONSONS - zvezda`→`zvezda`). **Всего 371 строка.** Дифф **`DIFF-new-vs-38-v2.csv` перегенерирован**
(164 album-строки, полнота 18/18 + 6/6 проверена).
Полный разбор — [[personal/plans/music-artist-albumartist-repair]] §СЕССИЯ 18.
> 🔴 **ПРИЁМКА NAVDROME ДАЛА ДВОЙНОЙ РЕЗУЛЬТАТ — оба важны:**
> 1. **Движок подтверждён на живых данных** — теги читаются, папки игнорируются, бага Jellyfin нет.
> 2. **Вскрыт дефект ВХОДНОГО ПЛАНА**, который не видел ни один валидатор и ни один дифф:
> мусор, унаследованный из источника, присутствует в **обеих** версиях плана → дифф показывает
> «совпало». **Открывать данные в UI-потребителе = самый эффективный способ найти мусор в тегах,
> который не ловится сверкой файлов.**
## 🔴 УРОК: разброс значений в папке ≠ дефект (метод-урок сессии 18)
При проверке «один ли `album`/`albumartist` в папке» получено **96 папок с разными `album`
@@ -19,7 +19,7 @@ related:
- '[[personal/tech/music-tags-repair-truenas]]'
- '[[personal/plans/tagwriter-container]]'
- '[[family/how-to/navidrome-truenas-android]]'
status: session18-complete-waiting-apply-step2
status: session18-step2-done-dryrun-ok-awaiting-apply-go
# Music `artist` + `albumartist` Repair — правила Alex и ход работ
## 🟣 СЕССИЯ 18 (2026-09-27) — NAVIDROME РАЗВЁРНУТ, ДЕФЕКТ-КЛАСС ВСКРЫТ, ПРАВИЛА ВОССТАНОВЛЕНЫ ИЗ ZULIP, 306 СТРОК ИСПРАВЛЕНЫ, НОВЫЙ ДИФФ ГОТОВ
@@ -35,9 +35,13 @@ status: session18-complete-waiting-apply-step2
> ✅ Дифф **`DIFF-new-vs-38-v2.csv` ПЕРЕГЕНЕРИРОВАН** (164 album-строки, +12), полнота проверена —
> все 18 целевых `album` и 6 `albumartist` внутри. Старый дифф **не тронут**.
> Alex дал команду **«применяй»** → **ШАГ 1 (сверка артефактов) ПРОЙДЕН**: найдено 2 расхождения
> (скрипт на NAS и план на NAS — оба старые). ⏭ **ШАГ 2 не начат — ждёт «шаг 2» от Alex.**
> (скрипт на NAS и план на NAS — оба старые). **ШАГ 2 ТОЖЕ ВЫПОЛНЕН:** артефакты залиты,
> MD5 сверены, **dry-run `exit=0` — 7651 к обработке, не найдено 0**. По ходу найден и закрыт
> дефект: 3 файла `Green Apelsin` имели изменённый `new_album` при старом флаге `changed=no`
> → пересчёт `changed` по всем 7861, контроль `0`. ⏭ **ШАГ 3 (`--apply --verify`) ждёт «go».**
> Канонический раннер: `~/Automation/music-tags/run_tagwriter.sh`.
> См. §ПРАВКА 3 ДЕФЕКТОВ, §КАЛИБРОВКА ДЕТЕКТОРОВ, §СДЕЛАНО: правка 306 строк, §НОВЫЙ ДИФФ,
> §СВЕРКА АРТЕФАКТОВ ШАГ 1.
> §СВЕРКА АРТЕФАКТОВ ШАГ 1, §ШАГ 2 ВЫПОЛНЕН.
> **Контекст:** Navidrome поднят и просканировал библиотеку (8043 трека). Alex открыл UI и
> спросил: «**Откуда альбомы в скобках взялись?** Типа `Дискотека Авария` — для корня,
@@ -533,18 +537,29 @@ Alex: «**Отлично, применяй**». Прогон **НЕ запуще
> CMD ["python3", "/app/apply_tags_v2.py", "--dry-run"] ← нет ENTRYPOINT, нет USER
> ```
### ⏭ ШАГ 2 (НЕ ВЫПОЛНЕН — ждёт «шаг 2» от Alex)
### ✅ ШАГ 2 ВЫПОЛНЕН — ЗАЛИВКА, DRY-RUN, ДЕФЕКТ `changed` ЗАКРЫТ
1. `docker cp` актуального `apply_tags_v2.py` (Mac) → контейнер `/app/apply_tags_v2.py`
2. Залить актуальный `APPLY-PLAN.csv` (Mac) → NAS `/mnt/RED_2TB/docker/arr/tagwriter/apply-plan.csv`
3. Сверить MD5 обоих на NAS после заливки
4. `--dry-run` → сверить счётчики
5. **Только после «go»** — `--apply --verify`
1. ✅ `apply_tags_v2.py` (Mac) → NAS `/mnt/RED_2TB/docker/arr/tagwriter/` — MD5 `c7a2bc14…` совпал
2. ✅ `APPLY-PLAN.csv` (Mac) → NAS `apply-plan.csv` — MD5 `bc20f2a4…` совпал
3. ✅ Dry-run `exit=0`: **7651 к обработке**, `не найдено 0`
> 🔴 **Найден и закрыт дефект:** 3 файла `Green Apelsin` имели изменившийся `new_album`,
> но **старый флаг `changed=no`** → скрипт их бы пропустил. Пересчёт `changed` по всем 7861
> (`/tmp/recalc_changed.py`) → **контроль: расхождений 0**. Детали —
> [[personal/plans/tagwriter-container]] §СЕССИЯ 18.
> 🔴 **Правило шага 1:** **перед необратимым прогоном сверять MD5 скрипта (Mac / образ / NAS)
> и плана (Mac / NAS).** В этой сессии расхождения нашлись в ДВУХ местах из двух проверенных —
> без сверки прогон пошёл бы **по старому плану без 371 правки**.
### ⏭ ШАГ 3 (НЕ ВЫПОЛНЕН — ждёт «go» от Alex)
```bash
bash ~/Automation/music-tags/run_tagwriter.sh apply # 7651 файл, --apply --verify
```
Канонический раннер — [[personal/plans/tagwriter-container]] §СЕССИЯ 18.
Бэкап `Music/backup` (50 ГБ) на месте, без него `cmd_apply()` → `exit 2`.
### ✅ Артефакты сессии 18
| Файл | Что |
+123 -14
View File
@@ -4,7 +4,7 @@ created: '2026-09-26'
updated: '2026-09-27'
type: plan
namespace: personal
status: session17-image-built-deployed-dryrun-ok-awaiting-apply-go
status: session18-artifacts-uploaded-dryrun-ok-awaiting-apply-go
tags:
- music
- truenas
@@ -20,7 +20,114 @@ related:
---
# tagwriter — применение тегов к MP3 (TrueNAS)
## 🔴 СЕССИЯ 17: АУДИТ APPLY-СКРИПТА ПЕРЕД БОЕВЫМ ПРОГОНОМ — ЧИТАТЬ ПЕРВЫМ
## 🟢 СЕССИЯ 18: КАНОНИЧЕСКИЙ РАННЕР, ЗАЛИВКА АРТЕФАКТОВ, DRY-RUN OK — ЖДЁТ «GO»
> **СОСТОЯНИЕ:** артефакты залиты на NAS, MD5 сверены, dry-run `exit=0`.
> **Осталось: боевой `--apply --verify` (7651 файл) — ждёт команды Alex.**
### ⭐ КАНОНИЧЕСКИЙ РАННЕР: `~/Automation/music-tags/run_tagwriter.sh`
Единая точка входа. Зафиксированная последовательность вместо ручных шагов
(жалоба Alex: «**почему у тебя каждый раз хуйня выходит при прогонах? У тебя записана
чёткая последовательность действий для прогона?**»).
```bash
bash ~/Automation/music-tags/run_tagwriter.sh dry # сухой прогон (Music :ro)
bash ~/Automation/music-tags/run_tagwriter.sh apply # боевой (Music rw, --apply --verify)
bash ~/Automation/music-tags/run_tagwriter.sh readback # сверка без записи
```
Скрипт сам: печатает MD5 (Mac скрипт/план + NAS скрипт/план) → `docker create` →
`docker start -a` → `docker rm -f`. **Механизм `create`+`start -a` не требует апрува**
(`docker run` — требует).
### 🔴 МЕХАНИЗМ ЗАПУСКА — восстановлен из базы Zulip (дока врала)
Предыдущая версия доки предписывала `docker cp` и образ `tagwriter:v2` — **оба неверны**.
Фактическая команда (из Zulip, сообщения 126978, 126989, 127135):
```bash
ssh truenas_admin@mallexxx.duckdns.org '
docker create --name twdry --entrypoint python3 \
-v /mnt/RED_2TB/docker/arr/tagwriter:/app/data \
-v /mnt/RED_2TB/storage/Music:/music:ro \
tagwriter:latest /app/data/apply_tags_v2.py --dry-run
docker start -a twdry; docker rm -f twdry
'
```
| Что | Правильно | Дока говорила |
|---|---|---|
| Образ | **`tagwriter:latest`** | ❌ `tagwriter:v2` (устаревший) |
| Скрипт | **монтируется** через `--entrypoint python3 /app/data/apply_tags_v2.py` | ❌ «нужен `docker cp`» |
| Музыка (боевой) | `/music` **без** `:ro` | ✅ верно |
| Музыка (сухой) | `/music:ro` | ✅ верно |
| `save(v1=0)` | ⚠️ ID3v1-комментарий **уцелел** в тесте | ❌ «сносит ID3v1 всегда» |
### ✅ РЕЗУЛЬТАТ ЗАЛИВКИ И DRY-RUN (факт, конец сессии 18)
**MD5 после заливки (Mac ↔ NAS сверены):**
```
скрипт apply_tags_v2.py c7a2bc143e38820f2050310cbe01b799 (Mac = NAS = образ latest)
план apply-plan.csv bc20f2a49e364ecd5ed57d9fc97bfbcb (Mac = NAS)
```
**Сухой прогон по актуальному плану (7651 файл):**
```
строк в плане: 7861
changed=yes требующих правки, но помеченных no: 0 ✅
changed=no без расхождений, но помеченных yes: 0 ✅
mp3 на диске: 7861 пропущено не-mp3: 561
пропущено changed=no: 210
не найдено на диске: 0
к обработке: 7651
exit=0
```
> 🔑 **Инвариант `_check_changed_consistency()` пройден:** оба счётчика расхождений `= 0`.
> Перед этим было `7648 к обработке`; стало **7651** (+3 = файлы `Green Apelsin`, см. ниже).
### 🔴 ДЕФЕКТ ПРАВКИ ПЛАНА: `changed` не пересчитывался после смены `new_album`
**Класс дефекта:** правка `new_album` в плане **не обновляет** колонку `changed`.
Если у файла до правки `cur_album == new_album` (флаг `no` стоял корректно), после правки
значения расходятся — **а флаг остаётся `no`. Скрипт такой файл ПРОПУСТИТ.**
Найдено **3 файла** (все `Green Apelsin`) — правка `album` → `Северный ветер` не применилась бы:
```
!Pop, Club, Любэ/Green Apelsin/В пасти смерти.mp3 'В пасти смерти' -> 'Северный ветер' changed=no ❌
!Pop, Club, Любэ/Green Apelsin/Кукловод.mp3 'Сердце не бьётся'-> 'Северный ветер' changed=no ❌
!Pop, Club, Любэ/Green Apelsin/Лес.mp3 'Лес' -> 'Северный ветер' changed=no ❌
```
Плюс встречный: 1 файл `changed=yes` **без** расхождений —
`The Prodigy/The Prodigy - Girls (Rex The Dog Remix).mp3` (лишний флаг, известен с сессии 17).
**Фикс** (`/tmp/recalc_changed.py`): `changed = "yes" if любой cur_* != new_* else "no"`, по всем 7861.
```
ДО: yes 7649 · no 212
ПОСЛЕ: yes 7651 · no 210 переключено 4
КОНТРОЛЬ: расхождений changed осталось 0
```
Бэкап: `APPLY-PLAN.csv.bak-before-changed-recalc-20260927-174900`.
> 🔴 **ПРАВИЛО ПАЙПЛАЙНА: любая правка `new_*` в плане ОБЯЗАНА пересчитывать `changed`.
> Скрипт-писатель читает флаг, а не сравнивает значения сам.**
### 🔴 ГЛАВНЫЙ ПИТФОЛЛ: `Dockerfile.v2` на Mac — УСТАРЕВШАЯ СПРАВОЧНАЯ КОПИЯ
В нём **нет** `ENTRYPOINT` и `USER 950:950`, хотя дока описывает их как фиксы.
Боевой образ `tagwriter:latest` собран **с** фиксами (проверено `docker inspect` + `id`).
**Не пересобирать из этого файла — потеряешь фиксы.**
### 🔴 ПИТФОЛЛ: `tagwriter:v2` — старая версия, НЕ ИСПОЛЬЗОВАТЬ
`tagwriter:v2` = `e5e2437a2fda90cb6b06d6daa6330cb` (скрипт старый).
`tagwriter:latest` = `c7a2bc14…` ✅. Оба образа сосуществуют — легко промахнуться.
---
## 🔴 СЕССИЯ 17: АУДИТ APPLY-СКРИПТА ПЕРЕД БОЕВЫМ ПРОГОНОМ
> Alex: «**Проверяй ещё раз руками apply script. Скажи алгоритм которым он будет применять
> правки и найди если есть какие-то проблемы.**»
@@ -610,18 +717,20 @@ Alex: «**Отлично, применяй**» → прогон **не запу
**Шаг 2 (не выполнен, ждёт Alex):** `docker cp` скрипта + заливка актуального плана → MD5-сверка →
`--dry-run` → `--apply --verify` только после «go».
### 🔴 ОТКРЫТЫЙ ДЕФЕКТ (требует проверки): `!Rock&Folk/Rammstein/misc`
### 📜 ИСТОРИЧЕСКАЯ ЗАПИСЬ (НЕДЕЙСТВИТЕЛЬНА — см. §ЗАКРЫТО выше)
Alex: «**`!Rock&Folk/Rammstein/misc` - album - rammstein - неправильно**».
```
ПАПКА album '' -> Rammstein ← ❌ имя АРТИСТА в поле альбома (3 файла с пустым cur_album)
ПАПКА albumartist <КОЛОНКИ НЕ БЫЛО> -> Puhdys ← ❌ чужая группа на всю папку Rammstein
```
Папка: 18 файлов. Остальные альбомы корректны: `The remix album 2002` (7), `Singles '99` (4),
`Live Aus Russland`, `Wilder Freiden`, `Demo`, `Golden Collection 2001`.
Файлы с пустым `cur_album`: `Pet Sematary.mp3`, `Песня о тревожной молодости.mp3` + 1.
⏳ Решение не получено.
> ~~### 🔴 ОТКРЫТЫЙ ДЕФЕКТ: `!Rock&Folk/Rammstein/misc`~~
>
> ~~Alex: «`!Rock&Folk/Rammstein/misc` - album - rammstein - неправильно».~~
>
> ```
> ПАПКА album '' -> Rammstein ← ❌ имя АРТИСТА в поле альбома
> ПАПКА albumartist <КОЛОНКИ НЕ БЫЛО> -> Puhdys ← ❌ чужая группа на всю папку
> ```
> ~~Папка: 18 файлов. Решение не получено.~~
>
> ✅ **ЗАКРЫТО в сессии 18:** факт по файлу — `album='misc'`, `albumartist='Rammstein'`.
> Дефекта нет, запись была снимком ДО правок. Детали — §ЗАКРЫТО выше.
### 🔴 ПИТФОЛЛ: `grep` по ИМЕНИ ПАПКИ ≠ проверка ЗНАЧЕНИЯ
@@ -778,7 +887,7 @@ docker start -a tw && docker rm -f tw
| 0d | `Филипок` / `Маленький_Мук` — `album='Сказочный сборник'` / транслит | ⏳ войдёт в 0c |
| 0e | **12 строк `changed=yes` без реальных правок** — снять флаг? | ⏳ **вопрос сессии 17** |
| 0f | **`track` с ведущим нулём** (`01`, `03/12`) — канонизировать в `1`/`3/12`? 11 файлов | ⏳ **вопрос сессии 17** |
| 1 | Боевой `--apply --verify` по 7861 | 🔴 **ЖДЁТ «go»** (после 0, 0a–0f) |
| 1 | Боевой `--apply --verify` по 7861 | 🔴 **ЖДЁТ шаг 2** (сверка артефактов пройдена, см. §СВЕРКА АРТЕФАКТОВ) |
| 2 | Приёмка Alex в UI (Navidrome/Jellyfin) | ⏳ |
| 3 | Откат 40 из `backup_conv40/` | ⏳ |
| 4 | Удаление 29 битых/DRM ([[personal/tech/music-broken-toload]]) | ⏳ |