[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 23:26:51 +06:00
1 parent bbfe0329e2
commit ac78b0b7a2
2 files changed
+181 -28

No files matched your search

+91 -24
View File
@@ -8,7 +8,7 @@ related:
- '[[family/projects/watchlist-automation]]' - '[[family/projects/watchlist-automation]]'
- '[[family/documents/movies-watchlist]]' - '[[family/documents/movies-watchlist]]'
- '[[personal/plans/lampac-watched-to-watchlist]]' - '[[personal/plans/lampac-watched-to-watchlist]]'
status: fourth-fix-2026-09-25-activity-hook-deployed-awaiting-verdict status: fifth-fix-2026-09-25-global-inject-flag-deployed-awaiting-verdict
tags: tags:
- lampac - lampac
- lampa - lampa
@@ -38,6 +38,59 @@ updated: '2026-09-25'
## 🔍 2026-09-25: РАССЛЕДОВАНИЕ — фид виден на всех экранах + «долго грузится» ## 🔍 2026-09-25: РАССЛЕДОВАНИЕ — фид виден на всех экранах + «долго грузится»
## 🟡 2026-09-25 (продолжение): ДУБЛИ СЕКЦИЙ — «watchlist снова уплыл вниз»
**Симптом Alex:** «Ну чето нифига не поменялось. Главная - фильмы - главная -
watchlist снова уплыл вниз».
**Диагноз по DOM-замеру — не «уплыл», а ДУБЛЬ.** В `document.querySelectorAll('.items-line')`
оказалось **13 заголовков вместо 9**:
```
0:Новинки 1:Фильмы 2:Мультфильмы 3:Аниме ← наши (вверху, верно)
4:Сейчас смотрят ... 8:В высоком качестве ← родные CUB
9:Новинки 10:Фильмы 11:Мультфильмы 12:Аниме ← НАШИ ВТОРОЙ РАЗ (внизу)
```
Визуально нижний комплект читается как «секции уплыли вниз», но это **второй экземпляр**:
первый остался наверху, Lampa его не удаляет.
### 🔴 КОРНЕВАЯ ПРИЧИНА: флаг вставки велся на МАССИВЕ, а не глобально
Было: `if (... && !parts.__wl_done) { parts.__wl_done = true; wlInject(...) }`.
🔴 **`parts` приходит НОВЫМ массивом на каждый вызов `partNext`** — на нём нет пометки
от прошлого вызова, поэтому `!parts.__wl_done` **истинно всегда**, и `wlInject`
добавлял второй комплект через `unshift`. Флаг, повешенный на объект пула, не работает.
Плюс `window.__wl_done = false` в хуке сбрасывал **не ту переменную**: проба показала
`wl_done = None` (undefined в глобальной области) — хук не влиял ни на что.
### ✅ ФИКС: глобальный `window.__wl_injected`
```js
// wlPatchPartNext:
if (wlOnMain() && Lampa.Arrays.isArray(parts) && !window.__wl_injected) {
window.__wl_injected = true; // ГЛОБАЛЬНО, не на parts
wlInject(parts, menu);
}
// wlWatchActivity: при уходе с главной разрешаем вставку при возврате
if (comp !== 'main') { window.__wl_injected = false; return; }
```
Логика: ушли с главной → флаг снят; вернулись → вставили ОДИН раз; повторные вызовы
`partNext` на той же главной → флаг держит, дублей нет.
**Инвариантов в сборщике — 20.** Добавлены: `глобальный флаг вставки` (=2, синхронная
и async-ветки), `флаг ставится` (=2), `флаг снимается при уходе` (=1),
`НЕТ старого parts.__wl_done` (=0).
🔴 **Проверка, что правка реально на ТВ:** проба `window.__wl_activity_hook === true`
(уникальный маркер этой сборки). `ready`/`patch` для этого НЕ годятся — они истинны
и для старой версии, из-за чего три круга правок выглядели «не применёнными».
## 🔍 2026-09-25: РАССЛЕДОВАНИЕ — фид виден на всех экранах + «долго грузится»
**Жалоба Alex (дословно):** «есть проблема с watchlist фидом в лампе: он отображается **Жалоба Alex (дословно):** «есть проблема с watchlist фидом в лампе: он отображается
не только на "главной" но даже когда я выбираю категорию например "документальные" не только на "главной" но даже когда я выбираю категорию например "документальные"
в разделе Фильмы, то есть независимо от активного экрана. и он как будто долго в разделе Фильмы, то есть независимо от активного экрана. и он как будто долго
@@ -340,35 +393,37 @@ dom_titles = Сейчас смотрят | Клиника | Мыс страха
параллельно с `loadCatalogs()`. Тогда к моменту отрисовки главной `__wl_menu` уже в параллельно с `loadCatalogs()`. Тогда к моменту отрисовки главной `__wl_menu` уже в
памяти, `wlInject` срабатывает в первом же раунде, секции встают в голову без отставания. памяти, `wlInject` срабатывает в первом же раунде, секции встают в голову без отставания.
#### 🔴 Коммитить правку НЕКУДА — это выяснено, не пытаться снова #### ✅ РЕШЕНО: репозиторий `~/Developer/lampac-scripts` — история НЕ в `lampac-tmp`
Alex: «Коммить и переходим к предзагрузке». Проверка обеих площадок: **Изначальная ошибка вывода (сохранена как урок).** Проверка «в лоб» дала:
``` ```
/Users/admin/lampac-tmp → fatal: not a git repository /Users/admin/lampac-tmp → fatal: not a git repository
/srv/.../lampac (на Кра́кене) → fatal: not a git repository /srv/.../lampac (на Кра́кене) → fatal: not a git repository
``` ```
Ни папка сборки, ни папка lampac на Кра́кене под git **не лежат**. `plugin.js` — файл, Отсюда был сделан **неверный вывод** «коммитить некуда». На деле репозиторий
который **монтируется** в контейнер (`./catalog/plugin.js:/lampac/module/Catalog/plugin.js:ro`), **существовал давно** — я искал его не там, потому что в доке пути к плагину были
рядом только `.bak`-копии. То есть версионировать «в лоб» нечего. записаны как `/Users/admin/lampac-tmp/`.
**Что служит страховкой вместо коммита:** 🔴 **Настоящее место: `~/Developer/lampac-scripts`** (Gitea
`git_admin/lampac-scripts`), там же `scripts/`, `tests/`. Alex дважды указал на это
(«Проект на маке в developer это в доке записано. Ведь так?!», «И на gitea залит»).
**Урок: прежде чем объявить «некуда коммитить» — искать репозиторий по
`~/Developer/`, а не только рядом с файлом.**
| Артефакт | Путь | Размер | **Репы на Кра́кене:** только `docker/`, `obsidian/`, `watchlist-sync/`. В `lampac/`
|---|---|---| git нет и не заводится — бэкапы там `.bak-*` (закрыты `.gitignore`).
| бэкап «до фильтра» | `catalog/plugin.js.bak-before-mainfilter-20260925-220809` | 17476 Б |
| бэкап «до фикса url» | `catalog/plugin.js.bak-before-fixurl-<ts>` | 19576 Б |
| текущий боевой | `catalog/plugin.js` | 20934 Б |
| локальные копии | `lampac-tmp/plugin-cub.js` + `build_plugin.py` | 20934 / 14619 Б |
Плюс `build_plugin.py` держит весь `INJECT` в себе → файл воспроизводится | Артефакт | Путь |
**детерминированно**, с 12 проверками инвариантов. |---|---|
| **Исходник (источник истины)** | `~/Developer/lampac-scripts/scripts/build_plugin.py` |
| **Собранный выход** | `~/Developer/lampac-scripts/scripts/plugin-cub.js` |
| Скрэтч, вне git | `/Users/admin/lampac-tmp/` |
| Боевой на Кра́кене | `.../lampac/catalog/plugin.js` + `.bak-*` рядом |
**Предложенная развилка (решение за Alex, не сделано):** завести в репе `build_plugin.py` держит весь `INJECT` в себе → файл воспроизводится
`watchlist-sync` папку `lampac_plugin/` и положить туда `build_plugin.py` + **детерминированно**, с 20 проверками инвариантов.
`plugin-cub.js` под git — тогда правка плагина поедет вместе с проектом и попадёт
в историю. Второе правильнее, но это новая сущность в репе → спрашивать один раз.
#### ✅ ЗАКОММИЧЕНО 2026-09-25 — репозиторий найден, «некуда коммитить» СНЯТО #### ✅ ЗАКОММИЧЕНО 2026-09-25 — репозиторий найден, «некуда коммитить» СНЯТО
@@ -676,19 +731,31 @@ docker exec lampac grep -c wlWatchActivity /lampac/module/Catalog/plugin.js →
### Приёмка (ждёт Alex) — полный цикл, а не только первый экран ### Приёмка (ждёт Alex) — полный цикл, а не только первый экран
1. главная → 4 секции Watchlist сверху 1. главная → 4 секции Watchlist сверху, **БЕЗ дублей внизу**
2. Фильмы → документальные → секций Watchlist нет 2. Фильмы → документальные → секций Watchlist нет
3. **назад на главную → секции СНОВА сверху** ← пункт, который ломался 3. **назад на главную → секции СНОВА сверху, снова без дублей** ← пункт, который ломался
⚠️ **Коммит четвёртого фикса НЕ сделан** — ждёт приёмки. Файлы в ✅ **Коммиты СДЕЛАНЫ и запушены** (в `~/Developer/lampac-scripts`, Gitea):
`/Users/admin/lampac-tmp/`; после приёмки скопировать в - `5e55a47` — «секции CUB только на главной экране» (фильтр `wlOnMain`)
`~/Developer/lampac-scripts/scripts/` и закоммитить порядком `5e55a47`. - `3a35475` — «фильтр по component + предзагрузка меню + хук на activity»
🔴 **Правки обязаны попадать в репу ДО доклада.** В этой сессии `5e55a47` был
закоммичен, а работа по фильтру по `component` осталась незакоммиченной, и Alex
справедливо спросил «Коммит ты сделал текущей работы я надеюсь». Порядок:
`build_plugin.py` правится → собирается → **копируется в `scripts/` → коммит → push**,
и только потом заливка и приёмка. Файлы в `/Users/admin/lampac-tmp/` — НЕ источник.
🔴 **Правило процесса:** Alex не принимает статус-репорты и вопросы вместо результата 🔴 **Правило процесса:** Alex не принимает статус-репорты и вопросы вместо результата
(«Ты заебал. Ты блядь задачу решаешь или мозг мне ебешь?!», «Ты делаешь блядь или («Ты заебал. Ты блядь задачу решаешь или мозг мне ебешь?!», «Ты делаешь блядь или
опять залип», «Ты все починил? Проверять?») — **доделать и доложить один раз**. опять залип», «Ты все починил? Проверять?») — **доделать и доложить один раз**.
Замер — часть работы, а не отдельная задача на согласование. Замер — часть работы, а не отдельная задача на согласование.
🔴 **Правило проверки перед докладом.** Дважды за сессию доклад «готово, проверяй»
был **ложным**: правка физически не доезжала до ТВ (неработающий рестарт приложения).
Перед словом «готово» обязателен замер уникального маркера сборки на ТВ. Alex ловил
это сам («С чего ты взял что лампа перезапущена?», «Ты где его взял? Эта ебанина
где-то записана?») — это не должно повторяться.
--- ---
## 🔴 2026-09-24 (вечер): ПЕРЕПИСЫВАНИЕ — один sync-скрипт вместо отдельного генератора ## 🔴 2026-09-24 (вечер): ПЕРЕПИСЫВАНИЕ — один sync-скрипт вместо отдельного генератора
+90 -4
View File
@@ -43,7 +43,7 @@ related:
| Слой | Путь | Примечание | | Слой | Путь | Примечание |
|---|---|---| |---|---|---|
| **Mac, репо** | `~/Developer/lampac-scripts/scripts/build_plugin.py` | сборщик, 14 инвариантов | | **Mac, репо** | `~/Developer/lampac-scripts/scripts/build_plugin.py` | сборщик, 20 инвариантов |
| **Mac, репо** | `~/Developer/lampac-scripts/scripts/plugin-cub.js` | 🔴 собранный выход, РУКАМИ не правим | | **Mac, репо** | `~/Developer/lampac-scripts/scripts/plugin-cub.js` | 🔴 собранный выход, РУКАМИ не правим |
| Mac, скрэтч | `/Users/admin/lampac-tmp/` | рабочая папка, вне git — не источник истины | | Mac, скрэтч | `/Users/admin/lampac-tmp/` | рабочая папка, вне git — не источник истины |
| Кра́кен, хост | `/srv/dev-disk-by-uuid-6194539b-.../lampac/catalog/plugin.js` | bind-mount, вне git | | Кра́кен, хост | `/srv/dev-disk-by-uuid-6194539b-.../lampac/catalog/plugin.js` | bind-mount, вне git |
@@ -53,7 +53,9 @@ related:
**Репозиторий:** `https://git.mallexxx.duckdns.org/git_admin/lampac-scripts.git` **Репозиторий:** `https://git.mallexxx.duckdns.org/git_admin/lampac-scripts.git`
(remote `origin`, ветка `main`). Локально в нём же: `scripts/gen_watchlist.py`, (remote `origin`, ветка `main`). Локально в нём же: `scripts/gen_watchlist.py`,
`scripts/deploy_lampac.py`, `scripts/mark_missing.py`, `tests/`. `scripts/deploy_lampac.py`, `scripts/mark_missing.py`, `tests/`.
Коммит плагина 2026-09-25: **`5e55a47`** «секции CUB только на главной экране». Коммиты плагина 2026-09-25:
- **`5e55a47`** — «секции CUB только на главной экране» (фильтр `wlOnMain`)
- **`3a35475`** — «фильтр по component + предзагрузка меню + хук на activity»
```bash ```bash
cd ~/Developer/lampac-scripts cd ~/Developer/lampac-scripts
@@ -82,8 +84,59 @@ git commit -m "..." && git push origin main
| **`Log.enable` + `Runtime.enable` дают 0 строк** | консольные сообщения, напечатанные ДО подключения, не отдаются | читать логи СРАЗУ после рестарта, либо подтверждать состояние пробами, а не логами | | **`Log.enable` + `Runtime.enable` дают 0 строк** | консольные сообщения, напечатанные ДО подключения, не отдаются | читать логи СРАЗУ после рестарта, либо подтверждать состояние пробами, а не логами |
⚠️ **`docker restart lampac` НЕ сбрасывает кэш `catalog.js` в браузере webOS** — нужен ⚠️ **`docker restart lampac` НЕ сбрасывает кэш `catalog.js` в браузере webOS** — нужен
рестарт приложения на ТВ через `luna-send`. И **не спать** в ожидании: Alex на паузу рестарт приложения на ТВ. И **не спать** в ожидании: Alex на паузу `sleep 20` отреагировал
`sleep 20` отреагировал резко («Чё нахуй за слип 20?!») — вместо сна опрашивать состояние. резко («Чё нахуй за слип 20?!») — вместо сна опрашивать состояние в цикле.
### 🔴🔴 ПЕРЕЗАПУСК LAMPA НА ТВ: `tv_restart.sh` БЕЗ PTY = НОЛЬ ЭФФЕКТА (2026-09-25)
**Самый дорогой питфолл сессии. Три круга правок «не доезжали» до ТВ из-за этого.**
`tv_restart.sh` (лежит в `/Users/admin/lampac-tmp/`) делает `close` → `sleep 5` → `launch`
и печатает свои `echo`. **Это НЕ ответ `luna-send`.** Без PTY `luna-send` детектирует
отсутствие TTY и **отказывается писать в pipe** — молча не работает. Вывод скрипта
(`--- закрываю --- --- запускаю --- --- готово ---`) означает лишь то, что `echo`
отработали; приложение при этом **не перезапускается**.
**КАК ПРАВИЛЬНО — только через враппер с `script -q -c`:**
```bash
TV_HOST=root@192.168.1.70 ~/Automation/tv-luna-send.sh -n 1 \
-f luna://com.webos.applicationManager/closeByAppId '{"id":"com.lampac.tv"}'
# → {"appId":"com.lampac.tv","returnValue":true} ← живой ответ, доказательство
TV_HOST=root@192.168.1.70 ~/Automation/tv-luna-send.sh -n 1 \
-f luna://com.webos.applicationManager/launch '{"id":"com.lampac.tv"}'
# → {"returnValue":true}
```
🔴 **Критерий «рестарт реально произошёл» = получен `returnValue: true`**, а НЕ
наличие `echo`-строк в выводе. Без этого критерия легко принять неработающий
рестарт за работающий и уйти в ложную диагностику «правка не применяется».
🔴 **Проверка, что новая версия плагина в памяти ТВ** — проба уникального маркера
новой сборки, а не `ready`/`patch` (они истинны и для старой версии):
```js
String(!!window.__wl_activity_hook) // маркер конкретной фичи этой сборки
```
Выводить новый глобальный маркер в каждой существенной правке — иначе нечем
отличить «старая версия в памяти» от «новая не применилась».
🔴 **Первый замер после рестарта — ВСЕГДА гонка.** Опрашивать в цикле:
```
попытка 1: ready=false activityHook=false ← Lampa грузится, НЕ вердикт
попытка 2: ready=false activityHook=false
попытка 3: ready=true activityHook=true ← только здесь вердикт
```
⚠️ **`docker restart lampac` для доставки `plugin.js` НЕ НУЖЕН.** Проверено в сессии:
`/catalog.js` отдаётся с `Cache-Control: no-cache, no-store, must-revalidate`,
`Expires: 0`, `Pragma: no-cache` — сервер прямо запрещает кэширование. `catalog.js`
подключается как **плагин** Lampa (`plugins_add` в `lampainit.js`), а не лежит в
приложении на ТВ; `localStorage` хранит только СПИСОК плагинов (`plugins`), не код.
Два адреса дают разный `Content-Length` (44198 через `127.0.0.1:9118` vs 44204 через
`192.168.1.14:9118`) — разница ТОЛЬКО в подстановке `api_host`, код идентичен.
### 🔴 Форма `Lampa.Activity.active()` — ИЗМЕРЕНО (CDP, 2026-09-25) ### 🔴 Форма `Lampa.Activity.active()` — ИЗМЕРЕНО (CDP, 2026-09-25)
@@ -149,15 +202,48 @@ node --check plugin-cub.js # SYNTAX OK обязателен
# бэкап боевого # бэкап боевого
ssh kraken 'cp .../lampac/catalog/plugin.js .../catalog/plugin.js.bak-<что>-$(date +%Y%m%d-%H%M%S)' ssh kraken 'cp .../lampac/catalog/plugin.js .../catalog/plugin.js.bak-<что>-$(date +%Y%m%d-%H%M%S)'
scp plugin-cub.js kraken:.../lampac/catalog/plugin.js scp plugin-cub.js kraken:.../lampac/catalog/plugin.js
# 🔴 права + macOS-мусор (обязательно: файл монтируется :ro в контейнер)
ssh kraken 'cd .../lampac/catalog && chmod 644 plugin.js && chown 1000:1000 plugin.js && rm -f ._*'
# проверка, что файл виден в контейнере (bind-mount) # проверка, что файл виден в контейнере (bind-mount)
ssh kraken 'docker exec lampac grep -c "<маркер>" /lampac/module/Catalog/plugin.js' ssh kraken 'docker exec lampac grep -c "<маркер>" /lampac/module/Catalog/plugin.js'
# проверка по HTTP — что реально отдаётся ТВ
ssh kraken 'curl -s http://127.0.0.1:9118/catalog.js | grep -c "<маркер>"'
# рестарт ТВ ТОЛЬКО через враппер (см. выше) + проба маркера сборки
``` ```
⚠️ `docker restart lampac` в порядок **НЕ входит** — сервер отдаёт файл с
`no-store`, рестарт нужен только приложению на ТВ (и только через `tv-luna-send.sh`).
🔴 **`plugin-cub.js` в репе — это ВЫХОД сборщика.** Правку вносим в `INJECT` внутри 🔴 **`plugin-cub.js` в репе — это ВЫХОД сборщика.** Правку вносим в `INJECT` внутри
`build_plugin.py`, потом пересобираем; руками `plugin-cub.js` не редактируем, иначе `build_plugin.py`, потом пересобираем; руками `plugin-cub.js` не редактируем, иначе
следующая сборка затрёт правку. Инварианты в `checks{}` обязаны покрывать любую следующая сборка затрёт правку. Инварианты в `checks{}` обязаны покрывать любую
новую функцию — иначе регресс пройдёт молча. новую функцию — иначе регресс пройдёт молча.
### 🔴 Питфолл: инварианты, считающие ПОДСТРОКИ, ловят КОММЕНТАРИИ
Дважды за сессию сборка падала с `[x] ИНВАРИАНТ НАРУШЕН` **на корректном коде**:
1. `"wlWatchActivity call": out.count("wlWatchActivity()")` → `3` вместо `1`:
вхождение есть и в определении функции, и в `startPlugin`.
**Фикс:** считать по уникальному контексту — `out.count("wlPrefetch();wlWatchActivity();")`.
2. `"НЕТ старого parts.__wl_done": _code.count("parts.__wl_done")` → `4` вместо `0`:
строки-комментарии в `INJECT` начинаются с эмодзи + пробелов (``, U+1F534),
поэтому фильтр `l.strip().startswith(("*","/*","//"))` их **не отфильтровал**.
**Рабочий фильтр — вырезать блочные комментарии регуляркой:**
```python
_inject_body = out.split(MARK)[1].split("function startPlugin")[0]
_code = re.sub(r"/\*.*?\*/", "", _inject_body, flags=re.DOTALL)
_code = "\n".join(l for l in _code.splitlines()
if not l.strip().startswith("//"))
# затем _code.count(...) — счёт только по исполняемому коду
```
⚠️ `re` надо **импортировать** — сборщик его изначально не подключал (`NameError`).
**Правило:** прежде чем «чинить» код из-за упавшего инварианта — проверить
`grep -n "<строка>"` в собранном файле и убедиться, что счёт не из комментариев.
Дважды соблазн был «поправить код», хотя ломалась ПРОВЕРКА.
### 🔴🔴 Как определить активный экран Lampa — по `component`, НЕ по `url` ### 🔴🔴 Как определить активный экран Lampa — по `component`, НЕ по `url`
**ИЗМЕРЕНО на живом ТВ 2026-09-25.** Форма объекта: **ИЗМЕРЕНО на живом ТВ 2026-09-25.** Форма объекта: