[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md personal/tech/id3-tag-repair.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-27 16:10:35 +06:00
1 parent 6b8bba3336
commit 559b192028
3 files changed
+305 -14

No files matched your search

+149
View File
@@ -54,6 +54,15 @@ related:
> ложных (3422 «кандидата» → 101 реальный дефект; `album == title` в основном ЛЕГИТИМНО).
> Прежде чем называть класс дефектом — сверять с `cur_*` и по соседним файлам папки.
>
> ⚠️ **Обновлён до v2.1.0 (сессия 17, третья часть):** правила **W21–W30**.
> Самое дорогое: **W21** — образ, собранный на Mac (`linux/arm64`), не запускается на
> amd64-NAS (`exec format error`) → только `docker buildx --platform linux/amd64 --load`;
> **W22** — `CMD` вместо `ENTRYPOINT` блокирует передачу `--apply`;
> **W25** — `normalize_track` не должен менять ФОРМУ записи (`01`→`01`, `03/12`→`03/12`),
> решение Alex «оставлять»; **W27** — детекторы по форме значения дают тысячи ложных
> (222 кандидата → 101 реальный; `album == title` 149 → 0); **W30** — генератор по `r`
> внутри условия от `r[c]` даёт тождество (ложные «7861 пробелов»).
>
> ⚠️ **Обновлён до v2.0.0 (сессия 15):** **D21** — 🔴 **КОРЕНЬ АРТИСТА = ОДИН АЛЬБОМ**:
> файлы ПРЯМО в `<категория>/<артист>/` (без подпапки) считаются одним альбомом → `album` =
> имя корня, `albumartist` = титульный исполнитель (= имя корня), **`cur_*` НЕ ЧИТАТЬ**.
@@ -1085,6 +1094,146 @@ archive/APPLY-PLAN-v0-*.csv — устаревшие версии
Суффикс `2` в имени файла-плана — источник ошибок: непонятно, какой применять. Один файл
без суффикса, остальные явно помечены. Перед переименованием — `tar czf` снимок каталога.
### 🔴 W21 — образ собран на ARM (Mac) не запускается на amd64-NAS
Первый билд `tagwriter` на Mac ушёл под `linux/arm64`. На NAS (TrueNAS, `x86_64`):
```
WARNING: The requested image's platform (linux/arm64) does not match
the detected host platform (linux/amd64/v2)
exec /usr/bin/sh: exec format error
```
**Собирать кросс-платформенно и всегда `--load`:**
```bash
docker buildx build --platform linux/amd64 -f Dockerfile.v2 -t tagwriter:latest --load .
docker save tagwriter:latest -o /tmp/tagwriter-amd64.tar
scp /tmp/tagwriter-amd64.tar truenas_admin@mallexxx.duckdns.org:/tmp/
ssh … "docker load -i /tmp/tagwriter-amd64.tar"
```
- `--load` **обязателен**: без него `buildx` кладёт образ только в кеш билдера, `docker images` пуст.
- Архитектуру хоста проверять заранее: `docker version --format '{{.Server.Arch}}'`.
- Приёмка после `docker load`: `docker run --rm --entrypoint sh <img> -c 'id; md5sum /app/<script>'`
— сверить uid и md5 со хостовым. **`docker load` не доказывает, что внутри актуальный скрипт.**
### 🔴 W22 — `CMD` вместо `ENTRYPOINT`: аргументы CLI не пробрасываются
```dockerfile
# ❌ docker run … tagwriter:latest --dry-run
# → exec: "--dry-run": executable file not found in $PATH
CMD ["python3", "/app/apply_tags.py"]
# ✅
ENTRYPOINT ["python3", "/app/apply_tags_v2.py"]
CMD ["--dry-run"]
```
С `CMD` флаг `--apply` передать нельзя — режим боевой записи физически недоступен.
### 🔴 W23 — в одном проекте два Dockerfile с РАЗНЫМИ скриптами
`Dockerfile` → `COPY apply_tags.py` (**v1**: без `TRCK`, без `TPE2`, без фильтра не-mp3 — та
версия, что испортила 16 файлов) · `Dockerfile.v2` → `apply_tags_v2.py`.
**Правило:** перед сборкой смотреть, что именно тянет `COPY`, и сверять размер/md5 скрипта
внутри образа с рабочим. «Сборка прошла успешно» ничего не говорит о версии кода.
Проверка: `docker run --rm --entrypoint sh <img> -c 'md5sum /app/*.py'`.
### 🔴 W24 — артефакты на NAS расходятся с локальными: сверять md5 перед прогоном
Перед боевым применением на NAS лежали **устаревшие** копии:
| Артефакт | На NAS было | Должно быть |
|---|---|---|
| `tagwriter:latest` | v1 `apply_tags.py`, 5990 Б, 15 ч | актуальный v2 |
| `tagwriter:v2` | `apply_tags_v2.py` 16549 Б (наш — 25971 Б) | — |
| `apply-plan.csv` | `bf5093b356a2c5c979037dbcf5d13a84` | `42d32ede85fef1b2fad6e2ce87012b7f` |
**Правило: перед `--apply` сверять md5 ПЛАНА на NAS с боевым локальным.** Если не совпал —
заливать и бэкапить старый (`cp apply-plan.csv apply-plan.csv.bak-<tag>-<ts>`).
### 🔴 W25 — `normalize_track` не должен менять ФОРМУ записи
Канонизация `01` → `1`, `03/12` → `3/12` создаёт тег-запись там, где правки нет
(в библиотеке было **132** таких файла). Решение Alex: «**оставлять**».
```python
# ✅ отбрасывать мусор, форму сохранять
v = (raw or "").strip()
if not v or not TRACK_OK.match(v):
return "" # "03/", "/10", "P@X@Tu*HCK*u"
if "/" in v:
a, b = v.split("/", 1)
if int(a) < 1 or int(b) < 1:
return "" # "0/5"
return f"{a}/{b}" # форма как есть
return v if int(v) >= 1 else "" # "0" -> "", "01" -> "01"
```
**Общий принцип:** нормализатор отбраковывает невалидное, но не переписывает валидное.
Иначе отчёт «изменено N файлов» врёт: изменений смысла нет, есть только запись.
### 🔴 W26 — любой детектор «разделителя» в теге матчит ВСЕ три тире
`(2012) Infected Mushroom – U r so fucked` — **en-dash `–` U+2013**, а не дефис. Регулярка
по `-` строку не поймала, 1 файл из 7 остался неисправленным (верификатор показал `BAD … 1`).
```python
DASH = r"[-–—]" # hyphen, en-dash, em-dash
```
Технический шум в теге/имени папки прячется за разными вариантами одного символа
(ср. A6: `(CD1)`/`(Disc 2)`).
### 🔴 W27 — детектор по ФОРМЕ значения даёт тысячи ложных срабатываний
Проход по диффу (`album`):
| Детектор | Кандидатов | Реальных |
|---|---:|---:|
| «дефис с пробелами в `album`» | 222 | **101** |
| `album == title` | 149 | **0** |
| «`new_album` входит в `folder`» | 3051 | **0** (ловит вложенность пути) |
`album == title` — в 130 папках это **одноимённые альбомы** (`Enigma/Sleep`, `Любэ/Атас`,
`Сектор Газа/Колхозный панк`), у всех `cur_album == new_album`. Дефект только там, где
`album` совпал с `title` у **одного** файла папки, а не у большинства.
**Правило:** прежде чем называть класс дефектом — проверить его против `cur_*` (план не
выдумывал?) и по **соседним файлам папки** (A2/D22). Реальная находка — только СКЛЕЙКА
`Родитель - Подпапка` и технический префикс `(год) Артист - `.
### 🔴 W28 — «ПОТЕРЯНО: N» в верификаторе ≠ потери правок
`verify_diff_completeness.py` дал `ПОТЕРЯНО: 2340` (881 после точной сверки). Разбор
(`loss_kind.py`) показал: у **всех** новое значение ПРИСУТСТВУЕТ в строке `ПАПКА` — отличается
только `old` (`<N разных>` при схлопывании, D16). **Правок, пропавших из диффа: 0.**
**Правило: перед словом «потеря» проверить, не покрыта ли пара строкой ПАПКА с другим
`old`.** Схлопывание по `(папка, поле, новое)` делает разные `old` нормой.
### 🔴 W29 — проверять расхождение артефактов с ПРЕДЫДУЩИМ снапшотом фактом, а не догадкой
Alex: «**В rc этого не было.. это новые дефекты?**». Прямая сверка (`were_these_in_rc.py`)
против снапшота RC: `7861/7861` строк совпадают, `отличий: 0`, все три класса присутствуют
в RC в тех же количествах (18/77/6). → **дефекты НЕ новые, они были пропущены в RC-проходе.**
**Правило: на вопрос «это было раньше?» отвечать `diff` + `md5` по снапшотам, не рассуждением.**
### 🔴 W30 — `python3 -c` с генератором по `r` даёт тождество
```python
# ❌ сравнивает значение САМО С СОБОЙ -> ложные «7861»
any(c != (r[c] or "").strip() and r[c] for c in r if c.startswith("new_"))
# ✅
any(v != v.strip() for k, v in r.items() if k.startswith("new_") and v)
```
Детектор пробелов отрапортовал 7861 нарушение, реально **0**. Условие, зависящее от `r[c]`,
внутри генератора по `r` — писать явным циклом.
### 🔴 W17 — аудит по ОДНОМУ полю пропускает записи, где это поле уже верное
Детектор искал `new_artist == 'Сказочный сборник'` и дал 15 файлов. В папке их **17**: