diff --git a/family/how-to/arr-stack-kraken.md b/family/how-to/arr-stack-kraken.md index ac43681d..0b60279f 100644 --- a/family/how-to/arr-stack-kraken.md +++ b/family/how-to/arr-stack-kraken.md @@ -81,15 +81,31 @@ Prowlarr → Radarr + Sonarr → Transmission → Jellyfin ## Cron'ы на Kraken (crontab) +⚠️ **Фактическое состояние (проверено 2026-09-20): строки watchlist в crontab ОТСУТСТВУЮТ.** Watchlist pipeline мёртв с 2026-06-20 — детали и разбор в [[family/projects/watchlist-automation]]. + +Фактически в `crontab -l` юзера `kraken` сейчас: ``` 0 * * * * bash ~/scripts/sync-vault.sh # Obsidian sync -0 * * * * pgs-to-srt.sh /media/downloads # PGS→SRT новые +0 * * * * pgs-to-srt.sh /media/downloads/complete # PGS→SRT новые 0 3 * * * pgs-to-srt.sh /media # PGS→SRT вся библиотека +``` + +Должно быть (НЕ восстановлено, ждёт решения Alex): +``` 0 1 * * * bash ~/scripts/watchlist-nightly.sh # watchlist nightly 0 9 * * 0 bash ~/scripts/watchlist-discover.sh # watchlist discover */10 * * * * bash .../media-pipeline/media-router.sh # media router (Telegram) ``` +Точная проверка наличия любого крон-джоба (не верить доке, смотреть систему): +```bash +crontab -l # юзер kraken +sudo crontab -l # root +sudo ls -la /var/spool/cron/crontabs/ # все юзерские +sudo grep -rl watchlist /var/spool/cron/ /etc/cron.d/ /etc/crontab +systemctl --user list-timers --all ; systemctl list-timers --all | grep -i watchlist +``` + ## API Keys | Service | API Key | @@ -432,9 +448,64 @@ sudo docker inspect -f '{{index .Config.Labels "com.docker.compose.projec Все сервисы стека (`radarr`, `sonarr`, `prowlarr`, `flaresolverr`, `transmission`) **плюс `jellyfin` и `portainer`** размечены label `com.centurylinklabs.watchtower.enable=true` → обновляются автоматически (7 контейнеров, проверено `scanned=7`). Полный разбор и список того, что НЕ обновляется: [[family/how-to/kraken-watchtower]]. Теория: [[personal/tech/watchtower-docker-updates]]. +## Watchlist-sync: ручной запуск и баг дублирования (2026-09-20) + +> Детальный разбор бага и полная механика — в [[family/projects/watchlist-automation]]. Здесь — операционная выжимка. + +### Ручной запуск (пока крон не восстановлен) + +```bash +ssh kraken +bash ~/scripts/watchlist-nightly.sh # pull → process-thumbs → resolve → sync-down → sync-up → push +``` +Отдельные шаги: +```bash +cd /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync +uv run --project . watchlist-sync [--apply] [--recheck] [--batch N] +``` +`uv` = `~/.local/bin/uv`. Без `--apply` — dry-run. + +### 🔴 Баг `mark_ambiguous()` — исправлен + +`watchlist_sync/parser.py`, функция `mark_ambiguous()`: паттерн снимал **только саму `❓`-строку**, а замена вставляла новые кандидаты **перед** старыми → под-буллеты дублировались **на каждом прогоне** `resolve` (+3 строки / +100 байт за раз). `❓`-строки при этом НЕ дублировались — только под-буллеты под ними. + +```python +# ФИКС: захват всего блока — маркер + под-буллеты +_SUB_BULLETS = r'(?:\n[ \t]{4,}-[ \t]*(?!\[[ xX]\])[^\n]*)' +pattern = re.compile( + r'(^[ \t]*-[ \t]*\[ \][ \t]*)[❓]?[ \t]*(' + escaped + r')[ \t]*' + r'(?P' + _SUB_BULLETS + r'*)', + re.MULTILINE +) +``` + +**Правило для парсеров этого файла:** под-буллет кандидата = отступ **≥4 пробела** + буллет + **НЕ чекбокс** (`- [ ]`/`- [x]`). Строки фильмов имеют отступ **2 пробела** — их надо исключать явно. Внутри `*`-группы **перенос строки обязателен** (`(?:` + `\n` + ...) + `*`): если сделать `\n?` опциональным, regex перешагивает на следующую строку и **проглатывает соседние фильмы** (проверено — так потеряли `❓ The kill room`, `The burial`, `Past lives`). + +**Верификация:** прогнать `mark_ambiguous()` 3× на свежей копии → размер обязан быть **идентичным**. Плюс проверка, что 0 не-кандидатных строк потеряно. + +### Скрипт дедупа кандидатов + +Одноразовая чистка накопленных дублей: под каждой `❓`/`🗓️`-строкой оставить уникальные под-буллеты (ключ `title|year|rating`), порядок не менять, строки фильмов не трогать. +```bash +python3 /tmp/wl_dedupe.py # dry-run +python3 /tmp/wl_dedupe.py --apply # запись + авто-бэкап .dedupe-backup- +``` +Прогон 2026-09-20: **168 дублей в 15 блоках**, 26430 → 21032 байт (838 → 670 строк). + +⚠️ **Скрипты этой сессии — в `/tmp` на Кракене** (`wl_dedupe.py`, `wl_fix_parser*.py`, `wl_verify*.py`); `/tmp` чистится при перезагрузке. Для постоянной копии — перенести в `~/scripts/`. + +### Sonarr 400 «This series has already been added» — не сбой + +При `sync-down` уже существующей серии Sonarr отвечает `400 SeriesExistsValidator` («Волшебный участок» = «Magical Police Department», tvdbId=442038). `arr.py::sonarr_add()` **не проверяет дубли перед POST** — в отличие от `radarr_add()`. Шумит в логе, данные не портит. Диагноз смотреть в теле ответа: `errorCode: SeriesExistsValidator`. + +### Zulip-пинг не настроен + +`_zulip_ping()` (`cli.py`) требует `ZULIP_URL` / `ZULIP_API_KEY` / `ZULIP_BOT_EMAIL`, которых **нет в `.env`** репо → `[warn] not set — skipping ping`. Уведомления о нерезолвленных items до Alex **не доходят** (59 штук ушли молча). + ## Notes - See [[arr-stack-taiga]] for Taiga (secondary) arr stack - Media pipeline pitfalls: [[media-pipeline-pitfalls]] - FlareSolverr / Cloudflare / Prowlarr-питфоллы: [[prowlarr-flaresolverr-cloudflare]] +- Watchlist pipeline (полный flow, cron, баги): [[family/projects/watchlist-automation]] - Синтез: [[concepts/kraken-media-stack]] diff --git a/family/projects/watchlist-automation.md b/family/projects/watchlist-automation.md index 86f37c81..8925209f 100644 --- a/family/projects/watchlist-automation.md +++ b/family/projects/watchlist-automation.md @@ -1,7 +1,7 @@ --- created: '2026-05-20' updated: '2026-09-20' -status: broken +status: degraded tags: - kraken - watchlist @@ -81,7 +81,95 @@ Jellyfin (8096) → rescan → виден контент **Побочный эффект — файл засран `❓`-мусором.** Так как `resolve --recheck` не бегает, ambiguous-строки копят дампы кандидатов: «Крестный отец» ×4 блока по 6 строк, «Nightmare Alley» ×31 строка, «Stand by me» ×40 строк, «Фарго» ×30. Секции `👍/👎` не обрабатываются — 40+ позиций висят в «Новинки», не переехав в основной список. -Аудит завершён без изменений на Кракане — ничего не трогалось. Три варианта для Alex: (1) восстановить крон, (2) сначала почистить `❓`-мусор, (3) оставить как есть. +> ⚠️ **Уточнение (2026-09-20, позже в тот же день):** изначальная гипотеза «мусор скопился от многомесячного простоя» **НЕВЕРНА**. Дубли создавались на **каждом** прогоне `resolve` из-за бага в `mark_ambiguous()` (см. ниже). 33 строки у «Nightmare Alley» = 11 прогонов. Простой лишь позволил мусору накопиться незамеченным. + +**Крон по-прежнему НЕ восстановлен** — Alex не просил, прогон делался вручную (см. «Ручной прогон»). + +## ✅ Баг дублирования кандидатов в `mark_ambiguous()` — НАЙДЕН И ИСПРАВЛЕН (2026-09-20) + +### Симптом +Под каждой `❓`-строкой копились одинаковые дампы кандидатов. Рост ровно **+100 байт за прогон** (3 строки кандидатов) — подтверждено экспериментом: 26430 → 26530 → 26630 → 26730 байт. + +### Корень (точная механика) +В `watchlist_sync/parser.py`, функция `mark_ambiguous()`: + +```python +pattern = re.compile( + r'(^\s*-\s*\[ \]\s*)[❓]?\s*(' + escaped + r')\s*$', # ← матчит ТОЛЬКО строку ❓ + re.MULTILINE +) +... +replacement = rf'\1❓ \2\n{cand_lines}' # ← новые кандидаты +new_text = pattern.sub(replacement, text, count=1) # ← ВСТАВЛЯЮТСЯ перед старыми +``` + +Паттерн снимал **только саму `❓`-строку**. Замена вставляла `\n` + 3 новых кандидата, а старые 33 строки оставались **ниже** — реплейс не знал о них. Итог: каждый прогон добавляет ещё один блок кандидатов поверх существующих. + +**Важно:** `❓`-строки НЕ дублировались (каждая — одна). Дублировались **под-буллеты** под ними. + +### Фикс +Паттерн теперь захватывает **весь блок**: строку-маркер ПЛЮС её под-буллеты, и перезаписывает целиком. + +```python +# Candidate sub-bullet: indent of 4+ spaces, a bullet, NOT a checkbox line. +# Each iteration REQUIRES its own leading newline -- otherwise `*` walks onto +# the following line and swallows neighbouring items. +_SUB_BULLETS = r'(?:\n[ \t]{4,}-[ \t]*(?!\[[ xX]\])[^\n]*)' + +pattern = re.compile( + r'(^[ \t]*-[ \t]*\[ \][ \t]*)[❓]?[ \t]*(' + escaped + r')[ \t]*' + r'(?P' + _SUB_BULLETS + r'*)', + re.MULTILINE +) +``` + +### ⚠️ Две НЕУДАЧНЫЕ версии паттерна (не повторять) +1. **v1** — `(?:\n[ \t]+-[^\n]*\n?)*`: `\n?` опционален, поэтому `*` перешагивал на следующую строку **без разделителя** и проглатывал соседние фильмы. Потеряны `❓ The kill room`, `The burial`, `Past lives`. Откачено. +2. **v2** — то же плюс негативный lookahead: не помогло, `[^\n]*` из предыдущей итерации уже захватил хвост следующей строки. +3. **v3 (рабочая)** — перенос обязателен внутри каждой итерации (`(?:` + `\n` + ...) + `*`), якорь не нужен. + +**Правило для парсеров этого файла:** под-буллет кандидата = отступ **≥4 пробела**, это буллет и он **НЕ чекбокс** (`- [ ]` / `- [x]`). Строки фильмов имеют отступ 2 пробела — именно поэтому их надо исключать явно. + +### Проверка фикса +На чистой копии (md5 `40295fac...`) — 3 прогона `mark_ambiguous()`: +``` +run 1: size=25430 +run 2: size=25430 +run 3: size=25430 +``` +Размер стабилен. Потеряно **0** не-кандидатных строк. `Past lives` на месте. 838 → 808 строк (минус 30 = старые дубли, 33 → 3). + +### Чистка существующих дублей +Скрипт `/tmp/wl_dedupe.py` (дедуп по `title|year|rating`, первое вхождение сохраняется, порядок не меняется; строки фильмов не трогаются). Прогон `--apply`: +- **168 дублей в 15 блоках**, 26430 → 21032 байт (838 → 670 строк). +- Топ: `Nightmare alley` 33→3, `Stand by me` 30→3, `Фарго` 30→3, `Left behind` 21→4, `Крестный отец` 12→3, `Godfathers` 12→3. + +### Ручной прогон `watchlist-nightly.sh` (от юзера `kraken`) +``` +bash ~/scripts/watchlist-nightly.sh +``` +Прошёл до `Done`, без падений. Результат: **270 resolved (38 horror), 37 ambiguous, 22 not found**. `sync-down` добавил 4 фильма в Radarr (Американская история X, Мастерская сатаны, Я ругаюсь, Гладиатор), `Scream` уже был → помечен `⬇️`. `sync-up`: 7 played items из Jellyfin, 1 отмечен. Vault закоммичен и запушен (`Committed: family/documents/movies-watchlist.md`, `Pushed ✓`). + +**После прогона дублей: 0** (проверено отдельным скриптом). Секции целы: Новинки / Фильмы / Мультфильмы / Аниме / Новинки👎 / Связанные заметки. + +### 🟡 Побочная находка: Zulip-пинг не настроен на Кракене +`_zulip_ping()` требует `ZULIP_URL`, `ZULIP_API_KEY`, `ZULIP_BOT_EMAIL`, но их **нет в `.env`** репо watchlist-sync. При 59 нерезолвленных items → `[warn] ... not set — skipping ping`. Уведомления о нерезолвленных до Alex не доходят. + +### 🟡 Побочная находка: Sonarr 400 «This series has already been added» +При `sync-down` для «Волшебный участок»: `400 Bad Request` на `/sonarr/api/v3/series`. Причина безобидная — **серия уже в Sonarr** (tvdbId=442038, «Magical Police Department»). `arr.py::sonarr_add()` не проверяет дубли перед POST, поэтому Sonarr отвечает `SeriesExistsValidator`. Не сбой, но шумит в логе — стоит добавить pre-check (как в `radarr_add`). + +### Бэкапы (перед правками) +``` +~/obsidian/family/documents/movies-watchlist.md.bak-20260920-192909 (838 строк, 26430 б) +~/obsidian/family/documents/movies-watchlist.md.dedupe-backup-20260920-193111 +/srv/.../watchlist-sync/watchlist_sync/parser.py.bak-20260920-192909 +``` + +### Статус на конец сессии +- ✅ Фикс `parser.py` применён на Кракене, записан на диск, **`ast.parse` OK**. +- 🔴 **Фикс НЕ закоммичен** в репо watchlist-sync (git status показывает только `?? arr.py.bak-web4k-...`). Нужен коммит. +- ✅ Файл watchlist вычищен и закоммичен в vault. +- 🔴 Крон по-прежнему отсутствует — pipeline живёт только вручную. ## Cron Jobs (на Кракене с 2026-05-27) @@ -102,7 +190,26 @@ Jellyfin (8096) → rescan → виден контент **Пути на Кракене (HDD UUID 49e8f586-3839-4c5d-a1e1-58bfc3579ade):** - `/home/kraken/obsidian` → symlink на `/srv/dev-disk-by-uuid-.../obsidian` -- `~/Developer/watchlist-sync` → symlink. **Реальный корень репо** (проверено 2026-09-04): `/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/watchlist-sync` — находится **в корне диска, НЕ под `Developer/`** (в доке ранее путь был ошибочный). Внутри: `watchlist_sync/` (cli.py, arr.py, sync_down.py, sync_up.py, resolver.py, parser.py, discover.py, jellyfin.py), `.venv` (py3.11+httpx+click), `.resolve_cache.json`, pyproject.toml. +- `/home/kraken/Developer/watchlist-sync` → symlink на `/srv/dev-disk-by-uuid-49e8f586-.../watchlist-sync` + +✅ **Путь репо в скриптах ВЕРНЫЙ** (проверено 2026-09-20). В `watchlist-nightly.sh` и `watchlist-discover.sh`: +``` +WS=/srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync +UV=~/.local/bin/uv +``` +`ls -la` подтверждает: `/srv/.../watchlist-sync` существует, внутри `watchlist_sync/`, `.venv/`, `.resolve_cache.json`, `pyproject.toml`, `.env` (есть, содержит TMDB_KEY/KP_KEY/RADARR_*/SONARR_*; **нет** ZULIP_*). + +> Ранее в доке фигурировал путь `.../6194539b-.../watchlist-sync` «как реальный корень». На 2026-09-20 рабочий путь — **`49e8f586`**, и он же используется в скриптах. Не «исправлять» его обратно. + +**Ещё одно подтверждённое отличие:** `~/scripts/` — **реальная директория** (`drwxr-xr-x root root`), а `~/.hermes/` на Кракане содержит **только** `skills/`. Скрипты лежат в `~/scripts/`, не в `~/.hermes/scripts/`. + +### Как запускать CLI вручную (рабочая команда) +```bash +ssh kraken +cd /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync +uv run --project . watchlist-sync <команда> # resolve / sync-down / sync-up / process-thumbs / discover +``` +Или целиком пайплайн: `bash ~/scripts/watchlist-nightly.sh` ## Discover — Фильтрация @@ -161,19 +268,34 @@ Jellyfin library mapping: Movies → `movies`, Cartoons → `mixed` (movies + ep ### KP API Endpoint `api.kinopoisk.dev` редиректит на `api.poiskkino.dev/v1.4/movie`. Использовать destination redirect напрямую. -### 🔴 Жёстко зашитый `qualityProfileId: 1` в arr.py (корень жирных remux) -Найдено 2026-09-04. В `watchlist_sync/arr.py` **`qualityProfileId: 1` захардкожен** и для Radarr, и для Sonarr: +### 🔴 Жёстко зашитый `qualityProfileId` в arr.py (корень жирных remux) — РЕШЕНО +Найдено 2026-09-04. В `watchlist_sync/arr.py` изначально был захардкожен **`qualityProfileId: 1`** и для Radarr, и для Sonarr: ```python -# radarr_add (~стр. 77) / sonarr_add (~стр. 112) +# radarr_add (~стр. 77) / sonarr_add (~стр. 112) "qualityProfileId": 1, ``` -Профиль 1 (Radarr) = **'Any'** — разрешает ВСЁ вплоть до Remux-2160p и BR-DISK. Поэтому **каждый новый фильм/сериал, добавляемый watchlist-sync, попадает в «Any»** и Radarr скачивает самый жирный доступный релиз (60GB remux'ы). Готового профиля «2160p, но без Remux» на Kraken нет (нужно создавать). Решение по стратегии — открыто (см. ниже). Чтобы новые шли в поддерживаемое качество: либо создать профиль «2160p без Remux/BR-DISK» и поменять `qualityProfileId` в `arr.py` (Radarr+Sonarr), либо сузить сам профиль 1, либо зашить `4` (HD-1080p). +Профиль 1 (Radarr) = **'Any'** — разрешает ВСЁ вплоть до Remux-2160p и BR-DISK, поэтому каждый новый фильм/сериал попадал в «Any» и Radarr скачивал 60GB remux'ы. + +✅ **Решение принято и применено 2026-09-04:** создан профиль **`WEB-4K (no remux)`** → Radarr id **7**, Sonarr id **7**; `arr.py` теперь пишет `qualityProfileId: 7`. Подробности профиля — в [[arr-stack-kraken]], секция «Качество загрузки для НОВЫХ фильмов/сериалов». + +### ✅ Баг накопления кандидатов в `mark_ambiguous()` (найден и исправлен 2026-09-20) +Кандидаты-под-буллеты под `❓`-строками дублировались **на каждом прогоне** `resolve` (+3 строки за раз), потому что `re.sub` снимал только саму `❓`-строку, а новые кандидаты вставлялись **перед** старыми. Паттерн исправлен на захват всего блока (маркер + под-буллеты). Полная механика, две неудачные версии паттерна и чистка 168 дублей — в [[family/projects/watchlist-automation]]. + +**Правило для парсеров watchlist:** под-буллет кандидата = отступ **≥4 пробела** + буллет + **НЕ чекбокс** (`- [ ]`/`- [x]`). Строки фильмов имеют отступ 2 пробела. Внутри `*`-группы перенос строки обязателен — иначе regex проглатывает соседние фильмы. ## Open Tasks -- 🔴 **[2026-09-20] Восстановить cron для watchlist pipeline** — pipeline мёртв с 20 июня (см. раздел выше). Решение за Alex: восстановить крон / сначала почистить `❓`-мусор / оставить как есть. -- 🔴 **[2026-09-04] Решить стратегию качества загрузки новых фильмов** (см. профили в [[arr-stack-kraken]]). Корень — захардкоженный `qualityProfileId: 1` (`Any`) в `watchlist_sync/arr.py`. Пока НЕ менялось (НИЧЕГО не правилось, ждём решение Alex). Вопрос юзеру: сохранить 4K (→ создать профиль «2160p без Remux/BR-DISK» и сменить id в arr.py) или опустить до 1080p (id 4); и менять ли и для Sonarr (сериалы) тоже. -- 🟡 Почистить `❓`-мусор в movies-watchlist.md (повторяющиеся дампы кандидатов после многомесячного простоя) +- 🔴 **[2026-09-20] Закоммитить фикс `parser.py`** в репо watchlist-sync. Правка применена на Кракене, но не в git — при следующем `git pull`/пересоздании `.venv` можно потерять. Файл: `watchlist_sync/parser.py` (функция `mark_ambiguous`, `_SUB_BULLETS`). +- 🔴 **[2026-09-20] Восстановить cron для watchlist pipeline** — pipeline мёртв с 20 июня. Скрипты готовы и проверены вручную (прогон 2026-09-20 прошёл чисто), нужно вернуть в crontab юзера `kraken`: + ``` + 0 1 * * * bash ~/scripts/watchlist-nightly.sh + 0 9 * * 0 bash ~/scripts/watchlist-discover.sh + ``` + ⚠️ В [[arr-stack-kraken]] эти строки в секции «Cron'ы на Kraken» указаны как действующие, хотя фактически их НЕТ — док в той части расходится с реальностью. +- 🟡 **[2026-09-20] Настроить Zulip-пинг** — `ZULIP_URL` / `ZULIP_API_KEY` / `ZULIP_BOT_EMAIL` отсутствуют в `.env` watchlist-sync, 59 нерезолвленных items ушли в никуда без уведомления. +- 🟡 **[2026-09-20] Добавить pre-check дублей в `sonarr_add()`** — сейчас при уже существующей серии прилетает `400 SeriesExistsValidator` и шумит в логе. В `radarr_add()` проверка есть. +- 🔴 **[2026-09-04] Решить стратегию качества загрузки новых фильмов** — уже РЕШЕНО 2026-09-04 (профиль `WEB-4K (no remux)`, id 7 для Radarr и Sonarr). Осталось только убедиться, что старые `⬇️`-позиции, добавленные до фикса, не тянут remux. +- 🟡 Оставшиеся `❓`-позиции (71 маркер) — **намеренно не чистятся вручную** (решение Alex 2026-09-20). Разбираются через `resolve --recheck` при работающем кроне. - 🟡 Передавать Transmission locations через router script при каждом импорте (сейчас вручную) - 🔴 rsync невыполненных фильмов с HTPC на Кракен (`Фильмы не перенесенные с htpc на кракен.md`) - 🟡 Watchlist sync-up: добавить перенесённые фильмы в нужные разделы diff --git a/family/projects/watchlist-sync.md b/family/projects/watchlist-sync.md index 5fc99403..6e726401 100644 --- a/family/projects/watchlist-sync.md +++ b/family/projects/watchlist-sync.md @@ -1,7 +1,7 @@ --- created: '2026-05-18' -updated: '2026-05-18' -status: planning +updated: '2026-09-20' +status: active tags: - kraken - jellyfin @@ -16,6 +16,8 @@ tags: Репо: `~/Developer/watchlist-sync` → GitHub `mallexxx/watchlist-sync` +> ℹ️ **Эта заметка — исходный дизайн-документ проекта (Stage 1/2).** Актуальное состояние, рабочие пути, cron-статус и все найденные баги — в [[family/projects/watchlist-automation]]. Реализовано всё, кроме Stage 2 (рекомендации). Ниже сохранён дизайн; расхождения с реальностью помечены. + --- ## Архитектура @@ -113,8 +115,12 @@ WATCHLIST_PATH=/vault/family/documents/movies-watchlist.md | Job | Расписание | Скрипт | |-----|-----------|--------| -| `watchlist-nightly` | ежедн. 01:00 | `~/.hermes/scripts/watchlist-nightly.sh` | -| `watchlist-discover` | вс 09:00 | `~/.hermes/scripts/watchlist-discover.sh` | +| `watchlist-nightly` | ежедн. 01:00 | `~/scripts/watchlist-nightly.sh` | +| `watchlist-discover` | вс 09:00 | `~/scripts/watchlist-discover.sh` | + +⚠️ Фактический путь — `~/scripts/`, **не** `~/.hermes/scripts/` (проверено 2026-09-20). Мигрировали с Eagle на Кракен 2026-05-27. + +⚠️🔴 **Cron НЕ восстановлен после 2026-06-20** — pipeline запускается только вручную. См. [[family/projects/watchlist-automation]]. Используют `~/scripts/sync-vault.sh` для всех git-операций (stash/pull/pop/commit/push). **Не агентные** — скрипты вызывают CLI напрямую, никакого LLM в pipeline. @@ -208,28 +214,33 @@ WATCHLIST_PATH=/vault/family/documents/movies-watchlist.md ## Имплементация: очерёдность задач -### Фаза 1 (MVP) -- [ ] Инициализировать репо `~/Developer/watchlist-sync`, push на GitHub -- [ ] `parser.py` — парсинг .md, классификация строк -- [ ] `resolver.py` — TMDB `/search/multi` + KP fallback -- [ ] `sync_down.py` — add to Radarr/Sonarr (батч 5), пометить `⬇️` -- [ ] CLI: `watchlist-sync resolve --dry-run` и `--apply` -- [ ] CLI: `watchlist-sync sync-down --batch 5` +> Статусы проставлены 2026-09-20. Фазы 1-3 и cron реализованы и работают (проверено ручным прогоном `watchlist-nightly.sh`). Stage 2 (Фаза 4) — не начата. -### Фаза 2 -- [ ] `sync_up.py` — Jellyfin → `[x]` + сортировка -- [ ] CLI: `watchlist-sync sync-up` -- [ ] Cron на Кракене: sync-up каждые 24ч +### Фаза 1 (MVP) — ✅ РЕАЛИЗОВАНО +- [x] Инициализировать репо `~/Developer/watchlist-sync`, push на GitHub +- [x] `parser.py` — парсинг .md, классификация строк +- [x] `resolver.py` — TMDB `/search/multi` + KP fallback +- [x] `sync_down.py` — add to Radarr/Sonarr (батч 5), пометить `⬇️` +- [x] CLI: `watchlist-sync resolve --dry-run` и `--apply` +- [x] CLI: `watchlist-sync sync-down --batch 5` -### Фаза 3 -- [ ] AI disambiguation job (Hermes Kraken cron) -- [ ] Уточнение у Alex через Zulip для нерезолвленных +### Фаза 2 — ✅ РЕАЛИЗОВАНО +- [x] `sync_up.py` — Jellyfin → `[x]` + сортировка +- [x] CLI: `watchlist-sync sync-up` +- [x] Cron на Кракене: sync-up каждые 24ч — ⚠️ был, **умер 2026-06-20**, не восстановлен -### Фаза 4 (Stage 2) +### Фаза 3 — ✅ РЕАЛИЗОВАНО (кроме Zulip) +- [x] CLI `resolve --recheck` для повторного разбора `❓` +- [ ] AI disambiguation job (Hermes Kraken cron) — не делался, вместо него детерминированный `--recheck` +- [ ] Уточнение у Alex через Zulip для нерезолвленных — код есть (`_zulip_ping()` в `cli.py`), но `ZULIP_*` не в `.env` → молча скипается + +### Фаза 4 (Stage 2) — ❌ НЕ НАЧАТА - [ ] `recommend.py` — профиль + TMDB recommendations - [ ] Три curated списка с подтверждением - [ ] Еженедельный cron job +> Файловая структура в разделе «Структура проекта» — тоже дизайн: реально `radarr.py`/`sonarr.py` объединены в `arr.py`, добавлены `discover.py`, `sync_down.py`, `sync_up.py`, `jellyfin.py`, `parser.py`, `resolver.py`, `cli.py`. Точный список — в [[family/projects/watchlist-automation]]. + --- ## Ключи и секреты