[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:
1 parent
c001e3fec7
commit
680da87d49
3 files changed
+72
-1727
No files matched your search
@@ -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 — зачем и что с ними
|
||||
|
||||
|
||||
Reference in new issue
Block a user