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

This commit is contained in:
Alexey Martemyanov committed 2026-09-22 16:07:21 +06:00
1 parent 9e1a06340f
commit 7a021b26c3
2 files changed
+192 -10

No files matched your search

+165 -3
View File
@@ -1658,6 +1658,122 @@ ssh kraken "sudo timeout 40 tcpdump -i any -n -A 'host 192.168.1.70 and port 911
⚠️ Также: `&&`-цепочка в `ssh -t` рвёт tty-сессию («Connection closed»).
Каждую команду — отдельным вызовом.
## 🔴 Staticache — `/catalog.js` собирается и КЭШИРУЕТСЯ
`/catalog.js` — **не файл**, а **сборка**: Lampac берёт `plugin.js` из
`/lampac/module/Catalog/plugin.js`, подставляет `api_host` и `catalogs`
(отсюда ~18 КБ разницы от исходника) и кэширует результат через
атрибут `[Staticache]`.
**Питфолл, который стоил времени:** залил новый `plugin.js` (14099 Б,
патч на месте — `grep` подтверждает), а `/catalog.js` продолжал отдавать
**старую сборку** (32205 Б, патча нет). Выглядело как «патч не применяется».
```
=== патч в контейнере:
14099 /lampac/module/Catalog/plugin.js ← патч ЕСТЬ
=== что отдаёт клиенту:
32205 байт, injectWatchlistParts: 0 ← патча НЕТ
```
**Лечение:** `docker restart lampac` (сбрасывает Staticache-кэш), затем
проверять **размер и маркеры в отдаваемом файле**, а не только в файле на диске:
```bash
curl -s "http://192.168.1.14:9118/catalog.js" -o /tmp/cj.js
wc -c < /tmp/cj.js # размер сборки
grep -c injectWatchlistParts /tmp/cj.js # наш маркер
grep -c patchCubMain /tmp/cj.js
```
⚠️ Заголовки при этом отдают `Cache-Control: no-cache` — но это HTTP-кэш
клиента, а `Staticache` кэширует **на сервере**, поэтому заголовки не помогают
и клиентская очистка кэша проблему не решает.
## Внедрение секций Watchlist в CUB-фид (`plugin-cub.js`)
**Требование Alex:** секции Watchlist должны быть видны **ВНУТРИ источника CUB**,
без ручного переключения «Основной источник». Переключение — не решение.
### Почему штатный хук не подходит
`plugin.js` содержит `Lampa.Listener.follow('menu', ...)` + `e.abort()`:
он **перехватывает и ОТМЕНЯЕТ** разделы `movie/tv/cartoon/anime/relise`,
подменяя их нашими. Но `Api.getCatalog()` читает
`Manifest.catalogs[Lampa.Storage.field('source')]` — то есть каталог
**текущего** источника. При активном CUB нашего каталога там нет → хук молчит →
открывается родной CUB. Подмена работает только когда активен наш источник.
### Рабочая механика: обернуть `main` CUB-источника
`main$1()` в `app.min.js:33880` собирает массив `parts_data` — функций-загрузчиков,
каждая добавляет секцию на главную, и отдаёт через `Lampa.Api.partNext`.
Значит наши секции надо **добавить в этот массив**, а не подменять раздел.
Патч (`patch_cub_inject.py` → `plugin-cub.js`):
```js
function injectWatchlistParts(original) {
return function (params, oncomplite, onerror) {
original(params, function (cub_data) {
var wl = Manifest.catalogs['watchlist'];
if (!wl) return oncomplite(cub_data);
var menu = wl.menu || {}, keys = Object.keys(menu);
var list = Lampa.Arrays.isArray(cub_data) ? cub_data.slice() : [cub_data];
var pending = keys.length;
keys.forEach(function (name) {
network.silent(Utils.account(menu[name]), function (json) {
json.title = name; // заголовок секции
json.source = 'watchlist'; // источник для карточек
(json.results || []).forEach(function (it) { it.source = 'watchlist'; });
list.push(json); // ДОБАВЛЯЕМ, не заменяем
if (--pending === 0) oncomplite(list);
}, function () { if (--pending === 0) oncomplite(list); });
});
}, onerror);
};
}
function patchCubMain() {
var src = Lampa.Api.sources.cub;
if (!src || src.__wl_injected) return;
src.main = injectWatchlistParts(src.main);
src.__wl_injected = true;
}
```
Вызов — **по событию `app:ready`**, потому что нужны и `Lampa.Api.sources.cub`,
и загруженный `Manifest.catalogs['watchlist']`:
```js
if(window.appready){patchCubMain();}
else{Lampa.Listener.follow('app',function(e){if(e.type=='ready')patchCubMain();});}
```
🔴 **Ключевое в карточках наших секций — `source: 'watchlist'`.** Без него
клик уйдёт в CUB-карточку по TMDB-id и «Мой список» потеряется.
### Питфолл скрипта-патчера
🔴 **Порядок проверок в `patch_cub_inject.py` критичен.** Если сначала вставить
`INJECT` (который содержит текст `patchCubMain`), то последующая проверка
`"patchCubMain()" not in s` даст ложное «уже есть» и **вызов не вставится**.
Правильно: сначала проверить/вставить **ВЫЗОВ** (`if(window.appready){patchCubMain();}`),
и только потом — определение `INJECT`.
Проверка синтаксиса обязательна: `node --check plugin-cub.js`.
### Файлы патча
| Файл | Что |
|---|---|
| `plugin.js` | оригинал из образа (10670 Б) |
| `plugin-patched.js` | + `enumerable:true` (видимость источника в UI) |
| `plugin-cub.js` | + инжект секций в CUB-фид (**финальный, заливается**) |
| `patch_plugin.py` | делает `plugin-patched.js` |
| `patch_cub_inject.py` | делает `plugin-cub.js` из `plugin-patched.js` |
`deploy_lampac.py` шаг 4 берёт **`plugin-cub.js`**, с фолбэком на `plugin-patched.js`.
## Итоговое состояние (2026-09-22, конец сессии)
| Что | Статус |
@@ -1669,9 +1785,55 @@ ssh kraken "sudo timeout 40 tcpdump -i any -n -A 'host 192.168.1.70 and port 911
| `tmdb_id` в карточке | числом через `format: "long.TryParse(value, out long v) ? v : 0"` |
| `runtime`, `vote_average` | тем же способом через `format` |
| `displayname` | «Мой список» — надпись в UI |
| ✅ Проверено Alex в UI | ожидает |
| **✅ Alex подтвердил в UI** | **фильмы Watchlist открываются и ПРОИГРЫВАЮТСЯ** |
| Инжект секций в CUB-фид | залито, ожидает проверки в UI (перезапуск Lampa) |
Скрипты: `/Users/admin/lampac-tmp/gen_watchlist_v2.py`, `deploy_lampac.py`
(`--skip-plugin` для быстрого прогона). Проверка в шаге 7 теперь валидирует
именно ключ `watchlist` и предупреждает, если в `/catalog` остался русский ключ.
(`--skip-plugin` для быстрого прогона), `patch_cub_inject.py`.
Проверка в шаге 7 валидирует именно ключ `watchlist` и предупреждает,
если в `/catalog` остался русский ключ.
## ⏳ Осталось добить (2026-09-22)
### 1. 5 фильмов с Кинопоиск-ID — «здесь пусто», без постера
`film/12432920/`, `film/5454868/`, `film/11888712/`, `film/11825624/`, `film/8184798/`
(«Рассекая волны: экспедиция „Лас Бальсас“», «Хамнет», «Цена вершины»,
«Астрал. Могильный ритуал 2», «О добре и зле»).
**Причина:** `href` в списке — ссылка на Кинопоиск, TMDB не нашёл (преимущественно
фильмы 2025), поэтому `id = film/NNNNN/` вместо `movie/NNNN`. Карточки
`card-film-...json` не существует → клик даёт пустоту; постера нет по той же причине.
**Решения (нужен выбор Alex):** заглушка с названием + кнопка поиска торрента,
либо дорисовывать `tmdb_id` вручную.
### 2. ~51 название без постера — проблема ЗАПИСИ В СПИСКЕ, не скрипта
Кэш резолвера `~/.lampac_tmdb_cache.json` содержит 51 запись со значением `null`.
По названиям видны три класса:
| Пример | В списке | TMDB знает как |
|---|---|---|
| `Плохие парни 1/2`, `Никто 1/2` | как есть | **ДВА фильма в одной строке** |
| `Солярис Тарковский`, `Идиот Бортко` | как есть | «Солярис» (1972) / «Идиот» (2003) |
| `Самолетом, поездом, машиной (1987)` | как есть | «Planes, Trains and Automobiles» |
Скрипт не может угадать, что `1/2` — два фильма, а «Тарковский» — уточнение
режиссёра. **Правится только список руками**, не генератором.
### 3. Секции CUB — проверить в UI
Инжект залит (`plugin-cub.js`, 35566 Б в отдаваемой сборке). Нужен перезапуск
Lampa на ТВ и проверка: секции Watchlist появляются **поверх родного фида CUB**.
## Питфоллы проверки на ТВ (сводка)
1. **Lampa кэширует манифест и ответы.** После правок — **полностью выгрузить
приложение**, не сворачивать. `loader.js` использует `cache_version`
(9e5 мс ≈ 15 мин), плюс свой `localStorage`.
2. `luna-send` пишет в **tty**, не в stdout → обёртка
`script -q -c "luna-send ..." /tmp/o.txt`, читать файл.
(Доки: `family/how-to/tv-luna-send.md`.)
3. Реальный запрос клиента ловить `tcpdump` на сервере — см. «ПРАВИЛО ДИАГНОСТИКИ» выше.