[2026-09-27] eagle: personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md personal/tech/id3-tag-repair-skill-notes.md personal/tech/music-tags-repair-truenas.md
This commit is contained in:
1 parent
50f91752bc
commit
8ce2b1e6a4
4 files changed
+413
-1353
No files matched your search
@@ -4,7 +4,7 @@ created: '2026-09-27'
|
|||||||
updated: '2026-09-27'
|
updated: '2026-09-27'
|
||||||
type: plan
|
type: plan
|
||||||
namespace: personal
|
namespace: personal
|
||||||
status: script-v12-verified-testing-passed-awaiting-go
|
status: script-v13-id3v1-logic-tested-awaiting-go
|
||||||
tags:
|
tags:
|
||||||
- music
|
- music
|
||||||
- metadata
|
- metadata
|
||||||
@@ -747,7 +747,137 @@ VERIFY: совпало 1/1, MISMATCH 0
|
|||||||
**17 509 496** байт, в бэкапе **19 174 756** (+1.6 МБ). Данные не потеряны, но
|
**17 509 496** байт, в бэкапе **19 174 756** (+1.6 МБ). Данные не потеряны, но
|
||||||
**при сверках размеров учитывать, что ID3-блок пересобирается компактнее.**
|
**при сверках размеров учитывать, что ID3-блок пересобирается компактнее.**
|
||||||
|
|
||||||
## ⏭ СЛЕДУЮЩИЕ ШАГИ (обновлено 2026-09-27, v11)
|
## ✅ v12–v13 (2026-09-27): ТРЕТЬЯ ПЕРЕПРОВЕРКА + ID3v1-логика
|
||||||
|
|
||||||
|
**Alex:** «Теперь после всех правок ещё раз просмотри скрипт на адекватность» → «Чини. Потом ещё
|
||||||
|
раз проверяй. И положи скрипт в `~/Automation` и в доках отрази» → «должен формироваться общий мап
|
||||||
|
где csv - главный источник истины… потом перезаписывается id3v2 и id3v1 сносится».
|
||||||
|
|
||||||
|
### 🔴 P7 (КРИТИЧНО): мусор в `new_track` уехал бы в `TRCK`
|
||||||
|
|
||||||
|
Проверка фактических значений колонки `new_track` дала:
|
||||||
|
|
||||||
|
```
|
||||||
|
'P@X@Tu*HCK*u' ×2 ← CПАМ ТЕГИРОВЩИКА в трек-номере
|
||||||
|
'03/' '/10' ×2 ← битый формат
|
||||||
|
'0' ×4 ← невалидный номер
|
||||||
|
'095' '087' '054' '005' … ×133 ← ведущие нули
|
||||||
|
```
|
||||||
|
|
||||||
|
**Нормализатор `normalize_track()`:** `^\d{1,4}(/\d{1,4})?$` + `int()` снимает ведущие нули,
|
||||||
|
нечисловое и `<1` → пусто.
|
||||||
|
|
||||||
|
🔴 **Питфолл первой версии:** регексп `^[1-9]\d{0,3}…` **отвергал ведущие нули** → 133 файла
|
||||||
|
получали бы **пустой TRCK**. Поймано юнит-тестом (18 кейсов, 2 FAIL).
|
||||||
|
**Нормализатор обязан иметь тесты на ведущие нули отдельно.**
|
||||||
|
|
||||||
|
### 🔴 P8–P11
|
||||||
|
|
||||||
|
| # | Дефект | Фикс |
|
||||||
|
|---|---|---|
|
||||||
|
| **P8** | `_self_check()` не сверял `expected()` с `FIELD_MAP` → verify молча пропустит поле | вторая проверка на старте |
|
||||||
|
| **P9** | `changed` не пересчитан после нормализации track | `_check_changed_consistency(rows)` |
|
||||||
|
| **P10** | `field_changes` считал по **сырому** `new_track`, писал **нормализованное** → отчёт врал | `canon_value()` |
|
||||||
|
| **P11** | `readback`/`dry-run` не показывали `TRCK` | добавлено |
|
||||||
|
|
||||||
|
### 🔴 ДЕФЕКТ В САМОМ ИНВАРИАНТЕ
|
||||||
|
|
||||||
|
`probe = {k: "x" …}` для `new_track` → `normalize_track("x")==""` → `TRCK` не создаётся →
|
||||||
|
**ложный ABORT**. Фикс: `probe = {k: ("1" if k=="new_track" else "x") …}`.
|
||||||
|
**Инвариант обязан использовать значения, проходящие собственную валидацию.**
|
||||||
|
|
||||||
|
### 🔴 ID3v1 — СНОСИТСЯ, поля вне CSV берутся v2→v1
|
||||||
|
|
||||||
|
**Alex:** «csv — главный источник истины, поля которых нет в csv — берутся из id3v2, если чего-то
|
||||||
|
нет в id3v2 — берется из id3v1, потом перезаписывается id3v2 и id3v1 сносится».
|
||||||
|
|
||||||
|
```
|
||||||
|
6 управляемых полей (TIT2/TPE1/TPE2/TALB/TCON/TRCK)
|
||||||
|
→ ТОЛЬКО из CSV. Пусто в CSV => тег УДАЛЯЕТСЯ, старое значение НЕ подставляется.
|
||||||
|
Поля вне CSV (APIC/TDRC/COMM/…) → из ID3v2; нет в v2 → из ID3v1
|
||||||
|
ID3v1 → save(disk, v1=0) — СНОСИТСЯ
|
||||||
|
```
|
||||||
|
|
||||||
|
**Из ID3v1 переносятся только 2 поля** — остальные 4 (title/artist/album/genre) дублируют управляемые:
|
||||||
|
|
||||||
|
| v1 | → v2 | Условие |
|
||||||
|
|---|---|---|
|
||||||
|
| `year` | `TDRC` | нет TDRC в v2 **и** год = ровно `\d{4}` |
|
||||||
|
| `comment` | `COMM` | нет ни одного COMM в v2 |
|
||||||
|
|
||||||
|
### 🔴 ПИТФОЛЛ: год из ID3v1 бывает байтовым мусором
|
||||||
|
|
||||||
|
Найдено на живом файле: `year = '\x00\x00\x000'` → мой код записал `TDRC='\x00\x00\x000'`.
|
||||||
|
Фикс: `_read_id3v1()` чистит нули/пробелы с **обеих** сторон, отбрасывает строки с управляющими
|
||||||
|
символами, и год принимает только как `\d{4}`. **Поймано тестом, не глазом.**
|
||||||
|
|
||||||
|
### 📊 МАСШТАБ ПРОБЛЕМЫ ID3v1 — 1099 из 2004 файлов (55%)
|
||||||
|
|
||||||
|
```
|
||||||
|
ID3v1 хранит latin-1 + поля обрезаны 30 байт
|
||||||
|
Чайковский: v1 title = '????? ????????? ??????? (?????' ← кириллица потеряна
|
||||||
|
v2 TIT2 = 'Танец маленьких лебедей (Лебединое озеро)' ← правильное
|
||||||
|
Бах: v1 title = 'J.S. Bach: Toccata and Fugue (' ← ОБРЕЗАН
|
||||||
|
Орф: v1 title = 'Carmina Burana: Fortuna Impera' ← ОБРЕЗАН
|
||||||
|
→ плеер читает v1 первым → показывает мусор вместо правильного v2
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ ЖИВОЙ ТЕСТ: 3 файла (общая логика) + 2 файла (ID3v1-логика)
|
||||||
|
|
||||||
|
```
|
||||||
|
3 файла: ok=3 MISMATCH=0
|
||||||
|
Моцарт: TRCK отсутствовал → '24' (из имени файла!) ← старый скрипт стёр бы
|
||||||
|
Лист: 11 фреймов сохранены (COMM/TCOP/TDRC/TKEY/TSSE/APIC)
|
||||||
|
Камбурова: TRCK='236' сохранён, WCOP/WXXX/COMM×2 целы
|
||||||
|
размеры: байт в байт (5993976 · 26343259 · 8270808)
|
||||||
|
|
||||||
|
2 файла (ID3v1): ВСЁ ОК
|
||||||
|
Чайковский: битый year отброшен → TDRC пусто ✓ · v1 снесён ✓ · APIC цел ✓
|
||||||
|
Орф: TDRC='2013' из v2 сохранён ✓ · v1 снесён ✓ · APIC цел ✓
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔴 ПИТФОЛЛ: скрипт-генератор перезаписывал основной скрипт
|
||||||
|
|
||||||
|
`fix_track_and_selfcheck.py` правил CSV **и перезаписывал `apply_tags_v2.py` целиком** —
|
||||||
|
повторный прогон затёр ручные правки `_frames()`/`_self_check()`.
|
||||||
|
**Разделено:** `normalize_track_csv.py` (только CSV, идемпотентный) + правки через `patch`.
|
||||||
|
**Правило: скрипт-генератор НЕ должен перезаписывать файл, который правится параллельно.**
|
||||||
|
|
||||||
|
### 🔴 ПИТФОЛЛ: образ содержит СВОЮ копию скрипта
|
||||||
|
|
||||||
|
`tagwriter:v2` имеет `/app/apply_tags_v2.py` внутри. Заливка нового файла в `data/` **не влияет** —
|
||||||
|
выполняется старая версия из образа. Симптом: `AttributeError: module 'at2' has no attribute '_read_id3v1'`.
|
||||||
|
**Обязателен `docker cp <new> <container>:/app/apply_tags_v2.py` перед запуском.**
|
||||||
|
|
||||||
|
### 📄 Файлы скопированы в `~/Automation/music-tags/`
|
||||||
|
|
||||||
|
```
|
||||||
|
apply_tags_v2.py писатель тегов (566 строк)
|
||||||
|
normalize_track_csv.py нормализация new_track (идемпотентный)
|
||||||
|
test_logic.py тесты нормализатора + инвариантов + consistency
|
||||||
|
test_v1.py тест ID3v1/ID3v2-логики на живых файлах
|
||||||
|
snapshot.py снимок всех фреймов (ДО/ПОСЛЕ)
|
||||||
|
pick_track_files.py выбор файлов с реальным TRCK
|
||||||
|
find_v1.py / dump_v1.py поиск и дамп ID3v1
|
||||||
|
Dockerfile.v2 python:3.11-slim + mutagen 1.47.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 📊 БОЕВОЙ ВХОД: `APPLY-PLAN-FINAL2.csv`
|
||||||
|
|
||||||
|
```
|
||||||
|
строк 7861 · track непустых 6423 (6432 − 9 мусорных) · changed=yes 7648 · no 213
|
||||||
|
мусорных new_track 0 · пустых title/artist/albumartist/genre 0 · дублей path 0 · спама 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔴 ПИТФОЛЛ ПРОЦЕССА (Alex: «это неоднократно», «почему каждый раз хуйня»)
|
||||||
|
|
||||||
|
**Договорённость, не записанная в файл, для агента не существует.** Контекст диалога
|
||||||
|
сбрасывается — файл не сбрасывается. Кейсы за одну задачу: `v1=0` (обсудили, не вписали),
|
||||||
|
колонка `track` (потеряна при сведении), тестовые файлы (оставались изменёнными между
|
||||||
|
итерациями), патч-скрипт (затирал основной), нормализатор (правился в одном файле, не в другом).
|
||||||
|
**Фиксировать решения в файл в том же ходе, а не в чате.**
|
||||||
|
|
||||||
|
## ⏭ СЛЕДУЮЩИЕ ШАГИ (обновлено 2026-09-27, v13)
|
||||||
|
|
||||||
1. ✅ план `fix-plan-FINAL.csv` — 7861 стр, 0 пустых
|
1. ✅ план `fix-plan-FINAL.csv` — 7861 стр, 0 пустых
|
||||||
2. ✅ жанры `genre-plan-FINAL.csv` — 65 уникальных, 0 пустых
|
2. ✅ жанры `genre-plan-FINAL.csv` — 65 уникальных, 0 пустых
|
||||||
@@ -771,12 +901,18 @@ VERIFY: совпало 1/1, MISMATCH 0
|
|||||||
`cur_track`/`new_track` добавлены, **6432 из 7861**; `TRCK` в `FIELD_MAP`/`_frames()`;
|
`cur_track`/`new_track` добавлены, **6432 из 7861**; `TRCK` в `FIELD_MAP`/`_frames()`;
|
||||||
`_self_check()` инвариант. **Тест: `TRCK='9'` записан, `MISMATCH=0`**
|
`_self_check()` инвариант. **Тест: `TRCK='9'` записан, `MISMATCH=0`**
|
||||||
12. ✅ **8 ТЕСТОВЫХ ФАЙЛОВ ВОЗВРАЩЕНЫ ИЗ БЭКАПА** — 8/8, подтверждено снимком
|
12. ✅ **8 ТЕСТОВЫХ ФАЙЛОВ ВОЗВРАЩЕНЫ ИЗ БЭКАПА** — 8/8, подтверждено снимком
|
||||||
13. 🔴 **ЖДЁТ «go» ОТ ALEX** — массовый прогон `--apply --verify` по `APPLY-PLAN-FINAL.csv` (7861).
|
13. ✅ **ТРЕТЬЯ ПЕРЕПРОВЕРКА (v12)** — P7 (мусор в `new_track` → `TRCK`), P8–P11,
|
||||||
|
дефект в самом инварианте, `normalize_track()`. Тест 3 файла: `MISMATCH=0`,
|
||||||
|
`TRCK='24'` восстановлен из имени файла
|
||||||
|
14. ✅ **ID3v1-ЛОГИКА (v13)** — поля вне CSV берутся v2→v1, `save(v1=0)` сносит v1.
|
||||||
|
Тест 2 файла: ВСЁ ОК (битый year отброшен, TDRC из v2 сохранён, APIC цел)
|
||||||
|
15. ✅ **Скрипты в `~/Automation/music-tags/`** — 9 файлов, дока переписана 268 КБ → 9 КБ
|
||||||
|
16. 🔴 **ЖДЁТ «go» ОТ ALEX** — массовый прогон `--apply --verify` по `APPLY-PLAN-FINAL2.csv` (7861).
|
||||||
Все тесты откачены, файлы в исходном состоянии, бэкап на месте
|
Все тесты откачены, файлы в исходном состоянии, бэкап на месте
|
||||||
14. ⏳ Приёмка Alex: **видно в UI** (Navidrome/Jellyfin), не «read back» ассистента
|
17. ⏳ Приёмка Alex: **видно в UI** (Navidrome/Jellyfin), не «read back» ассистента
|
||||||
15. ⏳ После приёмки: откат 40 из `backup_conv40/` (если нужен), удаление 29 битых/DRM,
|
18. ⏳ После приёмки: откат 40 из `backup_conv40/` (если нужен), удаление 29 битых/DRM,
|
||||||
удаление бэкапов `backup/` + `backup_raw/` + `backup_fix/` + `backup_conv40/`
|
удаление бэкапов `backup/` + `backup_raw/` + `backup_fix/` + `backup_conv40/`
|
||||||
16. ⏳ Lidarr + Navidrome (первоначальная задача Alex, ещё не начата)
|
19. ⏳ Lidarr + Navidrome (первоначальная задача Alex, ещё не начата)
|
||||||
|
|
||||||
## 🔴 ПРАВИЛО (открыто в этой сессии): «управляемый» набор фреймов = набор записываемых
|
## 🔴 ПРАВИЛО (открыто в этой сессии): «управляемый» набор фреймов = набор записываемых
|
||||||
|
|
||||||
|
|||||||
+193
-1346
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: Skill — id3-tag-repair (питфоллы и уроки)
|
title: Skill — id3-tag-repair (питфоллы и уроки)
|
||||||
created: '2026-09-26'
|
created: '2026-09-26'
|
||||||
updated: '2026-09-26'
|
updated: '2026-09-27'
|
||||||
type: tech
|
type: tech
|
||||||
namespace: personal
|
namespace: personal
|
||||||
tags:
|
tags:
|
||||||
@@ -13,6 +13,7 @@ tags:
|
|||||||
related:
|
related:
|
||||||
- '[[personal/tech/music-tags-repair-truenas]]'
|
- '[[personal/tech/music-tags-repair-truenas]]'
|
||||||
- '[[personal/tech/music-tags-final-review]]'
|
- '[[personal/tech/music-tags-final-review]]'
|
||||||
|
- '[[personal/plans/tagwriter-container]]'
|
||||||
---
|
---
|
||||||
|
|
||||||
# Skill `id3-tag-repair` — зеркало Obsidian
|
# Skill `id3-tag-repair` — зеркало Obsidian
|
||||||
@@ -20,6 +21,10 @@ related:
|
|||||||
Скилл: `media/id3-tag-repair` (`~/.hermes/hermes-whale/skills/media/id3-tag-repair/SKILL.md`).
|
Скилл: `media/id3-tag-repair` (`~/.hermes/hermes-whale/skills/media/id3-tag-repair/SKILL.md`).
|
||||||
Здесь — durable-правила, выученные на проекте Music Tags TrueNAS. Полный текст — в скилле.
|
Здесь — durable-правила, выученные на проекте Music Tags TrueNAS. Полный текст — в скилле.
|
||||||
|
|
||||||
|
> 🆕 **Скилл обновлён 2026-09-27 (v1.1.0):** добавлен раздел **«Writing tags back: the ID3v1 /
|
||||||
|
> ID3v2 block layer»** (правила W1–W13) — класс дефектов «применение отчиталось об успехе, данные
|
||||||
|
> потеряны». См. § ID3v1/ID3v2 ниже (пункты 14–23).
|
||||||
|
|
||||||
## Пакетные правки данных
|
## Пакетные правки данных
|
||||||
|
|
||||||
1. **Идемпотентность обязательна.** Цель считать от ИСХОДНОГО значения (номер в имени файла),
|
1. **Идемпотентность обязательна.** Цель считать от ИСХОДНОГО значения (номер в имени файла),
|
||||||
@@ -64,3 +69,57 @@ related:
|
|||||||
grep -c "^Benny Benassi," artists-single-track.csv # ожидание 0
|
grep -c "^Benny Benassi," artists-single-track.csv # ожидание 0
|
||||||
grep -c "^Benny Benassi," artists-multi-track.csv # ожидание всех
|
grep -c "^Benny Benassi," artists-multi-track.csv # ожидание всех
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🔴 ID3v1 / ID3v2 — выучено 2026-09-27 (v13)
|
||||||
|
|
||||||
|
14. 🔴 **ID3v1 — скрытый источник мусора, его надо СНОСИТЬ.** `ID3()` (mutagen) читает
|
||||||
|
**только v2-блок**; ID3v1 (128 байт в конце файла) он не видит, а `save(v2_version=3)`
|
||||||
|
его **не удаляет**. Найденo: **1099 из 2004 файлов** имеют v1. В v1 — latin-1
|
||||||
|
(`'????? ????????? ???????'`) и обрезка полей до 30 байт. Плеер читает v1 **первым** →
|
||||||
|
показывает мусор вместо правильного v2. **Фикс: `tags.save(disk, v1=0, v2_version=3)`.**
|
||||||
|
|
||||||
|
15. 🔴 **Граница «что из CSV, что из файла» проходит по КОЛОНКАМ CSV, а не по «есть/нет в файле».**
|
||||||
|
```
|
||||||
|
6 управляемых полей (TIT2/TPE1/TPE2/TALB/TCON/TRCK) → ТОЛЬКО из CSV.
|
||||||
|
Пусто в CSV => тег УДАЛЯЕТСЯ. Старое значение НЕ подставляется.
|
||||||
|
Поля вне CSV (APIC/TDRC/COMM/TCOM/WXXX… ) → из ID3v2; нет в v2 → из ID3v1
|
||||||
|
ID3v1 → сносится
|
||||||
|
```
|
||||||
|
Неправильная трактовка («пусто в CSV → взять старое») приводит к тому, что чистка мусора
|
||||||
|
не работает.
|
||||||
|
|
||||||
|
16. 🔴 **Из ID3v1 переносятся только 2 поля** — остальные 4 (title/artist/album/genre)
|
||||||
|
дублируют управляемые, их значения всё равно придут из CSV:
|
||||||
|
| v1 | → v2 | Условие |
|
||||||
|
|---|---|---|
|
||||||
|
| `year` | `TDRC` | нет TDRC в v2 **и** год = ровно `\d{4}` |
|
||||||
|
| `comment` | `COMM` | нет ни одного COMM в v2 |
|
||||||
|
|
||||||
|
17. 🔴 **Год из ID3v1 бывает байтовым мусором.** Реальный кейс: `year = '\x00\x00\x000'` →
|
||||||
|
записался бы `TDRC='\x00\x00\x000'`. Чистить нули/пробелы с **обеих** сторон,
|
||||||
|
отбрасывать строки с управляющими символами, год валидировать `re.fullmatch(r"\d{4}", y)`.
|
||||||
|
|
||||||
|
18. 🔴 **Нормализатор обязан иметь тесты на ведущие нули.** Регексп `^[1-9]\d{0,3}…` «незаметно»
|
||||||
|
отвергал `'095'` → 133 файла получили бы **пустой** `TRCK` вместо номера.
|
||||||
|
Поймано юнит-тестом, не глазом. Рабочий: `^\d{1,4}(/\d{1,4})?$` + `int()`.
|
||||||
|
|
||||||
|
19. 🔴 **Инвариант обязан использовать значения, проходящие собственную валидацию.**
|
||||||
|
`_self_check()` собирал probe `{"new_track": "x"}` → `normalize_track("x")==""` → фрейм
|
||||||
|
«не создаётся» → **ложный ABORT**. Для числовых полей probe = `"1"`.
|
||||||
|
|
||||||
|
20. 🔴 **Скрипт-генератор НЕ должен перезаписывать файл, который правится параллельно.**
|
||||||
|
`fix_track_and_selfcheck.py` правил CSV **и** перезаписывал `apply_tags_v2.py` целиком →
|
||||||
|
повторный прогон затёр ручные правки. Разделять: CSV-нормализатор + `patch`.
|
||||||
|
|
||||||
|
21. 🔴 **Тест обязан ИМПОРТИРОВАТЬ функции из скрипта, а не дублировать их** — иначе тест
|
||||||
|
проверяет свою копию логики и показывает устаревший результат (дал 2 ложных FAIL).
|
||||||
|
|
||||||
|
22. 🔴 **Docker-образ содержит СВОЮ копию скрипта.** Заливка нового файла в монтируемую `data/`
|
||||||
|
не влияет — выполняется старая версия из образа. Симптом:
|
||||||
|
`AttributeError: module 'at2' has no attribute '_read_id3v1'`.
|
||||||
|
**`docker cp <new> <container>:/app/script.py` обязателен.**
|
||||||
|
|
||||||
|
23. 🔴 **Договорённость, не записанная в файл, не существует.** Контекст диалога сбрасывается,
|
||||||
|
файл — нет. Alex: «это неоднократно». За одну задачу потерялось 5 решений
|
||||||
|
(`v1=0`, колонка `track`, тестовые файлы, патч-скрипт, нормализатор). Фиксировать в файл
|
||||||
|
в том же ходе.
|
||||||
@@ -23,6 +23,24 @@ related:
|
|||||||
|
|
||||||
# Music Tags Repair — TrueNAS (7906 → 7905 файлов)
|
# Music Tags Repair — TrueNAS (7906 → 7905 файлов)
|
||||||
|
|
||||||
|
> ### 🔴 АКТУАЛЬНОЕ СОСТОЯНИЕ (2026-09-27) — ЧИТАЙ ЭТО ПЕРВЫМ
|
||||||
|
>
|
||||||
|
> **Этот файл — исторический лог волны 38 (завершена). Актуальная работа идёт здесь:**
|
||||||
|
>
|
||||||
|
> | Документ | О чём |
|
||||||
|
> |---|---|
|
||||||
|
> | **[[personal/plans/tagwriter-container]]** | ⭐ скрипт-писатель `apply_tags_v2.py` (566 стр): логика полей, ID3v1-снос, тесты, 18 питфоллов, запуск |
|
||||||
|
> | **[[personal/plans/music-artist-albumartist-repair]]** | правила формирования плана, сборка `APPLY-PLAN-FINAL2.csv` |
|
||||||
|
> | **[[personal/tech/music-broken-toload]]** | 29 битых/DRM файлов на перезагрузку |
|
||||||
|
>
|
||||||
|
> **Состояние:** скрипт готов и протестирован, **боевой прогон НЕ запущен — ждёт «go» от Alex.**
|
||||||
|
> Боевой вход: `~/Downloads/music-tags/APPLY-PLAN-FINAL2.csv` (7861 строка).
|
||||||
|
> Скрипты: `~/Automation/music-tags/`.
|
||||||
|
>
|
||||||
|
> ⚠️ Волна 38 (TIT2/TPE1/TALB/TRCK, 7041 файл) — **завершена**, описывается ниже как история.
|
||||||
|
> Её дефекты (`apply_tags.py` испортил 16 не-MP3, `fix40.py` убил 40 MP3) разобраны в
|
||||||
|
> `tagwriter-container.md` § 8 «Питфоллы».
|
||||||
|
|
||||||
> ## ⏭ НОВАЯ ЗАДАЧА (2026-09-27): `artist` + `albumartist` + `album` ПО ВСЕЙ БИБЛИОТЕКЕ
|
> ## ⏭ НОВАЯ ЗАДАЧА (2026-09-27): `artist` + `albumartist` + `album` ПО ВСЕЙ БИБЛИОТЕКЕ
|
||||||
>
|
>
|
||||||
> Волна 38 закрыла **MP3-план** (TIT2/TPE1/TALB/TRCK). Но Alex поставил задачу **шире**:
|
> Волна 38 закрыла **MP3-план** (TIT2/TPE1/TALB/TRCK). Но Alex поставил задачу **шире**:
|
||||||
|
|||||||
Reference in new issue
Block a user