[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

+128 -9
View File
@@ -8,7 +8,7 @@ related:
- '[[family/projects/watchlist-automation]]'
- '[[family/documents/movies-watchlist]]'
- '[[personal/plans/lampac-watched-to-watchlist]]'
status: main-screen-filter-WORKS-2026-09-25-accepted-committed-5e55a47-prefetch-pending
status: third-fix-2026-09-25-component-filter+wlPrefetch-deployed-awaiting-verdict
tags:
- lampac
- lampa
@@ -444,14 +444,8 @@ cats = NOMANIFEST
1. ✅ **ПРИЁМКА ПРОЙДЕНА 2026-09-25** — фильтр `wlOnMain()` работает, фид виден только
на главной. Доказательство: на экране `movie` секций в DOM нет при заполненном `menu`.
2. 🔄 **ПРЕДЗАГРУЗКА `__wl_menu`** — единственный незакрытый пункт этой задачи,
выполняется сразу после коммита `5e55a47`. Корень отставания — асинхронный `/catalog`
в `loadCatalogs()`: меню приходит ПОСЛЕ первого `partNext`. Решение: подтягивать
`menu` синхронно в `startPlugin()`, параллельно с `loadCatalogs()`, чтобы `wlInject`
срабатывал в первом раунде среза. Правка идёт в те же два файла в репе
(`lampac-scripts/scripts/plugin-cub.js` + `INJECT` в `build_plugin.py`) + новый
инвариант сборщика. Порядок деплоя — как обычно: сборка → `node --check` → бэкап →
`scp` → рестарт Lampa на ТВ → замер CDP.
2. 🔄 **ПРЕДЗАГРУЗКА `__wl_menu`** — ВЫПОЛНЕНА, но НЕ ЗАКРЫТА: см. «ТРЕТИЙ ФИКС»
ниже. Требуется ещё один заход (фильтр по `component` + вставка при возврате).
3. **Диагностировать 66 с на карточке** — `docker logs lampac` + трейс `CardController`.
Запрос: `GET /catalog/card?plugin=watchlist&uri=movie%2F424781&type=movie`
→ 66 118 мс, 939 Б.
@@ -460,6 +454,131 @@ cats = NOMANIFEST
Развилка «`lampac_plugin/` в `watchlist-sync` vs бэкапы» **закрыта**: плагин
принадлежит `lampac-scripts` (там же `gen_watchlist.py`, `deploy_lampac.py`).
---
## 🔴🔴 2026-09-25 (финал): ТРЕТИЙ ФИКС — фильтр по `component` + `wlPrefetch()`
### Приёмка Alex частичная: два новых симптома
Alex после заливки `wlPrefetch`:
> «Так, хорошо, Сейчас при загрузке приложения сработало, коммить.
> Но когда я с главной ушел в раздел фильмы там снова были watchlist разделы
> А когда вернулся на главную опять разделы watchlist не сверху»
То есть: **при холодном старте — правильно** (секции сверху, фильтр держит), но
навигация ломает обе части: уход в «Фильмы» → секции появились; возврат на главную →
секции не сверху.
### 🔴 ГЛАВНАЯ НАХОДКА: на главной `url` ПУСТОЙ, а `component === 'main'`
Замер на живой Lampa (CDP) в момент, когда открыта именно главная
(`?component=main&source=cub&page=1`):
```
active_full = url= | title=Главная - CUB | component=main | source=cub | page=1 | params=obj | activity=obj
act_url_direct = "" ← url ПУСТОЙ, а не 'main'
act_act_url = undefined ← вложенный activity.url тоже пуст
menu = Новинки,Фильмы,Мультфильмы,Аниме
cats = NOMANIFEST
```
🔴 **Вывод: проверка `url === 'main'` НЕ РАБОТАЕТ ВООБЩЕ.** Она не срабатывала ни разу —
её проходила только смягчающая ветка `if (!act) { if (!catalogs) return true; }`.
Как только `Manifest.catalogs` заполнится, **фильтр начал бы резать саму главную**.
Это была мина, а не рабочее условие.
**Правильное поле — `component`:** главная `→ 'main'`, категория `→ 'category_full'` /
`'category'`. Оно стабильно на обоих экранах, в отличие от `url`.
```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 '';
}
// в wlOnMain(): if (comp === 'main') return true;
// смягчающая ветка: if (!comp) { пустые catalogs → true }
```
### Замер, доказавший что секции ВСЁ-ТАКИ вставлялись
```
cat_header = Сейчас смотрят | Рекомендуем посмотреть | Последнее добавление | Огонь! |
В высоком качестве | Новинки | Фильмы | Мультфильмы | Аниме | Фэнтези | ...
↑↑↑↑↑↑↑↑ наши 4 секции — ЕСТЬ, но ПОСЛЕ родных
wl_done = false
```
То есть на главной секции присутствовали, но стояли **после** «В высоком качестве» —
ровно тот симптом «сильно после первых секций», который Alex и называл.
### `wlPrefetch()` — предзагрузка меню при старте
```js
function wlPrefetch() {
if (window.__wl_prefetched) return;
window.__wl_prefetched = true;
wlMenu(function (m) { console.log('[watchlist] меню предзагружено: ' + Object.keys(m || {}).join(',')); });
}
// в startPlugin(), рядом с wlPatchPartNext():
// if(window.appready){wlPatchPartNext();wlPrefetch();} else { … 'ready' → wlPatchPartNext();wlPrefetch(); }
```
`/catalog` тянется **параллельно** `loadCatalogs()`, чтобы `__wl_menu` лежал в памяти
к моменту первой отрисовки главной. Меряно: `menu` заполнен при
`Manifest.catalogs = NOMANIFEST` → **на `loadCatalogs()` полагаться нельзя**, нужен
независимый запрос.
### Состояние сборки и деплоя
```
python3 build_plugin.py → 14/14 инвариантов, 18947 байт
node --check plugin-cub.js → SYNTAX OK
```
**Инварианты (14):** `INJECT=1`, `partNext def=1`, `wlInject def=1`,
`unshift (не push)=1`, `push в INJECT=0`, `startPlugin=1`,
`нет network.silent в INJECT=0`, **`wlOnMain def=1`**, **`wlComp def=1`**,
**`wlComp читает component=1`**, **`wlOnMain guard=1`**, **`wlPrefetch def=1`**,
**`wlPrefetch call=3`**. Инварианты `wlUrl def` / `wlUrl верхний уровень` — **убраны**
вместе с `wlUrl()` (поле оказалось не тем).
**Деплой выполнен:** бэкап `catalog/plugin.js.bak-before-comp-<ts>` →
`scp` (18947 Б) → проверка в контейнере
`docker exec lampac grep -c "wlPrefetch\|wlComp" /lampac/module/Catalog/plugin.js` → **4**
→ рестарт Lampa на ТВ через `luna-send` (`--- закрываю --- --- запускаю --- --- готово ---`).
### 🔴 Что ОСТАЛОСЬ (следующая сессия, начать отсюда)
**Гипотезы по двум симптомам (НЕ проверены, гадать не нужно — мерить):**
1. **Уход в «Фильмы» → секции снова появились.** Переход идёт через `category()` /
`category_full` (`plugin.js:36`). Фильтр по `component` там должен давать `false`,
НО `component` на экране Фильмов в замере **не логировался** — на тот момент
фиксировался только `url`. Возможные варианты: `component` на Фильмах тоже
`'main'`; либо вставка на этом экране идёт не из `partNext`.
→ Проверить `component` на экране Фильмов ОТДЕЛЬНОЙ пробой.
2. **Возврат на главную → секции не сверху.** `wlPrefetch()` **одноразовый**
(`__wl_prefetched`). На возврате `__wl_menu` уже заполнен, но `wlInject` не
срабатывает в первом раунде; плюс `unshift` в голову `parts` работает, только если
`partNext` вызван **заново с новым массивом** — на возврате Lampa может
переиспользовать уже отрисованный пул.
→ Нужен трейс порядка вызовов `partNext` на «главная → Фильмы → главная»
(`console.log` с номерами вызовов).
**Порядок работ:** сначала ЗАМЕР по обоим пунктам (пока Lampa в текущем состоянии,
без рестарта), потом правка, потом обычный цикл: сборка → `node --check` → бэкап →
`scp` → рестарт Lampa на ТВ → замер CDP → приёмка Alex.
**Коммит правки (третий фикс) НЕ сделан** — апрув истёк по таймауту. Файлы лежат в
`/Users/admin/lampac-tmp/`, в `~/Developer/lampac-scripts/scripts/` ещё НЕ скопированы.
После приёмки — скопировать и закоммитить тем же порядком, что `5e55a47`.
**Правило:** `git commit` через `ssh`/длинную команду может уйти в таймаут апрува —
подтверждение Alex обязательно, повторять команду автоматически нельзя.
**Правило диагностики этой сессии:** не читать `%{size_download}` как «размер фида»
(это gzip) — брать `ls -la` или `Accept-Encoding: identity`. И — `ifconfig` на Кра́кене
отсутствует: каждая ssh-команда печатает `bash: line 1: ifconfig: command not found`
+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{}`.** Иначе
регресс (например, потерянный фильтр по экрану) пройдёт сборку молча и всплывёт