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

This commit is contained in:
Alexey Martemyanov committed 2026-09-25 22:10:55 +06:00
1 parent a0d298568c
commit aed8c42aa3
1 file changed
+162 -18
+162 -18
View File
@@ -8,7 +8,7 @@ related:
- '[[family/projects/watchlist-automation]]'
- '[[family/documents/movies-watchlist]]'
- '[[personal/plans/lampac-watched-to-watchlist]]'
status: investigation-2026-09-25-all-screens-feed-and-latency
status: main-screen-filter-implemented-2026-09-25-awaiting-tv-acceptance
tags:
- lampac
- lampa
@@ -26,6 +26,7 @@ tags:
- matcher-parity
- partnext
- latency
- wlonmain
title: Watchlist в UI Lampa — каталог из списка
type: plan
updated: '2026-09-25'
@@ -41,14 +42,82 @@ updated: '2026-09-25'
прогружается относительно основного CUB списка, почему он не моментально проявляется,
он же статичный».
**Статус: расследование завершено, причина установлена, фикс НЕ применён (ждёт апрува).**
**Статус: ✅ ФИКС ПРИМЕНЁН И ЗАЛИТ 2026-09-25. Ждёт только перезапуска Lampa на ТВ
(ТВ `192.168.1.70` был выключен — ни ping, ни CDP-порт 9998 не отвечали).**
### Причина 1 — фид на всех экранах: в `plugin-cub.js` НЕТ фильтра по экрану
### ✅ РЕШЕНИЕ: `wlOnMain()` — строгий белый список по активному activity
Файл: `/Users/admin/lampac-tmp/plugin-cub.js` (196 строк, сборка `build_plugin.py`;
Alex: «В смысле всё кроме? **Только главный экран!**» — предикат сделан ЖЁСТКИМ
(`=== 'main'`), не «всё кроме списка исключений». Любое незнакомое имя activity
обязано по умолчанию секции НЕ получать.
```js
function wlOnMain() {
try {
var a = (window.Lampa && Lampa.Activity && Lampa.Activity.active)
? Lampa.Activity.active() : null;
var act = (a && a.activity && a.activity.url) ? a.activity.url : '';
if (act === 'main') return true;
console.log('[watchlist] не главная (' + (act || '?') + ') — секции пропущены');
return false;
} catch (e) {
console.log('[watchlist] wlOnMain error', e);
return false;
}
}
```
Гард в перехватчике (`plugin-cub.js:193`):
```js
if (wlOnMain() && Lampa.Arrays.isArray(parts) && !parts.__wl_done) { ... wlInject(parts, menu); }
```
🔴 **Второе, неочевидное изменение:** `parts.__wl_done = true` теперь ставится ТОЛЬКО
внутри ветки фактической вставки. Раньше флаг мог выставиться до готовности
`window.__wl_menu` — и тогда вставка была бы закрыта НАВСЕГДА, главная осталась бы без
секций после возврата из категории.
**Файлы:**
- `/Users/admin/lampac-tmp/plugin-cub.js` — рабочая сборка (бэкап
`plugin-cub.js.bak-before-filter-<ts>`)
- `/Users/admin/lampac-tmp/build_plugin.py` — тот же текст в константе `INJECT`
(сборщик детерминированный; `plugin-cub.js` руками не правим) + **2 новых инварианта**:
`wlOnMain def` и `wlOnMain guard` — сборка падает, если фильтр пропал
**Проверки перед заливкой (все пройдены):**
```
python3 build_plugin.py → 10/10 инвариантов, 17241 байт
node --check plugin-cub.js → SYNTAX OK
```
**Деплой (выполнен):**
1. бэкап боевого — `catalog/plugin.js.bak-before-mainfilter-20260925-220809` (17476 Б)
2. `scp plugin-cub.js kraken:...6194539b.../lampac/catalog/plugin.js` → 19576 Б,
md5 `53ccdc306cf6a14f343eaeb67a8a29c3`
3. bind-mount проверен: `docker exec lampac grep -c wlOnMain
/lampac/module/Catalog/plugin.js` → **3**
4. по HTTP тоже отдаётся: `curl http://127.0.0.1:9118/catalog.js` → 41050 Б, `wlOnMain` ×3
**Осталось — перезапуск Lampa на ТВ** (webOS кэширует `catalog.js`, `docker restart
lampac` его НЕ сбрасывает):
```bash
scp /Users/admin/lampac-tmp/tv_restart.sh root@192.168.1.70:/tmp/ \
&& ssh root@192.168.1.70 'sh /tmp/tv_restart.sh'
```
**Приёмка = видно в UI** (не «API отвечает»): главная — 4 секции Watchlist сверху;
Фильмы → документальные — секций НЕТ. Если фид вылезет ещё где-то — в логе ТВ будет
строка `[watchlist] не главная (<имя>)`, по ней добавить имя в разбор.
**Что НЕ трогалось:** `window.__wl_menu`, `wlGet`/`wlAbs`/`wlHost`, `unshift`,
`watchlist.yaml`, `sync_down.py`, nightly-пайплайн.
### Причина 1 (диагноз) — в `plugin-cub.js` НЕ БЫЛО фильтра по экрану
Файл: `/Users/admin/lampac-tmp/plugin-cub.js` (сборка `build_plugin.py`;
это тот же файл, что залит в Lampac как `module/Catalog/plugin.js`).
`wlPatchPartNext()` (строки 155–193) — перехват глобального `Lampa.Api.partNext`:
`wlPatchPartNext()` — перехват глобального `Lampa.Api.partNext` — до фикса выглядел так:
```js
Lampa.Api.partNext = function (parts, parts_limit, partLoaded, partEmpty) {
@@ -61,18 +130,16 @@ Lampa.Api.partNext = function (parts, parts_limit, partLoaded, partEmpty) {
}
```
**Ни одного обращения к активному экрану** — нет проверки `Lampa.Activity.active()`,
`activity.url`, `Lampa.Storage.field('source')` или чего-либо подобного.
Механика: `partNext` — глобальный API, через него строят страницу ВСЕ источники.
`plugin.js:30` (`main()`) и `plugin.js:36` (`category()`) **оба** вызывают
**Ни одного обращения к активному экрану** — это не регресс, а исходное отсутствие
проверки. Механика: `partNext` — глобальный API, через него строят страницу ВСЕ
источники. `plugin.js:30` (`main()`) и `plugin.js:36` (`category()`) **оба** вызывают
`Lampa.Api.partNext(parts_data, parts_limit, ...)`. Отсюда:
- CUB рисует главную → перехват срабатывает → наши 4 секции сверху;
- вход в категорию («документальные» внутри Фильмов) → Lampa перестраивает страницу
через **тот же** `partNext` → перехват срабатывает снова → фид снова сверху.
`wlInject()` (строки 121–127) кладёт секции через `unshift` в **начало** пула, поэтому
они рисуются поверх содержимого любой категории — ровно то, на что жалуется Alex.
`wlInject()` кладёт секции через `unshift` в **начало** пула, поэтому они рисуются
поверх содержимого любой категории — ровно то, на что жаловался Alex.
### Причина 2 — «долго грузится»: замеры (не догадки)
@@ -134,16 +201,25 @@ Lampa.Api.partNext = function (parts, parts_limit, partLoaded, partEmpty) {
### Открытые шаги (для следующей сессии)
1. **Фикс «фид на всех экранах»:** добавить в `wlPatchPartNext` проверку активного экрана —
инжектить только когда `Lampa.Activity.active()` указывает на главную, иначе
пропускать. Точный предикат определить CDP-пробой на живом ТВ (`probe_ui.py`):
прочитать `Lampa.Activity.active()` на главной и внутри категории, сравнить.
1. ✅ **ЗАКРЫТО 2026-09-25** — фикс «фид на всех экранах»: `wlOnMain()` + гард в
`wlPatchPartNext`, залито на Кра́кен. Осталась приёмка на ТВ (перезапуск Lampa +
визуальная проверка главной и «Фильмы → документальные»).
2. **Диагностировать 66 с на карточке** — `docker logs lampac` + трейс `CardController`.
Единственный замеренный источник ощущения «не моментально», который НЕ объясняется
холодным `hybridCache` (2.25 с). Запрос:
`GET /catalog/card?plugin=watchlist&uri=movie%2F424781&type=movie` → 66 118 мс, 939 Б.
3. **Matcher-parity** (главный блокер с 2026-09-24, см. ниже) — всё ещё не закрыт.
**Правило диагностики этой сессии:** не читать `%{size_download}` как «размер фида»
(это gzip). И — `ifconfig` на Кра́кене отсутствует, ssh-команды через `bash -c` иначе
падают шумом.
(это gzip) — брать `ls -la` или `Accept-Encoding: identity`. И — `ifconfig` на Кра́кене
отсутствует: каждая ssh-команда печатает `bash: line 1: ifconfig: command not found`
безвредным шумом, это НЕ ошибка команды.
**🔴 Питфолл инструментов этой сессии:** `execute_code` падал на `import`
(`No module named 'psutil'`) — расследование и правки сделаны через `terminal` +
`read_file` + `patch`, БЕЗ python-скриптов. `scp` файла `wwwroot/catalog.js` не нашёл:
такого пути НЕТ, `plugin.js` живёт в `lampac/catalog/plugin.js` и монтируется в
`/lampac/module/Catalog/plugin.js`.
---
@@ -492,6 +568,74 @@ Alex уточнил требование: «это же чистая дораб
---
## 🟢 ВОСЬМАЯ СЕССИЯ 2026-09-25: ФИЛЬТР «СЕКЦИИ ТОЛЬКО НА ГЛАВНОЙ» (PLAN)
**Задача Alex:** «watchlist фид отображается не только на главной, но даже когда я
выбираю категорию например "документальные" в разделе Фильмы» + «он как будто долго
прогружается относительно основного CUB списка». Требование: **ТОЛЬКО ГЛАВНЫЙ ЭКРАН**
(белый список, не чёрный — Alex: «В смысле всё кроме? Только главный экран!»).
### Диагноз (по коду, замеры)
1. **Причина «на любом экране».** `plugin-cub.js:164` — `wlPatchPartNext` вставляет
секции БЕЗУСЛОВНО:
```js
if (Lampa.Arrays.isArray(parts) && !parts.__wl_done) {
var menu = window.__wl_menu;
if (menu && Object.keys(menu).length) { parts.__wl_done = true; wlInject(parts, menu); }
```
`Lampa.Api.partNext` — глобальный: через него строят страницу И `main()`
(`plugin.js:30`), И `category()` (`plugin.js:36`). Никакой проверки активного
экрана нет → `unshift` кладёт секции в голову пула на КАЖДОМ экране.
Фильтра по activity в коде НЕ БЫЛО — это не регресс, а исходное отсутствие.
2. **Замеры скорости (Kraken, `127.0.0.1:9118`):**
- `catalog/list?cat=films` прогретый кэш — **4 мс**; первый холодный — **2 250 мс**
(`hybridCache` модуля Catalog парсит JSON целиком при первом запросе)
- `watchlist-films.json` (чистая статика) — 1 мс
- 🔴 `catalog/card?uri=movie/424781` — **66 118 мс (66 сек!)** на 939 байт.
Это НЕ диск и не фид: `card_parse.initUrl` исполняется как C# и ждёт внутри
CardController. Отдельный баг, в эту правку не входит.
- ⚠️ `curl -w %{size_download}` показывает размер **после** распаковки gzip —
на этом легко обмануться: `watchlist-films.json` = 88 КБ несжатых при 347
позициях, а `cards.json` = **548 КБ**. Реальный объём фида в разы больше «видимого».
### Решение
Белый список по активному activity Lampa:
```js
function wlOnMain() {
try {
var a = Lampa.Activity && Lampa.Activity.active && Lampa.Activity.active();
var act = (a && a.activity && a.activity.url) ? a.activity.url : '';
return act === 'main'; // ТОЛЬКО главная
} catch (e) { return false; }
}
```
Плюс `parts.__wl_done = true` ставить ТОЛЬКО при фактической вставке (иначе после
возврата из категории на главную секции не доберутся). `console.log` при пропуске —
для отладки на живом ТВ.
### Шаги
1. правка `plugin-cub.js` + `build_plugin.py` (INJECT + новый инвариант `wlOnMain`)
2. `python3 build_plugin.py && node --check plugin-cub.js`
3. бэкап боевого `catalog/plugin.js` → `scp` нового
4. **перезапуск Lampa на ТВ обязателен** (`tv_restart.sh` через `luna-send`;
`docker restart lampac` кэш webOS не сбрасывает)
5. 🔴 приёмка = **видно в UI**: главная — 4 секции сверху; Фильмы → документальные —
секций НЕТ. CDP-проба читает `Lampa.Activity.active().activity.url` на обоих экранах.
### Питфоллы, найденные при расследовании (не повторять)
- `ssh kraken` на каждом вызове печатает `bash: line 1: ifconfig: command not found` —
это безвредный шум из `~/.bashrc`, НЕ ошибка команды.
- 🔴 `execute_code` в этой сессии падает на `import` (`No module named 'psutil'`) —
работаем через `terminal` + `read_file` + `jq`, БЕЗ python.
- Обрыв `scp` из-за не найденного пути: `wwwroot/catalog.js` НЕ существует (в wwwroot
лежат только JSON фида) — `plugin.js` живёт в `lampac/catalog/plugin.js` и
монтируется в `/lampac/module/Catalog/plugin.js`.
## 🔴🔴 СЕДЬМАЯ СЕССИЯ: РАБОЧИЙ МЕХАНИЗМ НАЙДЕН — ПЕРЕХВАТ `Lampa.Api.partNext`
**Статус: ✅ секции Watchlist рендерятся ВНУТРИ главной CUB. Проверено на живом ТВ через CDP.**