[2026-09-22] eagle: personal/plans/lampac-watchlist-plugin.md personal/tech/lampac-deployment.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-22 16:22:40 +06:00
1 parent 7a021b26c3
commit 8441f3a4cb
2 files changed
+130 -9

No files matched your search

+105 -2
View File
@@ -1786,10 +1786,13 @@ else{Lampa.Listener.follow('app',function(e){if(e.type=='ready')patchCubMain();}
| `runtime`, `vote_average` | тем же способом через `format` |
| `displayname` | «Мой список» — надпись в UI |
| **✅ Alex подтвердил в UI** | **фильмы Watchlist открываются и ПРОИГРЫВАЮТСЯ** |
| Инжект секций в CUB-фид | залито, ожидает проверки в UI (перезапуск Lampa) |
| Инжект секций в CUB-фид | ⚠️ залито (35440 Б) — **ждёт перезапуска Lampa на ТВ** |
| Причина «секции не появились» | 🔴 `Manifest` не виден в обёртке → `ReferenceError` → фикс через `window.__wl_menu` |
| Сборщик плагина | `build_plugin.py` (детерминированный, с инвариантами); `patch_cub_inject.py` **отменён** |
Скрипты: `/Users/admin/lampac-tmp/gen_watchlist_v2.py`, `deploy_lampac.py`
(`--skip-plugin` для быстрого прогона), `patch_cub_inject.py`.
(`--skip-plugin` для быстрого прогона), `build_plugin.py`, `deploy_cub.py`,
`restart_lampa.sh`, `probe_tv.py`, `probe_cub_main.py`.
Проверка в шаге 7 валидирует именно ключ `watchlist` и предупреждает,
если в `/catalog` остался русский ключ.
@@ -1827,6 +1830,106 @@ else{Lampa.Listener.follow('app',function(e){if(e.type=='ready')patchCubMain();}
Инжект залит (`plugin-cub.js`, 35566 Б в отдаваемой сборке). Нужен перезапуск
Lampa на ТВ и проверка: секции Watchlist появляются **поверх родного фида CUB**.
### 🔴🔴 ШЕСТАЯ СЕССИЯ: ИСТИННАЯ ПРИЧИНА «СЕКЦИИ НЕ ПОЯВИЛИСЬ» — `Manifest` НЕДОСТУПЕН ВНУТРИ ОБЁРТКИ
Первая версия `injectWatchlistParts` читала `Manifest.catalogs['watchlist']` **напрямую**.
Проба через CDP на живом ТВ показала:
```
out.cubMainSrc = "function (params, oncomplite, onerror) { original(params, function (cub_data) {
try { var wl = Manifest.catalogs['watchlist']; if (!..."
out.err = "ReferenceError: Manifest is not defined"
wlInjected: true ← обёртка СТОЯЛА
вызов main({}) → len: 4, titles: ["Сейчас смотрят","Рекомендуем посмотреть","Последнее добавление","Огонь!"]
← только родные CUB, НИ ОДНОЙ нашей
```
**Корень:** `Manifest` объявлен **локально в замыкании плагина** (`/catalog.js`, IIFE).
Обёртка вызывается из другого контекста → `ReferenceError` → `catch` → молча
`oncomplite(cub_data)` с родными секциями. Ошибка **не видна в UI и не пишется в логи**.
**Фикс:** не полагаться на замыкание — сохранять `menu` в глобальный `window.__wl_menu`
при `loadCatalogs()`, в обёртке читать оттуда, с фолбэком на прямой запрос `/catalog`:
```js
// в loadCatalogs, ПОСЛЕ Manifest.catalogs=json;
if(json['watchlist']&&json['watchlist'].menu)window.__wl_menu=json['watchlist'].menu;
function wlEnsureMenu(cb) {
if (window.__wl_menu && Object.keys(window.__wl_menu).length) return cb(window.__wl_menu);
network.silent(Utils.account(Manifest.api_host + '/catalog'), function (json) {
var m = (json && json['watchlist'] && json['watchlist'].menu) || {};
window.__wl_menu = m; cb(m);
}, function () { cb(window.__wl_menu || {}); });
}
```
`wlEnsureMenu` вызывается внутри обёртки вместо прямого `Manifest.catalogs['watchlist']`.
⚠️ **`Manifest.api_host` в `wlEnsureMenu` — тоже замыкание.** Фолбэк сработает только
если `__wl_menu` уже заполнен `loadCatalogs`; в норме он заполнен. Если `__wl_menu`
пуст И `Manifest` недоступен — вернётся `{}` и родной фид.
### Сборка плагина: `build_plugin.py` вместо `patch_cub_inject.py`
🔴 **`patch_cub_inject.py` отменён — он не идемпотентен.** Разрезание по первому
`function startPlugin` + поиск `end` вставляло `INJECT` **дважды** и оставляло файл
с нарушенным балансом скобок (`{`=123, `}`=122 → `node --check` → `SyntaxError`).
**Новый сборщик — `build_plugin.py`, детерминированный:**
1. берёт базу `plugin-patched.js`;
2. вставляет `window.__wl_menu` в `loadCatalogs`;
3. вырезает ранее вставленный INJECT по маркеру `/* ==== WL-INJECT-START ==== */`;
4. разбивает файл на `[до startPlugin]` + `[startPlugin…]` + `[хвост })();]`;
5. собирает заново: `before` + `INJECT` + `NEW_START` + `tail`.
**Инварианты в конце скрипта** (падает с `[x] ИНВАРИАНТ НАРУШЕН`, если нарушено):
- `WL-INJECT-START` = **1**
- `function patchCubMain()` = **1**
- `function startPlugin` = **1**
⚠️ `patchCubMain();` встречается **2 раза** (в `window.appready` и в `app:ready`-листенере) —
это норма, в инвариант не входит (первая версия проверки ошибочно требовала 1).
Проверка: `node --check plugin-cub.js` + баланс скобок `{`/`}` = 0.
Итог: **13054 Б**, сборка на сервере **35566 → 35440 Б** (зависит от длины `catalogs`-JSON).
### Перезапуск Lampa на ТВ — правильный `luna-send`
🔴 **`luna-send` через `ssh 'script -q -c "luna-send … -a \"{...}\""'` НЕ парсит аргументы** —
печатает help. Кавычки в кавычках рвутся. Рабочая форма — **одинарные кавычки внутри
`script -c`**, файлом (не inline):
```bash
ssh $TV 'script -q -c "luna-send -n 1 -f luna://com.webos.applicationManager/close -a '"'"'{"id":"com.lampac.tv"}'"'"'" /dev/null'
sleep 4
ssh $TV 'script -q -c "luna-send -n 1 -f luna://com.webos.applicationManager/launch -a '"'"'{"id":"com.lampac.tv"}'"'"'" /dev/null'
```
Скрипт: `/Users/admin/lampac-tmp/restart_lampa.sh` (закрыть → пауза 4с → запустить → 20с → показать devtools).
⚠️ **`docker restart lampac` НЕ перезагружает плагин на ТВ.** После заливки `plugin.js`
ТВ продолжает держать **старую версию в памяти** — `cubMainSrc` через CDP показывает
старый код. Обязателен **перезапуск приложения на ТВ**.
### Диагностика рантайма Lampa через CDP (websocket-client)
⚠️ Модуля `ws` в Node нет, `websockets` в Python нет → использовать **`websocket-client`**
(установлен). Скрипты `/Users/admin/lampac-tmp/probe_tv.py`, `probe_cub_main.py`.
```python
WS=$(curl -s "http://192.168.1.70:9998/json/list" | jq -r '.[] | select(.title=="Lampac") | .webSocketDebuggerUrl' | head -1)
python3 probe_cub_main.py "$WS"
```
`probe_cub_main.py` вызывает `Lampa.Api.sources.cub.main({}, cb)` и печатает
`titles` секций — **единственный надёжный способ увидеть, что реально отдаёт обёртка**.
Успех = 8 секций (4 родные CUB + 4 наши).
Полезные поля пробы `probe_tv.py`: `Lampa.Storage.field('source')`,
`Object.keys(Lampa.Api.sources)`, `Lampa.Api.sources.cub.__wl_injected`,
`typeof Lampa.Api.sources.cub.main`.
⚠️ `Manifest` внутри `Runtime.evaluate` **тоже не виден** (`ReferenceError: Manifest is not defined`) —
он в замыкании плагина. Не использовать в пробах.
## Питфоллы проверки на ТВ (сводка)
1. **Lampa кэширует манифест и ответы.** После правок — **полностью выгрузить