188 lines
11 KiB
Markdown
Executable File
188 lines
11 KiB
Markdown
Executable File
---
|
||
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
|