diff --git a/family/how-to/jellyfin-config.md b/family/how-to/jellyfin-config.md index 9f7ab5d0..0d24210d 100644 --- a/family/how-to/jellyfin-config.md +++ b/family/how-to/jellyfin-config.md @@ -60,7 +60,7 @@ Used by automation scripts (watchlist-sync, media-pipeline agents). | Cartoons | `/media/cartoons` | Мультфильмы | | Documentaries | `/media/documentaries` | Документалки | | Documentary Series | `/media/documentaries-series` | Документальные сериалы | -| Music | | Музыка | +| Music | *(пусто — библиотека не создана)* | Музыка — **заблокировано до починки тегов** (см. Taiga-раздел ниже) | > **Важно:** Прямые пути sonarr (`/media/series-sonarr/`, `/media/movies-radarr/`) **не должны быть** в библиотеках Jellyfin. Router.py создаёт симлинки из sonarr-папок в `/media/series/` и `/media/movies/`. Если symlink уже есть — дубли не появятся. Если есть старые дубли — удалить orphaned items через `DELETE /Items/{id}`. @@ -152,6 +152,12 @@ sqlite3 jf-copy.db "SELECT Name, AccessToken FROM ApiKeys;" > потеряно 343 файла (Высоцкий и др.). Читать ID3 **напрямую из байтов** (`id3_direct.py`). > Детали — [[personal/tech/music-tags-repair-truenas]]. +> 📋 **ДОКУМЕНТЫ РЕВЬЮ ДЛЯ ALEX (2026-09-26)** — CSV с предлагаемыми исправлениями выдан не только +> файлом, но и нотами в vault (`personal/plans/music-tags-review/`): обзор + 3 блока +> (требует решения / средняя / высокая уверенность), каждый файл = чекбокс с `старое → новое` +> и источником. **Это вход для апрува (шаг 3 плана ремонта).** Генератор — +> `render_grouped_doc.py` в `~/Developer/personal-os/tools/music-tags/`. + > ✅ **Порядок работ (решение Alex 2026-09-26):** ремонт тегов → валидация Alex → удаление > `Music/backup/` → **только потом** создание Music-библиотеки. Бэкап кладётся в `Music/backup/` > и сносится до добавления библиотеки, поэтому исключать `backup` в настройках Jellyfin не нужно. diff --git a/family/plans/lidarr-taiga-deploy.md b/family/plans/lidarr-taiga-deploy.md index b9cd725b..d6c166b2 100644 --- a/family/plans/lidarr-taiga-deploy.md +++ b/family/plans/lidarr-taiga-deploy.md @@ -1,7 +1,7 @@ # Lidarr на Taiga — деплой + прокси для индексеров **Дата:** 2026-09-26 -**Статус:** 🟡 В работе (Фаза 0) +**Статус:** 🟡 Фаза 0 частично выполнена (3/6 индексеров зелёные), ждём решение Alex по FlareSolverr **Namespace:** family ## Задача @@ -87,19 +87,163 @@ docker exec prowlarr getent hosts xray-admin → 172.16.1.9 ✅ | Sonarr | `b1eb9cdd5c804e6c956dc9c8bd305dc8` | | Jellyfin | `eaglesetup2026taiga` | +### ✅ СДЕЛАНО в Фазе 0 (2026-09-26, end-of-session) + +| # | Действие | Факт | +|---|---|---| +| 1 | `vless-proxy` подключён к `media_net` | `172.16.17.6`; проверено `docker exec prowlarr getent hosts vless-proxy` → резолвится | +| 2 | Персистентность в compose | `/mnt/RED_2TB/docker/vless-proxy/docker-compose.yml` — добавлена `media_net` в `networks:` + `external: true`. Бэкап: `~/vless-proxy-compose.bak-20260926-012529` и `.bak-` рядом с файлом. Контейнер пересоздан `docker compose up -d --force-recreate`. MD5 файла: `4449e2b55a75362904438d4792abed90` | +| 3 | Тег `proxy` создан в Prowlarr | `POST /api/v1/tag {"label":"proxy"}` → `id=1`. До этого таблица `Tags` была **ПУСТА**, а `Indexers.Tags` у RuTracker ссылался на висячий id `[1]` — прокси физически не был привязан | +| 4 | Прокси `vless-socks5` (id=1) привязан к тегу | `PUT /api/v1/indexerproxy/1` c `"tags":[1]`. Socks5, host `vless-proxy`, port `1080` | +| 5 | **Все 6 индексеров** тегированы `[1]` | Подтверждено из БД `prowlarr.db`: `SELECT Id, Name, Tags FROM Indexers` → все `[1]` | +| 6 | `testall` прогнан | **3 из 6 зелёные** (было 0 из 6). NXDOMAIN ушёл ПОЛНОСТЬЮ | + +**Результат `POST /api/v1/indexer/testall` (фактические данные):** + +| id | Индексер | Результат | Сообщение | +|---|---|---|---| +| 1 | RuTracker | ❌ | `[403:Forbidden] [POST] at https://rutracker.org/forum/login.php` | +| 2 | RuTor | ✅ | OK | +| 3 | Byrutor | ✅ | OK | +| 4 | NoNaMe Club | ✅ | OK | +| 5 | Kinozal | ❌ | `Unable to access kinozal.me, blocked by CloudFlare Protection` | +| 6 | Kinozal (M) | ❌ | `Unable to access kinozal.me, blocked by CloudFlare Protection` | + +### 🔬 Точный диагноз по RuTracker (проверено curl'ом через прокси, из контейнера prowlarr) + +``` +GET https://rutracker.org/forum/index.php → HTTP 200 ✅ (сайт доступен через прокси!) +POST https://rutracker.org/forum/login.php → HTTP 403 ❌ "Just a moment..." + (Cloudflare JS-challenge) +``` + +Тело ответа содержит `challenge` ×8, `cloudflare` ×5 — это **страница Cloudflare-челленджа**, а не отказ +сайта. **Логин/пароль проверить невозможно** — челлендж режет запрос до отправки формы. + +**Вывод:** прокси полностью решил DNS-блокировку. Остаточная проблема — **другого класса**: +Cloudflare-защита, для которой нужен headless-браузер. Прокси тут бессилен. + +### 🔴 FlareSolverr НЕ УСТАНОВЛЕН (блокер для оставшихся 3 индексеров) + +``` +docker ps -a | grep -i flare → NOT FOUND +docker images | grep -i flare → NO IMAGE +docker exec prowlarr printenv | grep -i flare → NOT SET +docker exec prowlarr cat /config/config.xml | grep -i flare → NOT IN CONFIG +``` + +**Варианты, предложенные Alex (ждём решения):** + +| # | Вариант | Оценка | +|---|---|---| +| 1 | **Задеплоить FlareSolverr** — `ghcr.io/flaresolverr/flaresolverr:latest` в `media_net`+`caddy_default`, затем `PROWLARR__FLARESOLVERR_URL=http://flaresolverr:8191` в compose Prowlarr | ~10 мин; закроет RuTracker + Kinozal | +| 2 | Пропустить RuTracker/Kinozal — 3 рабочих индексера (RuTor, Byrutor, NoNaMe) уже дают музыку, идти к Lidarr сразу | 0 мин, но теряем Kinozal (помечен как MUSIC-трекер) | +| 3 | Только Kinozal — у него нет логина-формы, FlareSolverr может хватить для одного | компромисс | + +**Рекомендация агента — вариант 1:** Kinozal в описании значится как `MOVIES / TV / MUSIC`, +то есть именно музыкальный трекер; без него смысл задачи (Lidarr на музыку) падает. + +### Что уже сделано в коде/конфиге (для воспроизведения) + +```bash +# 1. Подключение vless-proxy к media_net (persistent, через alpine — каталог root-owned 0:0 755) +cat /tmp/vless-proxy-compose.yml | ssh truenas_admin@mallexxx.duckdns.org \ + "docker run --rm -i -v /:/host:rw alpine:latest sh -c \ + 'cp .../docker-compose.yml .../docker-compose.yml.bak-\$(date +%Y%m%d-%H%M%S) && \ + cat > .../docker-compose.yml && md5sum .../docker-compose.yml'" +ssh truenas_admin@mallexxx.duckdns.org "cd /mnt/RED_2TB/docker/vless-proxy && docker compose up -d --force-recreate" + +# 2. Создание тега proxy +curl -s -X POST -H "X-Api-Key: $PW_KEY" -H "Content-Type: application/json" \ + -d '{"label":"proxy"}' http://localhost:9696/api/v1/tag # → {"label":"proxy","id":1} + +# 3. Привязка прокси к тегу proxy +curl -s -X PUT -H "X-Api-Key: $PW_KEY" -H "Content-Type: application/json" \ + -d '{"id":1,"name":"vless-socks5","implementation":"Socks5","configContract":"Socks5Settings", + "tags":[1],"fields":[{"name":"host","value":"vless-proxy"},{"name":"port","value":1080}, + {"name":"username","value":""},{"name":"password","value":""}]}' \ + http://localhost:9696/api/v1/indexerproxy/1 + +# 4. Тегирование индексеров (Kinozal требует forceSave=true — иначе 400 на Cloudflare-проверке) +# RuTracker уже имел tags=[1]; RuTor/Byrutor/NoNaMe — обычный PUT 202 +# Kinozal (5) и Kinozal (M) (6): PUT /api/v1/indexer/?forceSave=true → 202 +# ⚠️ ПОСЛЕ PUT обязателен docker restart prowlarr — иначе API отдаёт 202, но БД хранит [] + +# 5. Проверка +curl -s -X POST -H "X-Api-Key: $PW_KEY" -H "Content-Type: application/json" -d '{}' \ + --max-time 240 http://localhost:9696/api/v1/indexer/testall +``` + +### 🔴 Пойманные питфоллы Фазы 0 (дорогие, записать обязательно) + +1. **`Indexers.Tags` ссылался на несуществующий тег.** В `prowlarr.db` таблица `Tags` была **пустой**, + а у RuTracker стоял `Tags=[1]`. Прокси существовал (`IndexerProxies` id=1 `vless-socks5`), но + `IndexerProxies.Tags=[]` — привязки не было. Симптом: `Test All` даёт NXDOMAIN, хотя прокси настроен. + **Диагностика: скопировать `prowlarr.db` на Mac (`sqlite3` на хосте TrueNAS НЕТ) и посмотреть + `SELECT Id,Name,Tags FROM Indexers; SELECT Id,Name,Tags FROM IndexerProxies; SELECT * FROM Tags;`** + +2. **`docker exec prowlarr wget` — BusyBox, НЕ умеет PUT и `-e`.** Ошибки + `unrecognized option: method=PUT` и `unrecognized option: e`. **Но в контейнере prowlarr есть + `/usr/bin/curl`** — использовать `curl`, не `wget`. (Linuxserver-образы содержат curl.) + +3. **Kinozal PUT → HTTP 400 `CloudFlare Protection`.** Prowlarr при PUT валидирует доступ к сайту. + Обход: добавить `?forceSave=true` в query string → HTTP 202. + +4. **PUT вернул 202, но БД осталась `Tags=[]`.** Prowlarr держит кеш. **`docker restart prowlarr` + обязателен** — после рестарта все 6 тегов на месте. + +5. **`aws`-подобная правка JSON через `awk`/`sed` ломается, если `tags` — ПОСЛЕДНЕЕ поле объекта.** + Строка `"tags": []` в конце (перед `}`) — awk, режущий блок, съедал закрывающую `}` → + `HTTP 400 Expected start of a property name... LineNumber: 583`. + **Надёжный приём:** `awk '/"tags":/ { exit } { print }' file > body.json` — обрезать ВСЁ от `tags` + до конца, затем дописать хвост вручную: `printf ' "tags": [1],\n "id": %s\n}\n' "$ID" >> body.json`. + +6. **`jq` с `\\(...)` внутри inline SSH-строки через `terminal` ломается** (`unexpected INVALID_CHARACTER`). + **Писать jq-выражение в файл-скрипт** (`/tmp/parse.sh`) и запускать `bash /tmp/parse.sh` — тогда работает. + +7. **`execute_code` требует апрува Alex и был отклонён** (таймаут без ответа). Для разбора JSON — + файл-скрипт + `terminal`, не `execute_code`. Это подтверждает правило из профиля Alex. + +8. **`vless-proxy` содержит ДВА инбаунда:** SOCKS5 на `1080` и HTTP на `1081` (обе `0.0.0.0`). + Оба проверены, оба обходят блокировку. Prowlarr настроен на SOCKS5/1080. + +9. **Мёртвый домен `kinozal.tv` — использовать `kinozal.me`.** `kinozal.tv` не резолвится + даже на выходной стороне прокси (мёртв). `kinozal.me` через прокси → **HTTP 200**. + `kinozal.me/login.php` → 403 (нормальная защита страницы логина от curl, не блокировка). + +10. **`byrutgame.org` — единственный, работавший напрямую** (без прокси). Через прокси даёт 404 + на `/` (норм — у сайта нет корневой страницы). + +### Проверка обхода блокировки — сырые данные (воспроизводимо) + +``` +# БЕЗ прокси (из контейнера radarr) +rutracker.org → NXDOMAIN rutor.info → NXDOMAIN +kinozal.tv → NXDOMAIN nnmclub.to → NXDOMAIN + +# ЧЕРЕЗ прокси (HTTP 1081 и SOCKS5 1080 — одинаково) +https://rutracker.org/forum/index.php → 200 +https://rutor.info/ → 200 +https://nnmclub.to/ → 200 +https://kinozal.me/ → 200 +https://kinozal.tv/ → 000 +``` + ## План -### Фаза 0 — обход DNS-блокировки через прокси (БЛОКЕР для фазы 2) +### Фаза 0 — обход DNS-блокировки через прокси — 🟡 ЧАСТИЧНО ВЫПОЛНЕНА -1. **Подключить `vless-proxy` к `media_net`** — runtime (`docker network connect`) + персистентно - (добавить `media_net` в `networks:` его compose). Без персистентности отвалится при пересоздании. -2. **Проверить прокси из контейнера arr-стека:** из `radarr` достучаться до `rutracker.org` через - `https_proxy=http://vless-proxy:1080`. Если DNS резолвится на выходной стороне — путь рабочий. -3. **Прописать прокси в Prowlarr** — Settings → Indexers → Add Proxy (SOCKS5 либо HTTP по факту), - host `vless-proxy`, tags `[proxy]`. Присвоить тег индексерам, которые блокируются (RuTracker обязательно, - остальные — по результату проверки). -4. **Сбросить `indexerstatus`** — иначе `disabledTill` держится до 27 сентября. -5. **ПРИЁМКА ALEX:** `https://prowlarr.mallexxx.duckdns.org` → Indexers → Test All → все зелёные. +✅ **шаги 1–4 сделаны** (см. «СДЕЛАНО» выше). Осталось: **решение Alex по FlareSolverr** и +шаг 5 (приёмка Alex в UI). + +Текст шагов (для истории): + +1. ✅ **Подключить `vless-proxy` к `media_net`** — СДЕЛАНО (runtime + персистентно в compose). +2. ✅ **Проверить прокси из контейнера arr-стека** — СДЕЛАНО: 3/4 сайта дают 200 через прокси. +3. ✅ **Прописать прокси в Prowlarr** — СДЕЛАНО: тег `proxy` создан, прокси привязан, все 6 тегированы. +4. ✅ **Сбросить `indexerstatus`** — СДЕЛАНО через `testall` (`DELETE /indexerstatus` → 405, метод не поддерживается). +5. ⏳ **ПРИЁМКА ALEX:** `https://prowlarr.mallexxx.duckdns.org` → Indexers → Test All → 3 зелёных минимум. +6. ⏳ **РЕШЕНИЕ ALEX по FlareSolverr** — блокирует RuTracker + Kinozal (MUSIC-трекер). ### Фаза 1 — деплой Lidarr diff --git a/personal/plans/balda-mm-pr-plan.md b/personal/plans/balda-mm-pr-plan.md index 0d788c40..4eb0d168 100644 --- a/personal/plans/balda-mm-pr-plan.md +++ b/personal/plans/balda-mm-pr-plan.md @@ -1,6 +1,8 @@ # Balda — Mattermost connector: PR-подготовка (закрыто) -> Создан 2026-09-26. Обновлён 2026-09-26 (PR-проход). Статус: **✅ ЗАКРЫТО — код, тесты, доки, коммиты готовы**. +> Создан 2026-09-26. Обновлён 2026-09-26 (PR-проход, финал). +> Статус: **🟡 код/тесты/доки/коммиты готовы — но осталось ОДНО решение по `tools/` в ветке** +> (см. §«Открытое решение» ниже). После него — `git push` и PR. > Команда Alex: «Коммит текущего стейта и продолжай пока не стыдно будет открывать PR mattermost connector. > Со всеми поддерживаемыми фичами и тестами. Вперёд! Без вопросов и требующих апрува команд.» > Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фаза 2 → PR в апстрим). @@ -91,16 +93,59 @@ DM/канале/треде, troubleshooting (`bad_seq`, `unauthorized`, `x509`). `tools/deltas-*.json` (15 файлов) оставлены **незакоммиченными**: это ops-инструменты и метод правок, в апстрим-репозиторий им не место. +## 🔴 Открытое решение: `tools/` закоммичены в ветку (выявлено в конце сессии) + +**Найдено проверкой `git diff --stat upstream/main...HEAD`:** в диапазоне PR — 56 файлов, +и среди них **16 файлов `tools/`**, которых в апстриме **нет вообще** +(`git ls-tree upstream/main tools/` → пусто). + +Кто занёс: **базовый коммит `004fda2f`** (из прошлой сессии, не из этого прохода). +Мои 5 новых коммитов чисты — проверено: + +```bash +git log --oneline --name-only fa1a2855^..HEAD -- 'tools/' # → пусто +``` + +Что именно закоммичено в `004fda2f` (лишнее для PR): + +``` +tools/.deltas-state.json +tools/apply-project-deltas.py +tools/run-project-deltas.sh +tools/deltas-mattermost.json +tools/deltas-mattermost-2.json … -13.json (12 файлов) +``` + +**Что нужно сделать — по выбору Alex:** + +| Вариант | Команда | Последствие | +|---|---|---| +| **(a) рекомендую** | `git rm -r --cached tools/` + коммит | файлы остаются на диске **untracked**, локальный инструментарий работает, ветка чистая | +| (b) оставить | — | PR содержит 16 лишних файлов, ревьюер спросит | +| (c) отдельная ветка | — | tooling живёт отдельно от PR | + +⚠️ **Попытка (a) в этой сессии НЕ выполнена.** `git rm -r --cached tools/` ушла на +апрув и **истекла по таймауту** — команда не применилась, состояние дерева целое +(подтверждено `git status --short`: все файлы на месте). Повторять без команды Alex +не стал: удаление из индекса — операция, требующая подтверждения. + +📌 **Правило на будущее:** проверять состав PR через +`git diff --stat upstream/main...HEAD` **перед** объявлением готовности, а не только +`git status` (последний показывает рабочее дерево, не содержимое коммитов). Иначе +мусор из прошлой сессии всплывает последним. + ## Что осталось **Ничего по коду.** Дальше по желанию Alex: +0. 🔴 **Убрать `tools/` из ветки** — вариант (a) выше. Блокирует «стыдный» PR. 1. `git push -u origin feat/mattermost-channel` и открыть PR в `baldaworks/balda`. 2. Фаза 3/4 (эксплуатация на Kraken) — [[personal/projects/balda/balda-kraken-mm-deploy]]. -⚠️ Незакоммиченные `tools/mm-*.sh` + `tools/deltas-*.json` — **локальный -инструментарий**. Если они нужны в репо — отдельным коммитом с пометкой, но -для PR в апстрим я бы их не отправлял. +⚠️ Скрипты `tools/mm-*.sh` (9 шт.) и дельты `tools/deltas-mattermost-14…26` + +`deltas-goal-command-fix.json` + `deltas-mm-prefixfix.json` — **untracked, локальный +инструментарий**. Для PR в апстрим не отправлять. Существенное продублировано в +[[personal/projects/balda/balda-kraken-mm-deploy]] и [[personal/tech/balda-go-fx-pitfalls]]. ## Связанные заметки diff --git a/personal/plans/music-tags-review/music-tags-review.md b/personal/plans/music-tags-review/music-tags-review.md index 538ff9ba..63ec6472 100644 --- a/personal/plans/music-tags-review/music-tags-review.md +++ b/personal/plans/music-tags-review/music-tags-review.md @@ -18,3 +18,19 @@ status: awaiting-approval | [[1-needs-decision\|Требует решения]] | 9 | | [[2-medium-confidence\|Средняя уверенность]] | 2611 | | [[3-high-confidence\|Высокая уверенность]] | 2341 | + +## Контекст + +Сгенерировано из `~/Downloads/music-tags/tag-fix-plan.csv` скриптом +`render_grouped_doc.py` (в `~/Developer/personal-os/tools/music-tags/`). + +- **Полный разбор проблемы, план и питфоллы:** [[personal/tech/music-tags-repair-truenas]] +- **Зачем это нужно:** Music-библиотеку Jellyfin нельзя создать на битых тегах + (Jellyfin раскладывает музыку по тегам, не по папкам) — [[family/how-to/jellyfin-config]] +- **Статус:** этап 3 плана ремонта (апрув Alex). Правки файлов ещё **не начаты**. + +## Ограничения формата + +Блоки 2 и 3 — по 730–780 КБ / 5000+ строк. Obsidian открывает, но скролл тяжёлый; +наверху каждого файла есть оглавление по папкам (клик → прыжок к разделу). +Дубли-папки в `!Детские/Сказки/` намеренно **не** включены — Alex разбирает их сам. diff --git a/personal/projects/balda/balda-mattermost-connector.md b/personal/projects/balda/balda-mattermost-connector.md index 5a66cea6..04d5affa 100644 --- a/personal/projects/balda/balda-mattermost-connector.md +++ b/personal/projects/balda/balda-mattermost-connector.md @@ -1,12 +1,14 @@ # Balda — Mattermost коннектор (архитектура) > Создано 2026-09-26. Обновлено 2026-09-26 (сессия PR-подготовки, завершена). -> Статус: **✅ ГОТОВО К PR — 6 коммитов, все гейты зелёные.** +> Статус: **🟡 КОД ГОТОВ К PR — 6 коммитов, все гейты зелёные. Осталось одно решение: `tools/` в ветке.** > 🟢 **Сессия PR-подготовки завершена:** починены 2 дефекта **в коде** (идемпотентный > `MattermostSubject`; `ProgressPolicy.Typing`), написаны 6 файлов тестов (~90 тестов) > на транспорт + хендлер, обновлены README / `docs/balda.md` и создан `docs/mattermost.md`. -> `golangci-lint` = 0 issues, `go-arch-lint` = OK. Детали и коммиты — -> [[personal/plans/balda-mm-pr-plan]]. +> `golangci-lint` = 0 issues, `go-arch-lint` = OK. +> 🔴 **Блокер PR:** в ветке 16 файлов `tools/` из базового коммита `004fda2f` — в апстриме +> такого каталога нет. Нужно `git rm -r --cached tools/` по команде Alex (заблокировано +> истёкшим апрувом). Детали, коммиты и варианты — [[personal/plans/balda-mm-pr-plan]]. > Часть проекта [[personal/projects/balda/balda-mm-delegation-plan]] (Фаза 2). > Требование Alex: поддержка **всех** функций Балды, по новым API, production-качество. > diff --git a/personal/tech/balda-go-fx-pitfalls.md b/personal/tech/balda-go-fx-pitfalls.md index ac60d68e..d4b386a8 100644 --- a/personal/tech/balda-go-fx-pitfalls.md +++ b/personal/tech/balda-go-fx-pitfalls.md @@ -1155,10 +1155,42 @@ MM отвечал раз в 30 секунд, ровно на каждый пин ## 📌 Артефакты деплоя — вон из PR -`tools/mm-*.sh` (9 ops-скриптов) и `tools/deltas-*.json` (15 файлов дельта-групп) -— **локальный инструментарий**, в PR в апстрим не идут. Оставлять -незакоммиченными и явно перечислять в доке, чтобы не потерять. -В коммиты PR шли только: код транспорта, wiring, тесты, доки. +`tools/mm-*.sh` (9 ops-скриптов) и `tools/deltas-*.json` — **локальный инструментарий**, +в PR в апстрим не идут. В коммиты PR шли только: код транспорта, wiring, тесты, доки. + +⚠️ **Поправка (та же сессия, позже):** я сначала написал, что `tools/` «оставлены +незакоммиченными». Это было **неверно** — проверка показала, что **16 файлов `tools/` +уже сидят в базовом коммите `004fda2f`** (из прошлой сессии), и в апстриме каталога +`tools/` **нет вообще** (`git ls-tree upstream/main tools/` → пусто). То есть в PR +уезжал мусор, а `git status` этого не показывал. + +## 🔴 Проверять СОСТАВ PR, а не рабочее дерево + +**Правило:** перед объявлением «готово к PR» смотреть содержимое коммитов, а не только +`git status`: + +```bash +git diff --stat upstream/main...HEAD # ЧТО реально уедет в PR +git ls-tree --name-only upstream/main # есть ли этот dir в апстриме вообще +git log --oneline --name-only ^..HEAD -- '/' # не моё ли +``` + +Три команды отвечают на три разных вопроса, и **`git status` не отвечает ни на один**: +он показывает рабочее дерево, а не то, что уже закоммичено ранее (в т.ч. чужой рукой). + +📌 **Симптом промаха:** «я всё вычистил, PR готов» → `git diff --stat` показывает +56 файлов вместо 40. Лишние пришли из **предыдущей** сессии, не из текущей. + +📌 **Полезное следствие:** `git log --name-only -- '/'` точно отделяет +**свой** мусор от **унаследованного** — без этого легко начать «чинить» не то. + +## ⚠️ `git rm --cached` требует апрува — не повторять вслепую + +`git rm -r --cached tools/` идёт на подтверждение; при таймауте **не применяется** +(безопасно — состояние целое, проверять `git status --short`). Не ретраить +автоматически: удаление из индекса — операция, требующая команды заказчика. +Альтернатива без «удаления»: оставить файлы untracked, а из индекса убрать одним +явным коммитом по команде Alex. ## Связанные заметки diff --git a/personal/tech/music-tags-repair-truenas.md b/personal/tech/music-tags-repair-truenas.md index 48515a46..111b9cb9 100644 --- a/personal/tech/music-tags-repair-truenas.md +++ b/personal/tech/music-tags-repair-truenas.md @@ -334,11 +334,15 @@ if cyr < 2: # одна буква — совпадение |---|---|---| | 1 | **Инвентаризация** read-only: `ffprobe` по 7906 файлам → CSV | ✅ **ГОТОВО (v2)** — `tag_inventory2.py`, `probe_failed`=0 | | 2 | **CSV с предлагаемым исправлением** → Alex | ✅ **ГОТОВО (v2)** — `~/Downloads/music-tags/tag-fix-plan.csv` | -| 3 | **Апрув Alex по CSV** → только после этого правки | ⏳ **ЖДЁМ** (v1 отклонён, v2 отдан) | +| 2a | **Ноты для ревью в Obsidian** → Alex (запрос Alex 2026-09-26) | ✅ **ГОТОВО** — `personal/plans/music-tags-review/`, синкнуто (`Pushed ✓`, коммит `14e9f570`) | +| 3 | **Апрув Alex по CSV** → только после этого правки | ⏳ **ЖДЁМ** (v1 отклонён, v2 + ноты отданы) | | 4 | **Бэкап** — копировать **только перезаписываемые** файлы в `Music/backup/` | ⏳ | | 5 | **Ремонт** — перекодировка mojibake + восстановление из имён путей + чистка мусора | ⏳ | | 6 | **Приёмка** — Alex слушает выборку | ⏳ | +> 📌 **Что именно ждёт апрува:** 4961 `fix` из 7906. Alex проходит по трём нотам ревью, +> отмечает чекбоксы. Ничего не применяется до его «go». + ### Ключевые решения по плану (правки Alex) - ❌ **НЕ** бэкапить всю библиотеку (48 ГБ) — только те файлы, которые будут перезаписаны. @@ -390,6 +394,7 @@ if cyr < 2: # одна буква — совпадение | **CSV-план правок v2** | `~/Downloads/music-tags/tag-fix-plan.csv` (Mac, 2.2 МБ) | **Отдан Alex 2026-09-26.** 4961 `fix` / 2945 `no_change` / 0 `probe_failed` | | Копия БД Jellyfin | `/tmp/jf-copy.db` (Mac) | Для чтения API-ключа (см. `[[family/how-to/jellyfin-config]]`) | | Копия плана на NAS | `/tmp/tag-fix-plan.csv` (NAS) | Для запуска `id3_direct.py`/`check_ddt_album.py` с хостовым Python | +| **Ноты ревью в vault** | `personal/plans/music-tags-review/` (4 файла) | Обзор + 3 блока по уверенности; **вход для апрува Alex**, синкнуто | ### Колонки `tag-fix-plan.csv` @@ -418,6 +423,13 @@ if cyr < 2: # одна буква — совпадение - 🔴 **`python3` НЕТ в контейнере `jellyfin`** — для скриптов поднимать отдельный `python:3-alpine`. - 🔴 **`ffprobe` НЕТ на хосте TrueNAS** — только внутри контейнеров (в `jellyfin` путь `/usr/lib/jellyfin-ffmpeg/ffprobe`). +- ⚠️ **Генерация нот для Obsidian: сравнивать ОТРЕНДЕРЕННЫЕ значения, не сырые.** Сравнение + `new != cur` даёт ложные блоки, где различие (двойной пробел) схлопывается при выводе — файл + раздувается и выглядит так, будто правка ничего не меняет. Правильно: `esc(new) != esc(cur)`. +- ⚠️ **Нечитаемые байты в нотах рендерить как `⟨повреждено⟩`, а не как есть.** Управляющие + символы (`\x1d`, `\x10`) и U+FFFD дают в markdown нечитаемую кашу (`56=>ABL (:D"@8`). +- ⚠️ **Один markdown-файл на 5000 чекбоксов (~750 КБ) тормозит Obsidian.** Помогает группировка + по папкам + оглавление со ссылками вверху; при жалобах на лаги — разбивать по категориям. - 🔴 **`sed -i` на удалённом хосте применять нельзя** (нарушение правил Alex). Правильно: правка локально → `scp` заново. Именно так и было исправлено (`FFPROBE` → `os.environ.get`). - ⚠️ **Кириллица в inline `ssh`-командах** поднимает tirith `confusable_text` (HIGH). Команда проходит только через approval Alex. Митигация: пути с кириллицей собирать в файле-скрипте, а не в командной строке. - ⚠️ `alpine` без `ffmpeg` → инвентарь падает молча. Ставить `apk add --no-cache ffmpeg` первой строкой.