29 KiB
created, namespace, related, status, tags, title, type, updated
| created | namespace | related | status | tags | title | type | updated | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-09-22 | personal |
|
active |
|
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. Серверная часть полностью рабочая. Осталось одно: вplugin.jsLampac баг (definePropertyбезenumerable:true) — из-за него источник «Мой список» не виден в списке выбора Lampa на ТВ. Фикс готов, ждёт апрува Alex (2026-09-22). См. «🔴 ГЛАВНЫЙ ПИТФОЛЛ».
Задача
Показывать список фильмов 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):
python3 $LAMPAC/scripts/gen_watchlist.py <watchlist.md> $LAMPAC/wwwroot/watchlist.jsondocker 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, а правка сервера. Плагин на ТВ уже добавлен и источник уже выбран
(Storage.field('source') = 'Мой список'). Проблема — баг в plugin.js Lampac
(см. «🔴 ГЛАВНЫЙ ПИТФОЛЛ»): источник не виден в UI из-за enumerable: false.
Ждёт апрува Alex (2026-09-22): смонтировать поправленный plugin.js через compose.
Путь на ТВ, который раньше писался («влево → панель → выбрать источник») — не сработает, пока баг не исправлен: в панели источника просто нет.
Приёмка = категории видны в UI Lampa, названия совпадают с movies-watchlist.md.
Диагностический скрипт для ТВ (/tmp/lampa_probe.py)
Правки в UI Lampa по SSH напрямую не сделать, но состояние читается через DevTools:
pages = json.load(urllib.request.urlopen("http://192.168.1.70:9998/json", timeout=8))
target = next(p for p in pages if "com.lampac.tv" in p.get("url",""))
ws = websocket.create_connection(target["webSocketDebuggerUrl"], timeout=10)
# Runtime.evaluate:
# Lampa.Storage.field('source')
# Object.keys(Lampa.Api.sources).join('|')
# Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список')
# Lampa.Api.sources['Мой список'].getCatalog()
Требует pip3 install websocket-client (стоит). См. family/how-to/tv-lampac-plugin.
Известные ограничения
| Ограничение | Детали |
|---|---|
| Постеров нет | 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 на ТВ — ПРИЧИНА НАЙДЕНА 2026-09-22 (см. ниже «🔴 ГЛАВНЫЙ ПИТФОЛЛ»).
Диагностика через Chrome DevTools ТВ (
http://192.168.1.70:9998/json):- ТВ физически достаёт Кра́кен —
curlс ТВ отдаёт/catalog200 за 0.26 с. - Lampa работает и ходит на сервер — соединение
192.168.1.70 → 192.168.1.14:9118ESTABLISHED. Storage.field('source')='Мой список'— источник уже выбран!Lampa.Api.sources['Мой список']существует и отдаёт каталог (getCatalog()→search, search_lazy, movie, cartoons, anime, menu, main).- ❌ НО:
Object.keys(Lampa.Api.sources)='tmdb|cub'— источника нет в перечислении. - Причина:
defineProperty(..., {get: ..., enumerable:false, configurable:false}). - Диагноз: баг в
plugin.jsсамого Lampac, а не в нашей настройке. UI Lampa строит список источников черезObject.keys()— «Мой список» из него выпадает, поэтому в UI источник не видно и выбрать его нельзя.
- ТВ физически достаёт Кра́кен —
- ❌ Боевой прогон крона в 02:30 (скрипт отработал вручную успешно).
- ❌ Карточка/постеры в UI.
🔴 ГЛАВНЫЙ ПИТФОЛЛ: источник не виден в UI Lampa (enumerable: false)
Симптом. Alex: «Ни на главной ни в Фильмах нет! Там только Сейчас смотрят, рекомендуем итд». Плагин добавлен, серверная часть отдаёт всё корректно, а в UI источника нет.
Диагностика (по возрастанию точности):
ssh kraken 'sudo ss -tn | grep ":9118"'— сначала показал 0 соединений (ложный вывод: замер сделан в момент старта Lampa). Через 30 с на ТВ:netstat -tn | grep 9118→ 1 ESTABLISHED + 6 TIME_WAIT — Lampa скачала все файлы.- Foreground-приложение:
luna-send -n 1 -f luna://com.webos.applicationManager/getForegroundAppInfo {}→com.lampac.tv. Запускать надоlaunchс экранированным JSON в кавычках, иначеerrorCode: 2 invalid parameters. - Доступ к отладчику ТВ:
curl http://192.168.1.70:9998/json— webOS отдаёт Chrome DevTools Protocol на порту 9998 (уWebAppMgrв аргументах--remote-debugging-port=9998). Черезwebsocket+Runtime.evaluateчитаются переменные живой Lampa. - Ключевой замер:
JSON.stringify(Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список'))→{"enumerable":false,"configurable":false}.
Корень. В /lampac/module/Catalog/plugin.js (строка 59, loadCatalogs(), два вхождения):
Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; } });
Свойство создаётся не-enumerable → Object.keys() его не видит → Lampa не рисует источник
в панели выбора. При этом Api.sources['Мой список'] доступен напрямую и каталог отдаёт.
Фикс (одна правка, два вхождения):
Object.defineProperty(Lampa.Api.sources, key,
{ get: function(){ return Api; }, enumerable: true, configurable: true });
Как применять — plugin.js живёт в образе, не на HDD! (проверено:
docker inspect не показывает монтирования на module/Catalog, монтируются только
catalog/sites и catalog/override). Правка через docker exec исчезнет при обновлении образа.
Предложенный путь (на апруве у Alex, 2026-09-22):
# docker-compose.yml
volumes:
- ./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro
# 1. забрать, поправить локально (sed/grep по файлу не применять — только patch)
ssh kraken 'docker exec lampac cat /lampac/module/Catalog/plugin.js' > /tmp/catalog-plugin.js
# 2. бэкап
cp /tmp/catalog-plugin.js /tmp/catalog-plugin.js.bak-$(date +%Y%m%d-%H%M%S)
# 3. правка двух вхождений defineProperty → +enumerable:true,configurable:true
# 4. проверка синтаксиса ПЕРЕД заливкой
node --check /tmp/catalog-plugin.js
# 5. на HDD, добавить volume, пересоздать
scp /tmp/catalog-plugin.js kraken:/srv/.../lampac/catalog/plugin.js
ssh kraken 'cd /srv/.../lampac && docker compose up -d'
# 6. проверка на ТВ: Object.keys(Lampa.Api.sources) должен стать 'tmdb|cub|Мой список'
Откат: убрать строку volume из compose → docker compose up -d. Плюс бэкап файла в /tmp.
Статус: правка НЕ применена — ждёт апрува Alex (2026-09-22).
Готовые команды диагностики ТВ (переиспользовать)
# 1. Что запущено на ТВ (luna-send пишет в tty → нужен script -q -c)
ssh root@192.168.1.70 'script -q -c "luna-send -n 1 -f \
luna://com.webos.applicationManager/getForegroundAppInfo {}" /dev/null'
# 2. Запустить приложение (JSON экранирован И обёрнут в кавычки)
ssh root@192.168.1.70 'script -q -c "luna-send -n 1 -f \
luna://com.webos.applicationManager/launch \"{\\\"id\\\":\\\"com.lampac.tv\\\"}\"" /dev/null'
# 3. Достаёт ли ТВ Кра́кен
ssh root@192.168.1.70 'curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \
--max-time 8 http://192.168.1.14:9118/catalog'
# 4. Живые соединения с Lampac (на ТВ, не на сервере!)
ssh root@192.168.1.70 'netstat -tn 2>/dev/null | grep 9118'
# 5. DevTools-цель Lampa на ТВ
curl -s --max-time 8 "http://192.168.1.70:9998/json" | /usr/bin/jq -r '.[] | "\(.type): \(.url)"'
Скрипты-пробы DevTools: /tmp/lampa_probe.py (состояние источника),
/tmp/lampa_probe3.py (асинхронный вызов menu() каталога).
⚠️ tcpdump на Кра́кене нет — для съёма трафика ставить не стали, использован
DevTools-подход (дал больше: видно состояние JS, а не только пакеты).
⚠️ luna://com.webos.service.capture/takeScreenShot на этом ТВ не существует
(Unknown method) — скриншот так не снять.
Файлы реализации (на 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
🔴 Питфоллы, найденные по ходу (не повторять)
-
Catalog ВЫБРАСЫВАЕТ элемент с пустым
href.ListController.cs:if (!IsNullOrWhiteSpace(name) && !IsNullOrWhiteSpace(href)). Симптом: из 404 позиций отдалось 52 — ровно те, у кого есть ссылка Кинопоиска. Фикс: генератор подставляет fallback-hrefhttps://www.kinopoisk.ru/index.php?kp_query=<название>. -
Catalog НЕ подставляет
{cat}вnodes. JSONPath статичен, отфильтровать секцию внутри одного файла нечем. Симптом: все категории отдавали весь список (404 вместо 360/34/3/7). Фикс: генератор пишет отдельный плоский файл на категорию (watchlist-films.jsonи т.д.),nodes: "$.items[*]"одинаков для всех. -
Кэш Catalog без версии данных. Ключ
catalog:{plugin}:{search}:{sort}:{cat}:{page}— не зависит от содержимого. Симптом: после перегенерации JSON выдача не менялась. Фикс:docker restart lampacв скрипте обновления. -
init.confне был смонтирован. Файл живёт в образе; правки черезdocker execтеряются при пересоздании. Правки шли вcurrent.conf(сгенерированный) — неверная цель. Фикс: volume:ro+ свой минимальныйinit.conf(перекрываетbase.conf). -
api_hostподставляется из запроса ({localhost}→ хост, по которому пришли). По IP192.168.1.14отдаёт правильный адрес — специально ничего настраивать не нужно. -
Кириллица в inline-jq внутри
ssh '...'ломает команду (tirith + quoting). Выносить в файл или убирать строки с кириллицей. -
Осиротевшие строки в
## Новинкидавали дубли в каталоге. Строки без👍/👎(например- [ ] ⬇️ Название)process-thumbsне трогал — они висели в Новинках навсегда. Если фильм уже[x]в главном списке — получался дубль: непросмотренный «Конец света» при просмотренном оригинале. Фикс вwatchlist-sync/parser.py(удаление сирот + флаг_patch_dropped) иcli.py. Разбор: family/projects/watchlist-automation, секция «ОСИРОТЕВШИЕ СТРОКИ». -
Двойное экранирование при правке
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 |
показывать (название как есть) |
Сортировка внутри списка — трёхключевая
⭐(U+2B50) — первыми- Рейтинг — по убыванию (IMDb/KP из строки файла; строки
[название](kp) (год) IMDb N.N KP N.N) - Ужасы (
🧟♀️, TMDB genre_id=27) — последними
⚠️ Только ⭐ (U+2B50) — как в sync-down (см. family/projects/watchlist-automation).
✭ (U+272D) приоритетом не является.
Порядок секций
- Главный список — первым. Это
## Фильмы - Дальше — остальные по порядку в файле:
## Новинки - 👍/👎,## Мультфильмы,## Аниме, …
⚠️ ## Новинки👎 — не показывать (это скрытая секция отказов).
⚠️ Секция ## Новинки содержит 👍/👎; 👎-строки прячем, 👍 показываем.
Источник рейтинга
В строках со ссылками Кинопоиска рейтинг уже в тексте: IMDb 7.6 KP 8.2.
Для строк без рейтинга (например - [ ] Gods don't give gifts) — сортировать как 0, в конец группы.
Опционально: добирать через TMDB (кэш tmdbproxy в Lampac) — решить при реализации.
Открытые вопросы (не блокируют, решение Alex не запрошено)
- Постеры — тянуть через TMDB-кэш Lampac или не тянуть вовсе? Сейчас пустые.
- Карточка — нужна ли (описание/рейтинг внутри Lampa), или достаточно списка названий?
🗓️upcoming — показывать или прятать? Сейчас показываем.❓ambiguous — сейчас показываем с названием как есть.
Ни один из них не мешает работе — каталог отдаётся, категории на месте. Спрашивать только если Alex сам затронет тему.
Связанные
- personal/plans/lampac-kraken — базовое развёртывание (шаги 1–6 сделаны)
- personal/tech/lampac-deployment — эксплуатация Lampac, модуль Catalog
- family/how-to/tv-lampac-plugin — подключение плагина на ТВ
- family/projects/watchlist-sync — парсер списка, CLI
- family/projects/watchlist-automation — форматы маркеров, cron
- family/documents/movies-watchlist — источник данных