[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

+25 -7
View File
@@ -7,7 +7,7 @@ related:
- '[[family/projects/watchlist-sync]]'
- '[[family/projects/watchlist-automation]]'
- '[[family/documents/movies-watchlist]]'
status: accepted-in-ui-cub-sections-pending
status: cub-sections-patched-awaiting-tv-restart
tags:
- lampac
- lampa
@@ -68,23 +68,41 @@ updated: '2026-09-22'
> Залито, отдаётся в сборке `/catalog.js` (35566 Б). **Ожидает приёмки в UI.**
> Механика — [[personal/tech/lampac-deployment]] раздел «Внедрение секций Watchlist в CUB-фид».
>
> **Обновление 2026-09-22 (шестая сессия): 🔴🔴 ПРИЧИНА «В CUB НИХУЯ НЕ ПОМЕНЯЛОСЬ» — `Manifest` НЕДОСТУПЕН В ОБЁРТКЕ.**
> Alex: «Ты вообще нихуя не поменял!!». Проверка через **CDP-пробу на живом ТВ** подтвердила
> его правоту: обёртка `Api.sources.cub.main` стояла (`__wl_injected: true`), но вызов
> `cub.main({})` возвращал **4 родные секции** и **ни одной нашей**.
> **Корень:** первая версия инжекта читала `Manifest.catalogs['watchlist']` напрямую, а
> `Manifest` объявлен **локально в замыкании плагина** → `ReferenceError: Manifest is not defined`
> → `catch` → молча отдавался родной фид. **Ошибки нет ни в UI, ни в логах.**
> **Фикс:** `window.__wl_menu` (заполняется в `loadCatalogs`) + `wlEnsureMenu()` с фолбэком
> на запрос `/catalog`. Переписан сборщик: **`build_plugin.py`** (детерминированный,
> с инвариантами `INJECT=1`/`def=1`/`startPlugin=1`, `node --check` + баланс скобок),
> вместо неидемпотентного `patch_cub_inject.py` (он вставлял INJECT дважды и ломал скобки).
> Залито (сборка **35440 Б**), **ждёт перезапуска Lampa на ТВ** — `docker restart lampac`
> НЕ перезагружает плагин в памяти ТВ.
> Механика + CDP-пробы — [[personal/tech/lampac-deployment]] раздел
> «🔴🔴 ШЕСТАЯ СЕССИЯ: ИСТИННАЯ ПРИЧИНА…».
> **Обновление 2026-09-22 (пятая сессия): ✅✅ ПРИЁМКА ALEX ПРОЙДЕНА — ФИЛЬМЫ ОТКРЫВАЮТСЯ И ПРОИГРЫВАЮТСЯ.**
> Дословно: «фильмы из списка Watchlist теперь открываются и проигрываются».
> Закрыт последний блокер — кириллический ключ плагина (`plugin: watchlist`).
> Плюс устранён питфолл `Staticache` (кэш собранного `/catalog.js` переживал
> заливку и выглядел как «патч не применяется» → `docker restart lampac`).
>
> **Осталось (2026-09-22, конец сессии):**
> 1. **5 фильмов с Кинопоиск-ID** (`film/NNNNN/`) — «Рассекая волны: экспедиция „Лас Бальсас“»,
> «Хамнет», «Цена вершины», «Астрал. Могильный ритуал 2», «О добре и зле».
> **Осталось (2026-09-22, конец шестой сессии):**
> 1. **Секции CUB в UI** — 🔴 инжект залит, но требовал **перезапуска Lampa на ТВ**
> (причина «ничего не поменялось» — `Manifest` в обёртке, см. выше). После перезапуска
> проверить через `probe_cub_main.py`: должно быть **8 секций** (4 родные CUB + 4 наши).
> 2. **5 фильмов с Кинопоиск-ID** (`film/NNNNN/`) — «Рассекая волны: экспедиция „Лас Бальсас“»,
> «Хамнет», «Эхопраксия», «Цена вершины», «Астрал. Могильный ритуал 2», «О добре и зле»,
> WeWork (2024).
> TMDB не нашёл (2025 год) → нет карточки и постера → клик даёт «здесь пусто».
> **Нужен выбор Alex:** заглушка с кнопкой поиска торрента либо дорисовать `tmdb_id`.
> 2. **~51 название без постера** — проблема ЗАПИСИ В СПИСКЕ, не скрипта:
> 3. **~51 название без постера** — проблема ЗАПИСИ В СПИСКЕ, не скрипта:
> `Плохие парни 1/2` (ДВА фильма одной строкой), `Солярис Тарковский` (уточнение
> режиссёра), `Самолетом, поездом, машиной (1987)` (русский аналог).
> Генератор такое угадать не может — **правится только список руками**.
> 3. **Секции CUB в UI** — инжект залит, нужен перезапуск Lampa и визуальная проверка:
> секции Watchlist должны появиться поверх родного фида CUB при активном источнике CUB.
>
> **Обновление 2026-09-22 (четвёртая сессия, ранее): ✅ ЧИСЛОВОЙ `tmdb_id` В КАРТОЧКАХ.**
> 352 из 352 карточек отдают `tmdb_id` числом, `source`, `runtime`, `vote_average`, жанры.
+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 кэширует манифест и ответы.** После правок — **полностью выгрузить