[2026-09-26] eagle: personal/plans/music-tags-review/1-needs-decision.sync-conflict-20260926-150525-XJRJQB2.md personal/plans/music-tags-review/1-needs-decision.sync-conflict-20260926-165021-C6DYBCZ.md personal/tech/music-tags-repair-truenas.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-26 16:43:32 +06:00
1 parent c001e3fec7
commit 680da87d49
3 files changed
+72 -1727

No files matched your search

+72 -15
View File
@@ -26,7 +26,7 @@ related:
> третья волна по замечаниям Alex → 3 бага (en-dash + подстроковый поиск стоп-слов),
> **четвёртая волна (ручной проход) → 10 багов (track-номера, disc-track, восклицательные знаки, регистр)**,
> **пятая волна (ручной разбор блока 1 глазами) → 28 файлов, 10 артистов вписано вручную**,
> **шестая волна (Alex правит мои md + ACL-фикс синка) → найдены баг `ÿþ`-BOM и конфликтные файлы**.
> **шестая волна (Alex правит мои md + ACL-фикс синка) → найден и ПОЧИНЕН баг `ÿþ`-BOM в парсере ID3**.
> Итог v5: 6773 `fix`, mojibake = 0, 121 тест PASS, блок «требует решения» 296 → 28.
## 🔴 ШЕСТАЯ ВОЛНА: ALEX ПРАВИТ МОИ НОТЫ + ACL-ФИКС СИНКА (2026-09-26)
@@ -59,26 +59,83 @@ Alex: **«Needs decision — забирай из syncthing. Medium confidence
> Alex поправил `title` на `Цех-126 Track` (кириллица). Вывод: в папках `!Radio/*`
> artist почти всегда = первые 1–3 слова имени, остальное = название. Проверять это первым.
### 🔴 БАГ: `ÿþ` (UTF-16 BOM) «починен» в мусор — 4 строки в `2-medium-confidence.md`
### ✅ БАГ `ÿþ` (UTF-16 BOM) — НАЙДЕНА ПЕРВОПРИЧИНА, ПАРСЕР ПОЧИНЕН
Alex: **«Какого хуя ты мне подсовываешь мусор со сбитой кодировкой?! Там cp1251 а не мусор!!»** —
замечание по сути верное, но диагноз оказался глубже: там **не CP1251 и не mojibake**,
а **битый ID3-тег: BOM при `enc_byte=0`**.
#### Что реально в байтах (дамп файла, `dump_id3.py`)
```
06 Steel Deluxe - Сердце открой.mp3
title: ÿþ!@54F5 B:@>9 → яю!@54F5 B:@>9 ← МУСОР вместо названия
10 Boom (Bimbo Jones Mix).mp3
title: ÿþY → яюY
artist: ÿþd → яюd
album: ÿþY → яюY
header: ID3 ver=3.0
frame TIT2 enc_byte=0 payload = 00 FF FE 21 40 35 34 46 35 20 42 3A 40 3E 39
frame TPE1 enc_byte=0 payload = 00 FF FE 53 74 65 65 6C 20 44 65 6C 75 78 65
frame TALB enc_byte=0 payload = 00 "Record Super Chart ¹ 90 (30.05.2009)"
```
**Причина:** байты `FF FE` в начале строки — это **UTF-16 BOM**, а не CP1251→Latin-1 mojibake.
`fix_encoding` конвертировал их как mojibake → `ÿþ` → `яю`. Результат **хуже исходного**.
**Суть:** заголовок кадра объявляет `enc=0` (latin-1), а **содержимое начинается с `FF FE`** —
UTF-16 BOM. Так писали древние теггеры: BOM поставили, байт кодировки оставили нулевым,
а текст после BOM — **обычные 8-битные символы**, не UTF-16.
> 🔴 **ПИТФОЛЛ: `ÿþ` (0xFF 0xFE) в начале тега — это BOM, не mojibake.**
> Не конвертировать. Настоящее значение — в имени файла (`Сердце открой`), artist — из имени.
> **Ложное срабатывание grep:** строки `Òåðÿþ ñâåò → Теряю свет` в `3-high-confidence.md`
> содержат `ÿþ` **внутри слова «Теряю»** — это корректная конвертация, НЕ баг. Различать по позиции.
Отсюда `ÿþSteel Deluxe` (= `FF FE` + ASCII «Steel Deluxe») — а не сломанная кириллица.
**Реальных багов — 4 строки (1 файл), не 2096.** Проверять адресно, а не по всему файлу.
> 🔴 **ПИТФОЛЛ: `ÿþ` (0xFF 0xFE) в начале тега — это BOM-префикс, а НЕ UTF-16-текст и НЕ mojibake.**
> Текст за ним читать как **8-битный**, сам BOM — отбросить.
> **Ловушка:** наивное `payload.decode("utf-16")` склеивает ASCII-пары →
> `Steel Deluxe` → `瑓敥\u206c敄畬數`. Проверено на живом файле: первая версия фикса дала именно это.
#### Фикс — `id3_direct.py`, функция `decode_text()`
```python
raw = data[1:]
# Broken tagger: encoding byte says latin-1 (0), payload starts with a
# UTF-16 BOM. In practice the BOM is a bogus prefix and the remaining
# bytes are 8-bit text -- decode them, don't let the BOM win.
if enc == 0 and raw[:2] in (b"\xff\xfe", b"\xfe\xff"):
raw = raw[2:]
text = raw.decode(codec, errors="replace")
return text.strip("\x00").strip().lstrip("\uffff\ufffe").strip("\ufeff")
```
**Проверено на `06 Steel Deluxe - Сердце открой.mp3`:**
```
artist: 'Steel Deluxe' ← было 'ÿþSteel Deluxe' / '瑓敥…' → ВОССТАНОВЛЕН ✅
title : '!@54F5 B:@>9' ← мусор в самом теге → брать из имени файла
```
#### Два класса дефекта (различать!)
| Класс | Пример | Как чинить |
|---|---|---|
| **BOM + валидный 8-бит текст** | TPE1 = `FF FE` + `Steel Deluxe` | отбросить BOM → **artist готов** ✅ |
| **BOM + мусор в теге** | TIT2 = `FF FE` + `21 40 35 34 46 35` | взять из **имени файла** |
#### Масштаб — 4 файла, НЕ 2096 (`scan_bom.py` по всем 7906 строкам CSV)
```
❌ 10 Boom (Bimbo Jones Mix) title='ÿþY' artist='ÿþd' → title из имени
❌ 06 Steel Deluxe - Сердце открой title='ÿþ!@54F5 B:@>9' → title из имени (artist OK)
❌ 12 Whizzkids Feat. Inusa Dawuda title="'ÿþRumours…" artist=';ÿþWhizzkids…' → оба из имени
❌ 31 Inna - Love (Play & Win Club Mix) title='7ÿþoLve…' artist='ÿþInna' → оба из имени
```
> ⚠️ **Ложное срабатывание grep — 5 строк в `3-high-confidence.md`:**
> `Òåðÿþ ñâåò → Теряю свет`, `Ïîçäðàâëÿþ → Поздравляю` — здесь `ÿþ` **внутри слова** («Теряю»),
> это корректная CP1251-конвертация. Различать по позиции: начало значения ≠ середина слова.
У всех 4 файлов artist/title выводятся из имени — правки возможны **без участия Alex**.
**Скрипты, появившиеся в этой волне:**
| Файл | Что делает |
|---|---|
| `dump_id3.py` | Дамп сырых байтов ID3-кадров (`enc_byte`, hex, repr, попытки декодирования). Незаменим при разборе кодировок |
| `scan_bom.py` | Поиск `ÿþ` по всем полям CSV-плана — отделяет реальный BOM от середины слова |
> 📌 **Правило диагностики кодировок: сначала дамп байтов, потом гипотеза.** Я дважды ошибся,
> угадывая («это CP1251» → «это UTF-16»), пока не посмотрел `payload.hex()`.
> `dump_id3.py --file <mp3>` показывает `enc_byte` и сырые байты за один прогон.
### 📂 Конфликтные файлы Syncthing — зачем и что с ними