--- 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`.