11 KiB
Executable File
title, created, updated, type, namespace, status, tags, related
| title | created | updated | type | namespace | status | tags | related | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Watchlist — фикс переполнения диска и фильтра ужасов | 2026-09-21 | 2026-09-21 | plan | family | done |
|
|
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/ |
Открытые вопросы
cleanupне видит/media/movies— удаляет только Radarr-файлы, а просмотренное у пользователя лежит в/media/movies(ручные торренты). Пересечение пусто, поэтому cleanup пишет «nothing to delete» каждый день. Не исправлено — отдельная задача. Разбор: watchlist-cleanup.- 41 фильм с
🧟♀️перестанет качаться. Многие — свежие релизы 2025–2026. Решение Alex: ожидаемое поведение; для исключения ставить⭐. 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