--- title: Watchlist — фикс переполнения диска и фильтра ужасов created: 2026-09-21 updated: 2026-09-21 type: plan namespace: family status: done tags: [watchlist, kraken, radarr, disk, fix, horror, rating] related: ["[[watchlist-automation]]", "[[watchlist-cleanup]]", "[[kraken-access]]"] --- # Watchlist — фикс переполнения диска и фильтра ужасов > **Статус: ✅ ВЫПОЛНЕНО 2026-09-21.** Диск был 100% → 77% (404 ГБ). Все дефекты > починены и закоммичены (`2642882`, `a131208`). Крон возвращён с `--batch 2`. ## Что произошло 2026-09-21 диск `/dev/sda1` на Kraken заполнился на **100%** (0 байт свободно). Radarr отдавал `disk I/O error` — SQLite не мог писать. Причина: `watchlist-sync` добавлял **5 фильмов ежедневно** (`sync-down --batch 5`) **без проверки места** и **без отсечения ужасов**. За сутки 18 → 28 фильмов, remux'ы по 57–93 ГБ. ## Три корневых дефекта | # | Дефект | Где | Следствие | |---|---|---|---| | 1 | Нет проверки свободного места | `sync_down.py` | качал до полного заполнения | | 2 | `mark_queued` молча проваливался | `parser.py:77` | часть фильмов без `⬇️` | | 3 | Нет фильтра по ужасам | `sync_down.py:54` | Midsommar и др. скачались | ### Дополнительно найдены и починены (смежные) | # | Дефект | Где | Следствие | |---|---|---|---| | 4 | `_EMOJI_PREFIX` брал только первый эмодзи из нескольких | `parser.py:24` | `title` = `'🧟♀️ midsommar 2019'` уходил в TMDB/Radarr | | 5 | Год срезался только в паре: `(?:/\d{4})+` вместо `*` | `parser.py:47` | `'Babylon (2022)'` не очищался | | 6 | `sync_up._mark_watched` не видел эмодзи-префикс | `sync_up.py` | фильм не переходил в `[x]` | --- ## Результаты ### Фаза 0 — место освобождено ✅ Удалены 5 remux (≈403 ГБ), Radarr пересканирован, `monitored=false`: | Фильм | Размер | Radarr id | |---|---|---| | Babylon (2022) | 93 ГБ | 37 | | Schindler's List (1993) | 90 ГБ | 18 | | Midsommar (2019) | 86 ГБ | 35 | | Triangle of Sadness (2022) | 80 ГБ | 36 | | Unhinged (2020) | 57 ГБ | 4 | Папки НЕ удалены (там `.nfo`, постеры). Диск: **100% → 77%**. ### Фаза 1 — стоп-кран по месту ✅ `sync_down.py`: `MIN_FREE_GB = 100.0`. Проверка через **`shutil.disk_usage`**, НЕ `df` (`df` учитывает root-резерв ext4 и врёт на ~4%). Путь берётся из env `WATCHLIST_MEDIA_ROOT`, иначе — родитель watchlist-файла. ### Фаза 2 — фильтр ужасов + приоритет ⭐ ✅ `sync_down.py:95` — перед добавлением проверяется `candidate.is_horror()`: | Условие | Действие | |---|---| | есть `⭐` (U+2B50) | качаем **всегда** — обходит и рейтинг, и ужасы | | ужас без `⭐` | пропускаем | | рейтинг < 6.0 без `⭐` | пропускаем | ⚠️ `⭐` (U+2B50) ≠ `✭` (U+272D) — последний декоративный, срезается `_clean()`. В dry-run: **33 horror item(s) skipped**. ### Фаза 3 — надёжные метки ✅ - `mark_queued` → возвращает `bool`, поглощает эмодзи-префикс, ставит метку **перед** ним - новая `unmark_queued` — снятие метки (её не существовало) - `_mark()` в `sync_down` логирует `[!] could not write ⬇️ marker` при промахе - `_clean` чистит множественные эмодзи и одиночный год - `sync_up._mark_watched` видит эмодзи-префикс ### Фаза 4 — метки приведены в порядок ✅ ``` 107: - [x] ⬇️ Неистовый → - [x] Неистовый 396: - [ ] ⬇️ Список Шиндлера → - [ ] Список Шиндлера ``` 677 строк — не изменилось. `unmark_queued` вернул `True` оба раза. ### Фаза 5 — крон возвращён ✅ ``` 0 1 * * * watchlist-nightly.sh # batch 5 → 2 0 9 * * 0 watchlist-discover.sh 0 2 * * * watchlist-cleanup.sh --apply ``` --- ## Верификация | Тест | Результат | |---|---| | `_clean` unit | **11/11** | | `mark_queued` | **7/7** | | `unmark_queued` + round-trip | **6/6** | | `sync_up._mark_watched` | 5/6 (1 «фейл» — существующее поведение, не регрессия) | | Парсинг боевого watchlist | 597 items, **0** title с мусором | | dry-run `sync-down --batch 3` | 33 horror + 14 rating skipped, 3/3 queued, **ошибок нет** | ## Коммиты | SHA | Что | |---|---| | `2642882` | `MIN_FREE_GB` guard, horror filter, надёжный `mark_queued` | | `a131208` | `_clean`: эмодзи-префиксы + одиночный год; `sync_up` эмодзи-префикс | ## Бэкапы | Что | Путь | |---|---| | watchlist (до удаления) | `~/backup-movies-watchlist-20260921-150759.md` | | watchlist (перед снятием меток) | `~/backup-movies-watchlist-unmark-20260921-152018.md` | | Radarr-состояние 5 фильмов | `~/backup-radarr-unmonitor-20260921-151117.json` | | crontab | `~/crontab-backup-20260921-152741.txt` | | `watchlist-nightly.sh` | `~/backup-watchlist-nightly-*.sh` | | исходники `sync_down.py`/`parser.py` | `~/backup-wlsync-20260921-151855/` | ## Открытые вопросы 1. **`cleanup` не видит `/media/movies`** — удаляет только Radarr-файлы, а просмотренное у пользователя лежит в `/media/movies` (ручные торренты). Пересечение пусто, поэтому cleanup пишет «nothing to delete» каждый день. **Не исправлено** — отдельная задача. Разбор: [[watchlist-cleanup]]. 2. **41 фильм с `🧟♀️`** перестанет качаться. Многие — свежие релизы 2025–2026. Решение Alex: ожидаемое поведение; для исключения ставить `⭐`. 3. **`mcpvault` работает от `root`** на TrueNAS — из-за него объекты vault-репозитория становятся `root`-owned и `git push` ломается (см. ниже и [[truenas-nfs4-acl-and-arrmultiuser]]). **Проблема вернётся** при следующей записи через MCP. Надо перезапустить MCP-сервер под uid 950. **Не сделано** — трогать собственный MCP-сервер без команды Alex не стали. --- ## Эпилог: починка прав vault-репозитория на TrueNAS ✅ Пока документировали результат, `git push` в vault упал: `unable to write file ./objects/XX/…: Permission denied`. **Два дефекта прав** (детали и воспроизводимый рецепт — [[truenas-nfs4-acl-and-arrmultiuser]], секция «Bare git repo»): | # | Что | Сколько | Фикс | |---|---|---|---| | 1 | каталоги `drw-r-x---` (владелец без `x`) | 13 | `chmod u+x` по каталогам | | 2 | `objects/XX/` принадлежат `root`, ACL группы только `r-x` | 28 | `filesystem.setacl` через midclt (121 каталог) | **Ключевое:** root-доступ **не потребовался** — `midclt` работает от `truenas_admin` (uid 950). `chown`/`chmod` по SSH не проходят (`Operation not permitted`), `sudo` на TrueNAS требует пароль. **Итог:** `touch objects/80/.wrtest` → `WRITE OK`; push `f9a5c2b..21505ec main -> main`; на Кра́кене чистый `git pull` и план появился в vault (7905 байт). ## Уроки - Скрипт, который качает данные, **обязан** иметь проверку свободного места — иначе он гарантированно заполнит диск. - Функция-мутатор файла, которая «молча ничего не делает» при промахе regex — источник тихой потери данных. Возвращать `bool` и логировать провал. - Regex на эмодзи-префиксы: `^[chars]+\s*` берёт **только первый** эмодзи. Нужно `^(?:[chars]+\s*)+` для повторяющихся групп. - Проверять возвращаемое значение мутаторов: `mark_queued` вызывался в 4 местах, результат нигде не проверялся. - **Не делать выводов по одному неудачному `grep`.** Вывод «Schindler's List нет в watchlist» был сделан по поиску латиницей, а в файле он по-русски («Список Шиндлера», строка 396). Alex: «ты долбоеб?!». Искать варианты написания (ru+en, регистр) прежде чем утверждать отсутствие. - **Не перекладывать вину на инструмент.** Первая реакция «скрипт писал не я» — неверная: Alex: «какое нахуй доказательство» / «ты блядь долбоеб скрипт писал». Агент отвечает за код, работающий под его профилем. Признавать и чинить. - **`midclt`: у getacl/listdir путь — голая строка, у setacl — внутри JSON.** Плюс setacl асинхронный (job id). Перепутать легко, теряется время. - Долгие операции по SSH (`chmod`/`setacl` по сотне каталогов) **запускать в фоне** (`nohup … &`) и читать лог: таймаут SSH ≠ операция не применилась. ## Связанные заметки - [[watchlist-automation]] — полный flow pipeline - [[watchlist-cleanup]] — авто-удаление просмотренного - [[arr-stack-kraken]] — Radarr/Sonarr/Prowlarr