11 KiB
Executable File
created, updated, status, tags
| created | updated | status | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|
| 2026-05-18 | 2026-05-18 | planning |
|
Watchlist Sync
Цель: автоматическая загрузка непросмотренных фильмов/сериалов из
family/documents/movies-watchlist.mdчерез Radarr/Sonarr, синхронизация просмотренных обратно из Jellyfin, и AI-curated рекомендации на основе профиля.
Репо: ~/Developer/watchlist-sync → GitHub mallexxx/watchlist-sync
Архитектура
movies-watchlist.md (Obsidian vault)
│
▼
watchlist-sync (Python CLI)
├── parse — читает .md, классифицирует [ ] / [x] / ❓ / ⬇️
├── resolve — TMDB + KP API → находит ID, тип (movie/series), рейтинг
├── sync-down — непросмотренные → Radarr / Sonarr (батчами)
└── sync-up — Jellyfin watched history → [x] в .md + перемещение наверх
│
▼
Hermes Kraken cron job
├── каждые 6ч: resolve ❓ → LLM disambiguation → уточнение у Alex
└── каждые 24ч: sync-up из Jellyfin
Stage 1: Core Sync
1.1 Парсинг watchlist.md
Формат строк:
- [ ] Название— непросмотрено, не добавлено- [ ] ⬇️ Название— добавлено в очередь (скачивается/скачано)- [ ] ❓ Название— ambiguous, ждёт ручного уточнения- [x] Название— просмотрено
Источник файла на Кракене: /home/kraken/obsidian/family/documents/movies-watchlist.md
(vault смонтирован в Hermes-контейнере как /vault)
1.2 Резолв названий (TMDB + KP)
Алгоритм:
- Для каждого
- [ ]без⬇️и❓— запрос к TMDB/search/multi(русский язык) - Если 0 результатов → fallback KP
/v1.4/movie/search?query=... - Однозначный результат (score > 0.8, рейтинг ≥ 6.0, один кандидат) → определить тип (movie/tv), взять ID
- Ambiguous (несколько кандидатов с близким score, или score < 0.8) → добавить
❓перед названием, пропустить - Не найдено → добавить
❓, пропустить
Пороги:
min_rating: 6.0— ниже 6 игнорировать (защита от мусора)min_score: 0.75— сходство названия (Jaccard/fuzzy)max_candidates_for_auto: 1— при 2+ близких кандидатах → ambiguous
Тип контента:
- TMDB
media_type: movie→ Radarr - TMDB
media_type: tv→ Sonarr - KP
type: MOVIE/MINI_SERIES/TV_SERIES→ маппинг аналогично
1.3 Загрузка (sync-down)
Батчинг:
- Максимум 5 новых в одном прогоне (защита от flood)
- Приоритет: порядок в файле (выше = важнее)
- После добавления в Radarr/Sonarr → заменить
- [ ]на- [ ] ⬇️в .md
API:
- Radarr:
POST /api/v3/movieсaddOptions.searchForMovie: true - Sonarr:
POST /api/v3/seriesсaddOptions.searchForMissingEpisodes: true - Проверка дублей перед добавлением (GET by tmdbId/tvdbId)
Конфиг (.env / config.json, не в репо):
RADARR_URL=http://localhost:7878
RADARR_KEY=...
SONARR_URL=http://localhost:8989
SONARR_KEY=...
TMDB_KEY=...
KP_KEY=...
JELLYFIN_URL=http://localhost:8096
JELLYFIN_TOKEN=...
WATCHLIST_PATH=/vault/family/documents/movies-watchlist.md
1.4 Обратная синхронизация (sync-up)
Алгоритм:
- Jellyfin API:
GET /Users/{userId}/Items?IsPlayed=true&IncludeItemTypes=Movie,Series - Для каждого просмотренного — fuzzy-match по названию с
⬇️строками в .md - При совпадении (score > 0.85):
- Заменить
- [ ] ⬇️ Название→- [x] Название - Переместить строку на первое место среди
[x]записей (под последней[ ])
- Заменить
- Обновить
.md, git commit
Jellyfin userId: получать через GET /Users (admin user = alex)
1.5 Cron Jobs (Hermes Eagle)
Два детерминированных shell-script крона:
| Job | Расписание | Скрипт |
|---|---|---|
watchlist-nightly |
ежедн. 01:00 | ~/.hermes/scripts/watchlist-nightly.sh |
watchlist-discover |
вс 09:00 | ~/.hermes/scripts/watchlist-discover.sh |
Используют ~/scripts/sync-vault.sh для всех git-операций (stash/pull/pop/commit/push). Не агентные — скрипты вызывают CLI напрямую, никакого LLM в pipeline.
Cron: каждые 6 часов
Промпт агенту на Кракене:
- Прочитать
.md, найти все❓строки - Для каждой — запросить TMDB/KP с расширенными вариантами запроса (LLM генерирует варианты: оригинальное название, транслит, английский перевод)
- Если нашёл с уверенностью → убрать
❓, поставить⬇️ - Если нет → собрать список всех нерезолвленных и спросить Alex в Zulip
#personal::Kraken torrents:❓ Не смог распознать 3 фильма, уточни: • "Дворец полански" — возможно "The Palace" (Polanski, 2023)? [да/нет/другое] • "Space merchants" — фильм или книга Пола? Нашёл несколько. • "Ради нескольких строчек" — не нашёл. Уточни название.
Stage 2: Profile & Recommendations
После накопления истории просмотров (≥ 50 фильмов)
2.1 Анализ профиля
- Топ жанры по просмотренным
[x] - Любимые режиссёры / актёры (через TMDB credits)
- Средний рейтинг принятых фильмов
- Паттерны: когда смотрит (с женой / один / семья) — вывести из тегов/времени просмотра в Jellyfin
2.2 Curated Lists (AI job, еженедельно)
Три списка, только рейтинг ≥ 6.0, не в watchlist уже:
| Список | Критерии |
|---|---|
| 🎬 Вдвоём с Лизой | Драма, комедия, триллер. Без слишком тяжёлого контента. |
| 💀 Один (трэш/боевики) | Хоррор, боевик, sci-fi, cult. Без ограничений. |
| 👨👩👧 Семейное с ребёнком | Возраст 7+. Мультфильмы, приключения, комедии. |
Источники кандидатов:
- TMDB
/movie/recommendationsна основе топ-10 просмотренных - TMDB
/movie/similarдля любимых жанров - KP подборки (русское кино / советская классика)
Формат вывода — добавить в конец movies-watchlist.md:
## 🤖 Рекомендации (2026-W21)
### 🎬 Вдвоём
- [ ] Past Lives (2023) ★8.0 — драма о двух корейцах разлучённых в детстве
...
### 💀 Один
- [ ] Nosferatu (2024) ★7.2 — ремейк Эгgers'а
...
### 👨👩👧 Семья
- [ ] The Wild Robot (2024) ★8.1 — анимация
...
После подтверждения Alex — строки перемещаются в основной список.
Структура проекта
~/Developer/watchlist-sync/
├── watchlist_sync/
│ ├── __init__.py
│ ├── parser.py — парсинг .md
│ ├── resolver.py — TMDB + KP API
│ ├── radarr.py — Radarr API client
│ ├── sonarr.py — Sonarr API client
│ ├── jellyfin.py — Jellyfin API client
│ ├── sync_down.py — непросмотренные → arr
│ ├── sync_up.py — Jellyfin → .md
│ └── recommend.py — Stage 2 recommendations
├── config.example.json — шаблон конфига (без секретов)
├── .env.example
├── .gitignore — .env, config.json, *.key
├── pyproject.toml
└── README.md
Конфиг секреты: только в .env (не в репо). config.example.json — шаблон.
Имплементация: очерёдность задач
Фаза 1 (MVP)
- Инициализировать репо
~/Developer/watchlist-sync, push на GitHub parser.py— парсинг .md, классификация строкresolver.py— TMDB/search/multi+ KP fallbacksync_down.py— add to Radarr/Sonarr (батч 5), пометить⬇️- CLI:
watchlist-sync resolve --dry-runи--apply - CLI:
watchlist-sync sync-down --batch 5
Фаза 2
sync_up.py— Jellyfin →[x]+ сортировка- CLI:
watchlist-sync sync-up - Cron на Кракене: sync-up каждые 24ч
Фаза 3
- AI disambiguation job (Hermes Kraken cron)
- Уточнение у Alex через Zulip для нерезолвленных
Фаза 4 (Stage 2)
recommend.py— профиль + TMDB recommendations- Три curated списка с подтверждением
- Еженедельный cron job
Ключи и секреты
| Секрет | Где хранить |
|---|---|
| TMDB API key | .env на Кракене (из config.json media-pipeline) |
| KP API key | .env (тот же что в media-pipeline config.json) |
| Radarr key | .env |
| Sonarr key | .env |
| Jellyfin token | .env |
Ключи TMDB/KP уже есть в /srv/.../docker/media-pipeline/config.json — взять оттуда при деплое.
.gitignore:
.env
config.json
*.key
__pycache__/
.venv/
Связанные проекты
- family/projects/media-toolbox-kraken — *arr стек, Jellyfin, media-pipeline
- family/documents/movies-watchlist — источник данных