[2026-09-26] eagle: family/how-to/truenas-infrastructure.md personal/plans/music-tags-review/1-needs-decision.md personal/plans/music-tags-review/2-medium-confidence.md personal/plans/music-tags-review/3-high-confidence.md personal/plans/music-tags-review/music-tags-review.md personal/tech/music-tags-repair-truenas.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-26 15:01:21 +06:00
1 parent 1bf8320446
commit 7564ae26ac
6 files changed
+22581 -22429

No files matched your search

+111 -18
View File
@@ -116,6 +116,7 @@
> - **Актуальная гонка udev живёт на t610** — см. [[family/how-to/zont-modbus-bridge-udev-race-protection]].
> - ⚠️ **Историческая справка (если когда-нибудь откатывать):** `restart: unless-stopped` НЕ перезапускает контейнер, упавший на `start` из-за отсутствующего device-узла (отказ до старта процесса, ExitCode 128/255, RestartCount=0); лечилось ручным `docker start modbus-bridge mbusd` после готовности симлинков.
> **⚠️ ПОСЛЕДСТВИЕ, ПОДТВЕРЖДЁННОЕ 2026-09-26 (File Browser):** потеря `.ix-apps` = потеря **всех docker NAMED VOLUMES**, а не только образов. Образы = кеш, перекачаются; **тома = данные, не восстанавливаются**. Первый пострадавший — `filebrowser`: его БД пользователей жила в томах `filebrowser_filebrowser_db` / `filebrowser_filebrowser_config` → при первом старте тома создались с нуля (`2026-08-24T02:04:12`), File Browser сгенерировал нового `admin`, прежние пользователи (`file_admin` в т.ч.) утрачены.
> ✅ **ЗАКРЫТО для `filebrowser` 2026-09-26:** переведён с named volumes на **bind-mount в `/mnt/RED_2TB/docker/filebrowser/`** — БД больше не в `.ix-apps`, попадает в обычный бэкап. Рецепт миграции и 5 питфоллов — §«File Browser» ниже. Остальные сервисы с томами (см. `docker volume ls`) — **не проверены, кандидаты на ту же процедуру**.
>
> 🔴 **Правило для будущих переносов:** перед `zpool destroy`/пересозданием всегда выгружать **`/mnt/.ix-apps/docker/volumes/`** отдельным шагом — `rsync -aHAX /mnt/.ix-apps/docker/volumes/ /mnt/<TARGET>/ix-apps-volumes/`. Одного `/mnt/RED_2TB/*` НЕДОСТАТОЧНО: `.ix-apps` — **отдельный ZFS-датасет**, смонтированный поверх одноимённой папки, в глоб пула не попадает.
>
@@ -220,7 +221,7 @@ TrueNAS использует **NFSv4 ACL**, а не стандартный POSIX
| transmission | linuxserver/transmission:latest | 9091, 51413 | transmission.mallexxx.duckdns.org |
| hermes-taiga | hermes-taiga:latest | — | — |
| vless-proxy | teddysun/xray:latest | — | — |
| filebrowser | filebrowser/filebrowser:latest | 8000 | (через Caddy `192.168.2.197:8000`) — см. раздел «File Browser» |
| filebrowser | filebrowser/filebrowser:latest (v2.63.23) | 8000 | (через Caddy → `192.168.2.197:8000`) — см. раздел «File Browser»; **данные в папке, bind-mount (2026-09-26)**, пользователь `file_admin` |
| webdav | hacdias/webdav:latest | — | webdav.mallexxx.duckdns.org |
| ~~homeassistant~~ ⛔ | home-assistant:stable | ~~8123~~ | **ПОГАШЕН 2026-09-14** — переехал на t610 |
| immich-server | immich-app/immich-server:release | 2283 | immich.mallexxx.duckdns.org |
@@ -397,12 +398,14 @@ nas_users:x:3000:root,truenas_admin,mallexxx,nas,transmission
- **Web:** https://transmission.mallexxx.duckdns.org
- **Порты:** 9091 (RPC/web), 51413 (TCP/UDP peers)
### File Browser — детали (разобрано 2026-09-26)
### File Browser — детали (разобрано и ПЕРЕВЕДЕНО НА ПАПКУ 2026-09-26)
**Папка:** `/mnt/RED_2TB/docker/filebrowser/` (только `docker-compose.yml`).
**Папка:** `/mnt/RED_2TB/docker/filebrowser/`.
**Порт:** `8000:80` (хост `0.0.0.0:8000`). Проверка: `nc -z 127.0.0.1 8000`.
**Образ:** `filebrowser/filebrowser:latest` → **v2.63.23** (проект заархивирован 2026-09-01, обновлений не будет).
**Состояние ПОСЛЕ миграции (актуальный compose):**
```yaml
services:
filebrowser:
@@ -415,22 +418,37 @@ services:
volumes:
- /mnt/RED_2TB/storage:/srv
- /mnt/RED_2TB/Edu:/srv/Edu
- filebrowser_db:/database # ← NAMED VOLUME, не bind-mount!
- filebrowser_config:/config # ← NAMED VOLUME, не bind-mount!
volumes:
filebrowser_db:
filebrowser_config:
- /mnt/RED_2TB/docker/filebrowser/database:/database # ← BIND-MOUNT
- /mnt/RED_2TB/docker/filebrowser/settings.json:/config/settings.json # ← BIND-MOUNT (файл)
```
> 🔴 **ДАННЫЕ ЖИВУТ В DOCKER NAMED VOLUMES, А НЕ В ПАПКЕ `/mnt/RED_2TB/docker/filebrowser/`.**
> В папке контейнера лежат только `docker-compose.yml` + `settings.json` (130 б, дата `Jul 27 20:00`).
> Реальные данные:
> ```
> filebrowser_filebrowser_db → /mnt/.ix-apps/docker/volumes/filebrowser_filebrowser_db/_data/filebrowser.db
> filebrowser_filebrowser_config → /mnt/.ix-apps/docker/volumes/filebrowser_filebrowser_config/_data/settings.json
> ```
> Физически это **внутри `.ix-apps`** (`DockerRootDir=/mnt/.ix-apps/docker`). Оба тома созданы
> `2026-08-24T02:04:12` — то есть **с нуля**, в день пересоздания пула.
> ✅ **2026-09-26: named volumes УБРАНЫ, данные переехали в папку.** Причина — они жили
> внутри `.ix-apps` и утратились при пересоздании пула (см. пифолл 0 ниже). Теперь
> `filebrowser.db` лежит в `/mnt/RED_2TB/docker/filebrowser/database/` и попадает в
> обычный бэкап `mailru-crypt:`. Проверка живьём: `docker inspect filebrowser` →
> `/database` = `/mnt/RED_2TB/docker/filebrowser/database`, `/config/settings.json` = путь на пуле.
**Пользователи (после миграции):** остался **только `file_admin`** (id=2, `perm.admin=true`,
`scope: /`). Прежний `admin` (id=1) **удалён** 2026-09-26 по указанию Alex.
#### 🔴 Пифолл 0 (ИСТОРИЧЕСКИЙ, причина всей истории): данные в named volumes внутри `.ix-apps` → потеряны при переносе пула
**Было:** БД жила в томах
```
filebrowser_filebrowser_db → /mnt/.ix-apps/docker/volumes/filebrowser_filebrowser_db/_data/filebrowser.db
filebrowser_filebrowser_config → /mnt/.ix-apps/docker/volumes/filebrowser_filebrowser_config/_data/settings.json
```
Физически внутри `.ix-apps` (`DockerRootDir=/mnt/.ix-apps/docker`). Оба тома созданы **с нуля**
`2026-08-24T02:04:12` — в день пересоздания пула. rsync шёл из `/mnt/RED_2TB/*` и `.ix-apps`
(отдельный датасет поверх `/mnt/.ix-apps`) не включал → тома погибли, File Browser сгенерировал
нового `admin`, все прежние пользователи (в т.ч. `file_admin`) исчезли.
**Почему это НЕ косяк конфигурации:** `docker-compose.yml` до и после переноса **идентичен**
(`/mnt/RED_2TB/docker.bak/filebrowser/docker-compose.yml` == прежний). `settings.json` тоже тот же
(130 б, `Jul 27 20:00`) — он пережил перенос, потому что лежал на пуле. Потерялись **данные**, не конфиг.
> 📌 **Общее правило:** любой сервис с docker NAMED VOLUMES теряет состояние при пересоздании пула,
> пока тома лежат в `.ix-apps`. Либо bind-mount на пул, либо включать `.ix-apps` в бэкап.
#### 🔴 Пифолл 1: логин не работает, потому что пользователя нет / `hideLoginButton: true`
@@ -469,7 +487,82 @@ docker exec filebrowser cat /config/settings.json
docker exec filebrowser cat /database/filebrowser.db # бинарь, читать strings
```
**Состояние на 2026-09-26:** решено НЕ менять (ждёт решения Alex по вариантам A/B/C из отчёта). Данные `admin`-пользователя утрачены при переносе пула; `file_admin` отсутствует.
#### 🔴 Пифолл 4: `docker compose up -d` → `permission denied` на compose-файл (права после записи из root-контейнера)
Файл, записанный через `docker run --rm -v ...:/dst alpine cp ...`, получает `-rw------- root root`.
`truenas_admin` (uid 950) не может его прочитать → compose падает:
```
open /mnt/RED_2TB/docker/filebrowser/docker-compose.yml: permission denied
```
Фикс (в root-контейнере): `chmod 644` + `chown 950:950` на compose и бэкап.
> Папка `/mnt/RED_2TB/docker/filebrowser/` = `root:root` — `truenas_admin` в неё писать не может,
> поэтому **любая** запись туда идёт через `docker run --rm -v <пул>:/dst alpine ...`, а не через `cp`/`scp` напрямую.
> Безопасный порядок для bind-mount-файлов: изнутри root-контейнера `chown` в `1000:1000` (для данных
> File Browser) или `950:950` (для файлов, которые читает `truenas_admin` как compose).
#### 🔧 Рецепт миграции named volume → папка (выполнен 2026-09-26, воспроизводим)
```bash
# 1. STOP (обязательно: bolt-БД залочена)
docker stop filebrowser
# 2. Бэкап тома на пул (root-контейнер, пул примонтирован целиком)
docker run --rm \
-v /mnt/.ix-apps/docker/volumes/filebrowser_filebrowser_db/_data:/src/db:ro \
-v /mnt/.ix-apps/docker/volumes/filebrowser_filebrowser_config/_data:/src/cfg:ro \
-v /mnt/RED_2TB/docker/filebrowser:/dst \
alpine sh -c 'mkdir -p /dst/_vol_backup_$(date +%Y%m%d)/db /dst/_vol_backup_$(date +%Y%m%d)/cfg
cp -av /src/db/. /dst/_vol_backup_$(date +%Y%m%d)/db/
cp -av /src/cfg/. /dst/_vol_backup_$(date +%Y%m%d)/cfg/'
# 3. Данные в рабочую папку
docker run --rm -v /mnt/RED_2TB/docker/filebrowser:/dst alpine sh -c '
mkdir -p /dst/database
cp -av /dst/_vol_backup_*/db/filebrowser.db /dst/database/filebrowser.db
cp -av /dst/_vol_backup_*/cfg/settings.json /dst/settings.json
chown -R 1000:1000 /dst/database /dst/settings.json'
# 4. Правка compose на bind-mount (см. блок выше) + chmod 644 / chown 950:950
# 5. Правки пользователей/паролей — ТОЛЬКО при остановленном контейнере (см. пифолл 2)
# 6. Старт
cd /mnt/RED_2TB/docker/filebrowser && docker compose up -d
```
#### 🔴 Пифолл 5: образ `filebrowser` — distroless, `sh`/`bash` НЕТ
`docker run --rm filebrowser/filebrowser:latest sh -c "..."` → `unknown command "sh" for "filebrowser"`.
Бинарь вызывается **напрямую с аргументами**:
```bash
# список пользователей
docker run --rm -v <db-dir>:/database filebrowser/filebrowser:latest users ls -d /database/filebrowser.db
# добавить пользователя-админа
docker run --rm -v <db-dir>:/database filebrowser/filebrowser:latest users add file_admin 'ПАРОЛЬ' --perm.admin -d /database/filebrowser.db
# удалить пользователя
docker run --rm -v <db-dir>:/database filebrowser/filebrowser:latest users rm admin -d /database/filebrowser.db
# снять скрытие кнопки логина (флаг deprecated: --hide-login-button → --hideLoginButton)
docker run --rm -v <db-dir>:/database filebrowser/filebrowser:latest config set --hide-login-button=false -d /database/filebrowser.db
```
> ⚠️ Правки из этих команд требуют **остановленного** контейнера (bolt-лок, пифолл 2).
#### ✅ Приёмка 2026-09-26 (проверено живьём)
| Проверка | Результат |
|---|---|
| `POST /api/login` с `file_admin` + верный пароль | **HTTP 200** + JWT ✅ |
| `POST /api/login` неверный пароль | HTTP 403 ✅ |
| `POST /api/login` старый `admin` | HTTP 403 (**удалён**) ✅ |
| `GET /login` | HTTP 200 ✅ |
| `docker ps` | `Up (healthy)` ✅ |
| `docker inspect` монтирование | `/database` = `/mnt/RED_2TB/docker/filebrowser/database` ✅ |
**Артефакты миграции в `/mnt/RED_2TB/docker/filebrowser/:**
`database/filebrowser.db`, `settings.json`, `docker-compose.yml`, `docker-compose.yml.bak-20260926`,
`_vol_backup_20260926/{db,cfg}/`.
**Открытые вопросы (решает Alex):**
1. `file_admin` имеет `scope: /` — видит весь корень включая `/srv/Edu` (у прежнего `admin` было `.`). Ограничить при необходимости.
2. Пароль `FileAdmin2026!` прошёл через логи чата/ssh — **сменить после первого входа**.
3. Стоит ли тем же приёмом перевести на bind-mount **другие** сервисы с named volumes (см. `docker volume ls` — там десятки хеш-имён от прежних compose-проектов).
> ⚠️ **`/mnt/RED_2TB/docker.bak/filebrowser/` — это НЕ бэкап данных.** Там те же два файла (`docker-compose.yml` + `settings.json`, дата `Apr 29`), которые пережили перенос, потому что лежали на пуле. Томов там нет.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,20 +1,33 @@
---
title: Music Tags — Review
type: task
namespace: personal
tags: [music, jellyfin, tags, review, truenas]
status: awaiting-approval
---
# Music Tags — проверка тегов
**К правке:** 4774 файлов · **без изменений:** 3132
**К правке:** 4777 файлов · **без изменений:** 3129
> [!warning] Ничего не изменено
> Это план. Ставь галочки, что одобряешь. После «go» — бэкап в `Music/backup/` и применение.
| Блок | Файлов |
|---|---:|
| [[1-needs-decision\|Требует решения]] | 296 |
| [[2-medium-confidence\|Средняя уверенность]] | 2031 |
| [[3-high-confidence\|Высокая уверенность]] | 2447 |
## Что в каком блоке
| Блок | Файлов | Что это значит |
|---|---:|---|
| [[1-needs-decision\|Требует решения]] | 28 | Нет ни исполнителя, ни альбома. Взять неоткуда — только имя файла. |
| [[2-medium-confidence\|Средняя уверенность]] | 2144 | Восстановлено из имён файлов и структуры папок. |
| [[3-high-confidence\|Высокая уверенность]] | 2605 | Точная перекодировка CP1251 → UTF-8, обратимая без потерь. |
## Важное про блок 1
Попали только 28 файлов, где **нечего показать вообще**. Сборники и аудиосказки
(`!Детские/Детский хит`, `Сказки/*`) сюда **не входят** — у них есть альбом
(название сборника или сказки), а исполнителя у такого материала и не бывает.
Если подставить туда имя папки, получится мусор вида `artist = Сказки`
(238 треков одним «исполнителем») — от этого отказались.
## Формат
Каждый файл — чекбокс `- [ ]`. Под ним: поле, старое значение → **новое**, и источник
(`из имени файла`, `из структуры папок`, `кодировка` и т.д.).
`⟨повреждено⟩` = байты уничтожены (U+FFFD), восстановить нельзя — берётся из имени.
[[../tech/music-tags-repair-truenas|Техническая сторона →]]
@@ -22,6 +22,63 @@ related:
> Следующий шаг — апрув Alex по нотам ревью, затем бэкап + применение.
> Перед выдачей проведён **полный аудит всех 4774 строк** (`audit_plan.py` + `final_gate.py`) — найдено и исправлено 6 багов.
## 📌 ПЛАН ТРЕТЬЕЙ ВОЛНЫ (2026-09-26, апрув Alex) — «сам проставь, потом на апрув»
Alex: **«Давай ка дорогой пройди по файлу и сам проставь нормально все, а только потом мне на апрув,
я не собираюсь очевидные вещи руками лопать.»** + указал 2 конкретных дефекта.
| # | Замечание Alex | Причина | Что делаю |
|---|---|---|---|
| 1 | «не распознаются как чекбокс, убери треугольные скобки» | `render_grouped_doc.py` оборачивал блок в цитату `> [!info]`, чекбоксы получили префикс `> ` → Obsidian читает как цитату | ✅ убрано `> ` с чекбоксов и подпунктов, выноски оставлены |
| 2 | «`DJ Ivan Scratchin' – Welcome To China` — очевидно артист + название, почему склеено в title?» | `split_artist_title()` искал только `" - "` (ASCII-дефис). En-dash `–` / em-dash `—` не матчились | ✅ `SEP_RE` + `DASH_GLUE_RE`, `find_separator()` |
| 3 | «куча подобных» | скрытый баг: `any(w in norm(left) for w in EVENT_WORDS)` — **подстроковый** поиск. `"va" in "Ivan"` → True, и `DJ Ivan Scratchin'` отбрасывался | ✅ `has_word()` — токенный поиск |
### 🔴 КОРНЕВОЙ БАГ ТРЕТЬЕЙ ВОЛНЫ: подстроковый поиск стоп-слов
`EVENT_WORDS` и `RELEASE_WORDS` проверялись через `any(w in text for w in WORDS)`.
Это **подстрока**, не слово. Последствия:
| Слово из списка | Ложное срабатывание | Кого теряли |
|---|---|---|
| `va` | `"va" in "dj ivan scratchin'"` → True | `DJ Ivan Scratchin'` |
| `set` | `"set" in "sunset"` → True | `Sunset`-исполнители |
| `mix` | `"mix" in "mixes"` → True | папки `Remixes, Mixes` |
| `live` | `"live" in "delivery"` → True | — |
**Фикс:** `has_word(text, words)` — токенизация `re.split(r"[^0-9a-zа-яё]+", norm(text))`
и сравнение множеств. `Ivan` больше не матчит `va`, `Settle` не матчит `set`.
> 🔴 **ПИТФОЛЛ НА БУДУЩЕЕ: стоп-слова проверять ТОЛЬКО по токенам.**
> `"in" string` на списке коротких слов даёт ложные срабатывания на каждом втором имени.
### Три формы разделителя artist/title в библиотеке
| Форма | Пример | Регекс |
|---|---|---|
| пробелы с двух сторон | `Matisse – Back To Sensation` | `SEP_RE = \s+[-\u2013\u2014]\s+` |
| дефис приклеен слева | `Arno Cost & Norman Doray- Apocalypse` | `DASH_GLUE_RE`, только если слева >1 слова |
| дефис внутри слова (не сплит) | `Рок-н-ролл`, `БИ-2` | отвергается |
### 📊 Третья волна: цифры
| Метрика | v3 | **v4 (финал)** |
|---|---:|---:|
| `fix` | 4774 | **4777** |
| `no_change` | 3132 | 3129 |
| Ноты: «требует решения» | 296 | **28** ↓ в 10 раз |
| Ноты: средняя | 2031 | 2144 |
| Ноты: высокая | 2447 | 2605 |
| mojibake в выводе | 0 | **0** |
| Тесты | 65 | **65 + 15 новых = 80** |
**Почему «требует решения» упало 296 → 28:** в блок 1 попадали все файлы с пустым artist,
включая 155 сборников `Детский хит` и ~80 аудиосказок. У них **нет исполнителя по природе
материала**, но есть альбом. Критерий блока 1 изменён на
`not new_artist and not new_album` — «показать нечего вообще».
Осталось 28 файлов, где действительно пусто всё.
---
## 🔴 ВТОРАЯ ВОЛНА ИСПРАВЛЕНИЙ (2026-09-26, «Сам проверил весь файл на адекватность?»)
Alex: **«Сам проверил весь файл на адекватность? Чтобы я в очевидные вещи тебя не тыкал.»**