diff --git a/personal/plans/lampac-watchlist-plugin.md b/personal/plans/lampac-watchlist-plugin.md index 35a79745..93b23ab2 100644 --- a/personal/plans/lampac-watchlist-plugin.md +++ b/personal/plans/lampac-watchlist-plugin.md @@ -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-`) +- `/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.**