From 559b19202890e777d4a94db1ee1f3445fe1fccf4 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sun, 27 Sep 2026 16:10:35 +0600 Subject: [PATCH] [2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md personal/tech/id3-tag-repair.md --- .../plans/music-artist-albumartist-repair.md | 58 ++++++- personal/plans/tagwriter-container.md | 112 +++++++++++-- personal/tech/id3-tag-repair.md | 149 ++++++++++++++++++ 3 files changed, 305 insertions(+), 14 deletions(-) diff --git a/personal/plans/music-artist-albumartist-repair.md b/personal/plans/music-artist-albumartist-repair.md index c2c479e2..947576a5 100755 --- a/personal/plans/music-artist-albumartist-repair.md +++ b/personal/plans/music-artist-albumartist-repair.md @@ -19,16 +19,16 @@ related: - '[[personal/tech/music-tags-repair-truenas]]' - '[[personal/plans/tagwriter-container]]' - '[[family/how-to/navidrome-truenas-android]]' -status: session17-gluefix-applied-awaiting-alex-check - +status: session17-gluefix-applied-image-deployed-dryrun-ok-awaiting-apply-go # Music `artist` + `albumartist` Repair — правила Alex и ход работ ## 🔵 СЕССИЯ 17 (2026-09-27) — ПОВТОРНЫЙ РУЧНОЙ ПРОХОД ДИФФА, ЧИТАТЬ ПЕРВЫМ > ``` -> APPLY-PLAN.csv 7861 строка md5 2269466bbc08c018482b35c855569a8f (НЕ ИЗМЕНЁН) -> DIFF-new-vs-38.csv 2845 строк md5 4e9e43318fb1c9dd353f96e3d125919c (НЕ ИЗМЕНЁН) -> Снапшот RC совпадает с рабочими файлами байт-в-байт (проверено diff + md5) +> APPLY-PLAN.csv 7861 строка md5 42d32ede85fef1b2fad6e2ce87012b7f (✅ ИЗМЕНЁН gluefix) +> DIFF-new-vs-38.csv 2852 строки md5 4e073fe50c191cc50015d3f15f6a6d28 (✅ ПЕРЕГЕНЕРИРОВАН) +> ДО правки было: APPLY-PLAN 2269466b… · DIFF 4e9e43318fb1c9dd353f96e3d125919c +> Снапшот RC: ~/Downloads/music-tags/milestones/RC-20260927/ (состояние ДО gluefix) > ``` > > **Alex потребовал ПОВТОРНЫЙ ручной проход по диффу.** ⚠️ Я ответил планом из 5 шагов @@ -152,6 +152,54 @@ status: session17-gluefix-applied-awaiting-alex-check > | `~/Downloads/music-tags/loss_kind.py` | разбор «881 потерь» → 0 реальных | > | `~/Downloads/music-tags/diff_new_vs_38.py` | генератор диффа (перегенерация) | +> ### 🔷 СЕССИЯ 17 (третья часть) — АУДИТ APPLY-СКРИПТА, СБОРКА ОБРАЗА, ЗАЛИВКА НА NAS +> +> Alex: «**Проверяй ещё раз руками apply script. Скажи алгоритм которым он будет применять +> правки и найди если есть какие-то проблемы**» → затем «**Собирай**». +> +> Полный разбор алгоритма и питфоллов — в `[[personal/plans/tagwriter-container]]` +> § «СЕССИЯ 17». Здесь — что касается ПЛАНА и приёмки. +> +> #### 🔴 ФИКС `normalize_track` по решению Alex +> На вопрос «канонизировать `03/12` → `3/12` или оставить?» Alex: «**6 - оставлять**». +> Функция переписана: мусор отбрасывается (`0`, `03/`, `/10`, `0/5`, `P@X@Tu*HCK*u`), +> **форма записи сохраняется** (`095`→`095`, `03/12`→`03/12`, `01`→`01`). +> Тест `test_normalize_track.py` — 16/16 OK. Записей `track` 344 → **335**. +> MD5 скрипта `c7a2bc143e38820f2050310cbe01b799` в обеих копиях. +> +> #### 🔴 ТРИ ДЕФЕКТА DOCKERFILE (см. tagwriter-container.md для деталей) +> | # | Дефект | Фикс | +> |---|---|---| +> | 1 | голый `Dockerfile` копирует **v1** `apply_tags.py` (5990 Б, без `TRCK`/`TPE2`) — та версия, что испортила 16 файлов | собирать только по `Dockerfile.v2` | +> | 2 | `Dockerfile.v2`: `CMD` вместо `ENTRYPOINT` → `--apply` не передать (`exec: "--dry-run": not found`) | `ENTRYPOINT` + `CMD ["--dry-run"]` | +> | 3 | `Dockerfile.v2` без `USER 950:950` → контейнер под root | `USER 950:950` | +> +> #### 🔴🔴 ПИТФОЛЛ: ARM-образ не запускается на NAS (amd64) +> Mac собирает `linux/arm64`; NAS `x86_64`. Симптом: `exec format error`. +> **Только `docker buildx build --platform linux/amd64 --load`.** `--load` обязателен. +> +> #### ✅ ЗАЛИТО НА NAS И ПРОВЕРЕНО +> ``` +> образ: tagwriter:latest f9de5c14b923 amd64 137MB +> внутри: uid=950 gid=950 · md5 c7a2bc143e38820f2050310cbe01b799 (= хостовый) · mutagen OK +> план: apply-plan.csv 42d32ede85fef1b2fad6e2ce87012b7f (= боевой) +> бэкап: apply-plan.csv.bak-before-gluefix-20260927-030612 +> backup/ библиотеки: /mnt/RED_2TB/storage/Music/backup — 50G (без неё exit 2) +> ``` +> До залива на NAS были **устаревшие** артефакты: `tagwriter:latest` = v1, `tagwriter:v2` = +> 16549 Б (не наш 25971), `apply-plan.csv` = `bf5093b3…`. **Не доверять артефактам на NAS +> без сверки md5.** +> +> #### ✅ `--dry-run` НА РЕАЛЬНОЙ БИБЛИОТЕКЕ (exit=0, `/tmp/tw-dry.log` на NAS) +> ``` +> строк в плане: 7861 · mp3 на диске: 7861 · пропущено не-mp3: 560 +> changed=no пропущено: 212 · не найдено на диске: 0 · к обработке: 7649 +> changed='no' с реальной правкой: 0 +> ``` +> +> #### ⏳ ОСТАЛОСЬ: `--apply --verify` (7649 файлов) — ЖДЁТ «GO» ALEX +> Порядок приёмки соблюдён: коммит ДО → правка → образ → dry-run → **ПРОВЕРКА АЛЕКСОМ** (ждём). + ## 🟢 АКТУАЛЬНО НА КОНЕЦ СЕССИИ 16 (2026-09-27) > ``` diff --git a/personal/plans/tagwriter-container.md b/personal/plans/tagwriter-container.md index 6123090b..278d55e5 100755 --- a/personal/plans/tagwriter-container.md +++ b/personal/plans/tagwriter-container.md @@ -4,7 +4,7 @@ created: '2026-09-26' updated: '2026-09-27' type: plan namespace: personal -status: session17-applyscript-audited-3-questions-open-no-run +status: session17-image-built-deployed-dryrun-ok-awaiting-apply-go tags: - music - truenas @@ -91,16 +91,110 @@ related: > Перепроверка отдельным скриптом — **0 пробелов**. **Генератор по `r` внутри условия, которое > зависит от `r[c]`, даёт тождество. Писать условие явным циклом.** > -> ### ⏳ СТАТУС ПОСЛЕ АУДИТА +> ### ⏳ СТАТУС ПОСЛЕ АУДИТА → ✅ ЗАКРЫТО В СЕССИИ 17 (вторая половина) > -> Три вопроса к Alex (пп. 3 и 5 — решение, п. 1/2 — действие перед прогоном): -> 1. образ собрать — **билд впервые**, `Dockerfile.v2`; -> 2. `changed=yes` без правок (12 строк) — снять флаг? -> 3. `track` с ведущим нулём (11) — канонизировать в `1`/`3/12`? -> -> Боевой прогон не запущен. +> Три вопроса заданы Alex, ответы получены и **выполнены**: +> 1. образ собрать → ✅ собран и залит (см. ниже); +> 2. `changed=yes` без правок → стало **1** вместо 12 (см. п. 5, эффект фикса формы); +> 3. `track` с ведущим нулём → 🔴 **Alex: «6 - оставлять»** — форма записи НЕ меняется. -## 🔴 СТАТУС: БОЕВОЙ ПРОГОН НЕ ЗАПУЩЕН +## ✅ СЕССИЯ 17 (вторая половина): ФИКС `normalize_track` + СБОРКА И ЗАЛИВКА ОБРАЗА + +> Ответ Alex на п. 5: «**6 - оставлять**» → `normalize_track` переписан. +> +> ### 🔴 ФИКС `normalize_track`: отбрасывать мусор, НЕ менять ФОРМУ записи +> +> ```python +> # БЫЛО (переписывало форму — 132 файла лишней записи): +> num = int(v); return str(num) if num >= 1 else "" +> if "/" in v: ... return f"{num}/{tot}" +> +> # СТАЛО: +> 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" +> ``` +> +> | Вход | Было | Стало | +> |---|---|---| +> | `095` | `95` | **`095`** | +> | `03/12` | `3/12` | **`03/12`** | +> | `01` | `1` | **`01`** | +> | `0`, `03/`, `/10`, `0/5`, `P@X@Tu*HCK*u` | `` | `` (мусор по-прежнему отбрасывается) | +> +> Тест: `~/Downloads/music-tags/test_normalize_track.py` — **16/16 OK**. +> Эффект на боевом плане: записей `track` было 344 → стало **335** (9 лишних убрано); +> кандидатов «меняется только форма» было 132 → **0**. +> MD5 скрипта `c7a2bc143e38820f2050310cbe01b799` — **обе копии** (`~/Automation/music-tags/` +> и `~/Downloads/music-tags/tagwriter/`). +> +> ### 🔴 ДВА ДЕФЕКТА `Dockerfile.v2`, найденных при первом запуске образа +> +> | # | Дефект | Симптом | Фикс | +> |---|---|---|---| +> | 1 | `CMD` вместо `ENTRYPOINT` | `docker run … tagwriter:latest --dry-run` → `exec: "--dry-run": executable file not found` — аргументы CLI не пробрасываются, `--apply` передать нельзя | `ENTRYPOINT ["python3", "/app/apply_tags_v2.py"]` + `CMD ["--dry-run"]` | +> | 2 | нет `USER 950:950` | контейнер стартовал под root | `USER 950:950` (uid/gid `truenas_admin`, владелец `/storage/Music`) | +> +> Базовый образ — `python:3.11-slim` + `mutagen==1.47.0`. Итоговый `Dockerfile.v2`: +> `FROM` · `pip install mutagen==1.47.0` · `WORKDIR /app` · `COPY apply_tags_v2.py` · +> `RUN chmod 755 /app/apply_tags_v2.py && chmod 755 /app` · `USER 950:950` · `ENTRYPOINT` · `CMD`. +> +> ### 🔴🔴 ГЛАВНЫЙ ПИТФОЛЛ: ПЛАТФОРМА ОБРАЗА — Mac ARM vs NAS amd64 +> +> Первая сборка на Mac ушла под `linux/arm64`. На NAS запуск дал: +> `WARNING: The requested image's platform (linux/arm64) does not match … (linux/amd64/v2)` → +> `exec /usr/bin/sh: exec format error`. +> NAS — **`x86_64`** (`docker version --format '{{.Server.Arch}}'` → `amd64`). +> +> **Собирать ТОЛЬКО так (кросс-платформа):** +> ```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` пуст. +> +> ### ✅ ПРОВЕРКА ОБРАЗА НА NAS (фактами) +> ``` +> docker run --rm --entrypoint sh tagwriter:latest -c 'id; md5sum /app/apply_tags_v2.py' +> → uid=950 gid=950 groups=950 +> → c7a2bc143e38820f2050310cbe01b799 /app/apply_tags_v2.py (= хостовый) +> mutagen OK +> ``` +> +> ### 🔴 СОСТОЯНИЕ NAS ДО ЗАЛИВА (три расхождения — не доверять старым артефактам) +> +> | Артефакт | Было на NAS | Стало | +> |---|---|---| +> | `tagwriter:latest` | **v1** `apply_tags.py`, 5990 Б, 15 часов | — | +> | `tagwriter:v2` | `apply_tags_v2.py` 16549 Б, 4 часа (**не актуальная**, у нас 25971) | — | +> | `tagwriter:latest` | — | `f9de5c14b923`, amd64, 137MB | +> | `apply-plan.csv` | `bf5093b356a2c5c979037dbcf5d13a84` (**старый**) | `42d32ede85fef1b2fad6e2ce87012b7f` | +> +> Бэкап плана: `apply-plan.csv.bak-before-gluefix-20260927-030612`. +> `backup/` библиотеки: `/mnt/RED_2TB/storage/Music/backup` — 50G, на месте +> (без неё `cmd_apply()` делает `exit 2`). +> +> ### ✅ `--dry-run` НА РЕАЛЬНОЙ БИБЛИОТЕКЕ (exit=0) +> ``` +> строк в плане: 7861 +> changed=no пропущено: 212 +> mp3 на диске: 7861 +> пропущено не-mp3: 560 +> не найдено на диске: 0 +> к обработке: 7649 +> ``` +> `changed=no` с реальной правкой — **0**. Все 7861 путь найден. +> Расхождение `changed=yes` без правок: **12 → 1** (`The Prodigy - Girls (Rex The Dog Remix)`) +> — эффект фикса формы `track`. +> +> ### ⏳ ОСТАЛОСЬ: `--apply --verify` (7649 файлов) — ЖДЁТ «GO» ALEX +> Лог dry-run на NAS: `/tmp/tw-dry.log`. Мусор `/tmp/tagwriter*.tar` — удалить вручную. + +## 🔴 СТАТУС: ОБРАЗ СОБРАН И ЗАЛИТ, DRY-RUN OK — ЖДЁТ «GO» НА `--apply --verify` > План правится в сессиях 8-17 > (дифф против 38-го прогона) — подробности в [[personal/plans/music-artist-albumartist-repair]] § «СЕССИЯ 17». diff --git a/personal/tech/id3-tag-repair.md b/personal/tech/id3-tag-repair.md index d0e3b55a..bb895b96 100755 --- a/personal/tech/id3-tag-repair.md +++ b/personal/tech/id3-tag-repair.md @@ -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 -c 'id; md5sum /app/