[2026-09-27] eagle: family/how-to/navidrome-truenas-android.md family/plans/music-jellyfin-lidarr-taiga.md personal/plans/music-artist-albumartist-repair.md personal/plans/tagwriter-container.md personal/tech/jellyfin-tags-cp1251-unreadable.md
This commit is contained in:
1 parent
7a6ec37e43
commit
d2a8ed8a06
5 files changed
+485
-5
No files matched your search
@@ -106,8 +106,16 @@ artist=Maria Makarova (organ) ← исполнитель вмес
|
||||
|
||||
## Следующие шаги
|
||||
- [x] ~~Сначала: ремонт тегов 7906 файлов~~ → ✅ выполнен (волна 38)
|
||||
- [ ] 🔴 **АКТИВНО (2026-09-27): ремонт `artist` + `albumartist` по 7861 файлу** —
|
||||
правила Alex зафиксированы, идёт ручная разбивка по группам.
|
||||
План — [[personal/plans/music-artist-albumartist-repair]]
|
||||
- `albumartist`: сборник → `Various Artists`, детские → `Детские песни`, сказки → `Сказки`
|
||||
- `artist`: из скобок в имени файла, иначе искать в интернете
|
||||
- мойибаке **492 файла** — расшифровывать CP1251→UTF-8, не маскировать
|
||||
- [ ] **Аудит семантики `artist`** — убрать «Классика в современной обработке» и подобное
|
||||
- [ ] **Опционально:** добавить `TPE2` = `TPE1` (нужен НОВЫЙ план правок с колонкой `album_artist`)
|
||||
(поглощён задачей выше)
|
||||
- [ ] Собрать `TPE2` по плану выше (нужен **НОВЫЙ** план правок с колонкой `album_artist` —
|
||||
в `tag-fix-plan.csv` её не было)
|
||||
- [ ] Поднять Navidrome в Docker на TrueNAS
|
||||
- [ ] Пробросить порт / настроить Caddy subdomain (Caddyfile root-owned → через alpine-контейнер)
|
||||
- [ ] Установить Symfonium на Android, подключить
|
||||
|
||||
@@ -1,4 +1,101 @@
|
||||
# Music → Jellyfin + Lidarr на Тайге — план (v7, 2026-09-27)
|
||||
# Music → Jellyfin + Lidarr на Тайге — план (v8, 2026-09-27)
|
||||
|
||||
> ## 🆕 v8: НОВАЯ ЗАДАЧА — ПОЛНЫЙ РЕМОНТ `artist` + `albumartist` (7861 файл)
|
||||
>
|
||||
> ✅ **Полный сбор тегов выполнен:** `raw_tags_full.tsv` — **7861 строка** (все mp3, бэкап-папки исключены).
|
||||
> ✅ **`tag-inventory3.csv`** — 7861 строка, **+колонка `albumartist`** (13 колонок).
|
||||
> 🔴 **Мойибаке по всей библиотеке: 492 файла** (было известно только 155/156) — см. §МОЙИБАКЕ.
|
||||
> 🔴 **Правила Alex для `albumartist` зафиксированы** — см. таблицу ниже.
|
||||
> ⏳ **Следующий шаг: РУЧНАЯ разбивка по группам (НЕ скриптом), списки → проверка → потом скрипт.**
|
||||
>
|
||||
> ### 🔴 ПРАВИЛА ALEX (v8) — `artist` / `albumartist` / `album` (дословно)
|
||||
>
|
||||
> | Поле | Правило |
|
||||
> |---|---|
|
||||
> | `artist` | **есть в скобках в имени файла → брать из скобок.** Нет в скобках и неизвестен → **искать в интернете** |
|
||||
> | `artist` | **НИКОГДА не маскировать мойибаке** — расшифровывать (CP1251→UTF-8), не оставлять как есть |
|
||||
> | `albumartist` | **сборник → `Various Artists`** |
|
||||
> | `albumartist` | **детские песни → `Детские песни`** |
|
||||
> | `albumartist` | **сказки → `Сказки`** |
|
||||
> | `albumartist` | не сборник/не детские/не сказки → **= `artist`** |
|
||||
> | `album` | **сборники и unsorted → ИМЯ ПАПКИ** (пример Alex: `y OOF`) |
|
||||
> | `album` | сказки → **оставить как есть** (там альбомы уже стоят) |
|
||||
>
|
||||
> ### 🔴 КРИТЕРИЙ СБОРНИКА (уточнён Alex в v8)
|
||||
>
|
||||
> Alex: «**Сборник это папка не одного артиста**», затем «**Не более чем одним [артистом]**»,
|
||||
> затем прямо: «**radio и retro — там сборники. Как и просто треки просто в корне артиста
|
||||
> если там есть альбомы — считать сборником типа НТЛ**».
|
||||
>
|
||||
> | Признак | ВЕРДИКТ |
|
||||
> |---|---|
|
||||
> | Папка `!Radio/*` | ✅ **СБОРНИК** (даже подпапки вида `x 2008-04`) |
|
||||
> | Папка `!Retro` | ✅ **СБОРНИК** |
|
||||
> | Треки в корне папки артиста, при наличии у него подпапок-альбомов | ✅ **СБОРНИК** (кейс НТЛ: 63 файла в `!Рэп/НТЛ` корне + 5 альбомов внутри) |
|
||||
> | `!A Not sorted/Sbornik_Novij_Rok` (184 арт.) | ✅ **СБОРНИК** |
|
||||
> | Папка с 1 артистом | ❌ **АЛЬБОМ** (albumartist = artist) |
|
||||
> | `!Pop, Club, Любэ/Benassi Bros` (3 арт. из-за фитов) | ⚠️ **АЛЬБОМ** — не сборник, это альбом с фитами |
|
||||
>
|
||||
> 🔴 **Alex ЗАПРЕТИЛ разметку скриптом:** «**Ручная разбивка по группам. Не скриптом блядь.
|
||||
> Сначала готовишь списки, проверяешь, а потом только скриптом можешь обновлять csv.**»
|
||||
> Порядок: **списки вручную → проверка Alex → скрипт обновляет CSV** → правка файлов.
|
||||
>
|
||||
> ### 📊 МОЙИБАКЕ ПО ВСЕЙ БИБЛИОТЕКЕ (факт, 2026-09-27) — 492 файла
|
||||
>
|
||||
> | Поле | Файлов с мойибаке |
|
||||
> |---|---:|
|
||||
> | `album_artist` (TPE2) | **269** |
|
||||
> | `artist` (TPE1) | **160** |
|
||||
> | `album` (TALB) | **63** |
|
||||
> | `title` (TIT2) | **1** |
|
||||
> | **строк с мойибаке хоть в одном поле** | **492** |
|
||||
>
|
||||
> **Топ папок:** `!Детские/Детский хит` **156**, `!Rock&Folk/Муммий Тролль` 54,
|
||||
> `!Pop, Club, Любэ/!Radio` 44, `!Rock&Folk/Пилот` 31, `!Rock&Folk/ЗАХАР МАЙ` 27,
|
||||
> `!Rock&Folk/Несчастный Случай` 23, `Король и Шут` 17, `Красная Плесень` 16, `Ария, Кипелов` 16,
|
||||
> `Ундервуд` 16, `Трофим` 14, `Запрещенные Барабанщики` 13, `Агата Кристи` 11, `Земфира` 9.
|
||||
>
|
||||
> **Мойибаке — ИСХОДНОЕ состояние файлов, НЕ следствие волны 38.** Доказано сравнением
|
||||
> `tag-inventory.csv` (v1) vs `tag-inventory2.csv`: значения тегов идентичны, строки отличаются
|
||||
> только добавленной колонкой `source`. Файлы прошли волну «не изменёнными».
|
||||
>
|
||||
> ### 🔴 ПОЧЕМУ ВОЛНА 38 ИХ НЕ ПОЧИНИЛА — корень в `apply_tags.py`
|
||||
>
|
||||
> 1. **Генератор плана сдался** на CP1251-байтах → вместо обратного декодирования поставил
|
||||
> `artist<-cleared-junk` (пусто). **155 файлов** с этим `source`.
|
||||
> 2. **`delall` стоял ВНУТРИ `if` с проверкой непустого `new_artist`:**
|
||||
> ```python
|
||||
> if (r.get("new_artist") or "").strip(): # new_artist пуст → False
|
||||
> out.append(TPE1(...)) # не выполнено
|
||||
> for frame in _frames(r):
|
||||
> tags.delall(frame.FrameID) # ← ТОЖЕ не выполнено
|
||||
> tags.add(frame)
|
||||
> ```
|
||||
> Пустой план → цикл не сделал ничего → **старый мойибаке уцелел**.
|
||||
> 3. **`_frames()` вообще не знает про `TPE2`** (`TIT2, TPE1, TALB, TRCK` — и всё) →
|
||||
> **269 файлов с мойибаке в albumartist скрипт не мог тронуть в принципе.**
|
||||
>
|
||||
> **Правило:** `delall` — **всегда**, `add` — только при непустом значении. Иначе «очистить»
|
||||
> не очищает. **«N/N применено, 0 ошибок» ≠ приёмка** — критерий «не упало», а не «тег стал верным».
|
||||
>
|
||||
> ### ✅ ИТОГ v8 (для следующей сессии)
|
||||
>
|
||||
> | Артефакт | Номер/статус |
|
||||
> |---|---|
|
||||
> | `raw_tags_full.tsv` | **7861 строк**, md5 `0c3647fadb6556b4100c555ed59bfb44` |
|
||||
> | `tag-inventory3.csv` | **7861 строк**, 13 колонок, +`albumartist` |
|
||||
> | Сборников по имени папки | **11 папок / 382 файла** (ранее недооценено — `!Radio`/`!Retro` не по имени) |
|
||||
> | `album_artist` пуст | **6451 из 7861 (82%)** |
|
||||
> | Мойибаке всего | **492** |
|
||||
> | Списки для ручной разметки | `list2-folders.txt` (633 папки), `list3-radio.md` (!Radio, 31 папка) |
|
||||
>
|
||||
> ⚠️ **Питфолл сбора тегов:** скрипт писал в `/mnt/RED_2TB/storage/tmp/` (**владелец root**),
|
||||
> а пользователь `truenas_admin` (uid 950) → `permission denied`. **Решение: `docker cp`
|
||||
> в `/home/truenas_admin/`.** Данные внутри контейнера при этом целы (`/tmp/raw_out.tsv`).
|
||||
> Ещё питфолл: в скрипте было **два разных имени выходного файла** (`raw_tags_full.tsv` в `OUT`,
|
||||
> `raw_out.tsv` в `printf`) — сверять имена при написании скрипта.
|
||||
|
||||
|
||||
|
||||
> ✅ **СТАТУС v7: JELLYFIN ОТКАЧЕН И СТАБИЛЕН. КУРС СМЕНЁН НА NAVIDROME.**
|
||||
> БД Jellyfin восстановлена на версию до импорта музыки: 6 библиотек, Audio=0, MusicArtist=0.
|
||||
@@ -75,11 +172,13 @@ Lidarr матчит по **MusicBrainz release groups** и пишет полны
|
||||
|
||||
| # | Задача | Статус | Детали |
|
||||
|---|---|---|---|
|
||||
| 0 | **Ремонт `artist`+`albumartist` (7861)** | 🔴 **АКТИВНАЯ (v8)** | правила Alex зафиксированы; **ручная разбивка по группам идёт**; список 1 (9 корневых), список 2 (633 папки), список 3 (`!Radio`, 31 папка) готовы |
|
||||
| 0a | Моибаке 492 файла | 🔴 **входит в задачу 0** | расшифровка CP1251→UTF-8; `ДЕТСКИЙ ХИТ` — образец работающего декодирования |
|
||||
| 1 | Дополнить `Music/backup` | 🟡 **скан готов, ждёт решения** | не хватало **1436 файлов** (876 mp3, 287 jpg, 6 mpg, 5 avi, 18 `.DS_Store`). Вопрос Alex по видео/мусору — ответа нет |
|
||||
| 2 | Бэкап CSV на Mac | ✅ **выполнено** | `/Users/admin/milestones/music-csv-2026-09-26/` — 3 CSV + MD5SUMS + README |
|
||||
| 3 | Аудит семантики `artist` | 🔴 не начат | `artist=Классика в современной обработке`, `Maria Makarova (organ)` и т.п. — формально валидны, семантически мусор |
|
||||
| 2 | Бэкап CSV на Mac | ✅ **выполнено** | `/Users/admin/milestones/music-csv-2026-09-26/` — 3 CSV + MD5SUMS + README (+v8: `tag-inventory3.csv`, `raw_tags_full.tsv`) |
|
||||
| 3 | Аудит семантики `artist` | 🔴 не начат | поглощён задачей 0 — `!Классика`: `Maria Makarova (organ)`, `Классика в современной обработке` |
|
||||
| 4 | Деплой Navidrome | 🔴 не начат | план — [[family/how-to/navidrome-truenas-android]] |
|
||||
| 5 | Проставить `TPE2` | ⏸ отложено | нужно **только** если в Navidrome раздробятся сборники; для Jellyfin бессмысленно (баг) |
|
||||
| 5 | Проставить `TPE2` | 🔴 **поглощено задачей 0** | уже не «отложено»: `albumartist` — центральная часть задачи v8 |
|
||||
| 6 | Lidarr | 🔴 не начат | задача из исходной команды, отложена до библиотеки |
|
||||
| 7 | Починить авто-бэкап Jellyfin | 🔴 не начат | `SQLiteBackups` пуста с 3 июня |
|
||||
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
---
|
||||
title: Music Artist + AlbumArtist Repair — правила и ход работ
|
||||
created: '2026-09-27'
|
||||
updated: '2026-09-27'
|
||||
type: plan
|
||||
namespace: personal
|
||||
tags:
|
||||
- music
|
||||
- metadata
|
||||
- id3
|
||||
- albumartist
|
||||
- repair
|
||||
- truenas
|
||||
related:
|
||||
- '[[family/plans/music-jellyfin-lidarr-taiga]]'
|
||||
- '[[personal/tech/music-tags-repair-truenas]]'
|
||||
- '[[personal/tech/jellyfin-tags-cp1251-unreadable]]'
|
||||
- '[[family/how-to/navidrome-truenas-android]]'
|
||||
---
|
||||
|
||||
# Music `artist` + `albumartist` Repair — правила Alex и ход работ
|
||||
|
||||
**Задача Alex (2026-09-27):**
|
||||
> «Артиста всем трём где он не указан или указан неправильно, включая детские песни ставишь
|
||||
> в реального артиста. Что не знаешь — ищешь в интернете. Для _всех_ треков.
|
||||
> Albumartist — смотришь по папке, для сборников various, для детских — Детские песни.
|
||||
> Для сказок — сказки. Альбом для сборников и unsorted ставишь по папке. Типа y OOF для сборников.
|
||||
> Для сказок альбомы и так стоят.»
|
||||
|
||||
**Охват:** 7861 mp3 (основная библиотека `/storage/Music`, бэкап-папки исключены).
|
||||
|
||||
## 🔴 ПРАВИЛА (дословно от Alex) — обязательны к исполнению
|
||||
|
||||
| Поле | Правило |
|
||||
|---|---|
|
||||
| `artist` | Есть в скобках в имени файла → **брать из скобок**. Нет → **искать в интернете** |
|
||||
| `artist` | **Никогда не маскировать мойибаке** — расшифровывать CP1251→UTF-8 |
|
||||
| `albumartist` | **сборник → `Various Artists`** |
|
||||
| `albumartist` | **детские → `Детские песни`** |
|
||||
| `albumartist` | **сказки → `Сказки`** |
|
||||
| `albumartist` | остальное → **= `artist`** |
|
||||
| `album` | **сборники и unsorted → ИМЯ ПАПКИ** (пример Alex: `y OOF`) |
|
||||
| `album` | сказки → **оставить как есть** |
|
||||
|
||||
### Пример «артист в скобках» (детские песни)
|
||||
|
||||
Файлы вида `Y/1. Песня о сказке В Шаинский - Ю.Энтин.mp3` — авторы прямо в имени файла
|
||||
(`В. Шаинский` = композитор, `Ю. Энтин` = поэт). **Это и есть источник `artist` по правилу Alex**,
|
||||
интернет нужен только там, где в скобках ничего нет.
|
||||
|
||||
## 🔴 КРИТЕРИЙ СБОРНИКА — уточнён Alex
|
||||
|
||||
Первые формулировки Alex звучали противоречиво («папка не одного артиста» → «не более чем одним»),
|
||||
поэтому зафиксированы **его прямые примеры** — они и есть определение:
|
||||
|
||||
> «**radio и retro — там сборники. Как и просто треки просто в корне артиста
|
||||
> если там есть альбомы — считать сборником типа НТЛ**»
|
||||
|
||||
| Признак папки | ВЕРДИКТ |
|
||||
|---|---|
|
||||
| `!Pop, Club, Любэ/!Radio/*` (все подпапки) | ✅ **СБОРНИК** → `Various Artists`, `album` = имя папки |
|
||||
| `!Retro` | ✅ **СБОРНИК** |
|
||||
| Треки в **корне** папки артиста, при наличии у него подпапок-альбомов | ✅ **СБОРНИК** (кейс НТЛ: 63 файла в `!Рэп/НТЛ` + 5 альбомов внутри) |
|
||||
| `!A Not sorted/Sbornik_Novij_Rok` (184 арт.) | ✅ **СБОРНИК** |
|
||||
| Папка с 1 уникальным артистом | ❌ **АЛЬБОМ**, `albumartist` = `artist` |
|
||||
| `!Pop, Club, Любэ/Benassi Bros` (3 арт. из-за фитов) | ⚠️ **АЛЬБОМ** — не сборник, это альбом с фитами |
|
||||
| `Benassi Bros/2003 Hypnotica Bonus` (4 арт.) | ⚠️ **АЛЬБОМ** — тот же класс, фиты |
|
||||
|
||||
> 🔴 **Питфолл:** механический критерий «>1 уникального артиста = сборник» **неверен** —
|
||||
> он записывает в `Various Artists` **альбомы с гостевыми фитами**. Alex: «Ты блядь сборник
|
||||
> от альбома отличить не сможешь или чё?!»
|
||||
|
||||
## 🔴 ПРОЦЕССНЫЙ ЗАПРЕТ ALEX
|
||||
|
||||
> «**Ручная разбивка по группам. Не скриптом блядь. Сначала готовишь списки, проверяешь,
|
||||
> а потом только скриптом можешь обновлять csv.**»
|
||||
|
||||
**Порядок исполнения (не нарушать):**
|
||||
1. **Списки вручную** — разбивка папок по группам, вычитка
|
||||
2. **Проверка Alex** — он глазами подтверждает разметку
|
||||
3. **Только потом** — скрипт обновляет CSV
|
||||
4. Правка файлов — после отдельного «go»
|
||||
|
||||
## 📊 СОСТОЯНИЕ РАБОТ
|
||||
|
||||
### Сделано
|
||||
|
||||
| # | Артефакт | Факт |
|
||||
|---|---|---|
|
||||
| 1 | Полный сбор тегов | `raw_tags_full.tsv` — **7861 строка**, md5 `0c3647fadb6556b4100c555ed59bfb44` |
|
||||
| 2 | `tag-inventory3.csv` | **7861 строка**, **13 колонок** (+`albumartist`) |
|
||||
| 3 | Бэкапы CSV | `tag-inventory2.csv.bak-20260927`, `tag-fix-plan.csv.bak-20260927` |
|
||||
| 4 | Список 1 | 9 корневых папок |
|
||||
| 5 | Список 2 | `list2-folders.txt` — **633 папки** с числом файлов и артистов |
|
||||
| 6 | Список 3 | `list3-radio.md` — `!Radio` разметка вручную: **31 папка, 19 сборников / 12 альбомов** |
|
||||
| 7 | `folder-categories.csv` | 633 папки с категориями и числом артистов |
|
||||
|
||||
### Факты по библиотеке
|
||||
|
||||
```
|
||||
КАТЕГОРИИ (по имени папки):
|
||||
ОБЫЧНАЯ: 7009 файлов
|
||||
СБОРНИК: 382 файла (11 папок — только по имени «сборник/sbornik/сборка»)
|
||||
СКАЗКИ: 310 файлов
|
||||
ДЕТСКИЕ: 157 файлов
|
||||
UNSORTED: 3 файла
|
||||
|
||||
album_artist пуст: 6451 из 7861 (82%)
|
||||
artist пуст: 178
|
||||
мойибаке хоть в одном поле: 492
|
||||
```
|
||||
|
||||
### ✅ Классификация папок (работает)
|
||||
|
||||
```
|
||||
СБОРНИК ← имя папки содержит «сборник» / «sbornik» / «сборка»
|
||||
СКАЗКИ ← «сказк» / «skazk» / «skazochn»
|
||||
ДЕТСКИЕ ← «детск» / «detsk» / «песни для детей»
|
||||
UNSORTED ← «not sorted» / «unsorted»
|
||||
```
|
||||
|
||||
⚠️ **Питфолл классификатора:** фильтр по «сказк» **ложно ловит**
|
||||
`!Rock&Folk/Пилот/2001 Сказка о прыгуне и скользящем` — это **альбом группы Пилот**, не сказки.
|
||||
**Ограничивать фильтр сказок префиксом `!Детские/Сказки`.**
|
||||
|
||||
## 🔴 ПОЧЕМУ МОЙИБАКЕ 492, А НЕ 155 — РАЗБОР `apply_tags.py`
|
||||
|
||||
Прошлый скрипт-писатель волны 38 **не испортил** файлы, но и **не исправил** их.
|
||||
Мойибаке уцелели из-за трёх дефектов:
|
||||
|
||||
### ① Генератор плана сдался на CP1251
|
||||
|
||||
Увидев нечитаемые байты, вместо обратного декодирования поставил `artist<-cleared-junk`
|
||||
(пусто) для **155 файлов** — включая все 156 из `!Детские/Детский хит`.
|
||||
|
||||
### ② `delall` внутри `if` с проверкой непустого значения
|
||||
|
||||
```python
|
||||
if (r.get("new_artist") or "").strip(): # new_artist пуст → False
|
||||
out.append(TPE1(...)) # ← не выполнено
|
||||
for frame in _frames(r):
|
||||
tags.delall(frame.FrameID) # ← ТОЖЕ не выполнено
|
||||
tags.add(frame)
|
||||
```
|
||||
|
||||
Пустой план → цикл не сделал ничего → **старый мойибаке уцелел**.
|
||||
**Правильно:** `delall` — **всегда**; `add` — только при непустом значении.
|
||||
|
||||
### ③ `_frames()` не знает про `TPE2`
|
||||
|
||||
```python
|
||||
# TIT2, TPE1, TALB, TRCK — и всё
|
||||
```
|
||||
|
||||
`albumartist` физически не мог быть записан → **269 файлов с мойибаке в TPE2 скрипт не трогал
|
||||
в принципе**, их даже не было в плане.
|
||||
|
||||
### Следствие — правило приёмки
|
||||
|
||||
> «N/N применено, **0 ошибок**» **≠ приёмка**. Критерий был «не упало», а не «тег стал верным».
|
||||
> ОБЯЗАТЕЛЬНА пост-сверка: прочитать тег ПОСЛЕ записи и сравнить с ожиданием; MISMATCH → отчёт.
|
||||
> Именно эта сверка поймала 165 ложных `no_change` в волне 38 — и не была сделана для `cleared-junk`.
|
||||
|
||||
## 📊 МОЙИБАКЕ — 492 ФАЙЛА (факт по всей библиотеке)
|
||||
|
||||
| Поле | Файлов |
|
||||
|---|---:|
|
||||
| `album_artist` (TPE2) | **269** |
|
||||
| `artist` (TPE1) | **160** |
|
||||
| `album` (TALB) | **63** |
|
||||
| `title` (TIT2) | **1** |
|
||||
| **строк с мойибаке хоть в одном поле** | **492** |
|
||||
|
||||
**Топ папок:**
|
||||
|
||||
```
|
||||
156 !Детские/Детский хит ← главный очаг
|
||||
54 !Rock&Folk/Муммий Тролль
|
||||
44 !Pop, Club, Любэ/!Radio
|
||||
31 !Rock&Folk/Пилот
|
||||
27 !Rock&Folk/ЗАХАР МАЙ
|
||||
23 !Rock&Folk/Несчастный Случай
|
||||
17 !Rock&Folk/Король и Шут
|
||||
16 z Другое.../Красная Плесень
|
||||
16 !Rock&Folk/Ария, Кипелов
|
||||
16 !Rock&Folk/Ундервуд
|
||||
14 !Рэп/Трофим
|
||||
13 !Rock&Folk/Запрещенные Барабанщики
|
||||
11 !Rock&Folk/Агата Кристи
|
||||
9 !Rock&Folk/Земфира
|
||||
```
|
||||
|
||||
### Мойибаке — исходное состояние, НЕ следствие волны 38
|
||||
|
||||
Доказано сравнением `tag-inventory.csv` (v1) vs `tag-inventory2.csv`:
|
||||
**значения тегов идентичны символ в символ**, строки отличаются только добавленной колонкой `source`.
|
||||
Файлы прошли волну «не изменёнными».
|
||||
|
||||
**Образец:**
|
||||
```
|
||||
path: !Детские/Детский хит/128 НЕ ХОДИТЕ В АФРИКУ.mp3
|
||||
cur_artist: ÄÅÒÑÊÈÉ ÕÈÒ ← мойибаке
|
||||
new_artist: (ПУСТО) ← план предлагал ОЧИСТИТЬ
|
||||
action: fix
|
||||
source: artist<-cleared-junk
|
||||
```
|
||||
|
||||
## 🔬 ДЕКОДЕР МОИБАКЕ (проверен, работает)
|
||||
|
||||
```python
|
||||
def decode_mojibake(s):
|
||||
"""CP1251-байты, прочитанные как Latin-1 → обратно в русский."""
|
||||
if not s or not s.strip():
|
||||
return s, False
|
||||
try:
|
||||
raw = s.encode("latin-1")
|
||||
except UnicodeEncodeError:
|
||||
return s, False
|
||||
try:
|
||||
fixed = raw.decode("cp1251")
|
||||
except UnicodeDecodeError:
|
||||
return s, False
|
||||
if fixed == s:
|
||||
return s, False
|
||||
cyr = sum(1 for c in fixed if "\u0400" <= c <= "\u04ff")
|
||||
junk = sum(1 for c in s if "\u00c0" <= c <= "\u00ff")
|
||||
if cyr > 0 and junk >= 2:
|
||||
return fixed, True
|
||||
return s, False
|
||||
```
|
||||
|
||||
**Проверено фактом:** `ÄÅÒÑÊÈÉ ÕÈÒ` → `ДЕТСКИЙ ХИТ` (156 файлов расшифрованы в `tag-inventory3.csv`).
|
||||
|
||||
## ⚠️ ПИТФОЛЛЫ СЕССИИ
|
||||
|
||||
### Сбор тегов: владелец каталога root
|
||||
|
||||
Скрипт писал в `/mnt/RED_2TB/storage/tmp/` — **каталог принадлежит root**, пользователь
|
||||
`truenas_admin` (uid 950) → `open ... permission denied`, файл не создан.
|
||||
|
||||
**Решение:** данные остаются в контейнере (`/tmp/raw_out.tsv`), забрать через
|
||||
`docker cp jellyfin:/tmp/raw_out.tsv /home/truenas_admin/` — там права есть.
|
||||
|
||||
### Сбор тегов: два разных имени выходного файла
|
||||
|
||||
В скрипте `OUT=/mnt/RED_2TB/storage/tmp/raw_tags_full.tsv`, а `printf` писал в
|
||||
`/tmp/raw_out.tsv` — `docker cp` забрал верный путь только потому, что он совпал с писателем.
|
||||
**Сверять имена выходных файлов при написании скрипта.**
|
||||
|
||||
### `docker exec` внутри контейнера — нет curl/sqlite3/ffprobe на хосте
|
||||
|
||||
Теги снимать **только** `ffprobe` из образа Jellyfin:
|
||||
`docker exec jellyfin /usr/lib/jellyfin-ffmpeg/ffprobe ...`
|
||||
|
||||
### `albumartist` из ffprobe ≠ реальный `TPE2`
|
||||
|
||||
ffprobe может **показать** `album_artist`, которого в байтах `TPE2` **нет**
|
||||
(читает из ID3v1-хвоста или TXXX). Проверять наличие `TPE2` — только `od` по байтам.
|
||||
|
||||
## ⏭ СЛЕДУЮЩИЕ ШАГИ
|
||||
|
||||
1. **Завершить ручную разбивку по группам** — оставшиеся корневые папки:
|
||||
`!Rock&Folk`, `!Барды`, `!Рэп`, `!Классика`, `z Другое…`, `!Pop, Club, Любэ` (без `!Radio`),
|
||||
`!Retro`, `!Детские`
|
||||
2. **Показать списки Alex на проверку**
|
||||
3. Скрипт обновляет `tag-inventory3.csv` → финальные колонки `fix_artist`, `fix_albumartist`, `fix_album`
|
||||
4. Поиск артистов в интернете — только для строк, где нет скобок и не мойибаке
|
||||
5. **Только после «go»** — правка файлов (с бэкапом, с пост-сверкой)
|
||||
|
||||
## 📁 ФАЙЛЫ РАБОТЫ (Mac)
|
||||
|
||||
```
|
||||
/Users/admin/milestones/music-csv-2026-09-26/
|
||||
├── raw_tags_full.tsv 7861 стр, сырые теги (md5 0c3647fa...)
|
||||
├── tag-inventory3.csv 7861 стр, 13 колонок, +albumartist
|
||||
├── tag-inventory2.csv.bak-20260927 бэкап
|
||||
├── tag-fix-plan.csv.bak-20260927 бэкап
|
||||
├── albumartist-plan.csv черновик (НЕ финал — см. §Процессный запрет)
|
||||
├── mojibake_rows.tsv 492 строки с мойибаке
|
||||
├── verify-problems.csv 12 строк без albumartist
|
||||
├── folder-categories.csv 633 папки с категориями
|
||||
├── list2-folders.txt 633 папки (файлы/артисты)
|
||||
├── list3-radio.md ручная разметка !Radio (31 папка)
|
||||
├── analyze.py / build_inventory3.py / verify_manual.py / audit_categories.py
|
||||
└── README.md, MD5SUMS.txt
|
||||
```
|
||||
@@ -20,6 +20,58 @@ updated: '2026-09-26'
|
||||
_Создан: 2026-09-26. **Статус (обновлён 2026-09-26, финал сессии): ✅ APPLY 7041 + ✅ ДЕЛЬТА-ПРАВКИ 165/165 + ✅ МУСОР `._*` ВЫЧИЩЕН (29) + ✅ КОНВЕРТАЦИЯ 41 (41/41) + ✅ ПРОБА 70 не-MP3 (41 ok · 13 drm · 16 corrupt) + ✅ ТЕГИ 40 НОВЫХ MP3 ЗАПИСАНЫ (`fix40.py`, 40/40, решение Alex «Нормально делать» → номер срезать). 🔴🔴 **КРИТИЧЕСКИЙ ДЕФЕКТ `fix40.py`: 40 MP3 СХЛОПНУЛИСЬ В ~1 КБ** — `ID3(p).save(tmp)` пишет ТОЛЬКО тег-блок, MPEG-поток не копируется. Аудио уничтожено. **ВОССТАНОВИМО:** `backup_conv40/` (40/40 целых, до записи тегов) + `backup_raw/` (41/41 оригиналов ogg/flac). Откат готов, ждёт «откат» от Alex. Решение Alex по TRCK = **B** (номера не удалять). Бэкапы: `backup/` 7041 · `backup_raw/` 70 · `backup_fix/` 165 · `backup_conv40/` 40. Образ с ffmpeg 8.1.2. ⏳ НЕ выполнено: удаление 29 битых/DRM, правка дыры в `apply_tags.py` (нет `.mp3`-фильтра), приёмка Jellyfin.**_
|
||||
_Основание: [[personal/plans/music-tags-wave38-fixes]]._
|
||||
|
||||
## 🔴 v8 (2026-09-27): ТРЕТИЙ КЛАСС ДЕФЕКТА `apply_tags.py` — БЕЗДЕЙСТВИЕ ПОД ВИДОМ РАБОТЫ
|
||||
|
||||
**Найдено при полном сборе тегов по всей библиотеке** (`raw_tags_full.tsv`, 7861 строка).
|
||||
Разбор — [[personal/plans/music-artist-albumartist-repair]].
|
||||
|
||||
### Дефект ①: `delall` ВНУТРИ `if` с проверкой непустого значения
|
||||
|
||||
```python
|
||||
if (r.get("new_artist") or "").strip(): # new_artist пуст → False
|
||||
out.append(TPE1(...)) # ← не выполнено
|
||||
for frame in _frames(r):
|
||||
tags.delall(frame.FrameID) # ← ТОЖЕ не выполнено (внутри того же блока)
|
||||
tags.add(frame)
|
||||
```
|
||||
|
||||
**План хотел «очистить» `artist` (значение `new_artist` пустое) — а код не сделал НИЧЕГО.**
|
||||
Пустой список фреймов → цикл не выполнился → **старый мойибаке уцелел**.
|
||||
|
||||
> **ПРАВИЛЬНО:** `delall` — **ВСЕГДА** (вне `if`), `add` — только при непустом значении.
|
||||
> Иначе «очистить поле» физически не очищает.
|
||||
|
||||
### Дефект ②: `_frames()` не знает про `TPE2` (albumartist)
|
||||
|
||||
```python
|
||||
# TIT2, TPE1, TALB, TRCK — и всё
|
||||
```
|
||||
|
||||
**`TPE2` физически не мог быть записан, даже если бы был в плане.** Следствие: **269 файлов
|
||||
с мойибаке в `albumartist` скрипт не трогал в принципе** — их даже не было в плане правок
|
||||
(`tag-fix-plan.csv` не содержит колонки `album_artist`).
|
||||
|
||||
### Дефект ③ (генератора плана): сдался на CP1251 вместо декодирования
|
||||
|
||||
Генератор увидел нечитаемые байты и вместо обратного декодирования CP1251→UTF-8 поставил
|
||||
`artist<-cleared-junk` (пусто) для **155 файлов** — включая все 156 из `!Детские/Детский хит`.
|
||||
**Правильная реакция — декодировать, а не очищать:** `ÄÅÒÑÊÈÉ ÕÈÒ` → `ДЕТСКИЙ ХИТ`.
|
||||
|
||||
### 🔴 КЛАСС ДЕФЕКТА: «бездействие под видом работы»
|
||||
|
||||
**Скрипт НЕ испортил файлы, но и НЕ исправил.** Отчёт «7041/7041 применено, 0 ошибок» был
|
||||
**ложной приёмкой**: критерий — «не упало», а не «тег стал правильным». Файл с мойибаке прошёл
|
||||
насквозь и был засчитан как исправленный.
|
||||
|
||||
> **ПРАВИЛО ПРИЁМКИ:** после записи **прочитать тег и сравнить с ожиданием**; MISMATCH → отчёт.
|
||||
> Именно эта сверка поймала 165 ложных `no_change` — и **не была сделана** для 155 `cleared-junk`,
|
||||
> поэтому дефект уцелел и вылез только на полном сборе тегов.
|
||||
|
||||
**Итог по ущербу:** файлы целы (старые теги на месте), данные не потеряны —
|
||||
**это не разрушение, это бездействие**. Правильная формулировка для Alex: скрипт был плохой,
|
||||
потому что пропускал пустые значения и не знал про `TPE2`.
|
||||
|
||||
|
||||
## 🆕 СОСТОЯНИЕ НА КОНЕЦ 2026-09-26 (последние шаги сессии)
|
||||
|
||||
| Шаг | Статус |
|
||||
|
||||
@@ -392,6 +392,41 @@ MusicBrainz отдаёт его для каждого релиза. Ручная
|
||||
|
||||
## 📦 ИНВЕНТАРИЗАЦИЯ БЭКАПОВ И CSV (факты 2026-09-27)
|
||||
|
||||
### 🔴 v8: ПОЛНЫЙ СБОР ТЕГОВ — ЦИФРЫ ПО ВСЕЙ БИБЛИОТЕКЕ
|
||||
|
||||
**Источник:** `raw_tags_full.tsv`, **7861 строка** (полный проход `ffprobe` по всем mp3,
|
||||
бэкап-папки исключены). Ранее все цифры брались из `tag-inventory*.csv` (7906 записей) —
|
||||
**устаревшего по составу** файлов.
|
||||
|
||||
| Показатель | Всего | Доля |
|
||||
|---|---:|---:|
|
||||
| Строк в сборе | **7861** | 100% |
|
||||
| `album_artist` **заполнен** | **1410** | 18% |
|
||||
| `album_artist` **пуст** | **6451** | **82%** |
|
||||
| `artist` заполнен | 7683 | 98% |
|
||||
| `artist` **пуст** | **178** | 2% |
|
||||
| `title` пуст | 7 | — |
|
||||
| **мойибаке хоть в одном поле** | **492** | 6% |
|
||||
|
||||
**Мойибаке по полям:** `album_artist` **269** · `artist` **160** · `album` **63** · `title` **1**.
|
||||
|
||||
> 🔴 **Ключевой факт:** **269 файлов имеют мойибаке в `album_artist`** — этого класса дефекта
|
||||
> раньше **не видели вообще**, потому что волна 38 не знала про `TPE2` (см. §КОРЕНЬ ПРОПУСКА выше).
|
||||
> `albumartist` центральная часть новой задачи — [[personal/plans/music-artist-albumartist-repair]].
|
||||
|
||||
### Расхождение ffprobe vs CSV по `album_artist`
|
||||
|
||||
```
|
||||
ffprobe (raw_tags_full.tsv): album_artist заполнен у 1410
|
||||
tag-inventory2.csv: album_artist заполнен у 3507
|
||||
```
|
||||
|
||||
**Разница 2097.** Причина — в ранее зафиксированном питфолле: ffprobe **не показывает**
|
||||
`album_artist`, если его нет в `TPE2` (в CSV значения приходили из ID3v1-хвоста/TXXX).
|
||||
Следствие: **первый сырой скан теряет часть `album_artist`**, а CSV его сохраняет.
|
||||
Для правки `TPE2` опираться на **байтовую проверку `od`**, не на ffprobe.
|
||||
|
||||
|
||||
Ответ на вопрос Alex: «в backup полный бэкап, а в csv полный список треков, или в нём не все?»
|
||||
|
||||
### Бэкап — 91%, НЕ полный
|
||||
|
||||
Reference in new issue
Block a user