15 KiB
Watchlist — автопоиск TMDB + маркер «нет в базе» (без ручных правок)
Дата: 2026-09-22 Статус: ПЛАН УТВЕРЖДЁН Alex — в работе Связано: personal/plans/lampac-watchlist-plugin, family/projects/watchlist-automation, family/projects/watchlist-sync
Требование Alex (дословно)
«Нет блядь, нахуя мне в плеере заглушки?! Нормальный автопоиск! Никаких БЛЯДЬ ручных правок! То чего нет для просмотра - того нет БЛЯДЬ в списке! И ставится вопрос. Все остальное из watchlist должно оказаться в списке!!»
«В смысле блядь прописал соответствия вручную?! Че за нахуй?! Убирай нахуй! Это блядь скрипт должен делать!»
«Скрипт должен поставить вопрос в списке напротив такого (кроме тех что ещё не вышли, там календарь стоит)»
Инварианты (нарушение = провал задачи)
- НИКАКИХ ручных словарей-соответствий в коде. Убрано 2026-09-22 (
MANUAL_TMDB). - НИКАКИХ заглушек в плеере. Позиция без TMDB не рисуется в каталоге вообще.
- Всё, что реально существует и смотрибельно, — ОБЯЗАНО попасть в каталог. Автопоиск обязан дотягивать то, что не находится по RU-названию.
- Того, чего нет для просмотра, нет в списке. Вместо позиции ставится
❓. - Исключение — upcoming. У вышедших позже маркер
🗓️(календарь) не трогать.
Разграничение по маркерам — ключевое
| Маркер | Значение | Действие |
|---|---|---|
🗓️ |
фильм ещё не вышел | НЕ трогать. Календарь = ждём. В каталоге не нужен |
❓ |
есть/должен быть, но не нашли в базах | оставить, ждёт правки названия пользователем |
| без маркера | вышел, должен смотреть | обязан попасть в каталог |
Сейчас 🗓️ ставится в discover для upcoming. Позиции с 🗓️ не должны получать ❓.
Что установлено фактами (2026-09-22)
Проблема 1: генератор не умеет дотягивать не-RU названия
gen_watchlist_v2.py::tmdb_search() ищет только title как есть, по ru-RU и en-US,
типы movie/tv. Если в списке русское название, которого TMDB не знает (премьеры
2025–2026, локальные алиасы) — возвращает None → нет tmdb_id → нет постера → нет карточки.
Готовая механика уже есть в watchlist_sync/resolver.py (строки 186–219) —
cross-reference через Кинопоиск:
# 1. TMDB по названию как есть
candidates = _search_tmdb(search_title, tmdb_key)
# 2. если пусто / скор низкий — идём в Кинопоиск
if not candidates or candidates[0].score < AMBIG_SCORE_THRESHOLD:
kp = _search_kp(search_title, kp_key) # KP /movie/search
# 3. берём alternativeName (EN) из KP-документа
for doc in kp_docs[:1]:
alt_en = doc.get("alternativeName") or ""
kp_year = doc.get("year")
if alt_en and alt_en != search_title:
tmdb_via_en = _search_tmdb(alt_en, tmdb_key) # ← ЭТО КЛЮЧ
if tmdb_via_en and tmdb_via_en[0].score > 0.5:
best = tmdb_via_en[0]
# 4. ПРОВЕРКА ГОДА (±1) — защита от ложных совпадений
if kp_year and best.year and abs(best.year - kp_year) > 1:
pass # год не сошёлся — пропускаем
else:
candidates = [best] + candidates
KP API: https://api.poiskkino.dev/v1.4/movie/search, ключ KP_KEY из .env.
⚠️ НЕ api.kinopoisk.dev — отдаёт 301. Нужен follow_redirects=True.
Проблема 2: discover не проверяет наличие в TMDB
discover.py фильтрует новинки только по рейтингу и голосам
(IMDb ≥ 7.0 / KP ≥ 8.5 для РФ; голосов ≥ 1000 / ≥ 500). Проверки «есть ли фильм
в TMDB» нет. Поэтому в ## Новинки попадают позиции, которых TMDB не знает.
Проблема 3: почему маркер не срабатывал
🗓️ ставится при добавлении для upcoming. resolve пропускает позиции
с ❓/🗓️ (флаг ambiguous в WatchItem, parser.py). Итог: маркер есть, но он
про «не вышел», а не про «нет в TMDB» — повторной проверки никогда не происходит.
Текущие цифры (задеплоено 2026-09-22)
| Категория | Позиций | Постеров | TMDB-ID |
|---|---|---|---|
| films | 360 | 310 | 314 |
| new | 33 | 31 | 31 |
| cartoons | 3 | 3 | 3 |
| anime | 7 | 7 | 7 |
| итого | 403 | 351 | 355 |
Без TMDB-ID: 48 позиций (в т.ч. 4 подтверждённо отсутствующих в TMDB).
🔴 Регресс от откатa ручных правок
При откате MANUAL_TMDB постеры у Хамнета, WeWork, Астрала. Могильный
ритуал 2 вернутся к «НЕТ». Это НЕ потеря — это корректное состояние до того, как
заработает автопоиск (Шаг 1). Автопоиск обязан их найти сам:
| Фильм | TMDB-цель | Как находится |
|---|---|---|
| Хамнет | 858024 «Гамнет» (2025) |
KP → alternativeName = "Hamnet" → TMDB |
| WeWork | 795622 (2021) |
KP → EN-алиас → TMDB |
| Астрал. Могильный ритуал 2 | 1291595 (2026) |
KP → EN-алиас → TMDB |
Если Шаг 1 не найдёт их сам — задача НЕ выполнена.
ПЛАН РАБОТ
Шаг 1 — автопоиск через Кинопоиск в генераторе каталога
Файл: /Users/admin/lampac-tmp/gen_watchlist_v2.py
1.1. Перенести cross-reference из резолвера. Новая функция:
def tmdb_via_kp(title, year, tmdb_key, kp_key, cache):
"""RU-название -> Кинопоиск -> alternativeName (EN) -> TMDB.
Возвращает (tmdb_id, media_type, poster_path, kp_id) или None.
"""
# 1. KP search по русскому названию
docs = kp_search(title, kp_key) # /movie/search, limit 5
if not docs:
return None
# 2. перебираем документы, ищем попарное совпадение названия
for doc in docs[:3]:
alt_en = doc.get("alternativeName") or ""
kp_year = doc.get("year")
kp_id = doc.get("id")
for q in (alt_en, doc.get("name") or ""):
if not q or q == title:
continue
res = tmdb_search(q, kp_year or year, tmdb_key, cache)
if not res:
continue
tid, mtype, pp = res
# 3. ПРОВЕРКА ГОДА (±1) — обязательно, иначе ложные совпадения
det = tmdb_details(tid, mtype, tmdb_key, details_cache)
ty = (det or {}).get("year") or None
if kp_year and ty and abs(int(ty) - int(kp_year)) > 1:
continue # год не сошёлся
return (tid, mtype, pp, kp_id)
return None
1.2. Подключить в enrich() как шаг 2b (после TMDB-поиска, перед miss):
1) Radarr по названию
2) TMDB поиск (как сейчас)
2b) Кинопоиск cross-reference ← НОВОЕ
3) если всё пусто -> miss
1.3. Маркер ❓ для тех, кого не нашли. После прогона — записать обратно
в movies-watchlist.md:
для каждой позиции:
tmdb_id найден -> ничего не менять
иначе если маркер 🗓️ -> НЕ ТРОГАТЬ (upcoming = ждём)
иначе если маркер ❓ уже есть -> не дублировать
иначе -> поставить ❓ перед названием
Формат строки (как в файле уже принято):
- [ ] ❓ Название фильма
Питфоллы 1.3:
🗓️— явное исключение, не трогать (требование Alex);- не трогать
[x](просмотренные); - не дублировать существующий
❓; - backup
.mdперед записью, проверка размера после; - писать атомарно — файл в git, может быть открыт в Obsidian.
Шаг 2 — генератор НЕ отдаёт в каталог позиции без TMDB
Файл: gen_watchlist_v2.py, сборка JSON.
Сейчас позиция без tmdb_id уходит в JSON с href = поиск Кинопоиска → в плеере
клик даёт «здесь пусто» (заглушка, которую Alex запретил).
Новое: позиция без tmdb_id не попадает в JSON каталога вообще.
Она остаётся в .md с ❓ — Alex видит её в списке и поправит название.
Питфолл: счётчик count в JSON и stats в выводе должны отражать реальное число,
а не исходное. Проверять: films уменьшится на число нерезолвленных.
Шаг 3 — discover не добавляет то, чего нет в TMDB
Файл: /Users/admin/Developer/watchlist-sync/watchlist_sync/discover.py
Добавить проверку после KP-фильтра: TMDB-поиск (с cross-reference через
alternativeName) → если не найдено и фильм вышел — не добавлять в список.
Для upcoming (🗓️) — оставить как есть (они и так не резолвятся, календарь
информативен).
Питфолл: не ломать существующую дедупликацию _all_watchlist_titles().
Порядок работ
| # | Шаг | Файл | Проверка |
|---|---|---|---|
| 1.1 | tmdb_via_kp() — cross-reference |
gen_watchlist_v2.py |
dry-run на 4 проблемных |
| 1.2 | подключить в enrich |
gen_watchlist_v2.py |
постеры 351 → ? |
| 1.3 | маркер ❓ в .md |
gen_watchlist_v2.py |
🗓️ не тронуты |
| 2 | не отдавать в JSON без TMDB | gen_watchlist_v2.py |
«здесь пусто» исчезло |
| 3 | TMDB-фильтр в discover |
discover.py |
мусор не добавляется |
Приёмка (формулировка Alex):
- Хамнет, WeWork, Астрал 2 — постеры есть, найдены автопоиском, не словарём.
- В плеере нет ни одной позиции, дающей «здесь пусто».
- Всё остальное из watchlist — в каталоге.
- Позиции, которых нет в TMDB и которые НЕ upcoming — с
❓в списке. - Позиции с
🗓️— не тронуты.
Приёмка = видно в UI плеера, не «API отвечает».
Питфоллы, подтверждённые ранее (не повторять)
- TMDB
/search/multiсортирует по популярности, не по релевантности → короткие названия матчат нерелевантные фильмы. Отсюда обязательная проверка года (±1). ModInit.cs:285возвращает любоеJValueСТРОКОЙ → вcard_argsнуженformat: "long.TryParse(value, out long v) ? v : 0".tmdb_idДОЛЖЕН быть числом —plugin.jsделаетif(json.tmdb_id).- Кириллица в ключе каталога →
ModInit.goInitчистит[^a-z0-9\-]→ пустая строка → 400. Ключ обязан бытьplugin: watchlist. - Кэш
catalog:card:*в ПАМЯТИ переживаетdocker restart lampac— проверять на незакэшированномuri. - Кэш TMDB генератора:
~/.lampac_tmdb_cache.json—Noneв кэше означает «не нашли»; после добавления cross-reference старыеNoneнадо инвалидировать, иначе новые запросы не пойдут. ⚠️ Критично для Шага 1 — иначе автопоиск не сработает на уже закэшированных промахах. - macOS
tarкладёт AppleDouble._*→tar --no-mac-metadata. - Права
root:root 600→ dotnetPermission denied→ статика молча 404. Нужно1000:1000+644.
Артефакты
| Файл | Роль |
|---|---|
/Users/admin/lampac-tmp/gen_watchlist_v2.py |
генератор каталога |
/Users/admin/lampac-tmp/deploy_lampac.py |
заливка на Кра́кен |
/Users/admin/lampac-tmp/check_fixed2.py |
проверка постера по названию |
~/Developer/watchlist-sync/watchlist_sync/resolver.py |
ЭТАЛОН cross-reference |
~/Developer/watchlist-sync/watchlist_sync/discover.py |
фильтр новинок |
~/.lampac_tmdb_cache.json |
кэш поиска TMDB |
~/.lampac_tmdb_details.json |
кэш деталей карточки |
Ключи: TMDB_KEY, KP_KEY — из ~/Developer/watchlist-sync/.env (на Маке).
API: https://api.poiskkino.dev/v1.4/movie/search (с follow_redirects=True).