[2026-09-22] eagle: personal/plans/lampac-watchlist-plugin.md personal/tech/lampac-deployment.md
This commit is contained in:
1 parent
f53eea7a6f
commit
6f63bc8810
2 files changed
+215
-6
No files matched your search
@@ -7,7 +7,7 @@ related:
|
||||
- '[[family/projects/watchlist-sync]]'
|
||||
- '[[family/projects/watchlist-automation]]'
|
||||
- '[[family/documents/movies-watchlist]]'
|
||||
status: card-pending
|
||||
status: card-done-tv-check-pending
|
||||
tags:
|
||||
- lampac
|
||||
- lampa
|
||||
@@ -39,9 +39,10 @@ updated: '2026-09-22'
|
||||
> РЕАЛЬНЫЕ ЭЛЕМЕНТЫ - а не сгенерируют пустые заглушки!»
|
||||
> См. «🔴 АРХИТЕКТУРНАЯ ОШИБКА» — читать ПЕРВЫМ.
|
||||
>
|
||||
> **Итог одной строкой:** серверная часть каталога работает (4 категории, 360/33/3/7),
|
||||
> но реализована не по замыслу: подменяет каталог вместо добавления секций,
|
||||
> и не использует Radarr как источник постеров/ID.
|
||||
> **Итог одной строкой:** серверная часть каталога работает полностью — «Мой список»
|
||||
> первым в `/catalog`, 4 категории (403 позиции, 348 постеров, 352 рабочих карточки),
|
||||
> карточка по клику отдаёт **HTTP 200** (339/339). Родной CUB не тронут.
|
||||
> Осталась приёмка глазами на ТВ.
|
||||
>
|
||||
> **Обновление 2026-09-22 (вечер):** Alex лично нашёл источник в UI
|
||||
> («остальное → основной источник → Мой список»), увидел результат и подтвердил
|
||||
@@ -117,6 +118,34 @@ updated: '2026-09-22'
|
||||
> `deploy_lampac.py` пока НЕ умеет лить каталог `cards/` — доклеить `scp -r`.
|
||||
> См. **«🔴 КАРТОЧКА: механика card_parse»**.
|
||||
|
||||
> **Обновление 2026-09-22 (ночь, четвёртая сессия): ✅ КАРТОЧКА ПОЧИНЕНА. 339/339 HTTP 200. СЕРВЕРНАЯ ЧАСТЬ ЗАКРЫТА.**
|
||||
> Схема «файл на фильм» залита и **проверена на живом сервере**:
|
||||
> - `wwwroot/cards/card-movie-NNN.json` (345 файлов), обёртка `{"card": {...}}`,
|
||||
> владелец `1000:1000`, режим `644`;
|
||||
> - `card_parse.initUrl` = C# `return $"{host}/cards/card-{uri.Replace("/", "-")}.json";`,
|
||||
> `node: card` (статичный — потому и нужен файл-на-фильм);
|
||||
> - **все 339 карточек с TMDB-ID отдают HTTP 200**, имена читаются
|
||||
> («Я буду жить», «На цепи», «Простите за беспокойство»);
|
||||
> - `deploy_lampac.py` дополнен шагом **2b** (заливка каталога `cards/` через tar)
|
||||
> и проверкой карточек в шаге 7.
|
||||
>
|
||||
> **Итог каталога:** Новинки 33/30, Фильмы 360/308, Мультфильмы 3/3, Аниме 7/7 —
|
||||
> **403 позиции, 348 постеров, 352 рабочих карточки**, постеров-подложек нет.
|
||||
>
|
||||
> 🔴 **Три причины поломки, все устранены** (детали — «🔴 ПИТФОЛЛЫ, найденные по ходу»):
|
||||
> 1. **права**: tar распаковывал от root → `root:root 600` → dotnet `Permission denied` →
|
||||
> статика молча **404** (выглядит как «файла нет»);
|
||||
> 2. **macOS AppleDouble**: BSD `tar` клал `._*.json` в `wwwroot` → нужен `--no-mac-metadata`;
|
||||
> 3. 🔴 **ложный диагноз — потерянный час**: карточку проверял по **`/card`**, где всегда 404.
|
||||
> Правильный путь — **`/catalog/card?plugin=watchlist&uri=movie/NNN`**.
|
||||
>
|
||||
> **ОСТАЛОСЬ: приёмка глазами на ТВ** (`192.168.1.70`): секции сверху с постерами,
|
||||
> клик → карточка с описанием → воспроизведение через TorrServer.
|
||||
> **Приёмка = результат виден в UI**, не «API отвечает».
|
||||
>
|
||||
> **Не тронуто, ждёт решения Alex:** 310 фильмов из списка, которых нет в Radarr
|
||||
> (51 без постера, идут через фолбэк Кинопоиска). Без апрува не делать.
|
||||
|
||||
## 🔴 КАРТОЧКА: механика card_parse (разобрано по коду 2026-09-22)
|
||||
|
||||
Клик по элементу каталога ведёт в `/catalog/card?plugin=watchlist&uri=<id>&type=movie`.
|
||||
@@ -1703,3 +1732,79 @@ cp radarr.db.bak-<дата> radarr.db && docker restart radarr
|
||||
- [[family/projects/watchlist-sync]] — парсер списка, CLI
|
||||
- [[family/projects/watchlist-automation]] — форматы маркеров, cron
|
||||
- [[family/documents/movies-watchlist]] — источник данных
|
||||
|
||||
## ✅ РАБОТАЕТ (2026-09-22, конец дня)
|
||||
|
||||
**Карточка фильма починена. Проверено: 339 из 339 карточек → HTTP 200.**
|
||||
|
||||
### Итог по каталогу
|
||||
|
||||
| Секция | Элементов | Постеров | Рабочих TMDB-ID |
|
||||
|---|---|---|---|
|
||||
| Новинки | 33 | 30 | 30 |
|
||||
| Фильмы | 360 | 308 | 312 |
|
||||
| Мультфильмы | 3 | 3 | 3 |
|
||||
| Аниме | 7 | 7 | 7 |
|
||||
| **Всего** | **403** | **348** | **352** |
|
||||
|
||||
Источники метаданных: Radarr 9, TMDB 343, Кинопоиск-фолбэк 51.
|
||||
|
||||
### Что сделано
|
||||
|
||||
1. Генератор `gen_watchlist_v2.py` — пишет **файл на каждый фильм**:
|
||||
`wwwroot/cards/card-movie-NNN.json`, владелец `1000:1000`, режим 644.
|
||||
2. `watchlist.yaml` → `card_parse.initUrl` собирает путь из `uri`:
|
||||
`return $"{host}/cards/card-{uri.Replace("/", "-")}.json";`, `node: card`.
|
||||
3. `deploy_lampac.py` — добавлен шаг `2b` (заливка `cards/` через tar), плюс
|
||||
проверка карточки в шаге 7.
|
||||
4. `arr.py` — `monitored: False`, `searchFor*: False` (коммит `9872016`).
|
||||
5. `plugin.js` — патч `enumerable:true`, смонтирован volume, переживает обновление образа.
|
||||
|
||||
### 🔴 ПИТФОЛЛЫ, найденные по ходу (все стоили времени)
|
||||
|
||||
1. **Проверять карточку надо по `/catalog/card`, НЕ по `/card`.**
|
||||
`/card` отдаёт 404 и выглядит как поломка каталога. Потерян час на ложном диагнозе.
|
||||
2. **`parse.node` — СТАТИЧНЫЙ JSONPath.** `{uri}`/`{cat}` в него НЕ подставляются
|
||||
(`json.SelectToken(parse.node)`). Слэш в ключе `movie/NNN` трактуется как вложенность
|
||||
JSON → `SelectToken` возвращает null → `BadRequest("parse.node")`.
|
||||
Именно поэтому выбран формат «файл на фильм» с фиксированным `$.card`.
|
||||
3. **`card_parse.initUrl` ИСПОЛНЯЕТСЯ как C#** (`CSharpEval`, объект
|
||||
`CatalogInitUrlCard` с полями `host/uri/type/args`). Только здесь можно подставить
|
||||
`uri`. Кавычки внутри YAML-heredoc писать экранированно.
|
||||
4. **`CardController.Index(plugin, uri, type)` — параметра `cat` НЕТ.**
|
||||
`{cat}` в карточке всегда пустой.
|
||||
5. **macOS `tar` кладёт в архив AppleDouble-мусор `._*`** — файлы попадают в
|
||||
`wwwroot` и мешают. Использовать `tar --no-mac-metadata` + `rm -f ._*`.
|
||||
6. **Права решают.** Если файлы отдать `root:root 600` — dotnet отвечает
|
||||
`Permission denied`, статика молча отдаёт **404**. Нужно `1000:1000` + `755`/`644`.
|
||||
7. **`find -exec` через `ssh -t` рвётся**: `missing argument to -exec`.
|
||||
Использовать `chmod -R` отдельными вызовами.
|
||||
8. **Счётчик TMDB-ID врал нулём**: `id` приходит как `movie/424781` (префикс),
|
||||
а проверялось `"/movie/"` (вхождение со слэшем спереди). Проверять `startswith`.
|
||||
9. **Первая категория в `menu.categories` игнорирует `format`** и берёт `list.uri`
|
||||
напрямую. Поэтому «Новинки» стоят первыми, и их `list.uri = watchlist-new.json`.
|
||||
10. **Ключи карточки.** `CardController` отдаёт `title`/`original_title`, а
|
||||
`card_parse` ищет `name`/`original`. В JSON пишем **оба** набора ключей.
|
||||
|
||||
### Воспроизведение
|
||||
|
||||
```bash
|
||||
cd /Users/admin/lampac-tmp
|
||||
python3 gen_watchlist_v2.py movies-watchlist.md /tmp/wl-out/watchlist.json
|
||||
python3 deploy_lampac.py /tmp/wl-out --skip-plugin # ~2 мин
|
||||
```
|
||||
|
||||
Cron: генератор в **02:30**, после `watchlist-nightly` (01:00).
|
||||
|
||||
### Осталось на ТВ
|
||||
|
||||
Проверить глазами в Lampa на `192.168.1.70`: секции сверху с постерами,
|
||||
клик → карточка с описанием → воспроизведение через TorrServer.
|
||||
Приёмка = результат виден в UI.
|
||||
|
||||
### Не сделано (ждёт решения, без апрува не трогать)
|
||||
|
||||
310 фильмов из списка, которых нет в Radarr — пока показываются через фолбэк
|
||||
Кинопоиска (51 без постера). Решить судьбу отдельно.
|
||||
|
||||
## Связанные
|
||||
@@ -48,7 +48,7 @@ related:
|
||||
├── catalog/sites/ → /lampac/module/Catalog/sites
|
||||
├── catalog/override/ → /lampac/module/Catalog/override
|
||||
├── catalog/plugin.js → /lampac/module/Catalog/plugin.js:ro (ПАТЧ, см. ниже)
|
||||
├── wwwroot/ → /lampac/wwwroot (JSON-каталог watchlist)
|
||||
├── wwwroot/ → /lampac/wwwroot (JSON-каталог watchlist + cards/)
|
||||
└── scripts/gen_watchlist.py (генератор .md → JSON)
|
||||
```
|
||||
|
||||
@@ -56,7 +56,22 @@ related:
|
||||
`gen_watchlist_v2.py`, `patch_plugin.py`, `plugin-patched.js`, `watchlist.yaml`,
|
||||
`docker-compose.yml`, `deploy_lampac.py`.
|
||||
|
||||
Владелец `kraken:kraken`, права `755`.
|
||||
🔴 **Права на файлы в `wwwroot/` — владелец `1000:1000` (= пользователь `lampac`
|
||||
в контейнере), каталоги `755`, файлы `644`.** Не `kraken:kraken` — контейнер ходит
|
||||
не под хост-пользователем. Если файл окажется `root:root 600`, dotnet ответит
|
||||
`Permission denied`, а веб-сервер **молча отдаст 404** — отсутствие прав выглядит
|
||||
снаружи как «файла нет».
|
||||
|
||||
### Раздача статики из `wwwroot/`
|
||||
|
||||
- Всё, что положено в `/lampac/wwwroot/`, доступно **по корню**:
|
||||
`wwwroot/watchlist-films.json` → `GET /watchlist-films.json` (**200**).
|
||||
- `UseStaticFiles` — в `Core.dll` (серверное ядро), настраивать не нужно.
|
||||
- 🔴 **`/lampac/wwwroot` ≠ `/lampac/current.conf`**: статика ядра, а не модуль.
|
||||
Проверять наличие файла надо `docker exec lampac ls ...`, а **доступность** —
|
||||
обязательно через `curl`, потому что это разные вещи (см. права выше).
|
||||
- ⚠️ Веб-корень отдаёт **только** `wwwroot/`. Пути вида `/cards/...` работают,
|
||||
но `/card` — это **не** статика (см. ниже).
|
||||
|
||||
⚠️ Всё пишущее — на data-диск (1.8T). **НЕ на SD** (`/`, 14G, 8.6G свободно).
|
||||
|
||||
@@ -1267,3 +1282,92 @@ prowlarr · sonarr · watchtower · rclone · xray-reverse-bridge · hermes-krak
|
||||
5. **Автоскачка Radarr + Sonarr** — 45 monitored / очередь 6 у Radarr,
|
||||
13 monitored / очередь 19 у Sonarr. План отключения на апруве
|
||||
([[personal/plans/lampac-watchlist-plugin]], «ПЕРЕДЕЛКА 2»).
|
||||
|
||||
## Карточка фильма в модуле Catalog — рабочая схема (проверено 2026-09-22)
|
||||
|
||||
Модуль `Catalog` (в образе `/lampac/module/Catalog/`). Каталог watchlist:
|
||||
`sites/watchlist.yaml` + JSON в `wwwroot/`.
|
||||
|
||||
### Списки
|
||||
|
||||
```yaml
|
||||
menu:
|
||||
- categories:
|
||||
Новинки: new # ПЕРВОЙ — она игнорирует format, берёт list.uri
|
||||
Фильмы: films
|
||||
Мультфильмы: cartoons
|
||||
Аниме: anime
|
||||
format:
|
||||
"-": |
|
||||
return $"http://127.0.0.1:9118/watchlist-{cat}.json";
|
||||
list:
|
||||
uri: watchlist-new.json # ← сюда смотрит первая категория
|
||||
content:
|
||||
nodes: "$.items[*]"
|
||||
href: { attribute: href } # href = TMDB URL → id = "movie/NNN"
|
||||
```
|
||||
|
||||
### Карточка
|
||||
|
||||
`CardController.Index(plugin, uri, type)` — **`cat` не принимает**.
|
||||
|
||||
- `url = $"{init.host}/{uri}"` — дефолт, даёт `http://127.0.0.1:9118/movie/424781` → 500.
|
||||
- `card_parse.initUrl` **исполняется как C#** (`CSharpEval`, объект
|
||||
`CatalogInitUrlCard(host, args, uri, query, type)`) → можно собрать динамический URL.
|
||||
- `card_parse.node` — **статичный JSONPath** (`json.SelectToken(node)`),
|
||||
подстановок нет; `/` = вложенность.
|
||||
|
||||
Рабочая конфигурация:
|
||||
|
||||
```yaml
|
||||
card_parse:
|
||||
initUrl: |
|
||||
return $"{host}/cards/card-{uri.Replace("/", "-")}.json";
|
||||
node: card
|
||||
name: { attribute: name }
|
||||
original_name: { attribute: original }
|
||||
year: { attribute: year }
|
||||
image: { attribute: poster }
|
||||
description: { attribute: overview }
|
||||
```
|
||||
|
||||
Файл `wwwroot/cards/card-movie-424781.json`:
|
||||
|
||||
```json
|
||||
{ "card": { "name": "...", "title": "...", "original": "...", "original_title": "...",
|
||||
"year": "...", "rating": "...", "poster": "...", "img": "...",
|
||||
"overview": "...", "genres": "...", "cast": "...", "runtime": 116 } }
|
||||
```
|
||||
|
||||
Ключи дублируются (`name`+`title`, `poster`+`img`): `CardController` отдаёт
|
||||
`title`/`original_title`, а `card_parse` ищет `name`/`original`.
|
||||
|
||||
### Эндпоинты
|
||||
|
||||
| Запрос | Назначение |
|
||||
|---|---|
|
||||
| `GET /catalog` | список каналов; `main` = категории |
|
||||
| `GET /catalog/list?plugin=watchlist&cat=films&page=1` | элементы секции |
|
||||
| `GET /catalog/card?plugin=watchlist&uri=movie/424781` | **карточка** |
|
||||
|
||||
🔴 **Не по `/card`** — там 404 и ложный вывод «карточка сломана».
|
||||
|
||||
### Файлы и права
|
||||
|
||||
- Статика раздаётся из `/lampac/wwwroot/` (`core` UseStaticFiles).
|
||||
- Владделец — `1000:1000` (пользователь `lampac` в контейнере), каталоги `755`,
|
||||
файлы `644`. Иначе `Permission denied` → **404**.
|
||||
- macOS `tar` → AppleDouble `._*`, добавлять `--no-mac-metadata`.
|
||||
|
||||
### Деплой
|
||||
|
||||
```bash
|
||||
python3 gen_watchlist_v2.py movies-watchlist.md /tmp/wl-out/watchlist.json
|
||||
python3 deploy_lampac.py /tmp/wl-out --skip-plugin
|
||||
```
|
||||
|
||||
Скрипты: `/Users/admin/lampac-tmp/`. Логика заливки — tar+scp+sudo,
|
||||
`ssh -t` для sudo (без tty `sudo` требует пароль), каждая команда отдельным
|
||||
вызовом (`&&`-цепочка рвёт tty).
|
||||
|
||||
### Связанные
|
||||
Reference in new issue
Block a user