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

This commit is contained in:
Alexey Martemyanov committed 2026-09-25 22:41:20 +06:00
1 parent f295d5e903
commit f184b707a1
2 files changed
+208 -13

No files matched your search

+80 -4
View File
@@ -43,7 +43,7 @@ related:
| Слой | Путь | Примечание |
|---|---|---|
| **Mac, репо** | `~/Developer/lampac-scripts/scripts/build_plugin.py` | сборщик, 12 инвариантов |
| **Mac, репо** | `~/Developer/lampac-scripts/scripts/build_plugin.py` | сборщик, 14 инвариантов |
| **Mac, репо** | `~/Developer/lampac-scripts/scripts/plugin-cub.js` | 🔴 собранный выход, РУКАМИ не правим |
| Mac, скрэтч | `/Users/admin/lampac-tmp/` | рабочая папка, вне git — не источник истины |
| Кра́кен, хост | `/srv/dev-disk-by-uuid-6194539b-.../lampac/catalog/plugin.js` | bind-mount, вне git |
@@ -71,6 +71,27 @@ git commit -m "..." && git push origin main
(`watchlist.json`, `watchlist-<cat>.json`, `cards.json`, `cards/`, `missing.json`).
Попытка `scp` оттуда падает с «No such file or directory» — это не сбой сети.
### 🔴 CDP-пробы на ТВ (порт 9998) — питфоллы, стоившие сессии
| Питфолл | Симптом | Как правильно |
|---|---|---|
| **Цель искать по `url`, а не `title`** | сразу после рестарта Lampa `title=""`, `url=about:blank` → скрипт молча не находит цель, проба даёт пустоту | выбирать `"com.lampac.tv" in t.get("url")`, НЕ `title == "Lampac"` |
| **Первый замер — гонка** | `ready=false, plugin=false, patch=false, act_url=ERR` выглядит как «плагин не доехал» | подождать/опрашивать `ready`, пока не станет `true` |
| **`JSON.stringify(active())` падает** | `TypeError: Converting circular structure to JSON` | собирать поля по одному или строку вручную |
| **Отсутствующее поле без обёртки** | CDP отвечает без `result.value` — выглядит как «все пробы пустые» (получили 9 `<none>` подряд) | каждую пробу оборачивать в `String(...)` / `typeof ...`, проверять `subtype == "error"` |
| **`Log.enable` + `Runtime.enable` дают 0 строк** | консольные сообщения, напечатанные ДО подключения, не отдаются | читать логи СРАЗУ после рестарта, либо подтверждать состояние пробами, а не логами |
⚠️ **`docker restart lampac` НЕ сбрасывает кэш `catalog.js` в браузере webOS** — нужен
рестарт приложения на ТВ через `luna-send`. И **не спать** в ожидании: Alex на паузу
`sleep 20` отреагировал резко («Чё нахуй за слип 20?!») — вместо сна опрашивать состояние.
🔴 **Правило диагностики:** не читать `curl -w %{size_download}` как «размер фида» —
это размер ПОСЛЕ распаковки gzip. Брать `ls -la` или `Accept-Encoding: identity`.
Пример: `watchlist-films.json` = 88 КБ несжатых при 347 позициях, а `cards.json` = 548 КБ.
⚠️ **`ifconfig` на Кра́кене отсутствует** — каждая `ssh kraken` печатает
`bash: line 1: ifconfig: command not found` безвредным шумом. Это НЕ ошибка команды.
### Порядок деплоя
```
@@ -93,6 +114,59 @@ ssh kraken 'docker exec lampac grep -c "<маркер>" /lampac/module/Catalog/p
следующая сборка затрёт правку. Инварианты в `checks{}` обязаны покрывать любую
новую функцию — иначе регресс пройдёт молча.
### 🔴🔴 Как определить активный экран Lampa — по `component`, НЕ по `url`
**ИЗМЕРЕНО на живом ТВ 2026-09-25.** Форма объекта:
```
Lampa.Activity.active() → {url, title, component, source, page, params, activity}
Lampa.Activity.active().activity → {component, object, slide, body, is_started, is_stopped}
```
🔴 **На главной `url` ПУСТОЙ.** Реальный замер в момент, когда открыта главная
(`?component=main&source=cub&page=1`):
```
url = "" title = "Главная - CUB" component = "main"
source = "cub" page = 1
```
`url === 'main'` **не срабатывает никогда.** Если строить фильтр секций на `url`,
он либо не работает, либо работает случайно — через смягчающую ветку «пусто +
`Manifest.catalogs` пуст». Как только манифест заполнится, **фильтр начнёт резать
саму главную** и секции исчезнут.
**Правильное поле — `component`:**
| Экран | `component` | `url` |
|---|---|---|
| Главная | `main` | `""` (пусто) |
| Категория / Фильмы | `category_full` / `category` | `movie`, `cartoons`, … |
```js
function wlComp(a) {
if (!a) return '';
if (a.component) return String(a.component);
if (a.activity && a.activity.component) return String(a.activity.component);
return '';
}
// фильтр: if (wlComp(Lampa.Activity.active()) === 'main') → вставляем
```
⚠️ `url` при этом остаётся полезным для другого: на экране Фильмов он даёт
`"movie"`. Замер `act_url = "movie"` использовался как доказательство работы фильтра.
### 🔴 `Manifest.catalogs` и `window.__wl_menu` рассинхронизированы
Меряно одновременно: `menu = Новинки,Фильмы,Мультфильмы,Аниме` при
`Manifest.catalogs = NOMANIFEST`. То есть `__wl_menu` заполнен, а штатный манифест —
нет. **На `loadCatalogs()` полагаться нельзя**, для меню нужен независимый запрос
(`wlPrefetch()` в `startPlugin()`, параллельно `loadCatalogs()`).
Без предзагрузки первый `partNext` застаёт `__wl_menu` пустым → уходит в
асинхронный `wlMenu()` → пока летит запрос, родной `cub.main()` уже отрисовал свои
секции и `slice(0, parts_limit)` отработал → наши секции попадают в пул только
следующим раундом. Alex видел это как «есть, но сильно после первых секций».
### 🔴 Перезапуск Lampa на ТВ обязателен
`docker restart lampac` **НЕ сбрасывает** кэш `catalog.js` в браузере webOS — ТВ
@@ -1724,10 +1798,12 @@ node --check plugin-cub.js
`lampac-tmp` вне git, файлы там не переживут чистку. Коммит: `5e55a47`.
Детерминирован: режет `[до startPlugin] + INJECT + [новый startPlugin] + '})();'`,
проверяет инварианты. **Текущий набор (12):** `INJECT=1`, `partNext def=1`,
проверяет инварианты. **Текущий набор (14):** `INJECT=1`, `partNext def=1`,
`wlInject def=1`, `unshift (не push)=1`, `push в INJECT=0`, `startPlugin=1`,
`нет network.silent в INJECT=0`, плюс добавленные 2026-09-25 —
**`wlOnMain def=1`, `wlOnMain guard=1`, `wlUrl def=1`, `wlUrl верхний уровень=1`**.
`нет network.silent в INJECT=0`, `wlOnMain def=1`, `wlComp def=1`,
`wlComp читает component=1`, `wlOnMain guard=1`, `wlPrefetch def=1`,
`wlPrefetch call=3`. Инварианты `wlUrl*` **убраны** вместе с `wlUrl()` —
поле `url` оказалось не тем (см. раздел про `component`).
🔴 **Правило: новая функция в INJECT = новый инвариант в `checks{}`.** Иначе
регресс (например, потерянный фильтр по экрану) пройдёт сборку молча и всплывёт