Files
obsidian-vault/personal/plans/lampac-watchlist-plugin.md
T

22 KiB
Raw Blame History

created, namespace, related, status, tags, title, type, updated
created namespace related status tags title type updated
2026-09-22 personal
personal/plans/lampac-kraken
personal/tech/lampac-deployment
family/projects/watchlist-sync
family/projects/watchlist-automation
family/documents/movies-watchlist
active
lampac
lampa
watchlist
catalog
webos
plugin
kraken
Watchlist в UI Lampa — каталог из списка plan 2026-09-22

Watchlist в UI Lampa — каталог из списка

Реализовано 2026-09-22. План был переписан после разведки кода модуля Catalog. Базовое развёртывание — personal/plans/lampac-kraken (шаги 1–6 закрыты).

Итог одной строкой: Lampac отдаёт movies-watchlist.md как каталог Lampa через модуль Catalog → 4 категории (360/33/3/7 позиций), автообновление кроном 02:30. Работает на стороне сервера; на ТВ остаётся добавить плагин http://192.168.1.14:9118/catalog.js и выбрать источник (см. «Что осталось на Alex»).

Задача

Показывать список фильмов Alex (family/documents/movies-watchlist.md) в UI Lampa на ТВ — каталогом. Не локальный плейбэк. Формулировка Alex: «отображение фильмов из списка».

⚠️ Что НЕ спрашивать повторно. Задача — отображение, не воспроизведение. Список — это перечень, а не источник видео. Вопрос «а что играть по клику?» — выдумка сверх постановки, Alex на него реагировал раздражением («я чё тебе сказал в постановке задачи»). Штатное воспроизведение Lampa (торренты/онлайн) работает само, отдельно решать нечего.

✅ Затык снят: чем это делается

В образе Lampac уже есть модуль Catalog — YAML-каталоги → плагин Lampa /catalog.js. Сейчас выключен. Писать плагин с нуля не нужно.

Как работает Catalog (разобрано по коду 2026-09-22)

Файлы в контейнере: /lampac/module/Catalog/ — Controllers/, plugin.js, README.md, sites/*.yaml (10 готовых примеров: vokino, rezka, kinopub, anime, …).

Механика

Что Как
Данные sites/<site>.yaml — описание сайта: host, menu, search, list, content
Подстановки override/<site>.yaml и override/_.yaml — переопределения поверх базового
Роуты /catalog.js (плагин), /catalog (JSON каналов), /catalog/list, /catalog/card
WAF ^/catalog/ → 10 запросов/сек
Имя файла только [a-z0-9-]; есть fallback-поиск по displayname

Ключевое: обязателен HTTP

if (!url.StartsWith("http"))
    url = $"{init.host}/{url}";     // ListController.cs:87

host из YAML сканируется по HTTP (Http.Get / Playwright / RCH). Локальные файлы напрямую Catalog читать не умеет — только HTTP-ответ.

Два режима парсинга

bool? jsonPath = contentParse.jsonPath ?? init.jsonPath;

if (jsonPath == true)  json = JToken.Parse(html);   // JSON-режим
else                   doc.LoadHtml(html);          // HTML-режим
  • jsonPath: true → ответ парсится как JSON, элементы берутся по nodes ($.channels[*].details)
  • иначе → HTML через HtmlAgilityPack

🔴 Это и есть рычаг: любой свой HTTP-эндпоинт, отдающий JSON нужной формы, подойдёт как host. Тогда задача сводится к генерации JSON + YAML-обёртки, без правки кода Lampac.

Форма элемента (content в YAML)

content:
  nodes: "$.channels[*].details"     # jsonPath-выражение (JSON) или CSS/XPath (HTML)
  name:         { attribute: name }
  original_name:{ attribute: originalname }
  href:         { attribute: ident }
  image:        { attribute: poster }
  year:         { attribute: released }
  serial_key:   { attribute: type, format: |  return value is "serial" ? value : null; }
  args:
    - { name_arg: vote_average, attribute: rating_kp }

Карточка — отдельная секция card_parse + card_args (описание, рейтинги, жанры, страна). Полный живой пример — sites/vokino.yaml (копия в personal/tech/lampac-deployment, раздел Catalog).

Включение модуля — 2 правки

// current.conf (внутри контейнера /lampac/current.conf)
"SkipModules": [ "Tracks", "Transcoding", ... ]    // ← убрать "Catalog" (строка ~277)
"plugins": { ..., "catalog": true }                // ← было false (строка ~1653)

🔴 ПИТФОЛЛ — init.conf НЕ смонтирован. В нашем docker-compose.yml нет volume на /lampac/init.conf, файл живёт в образе. Правки через docker exec исчезнут при пересоздании контейнера (docker compose up, watchtower-обновление образа). Варианты:

  • (а) добавить volume ./config/init.conf:/lampac/init.conf:ro — надёжно, но нужен первичный docker cp
  • (б) править через AdminPanel /adminpanel (модуль, правит init.conf + current.conf)
  • (в) docker cp + commit — плохо

Рекомендация: (а) — конфиг на HDD, переживает пересоздание.

Архитектура решения

family/documents/movies-watchlist.md          ← источник (vault, git-синхрон)
        │  существующий парсер: watchlist-sync/parser.py
        ▼
генератор (Python, на Кра́кене)
        │  пишет JSON нужной формы
        ▼
/lampac/... JSON-эндпоинт, доступный по HTTP с Lampac
        │
        ▼
sites/watchlist.yaml  ← Catalog-описание, host=<наш JSON URL>
   menu: Фильмы / Мультфильмы / Аниме  (по секциям .md)
        │
        ▼
Lampa на ТВ: плагин http://192.168.1.14:9118/catalog.js

Откуда отдавать JSON — варианты (выбрать на этапе реализации):

# Где Плюс Минус
1 статика в /lampac/wwwroot/ ничего не поднимать, Lampac сам отдаёт только чтение, обновление = перезапись файла
2 отдельный лёгкий HTTP-сервер на Кра́кене есть API/логика ещё один контейнер
3 file:// — ❌ не поддерживается (только http)

Склоняюсь к (1) — генерировать wwwroot/watchlist.json, Lampac отдаёт его сам, без новых сущностей.

Обновление каталога — реализовано

Скрипт ~/scripts/watchlist-catalog.sh (root:root, ставить только sudo install -m 755):

  1. python3 $LAMPAC/scripts/gen_watchlist.py <watchlist.md> $LAMPAC/wwwroot/watchlist.json
  2. docker restart lampac — обязательно, сбрасывает кэш Catalog (см. питфолл 3)

Cron: 30 2 * * * → лог ~/logs/watchlist-catalog.log. Время выбрано после watchlist-nightly (01:00) и watchlist-cleanup (02:00).

Бэкап crontab перед установкой: ~/crontab-backup-20260922-112915.txt.

⚠️ watchlist-catalog.sh сделан отдельной задачей, а не врезан в watchlist-nightly.sh. Причина: nightly — рабочий боевой скрипт, его падение остановило бы весь пайплайн списка. Отдельный cron падает изолированно.

Что осталось на Alex (единственное незакрытое)

Добавить плагин на ТВ — пультом, 1 минута:

  1. Lampa → Настройки → Плагины → добавить URL http://192.168.1.14:9118/catalog.js
  2. Полностью перезапустить Lampa (не сворачивать) — каталоги регистрируются только при старте
  3. На главной встать на ряд каталога и нажать влево → в выехавшей панели выбрать «Мой список»

🔴 Пункта «Настройки → Источник» в Lampa НЕ СУЩЕСТВУЕТ — я его выдумал, Alex дважды указал на ошибку. Источник переключается панелью слева в меню каталогов. Подробно: family/how-to/tv-lampac-plugin.

Приёмка = категории видны в UI Lampa, названия совпадают с movies-watchlist.md.

Автоматизация этого шага по SSH не проверялась — см. family/how-to/tv-lampac-plugin (там заготовка подхода и питфоллы localStorage).

Известные ограничения

Ограничение Детали
Постеров нет image пустой. Опционально — тянуть через TMDB-кэш Lampac (tmdbproxy)
Карточки нет Только список названий. card_parse не настроен — Lampa может показывать пустую карточку
🗓️ upcoming показываются Не прячем. Решение Alex не запрашивалось
Перезапуск контейнера Каждое обновление каталога = docker restart lampac (~30 сек простоя). Кэш не инвалидируется иначе

⚠️ Что проверено, а что нет

Проверено по HTTP (серверная часть):

Проверка Результат
/catalog.js по IP 200, ~32 КБ, api_host:'http://192.168.1.14:9118'
Каталог в /catalog 7 ключей, «Мой список» первым (Rezka, Kinobase, FilmixApp, VoKino, Eneyida, KinoUkr, Мой список)
JSON-файлы watchlist.json + watchlist-{films,new,cartoons,anime}.json — все 200
Выдача по категориям films 360 · new 33 · cartoons 3 · anime 7 (после фикса сирот 2026-09-22)
Формат ответа /catalog/list {page, results, total_pages}, total_pages: 1 для films
Сортировка ⭐ → рейтинг ↓ → ужасы
Чистота названий эмодзи-маркеров 0
Сирота «Конец света» ❌ отсутствует во всех 4 категориях

НЕ проверено:

  • ❌ UI Lampa на ТВ — Alex добавил плагин, но каталог в UI не увидел. Соединений ТВ → :9118 на момент диагностики — ноль. Гипотеза: не сделан полный перезапуск Lampa (каталоги регистрируются только при старте) либо не выбран источник в панели слева. Требуется подтверждение от Alex.
  • ❌ Боевой прогон крона в 02:30 (скрипт отработал вручную успешно).
  • ❌ Карточка/постеры в UI.

Файлы реализации (на HDD Кра́кена)

Корень: /srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac/

Путь Роль
config-module/init.conf включает Catalog → /lampac/init.conf:ro
catalog/sites/watchlist.yaml описание каталога
catalog/override/ переопределения (пусто)
wwwroot/watchlist.json все секции
wwwroot/watchlist-<code>.json по файлу на категорию
scripts/gen_watchlist.py генератор
docker-compose.yml 7 монтирований; бэкапы .bak-*

На хосте: ~/scripts/watchlist-catalog.sh, лог ~/logs/watchlist-catalog.log.

Откат

# выключить каталог
ssh kraken 'sed -i "s/\"catalog\": true/\"catalog\": false/" \
  /srv/.../lampac/config-module/init.conf && docker restart lampac'
# в Lampa убрать плагин catalog.js
# cron: crontab -l | grep -v watchlist-catalog | crontab -

Ничего в коде Lampac не патчилось — только конфиг и данные.

Статус: ✅ РЕАЛИЗОВАНО 2026-09-22

Каталог «Мой список» работает, отдаётся в Lampa через /catalog.js.

Что сделано

# Шаг Результат
1 Включён модуль Catalog SkipModules без Catalog + catalog: true → /catalog.js 200
2 init.conf смонтирован ./config-module/init.conf:/lampac/init.conf:ro — правится на HDD, не теряется
3 sites/ + override/ смонтированы ./catalog/sites, ./catalog/override
4 wwwroot смонтирован ./wwwroot — Lampac сам отдаёт JSON по HTTP
5 Генератор написан lampac/scripts/gen_watchlist.py
6 sites/watchlist.yaml создан lampac/catalog/sites/watchlist.yaml
7 Автообновление в cron 30 2 * * * → ~/scripts/watchlist-catalog.sh
8 Проверка 4 категории: films 360, new 34, cartoons 3, anime 7

Архитектура (финальная)

family/documents/movies-watchlist.md          ← источник
        │  gen_watchlist.py (свой парсер, ~200 строк)
        ▼
lampac/wwwroot/watchlist.json                 ← все секции
lampac/wwwroot/watchlist-<code>.json          ← по файлу на категорию
        │  Lampac отдаёт wwwroot по HTTP сам
        ▼
lampac/catalog/sites/watchlist.yaml           ← описание для Catalog
        │  plugin=watchlist, jsonPath: true, nodes: $.items[*]
        ▼
Lampa на ТВ: плагин http://192.168.1.14:9118/catalog.js

🔴 Питфоллы, найденные по ходу (не повторять)

  1. Catalog ВЫБРАСЫВАЕТ элемент с пустым href. ListController.cs: if (!IsNullOrWhiteSpace(name) && !IsNullOrWhiteSpace(href)). Симптом: из 404 позиций отдалось 52 — ровно те, у кого есть ссылка Кинопоиска. Фикс: генератор подставляет fallback-href https://www.kinopoisk.ru/index.php?kp_query=<название>.

  2. Catalog НЕ подставляет {cat} в nodes. JSONPath статичен, отфильтровать секцию внутри одного файла нечем. Симптом: все категории отдавали весь список (404 вместо 360/34/3/7). Фикс: генератор пишет отдельный плоский файл на категорию (watchlist-films.json и т.д.), nodes: "$.items[*]" одинаков для всех.

  3. Кэш Catalog без версии данных. Ключ catalog:{plugin}:{search}:{sort}:{cat}:{page} — не зависит от содержимого. Симптом: после перегенерации JSON выдача не менялась. Фикс: docker restart lampac в скрипте обновления.

  4. init.conf не был смонтирован. Файл живёт в образе; правки через docker exec теряются при пересоздании. Правки шли в current.conf (сгенерированный) — неверная цель. Фикс: volume :ro + свой минимальный init.conf (перекрывает base.conf).

  5. api_host подставляется из запроса ({localhost} → хост, по которому пришли). По IP 192.168.1.14 отдаёт правильный адрес — специально ничего настраивать не нужно.

  6. Кириллица в inline-jq внутри ssh '...' ломает команду (tirith + quoting). Выносить в файл или убирать строки с кириллицей.

  7. Осиротевшие строки в ## Новинки давали дубли в каталоге. Строки без 👍/👎 (например - [ ] ⬇️ Название) process-thumbs не трогал — они висели в Новинках навсегда. Если фильм уже [x] в главном списке — получался дубль: непросмотренный «Конец света» при просмотренном оригинале. Фикс в watchlist-sync/parser.py (удаление сирот + флаг _patch_dropped) и cli.py. Разбор: family/projects/watchlist-automation, секция «ОСИРОТЕВШИЕ СТРОКИ».

  8. Двойное экранирование при правке parser.py скриптами. r'\1' превращался в байт \x01, r'\b' — в \x08. Синтаксис валиден, регекспы молчат. Проверять файл на chr(1) и chr(8) после патча.

Управление

ssh kraken
cd /srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac

# перегенерировать каталог + сбросить кэш
bash ~/scripts/watchlist-catalog.sh

# логи
tail -f ~/logs/watchlist-catalog.log

# только генератор (без перезапуска)
python3 scripts/gen_watchlist.py \
  /home/kraken/obsidian/family/documents/movies-watchlist.md \
  wwwroot/watchlist.json

Откат

# выключить каталог
ssh kraken 'sed -i "s/\"catalog\": true/\"catalog\": false/" \
  /srv/.../lampac/config-module/init.conf && docker restart lampac'
# в Lampa убрать плагин catalog.js

Правила отбора и сортировки (утверждено Alex 2026-09-22)

Фильтрация

Правило Действие
[ ] непросмотренные показывать
[x] просмотренные ❌ скрыть
👎 ❌ скрыть
🗓️ upcoming показывать (решить при реализации — есть в файле)
❓ ambiguous показывать (название как есть)

Сортировка внутри списка — трёхключевая

  1. ⭐ (U+2B50) — первыми
  2. Рейтинг — по убыванию (IMDb/KP из строки файла; строки [название](kp) (год) IMDb N.N KP N.N)
  3. Ужасы (🧟♀️, TMDB genre_id=27) — последними

⚠️ Только ⭐ (U+2B50) — как в sync-down (см. family/projects/watchlist-automation). ✭ (U+272D) приоритетом не является.

Порядок секций

  1. Главный список — первым. Это ## Фильмы
  2. Дальше — остальные по порядку в файле: ## Новинки - 👍/👎, ## Мультфильмы, ## Аниме, …

⚠️ ## Новинки👎 — не показывать (это скрытая секция отказов). ⚠️ Секция ## Новинки содержит 👍/👎; 👎-строки прячем, 👍 показываем.

Источник рейтинга

В строках со ссылками Кинопоиска рейтинг уже в тексте: IMDb 7.6 KP 8.2. Для строк без рейтинга (например - [ ] Gods don't give gifts) — сортировать как 0, в конец группы. Опционально: добирать через TMDB (кэш tmdbproxy в Lampac) — решить при реализации.

Открытые вопросы (не блокируют, решение Alex не запрошено)

  1. Постеры — тянуть через TMDB-кэш Lampac или не тянуть вовсе? Сейчас пустые.
  2. Карточка — нужна ли (описание/рейтинг внутри Lampa), или достаточно списка названий?
  3. 🗓️ upcoming — показывать или прятать? Сейчас показываем.
  4. ❓ ambiguous — сейчас показываем с названием как есть.

Ни один из них не мешает работе — каталог отдаётся, категории на месте. Спрашивать только если Alex сам затронет тему.

Связанные