40 KiB
created, updated, status, tags
| created | updated | status | tags | ||||
|---|---|---|---|---|---|---|---|
| 2026-05-20 | 2026-09-20 | active |
|
Watchlist Automation — Полный Flow
Полная цепочка: как family/documents/movies-watchlist.md превращается в скачанный контент.
Полная цепочка
[вс 09:00] watchlist-discover ← KP API топ фильмов 2024-2025
↓ добавляет в секцию "Новинки 👍/👎"
movies-watchlist.md
Alex ставит 👍 / 👎
↓
[ежедн 09:00] watchlist-resolve ← SSH Кракен → resolve → commit
[ежедн 10:00] watchlist-sync-down
→ process-thumbs (перемещает 👍 → начало списка)
→ sync-down (добавляет батч в Radarr/Sonarr)
↓
Radarr (7878) / Sonarr (8989)
→ Prowlarr (9696) → RuTracker / Kinozal(M) / NoNaMe / etc.
→ Transmission (9091)
→ /media/movies-radarr или /media/series-sonarr
↓
router.py (Custom Script) → читает жанры из NFO/TMDb
→ symlink в /media/{movies,cartoons,documentaries,...}/
↓
Jellyfin (8096) → rescan → виден контент
Статусы в movies-watchlist.md
| Символ | Значение | Действие |
|---|---|---|
[ ] |
не смотрел, в очереди | sync-down добавляет в Radarr/Sonarr |
[x] |
просмотрено | sync-up из Jellyfin watched history |
⬇️ |
добавлено в Radarr/Sonarr | ждёт скачки |
👍 |
нравится (новинка) | process-thumbs переносит в начало → попадёт в sync-down |
👎 |
не хочу (новинка) | process-thumbs перемещает в ## Новинки👎 |
🗓️ |
upcoming (ещё не вышел) | discover помечает, не скачивается |
❓ |
не найдено / ambiguous | ждёт ручного уточнения |
🧟♀️ |
horror (genre_id=27) | информационный маркер |
⭐ |
приоритет к загрузке (U+2B50) | sync-down берёт в первую очередь (ПЛАН, не реализован) |
⚠️ ✭ (U+272D) — НЕ приоритет. Alex 2026-09-20 явно указал: звёздочкой-приоритетом считается только ⭐ (U+2B50). Символ ✭ используется в файле как обычная пометка «выделенное», приоритетом не является.
Также: ⭐ засчитывается только в строке фильма (- [ ] Название ⭐ …), НЕ в под-буллетах кандидатов, которые генерирует mark_ambiguous (там формат «Название (год) ★N.N»). [x] (просмотренные) с ⭐ игнорируются.
Структура секций movies-watchlist.md
## Новинки - 👍/👎 ← discover добавляет сюда
## Фильмы ← основной список, отступ 2 пробела ( - [ ])
## Мультфильмы
## Аниме
## Новинки👎 ← без пробела перед 👎
## Связанные заметки
Важно: process_thumbs автоматически определяет отступ из ## Фильмы и сохраняет при переносе. SECTION_THUMBDN = "## Новинки👎" (без пробела) — должно точно совпадать с заголовком.
🔴 Крон pipeline ОТСУТСТВУЕТ (аудит 2026-09-20) — но баг с мусором УЖЕ ИСПРАВЛЕН
Автодобавление фильмов НЕ работает по расписанию. Скрипты живы, но их никто не запускает.
Доказательства (проверено 2026-09-20):
crontab -lюзераkraken— watchlist-строк НЕТ. Есть толькоsync-vault.sh(каждый час) и дваpgs-to-srt(часовой + 03:00).- root crontab и
/etc/cron.d/:grep -rl watchlist→ пусто. - systemd timers (user + system) → 0 штук.
- Внутри Hermes-контейнера на Кракене:
docker exec hermes-kraken /opt/hermes/.venv/bin/hermes cron list→ "No scheduled jobs." - Последний коммит автором
Hermes(= nightly-скрипт) — 2026-06-20. Распределение по месяцам: май 1, июнь 9, июль/август/сентябрь — 0. Единственные коммиты в vault после июня — отeagle,syncthing,taiga.
Скрипты существуют и исполняемы, но не в кроне:
~/scripts/watchlist-nightly.sh mtime 2026-05-29
~/scripts/watchlist-discover.sh mtime 2026-05-29
⚠️ Изначальная гипотеза «файл засран от многомесячного простоя» — НЕВЕРНА. Дубли создавались на каждом прогоне
resolveиз-за бага вmark_ambiguous()(см. ниже, исправлен). 33 строки у «Nightmare Alley» = 11 прогонов. Простой лишь позволил мусору накопиться незамеченным. Мусор вычищен, баг закрыт.
✅ Баг дублирования кандидатов в mark_ambiguous() — НАЙДЕН И ИСПРАВЛЕН (2026-09-20)
Симптом
Под каждой ❓-строкой копились одинаковые дампы кандидатов. Рост ровно +100 байт за прогон (3 строки кандидатов) — подтверждено экспериментом: 26430 → 26530 → 26630 → 26730 байт.
Корень (точная механика)
В watchlist_sync/parser.py, функция mark_ambiguous():
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 строки оставались ниже — реплейс не знал о них. Итог: каждый прогон добавляет ещё один блок кандидатов поверх существующих.
Важно: ❓-строки НЕ дублировались (каждая — одна). Дублировались под-буллеты под ними.
Фикс
Паттерн теперь захватывает весь блок: строку-маркер ПЛЮС её под-буллеты, и перезаписывает целиком.
# 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<subs>' + _SUB_BULLETS + r'*)',
re.MULTILINE
)
⚠️ Две НЕУДАЧНЫЕ версии паттерна (не повторять)
- v1 —
(?:\n[ \t]+-[^\n]*\n?)*:\n?опционален, поэтому*перешагивал на следующую строку без разделителя и проглатывал соседние фильмы. Потеряны❓ The kill room,The burial,Past lives. Откачено. - v2 — то же плюс негативный lookahead: не помогло,
[^\n]*из предыдущей итерации уже захватил хвост следующей строки. - 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 alley33→3,Stand by me30→3,Фарго30→3,Left behind21→4,Крестный отец12→3,Godfathers12→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 (проверено отдельным скриптом). Секции целы: Новинки / Фильмы / Мультфильмы / Аниме / Новинки👎 / Связанные заметки.
🔴 БАГ: sync-down игнорирует порядок файла — сортирует по рейтингу (найден 2026-09-20)
Симптом (сообщил Alex): в батч на закачку попали фильмы, которые «далеко не в начале списка» — Гладиатор, Американская история X, Я ругаюсь, Мастерская Сатаны.
Корень: watchlist_sync/sync_down.py:41:
resolved.sort(key=lambda x: x[1].rating or 0, reverse=True)
Код полностью игнорирует позицию строки в файле и берёт топ-5 по рейтингу TMDB/KP из всех неотмеченных [ ]-строк. Порядок списка не имеет значения вообще.
Расхождение с докой: в watchlist-sync §1.3 заявлено «Приоритет: порядок в файле (выше = важнее)» — это не соответствует коду. Реальный приоритет = рейтинг.
Вторая половина причины: в том же прогоне process-thumbs выполняется до sync-down и переносит 👍-позиции из ## Новинки в начало основного списка. Среди перенесённых оказались Мастерская Сатаны (8.3) и Я ругаюсь (8.2) — с высокими рейтингами, поэтому они сразу выиграли отбор.
Как считался батч 2026-09-20 (5 слотов):
| Слот | Фильм | Рейтинг | Итог |
|---|---|---|---|
| 1 | Крик / Scream | 10.0 | уже в Radarr → помечен ⬇️ |
| 2 | Волшебный участок | 9.0 | 400 «already added» в Sonarr |
| 3 | Гладиатор | 8.225 | добавлен |
| 4 | Я ругаюсь | 8.229 | добавлен |
| 5 | Мастерская Сатаны | 8.3 | добавлен |
| — | Американская история X | ~8.2 | добавлен (слот освободился после первых двух) |
Saving Private Ryan (8.223) и Иди и смотри (8.214) проиграли Гладиатору (8.225) на тысячные доли.
❌ В sync_down ОТСУТСТВУЕТ фильтр по рейтингу
Проверено grep -rn "6\.0\|min_rating" watchlist_sync/*.py:
cli.py:92—< 6.0только для сброса кэша при--recheck, не отборdiscover.py:105,203— фильтр только для шага discoverresolver.py:264—0.5это сравнение рейтингов при ничьей, не порог
В sync_down порога нет вообще. Порог min_rating: 6.0 из доки watchlist-sync §1.2 в коде не реализован. Итог: в очередь могут попасть фильмы с рейтингом 0.0-5.9.
Замер 2026-09-20: из 251 resolved-кандидатов 18 имеют рейтинг < 6.0 — например Шестой элемент 4.3, Мегалополис 5.2, Бешеная любовь 3.1, Ворон 2024 5.8.
⚠️ Отдельный случай: rating == 0.0 означает «TMDB не дал оценку», а НЕ «плохой фильм». Таких двое: Уроки благи, Годзилла и конг. Наивный фильтр < 6.0 их отсечёт и потеряет нормальное кино.
✅ РЕАЛИЗОВАНО (2026-09-20): sync_down следует порядку файла + фильтр MIN_RATING
План из «📋 ПЛАН» выполнен. Alex подтвердил: порядок файла = приоритет, рейтинг = отбор, нулевые рейтинги (0.0) отсекать вместе с остальными ниже порога.
Коммиты в репо watchlist-sync (оба сделаны в этой сессии):
ada47da feat(sync-down): follow file order, filter by MIN_RATING
2dab180 fix(parser): mark_ambiguous replaces candidate block instead of prepending
Предыдущий HEAD до сессии — 20ca511. Рабочее дерево чистое.
Правка 1 — убран resolved.sort() (был на строке 41). resolved заполняется циклом по parse(), то есть остаётся в порядке файла сверху вниз.
Правка 2 — добавлена константа и фильтр в начало sync_down.py:
# Minimum TMDB/KP rating for a title to be queued. 0.0 means "no rating in
# the database" -- per Alex it is discarded together with everything below
# the threshold, so unrated items never reach Radarr/Sonarr.
MIN_RATING = 6.0
if result.status == "ok":
if (result.candidate.rating or 0) < MIN_RATING:
skipped_low += 1
print(f" [−] {item.title!r} — rating "
f"{result.candidate.rating or 0:.1f} < {MIN_RATING}, skipped")
continue
resolved.append((item, result.candidate))
else:
deferred.append((item, result))
Плюс итоговая строка (N item(s) below rating 6.0 skipped) — чтобы отсев был виден в логе, а не молчал.
process_thumbs сознательно НЕ тронут — 👍 по-прежнему уезжают в верх основного списка, то есть свежие новинки намеренно идут первыми (выбор Alex, «вариант 1»).
Dry-run после правки (проверено):
[−] ... 18 строк «skipped» (все < 6.0, включая два нуля: Уроки благи 0.0, Годзилла и конг 0.0)
(18 item(s) below rating 6.0 skipped)
[→] 'Под огнём' (2025) ★7.1 [dry-run]
[→] 'Секретный агент' (2025) ★7.3 [dry-run]
[→] 'Охота на воров 2: Пантера' (2025) ★6.6 [dry-run]
[→] 'Гадкая сестра' (2025) ★7.3 [dry-run]
[→] 'Семья в аренду' (2025) ★7.8 [dry-run]
Queued 5/5 items [dry-run]
Порядок ровно как в файле, с самого верха ## Новинки. Рейтинг больше не влияет на очерёдность — только на отбор.
⚠️ Реальный прогон (bash ~/scripts/watchlist-nightly.sh) после этой правки был ПРЕРВАН — не отработал. Vault проверен: рабочее дерево чистое, movies-watchlist.md остался на коммите d7ce0a9 (результат первого прогона), частичных изменений нет.
⭐ Приоритет по звёздочке ⭐ (U+2B50) — ПЛАН, НЕ реализован
Требование Alex (2026-09-20, после реализации «порядка файла»): фильмы, отмеченные звёздочкой ⭐, должны идти в первую очередь к загрузке — приоритет поверх порядка файла.
Уточнения Alex:
- Только символ
⭐(U+2B50). Символ✭(U+272D) приоритетом НЕ является. - Только те
⭐, что стоят в названии фильма в списке, а не в под-буллетах кандидатов («Название (год) ★N.N»), которые генерирует скрипт. [x](просмотренные) — пропускать.
Аудит файла на 2026-09-20:
| Символ | Где | Значение |
|---|---|---|
⭐ U+2B50 |
строка 79: - [ ] Простите за беспокойство ⭐ 2018 |
единственный приоритетный кандидат |
✭ U+272D |
⬇️ Наш флаг означает смерть ✭, ⬇️ Битва за битвой ✭ |
не приоритет (уже в очереди) |
✭ U+272D |
[x] Бруталист ✭, [x] Анора ✭ |
не приоритет (просмотрены) |
Проверено: parser._clean() НЕ срезает ⭐ (срезает только ✭★☆✩✪✫✬✮✯✰) — символ остаётся и в item.raw, и в item.title. Значит проверку можно делать по item.raw. Элемент имеет status=UNWATCHED, queued=False, ambiguous=False → уже попадает в candidates_raw.
Согласованный подход (двухпроходная выборка):
starred = [i for i in candidates_raw if "\u2b50" in i.raw]
rest = [i for i in candidates_raw if "\u2b50" not in i.raw]
for item in starred + rest: # приоритетный проход первым
...
Открытые вопросы (заданы Alex, ответа ещё НЕТ):
- Фильтр рейтинга vs ⭐ — если
⭐-фильм имеет рейтинг < 6.0, качать всё равно или отсеять? Рекомендация: отсеять (⭐ меняет очерёдность, не отменяет фильтр качества). Сейчас конфликта нет —Простите за беспокойствоимеет 6.8. - Чистить
⭐из поискового запроса перед TMDB? Сейчас не чистится и работает (TMDB находит), но это случайность. Рекомендация: добавить⭐в класс срезаемых символов_clean().
Ответы нужны до начала правки. Правка будет отдельным коммитом поверх ada47da.
📦 Результат закачки по итогам прогона 2026-09-20
sync-down добавил 4 фильма (Scream уже был → ⬇️). Проверено через Radarr API + Transmission:
| Фильм | Взятый релиз | Статус |
|---|---|---|
| Gladiator (2000) | UHD BDRip [H.265/2160p] [4K] |
качается |
| American History X (1998) | BDRip [AVC] |
качается |
| I Swear (2025) / Я ругаюсь | BDRip [H.264/1080p] [DVO] |
качается |
| Oficina do Diabo (2025) / Мастерская Сатаны | — | релиз не найден, ждёт RSS |
✅ Профиль качества работает как задумано: Gladiator взят в 4K, но BDRip, не Remux — профиль 7 WEB-4K (no remux) фильтрует корректно.
Застрявшие в очереди Radarr (со статусом warning, к этому прогону не относятся): Brokeback Mountain, Мортал Комбат 2, Sorry to Bother You, Город бога, День хомячка. Отдельная задача — разобрать.
🟡 Побочная находка: 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применён, проверен 3 прогонами, ЗАКОММИЧЕН —2dab180. - ✅ Фикс
sync_down.py(порядок файла +MIN_RATING = 6.0) ЗАКОММИЧЕН —ada47da. Dry-run подтверждён (18 отсеяно, 5 в батче по порядку файла). - ✅ Файл watchlist вычищен (168 дублей убрано) и закоммичен в vault.
- ✅ Ручной прогон
watchlist-nightly.shпрошёл чисто, 4 фильма ушли в закачку (3 качаются, 1 ждёт релиз). - 🔴 Реальный прогон ПОСЛЕ правки
sync_downбыл прерван — не выполнен. Новый отбор (порядок файла + фильтр) пока проверен только в dry-run. - 🔴 Крон по-прежнему отсутствует — pipeline живёт только вручную.
- ⏸ Ожидает решения Alex: приоритет
⭐(U+2B50) и два связанных вопроса (см. секцию «⭐ Приоритет по звёздочке»). - ℹ️ Доки watchlist-automation и watchlist-sync были обновлены агентом Eagle в этой же сессии (коммиты
bd743da,abee5a8в vault) — при дальнейших правках свериться, чтобы не затереть.
Бэкапы, созданные в этой сессии:
~/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 (md5 0f597606fa14f357ff3b25547bd18ea6)
/srv/.../watchlist-sync/watchlist_sync/sync_down.py.bak-20260920-194447
⚠️ .bak-* добавлены в .gitignore репо watchlist-sync, чтобы не попадать в коммиты.
Скрипты диагностики/фикса (на Кракене, /tmp/): wl_dedupe.py, wl_verify_final.py, wl_postcheck.py, wl_order2.py, wl_grab_check.py, wl_fix_parser3.py, wl_fix_syncdown.py, wl_star.py, wl_star_check.py. wl_dedupe.py пригоден для повторного использования при накоплении дублей; wl_postcheck.py — для проверки дублей после прогона.
Пути к конфигам *arr (проверено 2026-09-20, в доке ранее были ошибочны)
Конфиги Radarr/Sonarr/Transmission лежат на втором диске (6194539b-03e0-4f33-986b-4da09834e82f), не на том же, что медиа:
Radarr API key: /srv/dev-disk-by-uuid-6194539b-.../docker/radarr/config/config.xml
Sonarr: /srv/dev-disk-by-uuid-6194539b-.../docker/sonarr/config/config.xml
Transmission: /srv/dev-disk-by-uuid-6194539b-.../docker/transmission/settings.json
Radarr API base: http://localhost:7878/radarr/api/v3/ (ключ из config.xml)
Sonarr API base: http://localhost:8989/sonarr/api/v3/
Проверка закачки: queue в Radarr показывает grabbed/downloading; history даёт точное имя взятого релиза. Transmission — docker exec transmission transmission-remote --list (RPC-юзер в settings.json пустой).
Cron Jobs (на Кракене с 2026-05-27)
Весь watchlist pipeline работает на Кракене (мигрировал с Eagle 2026-05-27).
| Job | Расписание | Скрипт |
|---|---|---|
watchlist-nightly |
ежедн. 01:00 | ~/scripts/watchlist-nightly.sh |
watchlist-discover |
вс 09:00 | ~/scripts/watchlist-discover.sh |
⚠️ Фактический путь — ~/scripts/, не ~/.hermes/scripts/ (в доке ранее было ошибочно). Оба скрипта вызывают CLI напрямую через uv run --project $WS, без LLM. uv лежит в ~/.local/bin/uv.
WS в обоих скриптах: /srv/dev-disk-by-uuid-49e8f586-3839-4c5d-a1e1-58bfc3579ade/watchlist-sync — другой UUID, чем фактический корень репо (см. ниже). Возможный источник поломки: проверить, что путь резолвится.
watchlist-nightly.sh (на Кракене):
sync-vault.sh(pull) →process-thumbs --apply→sync-vault.sh(push)resolve --recheck --apply→sync-down --apply --batch 5→sync-up --apply→sync-vault.sh(push)
Пути на Кракене (HDD UUID 49e8f586-3839-4c5d-a1e1-58bfc3579ade):
/home/kraken/obsidian→ symlink на/srv/dev-disk-by-uuid-.../obsidian/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 вручную (рабочая команда)
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 — Фильтрация
| Тип | Порог |
|---|---|
| Мировые | IMDb ≥ 7.0 или KP ≥ 7.0 |
| Российские | KP ≥ 8.5 (защита от накрутки) |
| Upcoming | год ≥ текущий−1 |
| Минимум голосов | IMDb ≥ 1000 или KP ≥ 500 |
KP API endpoint: https://api.poiskkino.dev/v1.4/movie (redirect с api.kinopoisk.dev).
*arr Stack — Индексеры (Prowlarr)
| ID | Имя | Тип | Примечание |
|---|---|---|---|
| 1 | RuTracker | semi-private | блокирует при rate limit на сутки |
| 2 | RuTor | public | поиск по imdbid |
| 3 | Byrutor | public | |
| 4 | NoNaMe Club | semi-private | анонимный |
| 5 | Kinozal | semi-private | резерв |
| 6 | Kinozal (M) | semi-private | магнет ✅ — без grab limit, предпочтительный |
Pi-hole питфолл: блокирует skyhook.sonarr.tv → Sonarr/Radarr поиск молча не запускается. Фикс: dns: [8.8.8.8] в docker-compose.yml.
Router Script — Маршрутизация по жанру
После импорта Sonarr/Radarr: router.py --apply создаёт symlinks:
- Animation (не R/18+) →
/media/cartoons/или/media/cartoons-series/ - Documentary →
/media/documentaries/или/media/documentaries-series/ - Прочее →
/media/movies/или/media/series/
Копирует NFO + постер рядом с symlink (Jellyfin не видит метадату через symlink напрямую).
watchlist-sync — Python CLI
Репо: ~/Developer/watchlist-sync (GitHub: mallexxx/watchlist-sync).
watchlist-sync resolve — TMDB + KP API → ID, тип (movie/tv)
watchlist-sync sync-down — добавить батч (max 5) в Radarr/Sonarr, поставить ⬇️
watchlist-sync sync-up — Jellyfin watched → [x] в .md
Резолвер: TMDB /search/multi + KP fallback. Пороги: min_rating: 6.0, min_score: 0.75. При 2+ кандидатах → ❓ (LLM разрешает в cron каждые 6ч, нерешённые → уточнение в Zulip).
Stage 2 (после ≥50 просмотров): AI-curated recommendations — три списка (с женой / один / семья) на основе TMDB профиля, добавляются в конец watchlist как ## 🤖 Рекомендации (YYYY-WNN).
Jellyfin library mapping: Movies → movies, Cartoons → mixed (movies + episodes).
Питфоллы
KP Markdown Link Parsing Bug
Фильмы из Kinopoisk с синтаксисом [Title](https://kinopoisk.ru/film/12345/) (2025) передавались как есть в TMDB search → не находились → помечались ❓ навсегда (not i.ambiguous пропускает при следующем запуске). Фикс: commit 2db1ed9 — извлекать plain title из markdown link до передачи в TMDB. Ошибочно помеченные ❓ требуют ручного --recheck.
KP API Endpoint
api.kinopoisk.dev редиректит на api.poiskkino.dev/v1.4/movie. Использовать destination redirect напрямую.
🔴 Жёстко зашитый qualityProfileId в arr.py (корень жирных remux) — РЕШЕНО
Найдено 2026-09-04. В watchlist_sync/arr.py изначально был захардкожен qualityProfileId: 1 и для Radarr, и для Sonarr:
# radarr_add (~стр. 77) / sonarr_add (~стр. 112)
"qualityProfileId": 1,
Профиль 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] Фикс
sync_down.pyРЕАЛИЗОВАН — коммитada47da. Убранresolved.sort(), добавленMIN_RATING = 6.0с видимымprint. Нулевые рейтинги отсекаются (решение Alex: «отсекать»). - ⏸ [2026-09-20] Приоритет
⭐(U+2B50) вsync_downНЕ реализован — ждёт ответа Alex на два вопроса (фильтр рейтинга vs ⭐; чистить ли⭐из поискового запроса). Детали в секции «⭐ Приоритет по звёздочке» выше. Только⭐(U+2B50),✭(U+272D) не считается;[x]пропускать; только строки фильмов, не под-буллеты кандидатов. - 🔴 [2026-09-20] Догнать реальный прогон — после правки
sync_downполныйwatchlist-nightly.shни разу не отработал (был прерван). Прогнать и убедиться, что батч =Под огнём,Секретный агент,Охота на воров 2,Гадкая сестра,Семья в аренду(dry-run это предсказал). - 🟡 [2026-09-20]
sync-downберёт всего 5 за прогон — при 254 кандидатах разбор очереди займёт ~50 ночей. Рассмотреть увеличение--batch(сейчас--batch 5вwatchlist-nightly.sh). - 🔴 [2026-09-20] Восстановить cron для watchlist pipeline — pipeline мёртв с 20 июня. Скрипты готовы и проверены вручную (прогон 2026-09-20 прошёл чисто), нужно вернуть в crontab юзера
kraken:⚠️ В arr-stack-kraken эти строки в секции «Cron'ы на Kraken» указаны как действующие, хотя фактически их НЕТ — док в той части расходится с реальностью.0 1 * * * bash ~/scripts/watchlist-nightly.sh 0 9 * * 0 bash ~/scripts/watchlist-discover.sh - 🟡 [2026-09-20] Разобрать 5 застрявших закачек в очереди Radarr со статусом
warning:Brokeback Mountain,Мортал Комбат 2,Sorry to Bother You,Город бога,День хомячка. Висят давно, релиз не находится/не качается. - 🟡 [2026-09-20] Настроить Zulip-пинг —
ZULIP_URL/ZULIP_API_KEY/ZULIP_BOT_EMAILотсутствуют в.envwatchlist-sync, 59 нерезолвленных items ушли в никуда без уведомления. - 🟡 [2026-09-20] Добавить pre-check дублей в
sonarr_add()— сейчас при уже существующей серии прилетает400 SeriesExistsValidatorи шумит в логе. Вradarr_add()проверка есть. - 🟡 [2026-09-20] Синхронизировать документацию с кодом — watchlist-sync §1.2 и §1.3 описывают порог
min_rating: 6.0и «приоритет = порядок в файле», которых в коде не было. Порог теперь реализован (MIN_RATING), приоритет по порядку тоже — обновить формулировки в watchlist-sync. - 🔴 [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: добавить перенесённые фильмы в нужные разделы
Связанные заметки
- watchlist-discover — шаг discover: KP API, format, CLI команды
- watchlist-sync — основной проект (resolve, sync-down, sync-up)
- arr-stack-kraken — детальные питфоллы *arr стека
- jellyfin-transcode-rpi5 — Jellyfin PGS/ASS питфоллы