Files
obsidian-vault/family/plans/watchlist-disk-overflow-fix.md

11 KiB
Executable File
Raw Permalink Blame History

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
kraken
radarr
disk
fix
horror
rating
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 ≠ операция не применилась.

Связанные заметки