Files
obsidian-vault/family/how-to/tv-lampac-plugin.md
T

353 lines
22 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
created: '2026-09-22'
namespace: family
related:
- '[[family/how-to/tv-luna-send]]'
- '[[family/how-to/htpc-webos-luna]]'
- '[[personal/tech/lampac-deployment]]'
tags:
- tv
- webos
- lampa
- lampac
- plugin
- how-to
title: tv-lampac-plugin — подключение плагина Lampa на LG TV по SSH
type: how-to
updated: '2026-09-22'
---
# tv-lampac-plugin.sh — подключение плагина Lampa к Lampac на LG TV
> Создан 2026-09-22. Проблема, которую решает: добавить плагин/источник в Lampa на ТВ
> **без пульта**, автоматически по SSH.
> Связано: [[family/how-to/tv-luna-send]], [[family/how-to/htpc-webos-luna]], [[personal/tech/lampac-deployment]].
## Зачем
Настройки Lampa хранятся в localStorage webOS-приложения. Пульта под рукой может не быть,
а добавлять вручную (`Настройки → Плагины → ввести URL`) долго и неудобно.
Данный скрипт прописывает плагин/источник прямо в localStorage приложения Lampa.
## Ключевые пути на ТВ
| Что | Путь |
|---|---|
| Приложение Lampa | `/media/developer/apps/usr/palm/applications/com.lampac.tv/` |
| localStorage (LevelDB) | `/media/developer/apps/usr/palm/applications/com.lampac.tv/.localstorage/` |
| Настройки Lampa | ключ `lampac_settings` (JSON) в LevelDB |
## Как Lampa хранит настройки
Lampa в webOS использует `localStorage`, который Chromium хранит в **LevelDB**
(`.localstorage/leveldb/`). Прямая запись в LevelDB при работающем приложении **опасна**:
приложение перезапишет значение из памяти при выходе.
**Правильный порядок:**
1. Закрыть приложение Lampa (иначе перезапишет)
2. Записать значение в localStorage
3. Запустить приложение
## 🔴 Пункта «Настройки → Источник» НЕ СУЩЕСТВУЕТ (проверено 2026-09-22)
**Не повторять эту ошибку — Alex дважды сказал, что пункта нет.**
Я его выдумал, отвечая «как увидеть списки».
**Правильный путь (Alex нашёл сам 2026-09-22):**
**Настройки → «остальное» → основной источник → «Мой список».**
⚠️ Мой предыдущий вариант («влево на ряду → панель источников») **не подтверждён**.
⚠️ **При выборе «Мой список» исчезает родной каталог CUB** — это **не баг Lampa**,
а следствие перехвата кликов в `plugin.js` (см. раздел «🔴 Что на самом деле подменяет CUB»).
## 🔴 Что на самом деле подменяет CUB — `plugin.js`, а не YAML (проверено 2026-09-22)
**Что хотел Alex:**
> «Я предполагал что будет отображаться то что и раньше (список cub) но В НЕГО будет
> сверху добавлено несколько секций в которых будут РЕАЛЬНЫЕ ЭЛЕМЕНТЫ — а не сгенерируют
> пустые заглушки!»
⚠️ **Гипотеза «`menu` подменяет / `main` добавляет» — ОПРОВЕРГНУТА на живом сервере.**
Её здесь не повторять. Факты:
| Гипотеза | Факт |
|---|---|
| `menu` подменяет родные разделы | ❌ `menu` **регистрирует канал**. Без него сайт отбрасывается: `ApiController.cs:70` → `if (init == null \|\| !init.enable \|\| init.menu == null \|\| init.hide) continue;` |
| `main` добавляет секции сверху | ❌ блока `main` **в YAML не существует** — YamlDotNet давится неизвестным полем, `goInit` → null, сайт молча пропадает |
| «Убрать `menu` из watchlist.yaml» | ❌ **сломало канал**: «Мой список» исчез из `/catalog`, список пустой. Три раунда правок, три рестарта |
**Настоящий виновник — `plugin.js`** (он же `/catalog.js`):
```js
Lampa.Listener.follow('menu', function(e){
if (e.type=='action' && (e.action=='tv'||'cartoon'||'anime'||'movie'||'relise')) {
var catalog = Api.getCatalog();
if (!catalog) return;
...
Lampa.Router.call('category', {...});
e.abort(); // ← перехват клика по родному разделу
}
});
```
**Перехват работает ТОЛЬКО когда выбран источник «Мой список».** Родной CUB/tmdb не
трогается, пока источник не переключён на наш.
**Вывод про «CUB + мой список одновременно»:** родной CUB и «Мой список» — **два источника
Lampa** (переключаются в «остальное → основной источник»). Показать одновременно нельзя
без своего плагина. Варианты — [[personal/tech/lampac-deployment]], раздел
«🎯 Как добиться „CUB + мой список сверху“».
## 🟡 «Здесь пусто» и нет постеров — потому что данные не из Radarr
Второй симптом: элементы без превью, при открытии «здесь пусто».
Замер выдачи `/catalog/list?plugin=watchlist&cat=films`:
| Метрика | Значение |
|---|---|
| Всего позиций | 360 |
| С постером (`img != null`) | **0** |
| С реальным ID (`film/...`) | **18** |
Причина: генератор парсил `movies-watchlist.md` — **текст названий**, без TMDB ID и постеров.
Остальным 342 подставлялся `href` на **поиск** Кинопоиска → Lampa открывала страницу поиска.
**Правильный источник — Radarr API** (туда фильмы и добавлялись, там уже есть `tmdbId`
и `images[].remoteUrl`). Alex: «Они уже резолвятся же блядь!! Оно же добавлялось в arr!»
```bash
curl -sL "http://localhost:7878/api/v3/movie?apikey=$KEY" | jq length # → 50
# ⚠️ -L обязателен, без него 307 и пустое тело
```
Покрытие: Radarr **50** фильмов vs `.md` **360**. Что делать с остальными 310 — открытый
вопрос к Alex.
## 🔴 БАГ: источник не виден в панели (`enumerable: false`)
**Найдено 2026-09-22** диагностикой через Chrome DevTools ТВ (см. раздел ниже).
Факты с живого ТВ:
| Замер | Значение | Смысл |
|---|---|---|
| `Lampa.Storage.field('source')` | `'Мой список'` | источник **уже выбран** |
| `Object.keys(Lampa.Api.sources)` | `'tmdb|cub'` | ❌ источника нет в перечислении |
| `Lampa.Api.sources['Мой список'].getCatalog()` | `search, movie, cartoons, anime, menu, main` | ✅ каталог доступен напрямую |
| `Object.getOwnPropertyDescriptor(Lampa.Api.sources,'Мой список')` | `{"enumerable":false,"configurable":false}` | ← **корень** |
Причина в `/lampac/module/Catalog/plugin.js`, строка 59 (`loadCatalogs()`, **два вхождения**):
```js
// БЫЛО (баг)
Object.defineProperty(Lampa.Api.sources, key, { get: function(){ return Api; } });
// НАДО
Object.defineProperty(Lampa.Api.sources, key,
{ get: function(){ return Api; }, enumerable: true, configurable: true });
```
**`plugin.js` живёт в ОБРАЗЕ, не на HDD** (`docker inspect` показывает монтирования только
на `catalog/sites` и `catalog/override`). Правка через `docker exec` исчезнет при обновлении.
Фикс — смонтировать поправленный файл через compose:
`./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro`.
Полные команды и откат — [[personal/plans/lampac-watchlist-plugin]], раздел «ГЛАВНЫЙ ПИТФОЛЛ».
**Статус: фикс НЕ применён, ждёт апрува Alex (2026-09-22).**
## 🛠 Диагностика ТВ через Chrome DevTools (главный инструмент, найден 2026-09-22)
**Ключевое открытие: webOS отдаёт Chrome DevTools Protocol на порту 9998.**
У процессов `WebAppMgr` в аргументах есть `--remote-debugging-port=9998`.
Это даёт **чтение состояния живой Lampa** — то, чего не дают ни логи, ни tcpdump.
```bash
# 1. Список отлаживаемых страниц + URL приложения
curl -s --max-time 8 "http://192.168.1.70:9998/json" | /usr/bin/jq -r '.[] | "\(.type): \(.url)"'
# 2. Проверка сети до Lampac (на ТВ!)
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'
# 3. Живые соединения с Lampac (смотреть на ТВ, НЕ на сервере)
ssh root@192.168.1.70 'netstat -tn 2>/dev/null | grep 9118'
# 4. Что запущено сейчас
ssh root@192.168.1.70 'script -q -c "luna-send -n 1 -f \
luna://com.webos.applicationManager/getForegroundAppInfo {}" /dev/null'
# 5. Перезапустить приложение (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'
```
Чтение переменных Lampa — через `websocket-client` + `Runtime.evaluate`:
готовые скрипты `/tmp/lampa_probe.py` и `/tmp/lampa_probe3.py` (см. план).
### Ловушки диагностики (проверено на живом ТВ)
⚠️ **Момент замера.** `sudo ss -tn | grep :9118` на Кра́кене сразу после старта дал **0 соединений** —
я сделал ложный вывод «ТВ не ходит на сервер». Через 30 с на ТВ:
**1 ESTABLISHED + 6 TIME_WAIT** — всё работает. Смотреть соединения **на ТВ**, не на сервере.
⚠️ **`launch` через luna требует экранированного JSON в кавычках.** Без кавычек —
`errorCode: 2, invalid parameters` (молча падает). `getForegroundAppInfo {}` работает без кавычек.
⚠️ **`tcpdump` на Кра́кене НЕТ.** Не ставили — DevTools-подход дал больше (состояние JS, а не пакеты).
⚠️ **`luna://com.webos.service.capture/takeScreenShot` не существует** на этом ТВ
(`Unknown method "takeScreenShot"`). Скриншот так не снять.
⚠️ **Логи HTTP Lampac не ведёт** — `docker logs` не покажет запросы ТВ. Не искать там.
## Как плагин `catalog.js` регистрирует источник (механика)
Важно для диагностики — плагин **не добавляет новый пункт в главное меню**, он **подменяет
существующие**:
```js
// при старте / app ready:
Lampa.Utils.account(Manifest.api_host + '/catalog', function(json){
Manifest.catalogs = json; // все каталоги с сервера
keys.forEach(function(key){
Lampa.Params.values.source[key] = key; // регистрирует источник
Object.defineProperty(Lampa.Api.sources, key, { // ← ЗДЕСЬ БАГ
get: function(){ return Api; } // нет enumerable:true
});
if (Manifest.catalogs[key].search)
Lampa.Search.addSource(Api.discovery(key)); // добавляет в поиск
});
});
// хук на меню: клик по movie/tv/cartoon/anime/relise подменяется на URL из catalogs[key].menu
Lampa.Listener.follow('menu', ...)
```
⚠️ Свойство ставится через `defineProperty` **без `enumerable: true`** → `Object.keys()` его
не видит → источник не появляется в панели выбора (разбор бага — выше).
Отсюда следует:
- Отдельного переключателя в настройках нет **by design** — источник выбирается панелью слева.
- Плагин **подтягивает каталоги только при старте приложения**. Если добавить плагин и не
перезапустить Lampa — каталоги не зарегистрируются. Нужен полный перезапуск (не сворачивание).
- Заголовок каталога после переключения содержит имя источника в верхнем регистре:
«Фильмы — МОЙ СПИСОК».
## Что должен отдавать сервер (проверено 2026-09-22)
| Endpoint | Ответ | Значение |
|---|---|---|
| `/catalog.js` | 200, ~32 КБ | сам плагин, манифест с `api_host` |
| `/catalog` | JSON, 7 ключей | список каталогов: Rezka, Kinobase, FilmixApp, VoKino, Eneyida, KinoUkr, **Мой список** |
| `/catalog/list?plugin=watchlist&cat=films` | `{page, results, total_pages}` | 360 позиций, `total_pages: 1` |
`api_host` подставляется по IP запроса → `http://192.168.1.14:9118` (не `localhost`),
поэтому ТВ получает рабочие ссылки.
**7 источников в `/catalog`** — нормальный минимум: модуль `Catalog` собирает их из bundled-плагинов
плюс `watchlist`. «Мой список» идёт **первым** ключом.
## ⚠️ Ловушка диагностики: `plugins_add` в `lampainit.js` — НЕ то, что кажется
`http://192.168.1.14:9118/lampainit.js` содержит:
```js
var plugins_add = [
{"url":"http://192.168.1.14:9118/catalog.js","status":1,"name":"Альтернативные источники каталога", ...},
...
];
```
Это **серверный список плагинов, которые Lampac сам подсовывает** в UI.
Клиент на ТВ (webOS-приложение `com.lampac.tv`) — **не отдельная Lampa**: он грузит `app.min.js`
и `lampainit.js` **с сервера** (`loader.js` → `lampac_url` из localStorage, дефолт `http://192.168.1.14:9118`).
Следствие: каталог watchlist работает через **серверный модуль `Catalog`**, а не через строку
`plugins_add`. Не искать причину поломки в этом массиве.
## Питфоллы (проверено 2026-09-22)
1. **localStorage у webOS-приложений изолирован.** Файл в `.localstorage/` у сторонних
(Homebrew) приложений **может отсутствовать** — Chromium держит стор в памяти и
сбрасывает на диск не сразу. Если каталога нет, сначала запустить приложение один раз.
2. **`luna-send` через SSH молчит** — вывод в tty. Оборачивать в `script -q -c "..." /tmp/o.txt`
и читать файл. См. [[family/how-to/tv-luna-send]].
3. **LevelDB нельзя править на живом приложении.** Сначала `closeByAppId`, потом запись.
4. **`init.conf` лежит НЕ в корне каталога Lampac, а в `config-module/init.conf`**
(монтируется в `/lampac/init.conf:ro`). Искать там, а не по `lampac/init.conf`
— иначе получишь ложный «файл пропал».
5. **После добавления плагина обязательно полный перезапуск Lampa** — каталоги регистрируются
только на старте. ⚠️ Но **не делай из «нуля соединений» вывод о поломке**: сразу после старта
соединений действительно 0, через ~30 с появляются. Смотреть **с ТВ** (`netstat -tn | grep 9118`).
6. **`plugin.js` лежит в ОБРАЗЕ, а не на HDD.** Монтируются только `catalog/sites` и
`catalog/override`. Правки `/lampac/module/Catalog/plugin.js` через `docker exec` **теряются
при обновлении образа** — нужен bind-mount через compose.
7. **Не верить `Object.keys()` на слово — проверять `getOwnPropertyDescriptor`.** Свойство может
существовать (доступно напрямую) и при этом не быть enumerable. `Object.keys` вернёт список
без него, и это выглядит как «источника нет», хотя он есть.
8. **Проще всего — через UI самого пульта**, если он доступен:
Lampa → Настройки → Плагины → добавить URL. Скрипт нужен, когда пульта нет.
## Что должно быть прописано для Lampac
| Настройка | Значение |
|---|---|
| Плагин (онлайн-источники) | `http://192.168.1.14:9118/online.js` |
| Каталог (свой список) | `http://192.168.1.14:9118/catalog.js` |
| TorrServer | `http://192.168.1.14:9118` (Настройки → TorrServer → «Использовать основную ссылку») |
| Буфер плеера | 50–100 МБ |
## Скрипт
Лежит на Mac: `~/Automation/tv-lampac-plugin.sh`
(дописывается при первом реальном применении; пока не создан — см. «Статус»).
## Статус
**Проверено на живом ТВ 2026-09-22** (диагностика через DevTools, порт 9998).
**Что установлено точно:**
- ТВ достаёт Кра́кен (`/catalog` 200 за 0.26 с), Lampa ходит на сервер (ESTABLISHED).
- Плагин `catalog.js` **добавлен**, `window.plugin_catalog = true`, `appready = true`.
- `Storage.field('source')` = `'Мой список'` — источник **выбран**.
- `Lampa.Api.sources['Мой список']` **существует** и отдаёт каталог
(`search, movie, cartoons, anime, menu, main`) — **технически доступен**.
- ❌ В панели выбора источника его **нет** — `plugin.js` регистрирует свойство
без `enumerable: true`. **Баг в Lampac, не в нашей настройке.**
**Что переделывается (обновлено 2026-09-22 ночь):**
1. ❌ ~~Убрать блок `menu` из `watchlist.yaml`, оставить `main`~~ — **ОТМЕНЕНО, невозможно.**
`main` в YAML ломает парсинг (сайт исчезает из `/catalog`), `menu` обязателен для
регистрации канала. Подмену делает `plugin.js`. См. раздел выше.
2. ⏳ Переключить источник данных с парсинга `.md` на **`resolver.py`** (TMDB + Кинопоиск)
→ постеры + TMDB ID для всех 360. *(Radarr API даёт только 50 — в нём нет остальных 310.)*
3. ⏳ Смонтировать поправленный `plugin.js` (`enumerable: true`)
4. ✅ **Выключена автоскачка на HDD** — Radarr (45→0 monitored, очередь 6→0, RSS 30→0),
Sonarr (13→0, 19→0, 15→0). Бэкапы `20260922-123404`. Метаданные целы: 50 фильмов,
46 с постерами, 24 файла на диске не тронуты.
**Текущее состояние каталога: РАБОЧИЙ.** «Мой список» на месте в `/catalog`, 4 категории
(360/33/3/7). `watchlist.yaml` откачен к проверенной форме.
**Открытое решение Alex:** родной CUB и «Мой список» одновременно показать нельзя —
нужен свой плагин. Варианты (оставить как есть / патч `plugin.js` / свой плагин) —
[[personal/tech/lampac-deployment]], раздел «🎯 Как добиться „CUB + мой список сверху“».
Полный разбор — [[personal/plans/lampac-watchlist-plugin]].
**Скрипт-автоматизация по-прежнему не создана** — и, вероятно, не нужна: источник
переключается через **Настройки → «остальное» → основной источник** (Alex сделал сам).
**Скрипт-автоматизация по-прежнему не создана.** Прямая запись в localStorage **не нужна**:
проблема оказалась не в настройках Lampa, а в коде плагина. Если автоматизация понадобится —
подход: `closeByAppId com.lampac.tv` → запись ключа в localStorage → `launch com.lampac.tv`
(учесть питфоллы 1–3). Но сначала — фикс `plugin.js`.