Files
obsidian-vault/family/projects/watchlist-sync.md
T
2026-05-29 21:02:08 +00:00

11 KiB
Executable File
Raw Blame History

created, updated, status, tags
created updated status tags
2026-05-18 2026-05-18 planning
kraken
jellyfin
radarr
sonarr
watchlist
project

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)

Алгоритм:

  1. Для каждого - [ ] без ⬇️ и — запрос к TMDB /search/multi (русский язык)
  2. Если 0 результатов → fallback KP /v1.4/movie/search?query=...
  3. Однозначный результат (score > 0.8, рейтинг ≥ 6.0, один кандидат) → определить тип (movie/tv), взять ID
  4. Ambiguous (несколько кандидатов с близким score, или score < 0.8) → добавить перед названием, пропустить
  5. Не найдено → добавить , пропустить

Пороги:

  • 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)

Алгоритм:

  1. Jellyfin API: GET /Users/{userId}/Items?IsPlayed=true&IncludeItemTypes=Movie,Series
  2. Для каждого просмотренного — fuzzy-match по названию с ⬇️ строками в .md
  3. При совпадении (score > 0.85):
    • Заменить - [ ] ⬇️ Название- [x] Название
    • Переместить строку на первое место среди [x] записей (под последней [ ])
  4. Обновить .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 часов

Промпт агенту на Кракене:

  1. Прочитать .md, найти все строки
  2. Для каждой — запросить TMDB/KP с расширенными вариантами запроса (LLM генерирует варианты: оригинальное название, транслит, английский перевод)
  3. Если нашёл с уверенностью → убрать , поставить ⬇️
  4. Если нет → собрать список всех нерезолвленных и спросить 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 fallback
  • sync_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/

Связанные проекты