[2026-09-22] eagle: family/how-to/gitea-config.md family/projects/watchlist-sync.md personal/plans/watchlist-tmdb-missing-marker.md personal/tech/lampac-deployment.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-22 17:38:36 +06:00
1 parent e3618d5001
commit 62c4ba3d06
4 files changed
+199 -15

No files matched your search

+5 -1
View File
@@ -64,7 +64,7 @@ git push -u origin main
| `git_admin/obsidian-vault` | **public** (единственный) |
| `git_admin/reflect-app` | private |
| `git_admin/HA-ZONT-Modbus` | private (создан 2026-09-14, см. `[[family/how-to/home-automation]]` §5-кватер-Е) · документация конвертеров конфига ZONT: [[personal/projects/zont-config-compiler]] |
| `git_admin/lampac-scripts` | private (создан **2026-09-22**) — скрипты Lampac-каталога. См. [[personal/tech/lampac-deployment]] |
| `git_admin/lampac-scripts` | private (создан **2026-09-22**) — прод-скрипты каталога Lampa: генератор, деплой, маркер ❓. См. [[personal/tech/lampac-deployment]] |
### `lampac-scripts` — эталонный пример создания (2026-09-22)
@@ -77,6 +77,10 @@ git push -u origin main
Проверка: `GET /api/v1/user` → HTTP 200, `login: git_admin`, `is_admin: true` (токен жив).
**Проверка самого push (2026-09-22):** сверять `git rev-parse HEAD` с
`git ls-remote origin refs/heads/main` — обе величины должны быть равны
(в этой сессии `1765c05` = `1765c05`). «Команда отработала без ошибки» push не доказывает.
## Питфоллы
1. 🔴 **Токен в URL = утечка.** В `.git/config` репо `nolvu-landing` remote записан как `https://git_admin:<token>@git.mallexxx.duckdns.org/...` → токен светится в каждом `git remote -v` и в выводах команд/логах. **Правильно:** чистый URL + токен в `~/.git-credentials` (chmod 600) + `credential.helper=store`. Кандидат на **ротацию токена**.
+31
View File
@@ -20,6 +20,37 @@ tags:
---
## 2026-09-22 — `discover` больше не добавляет кино, которого нет в TMDB (коммит `396acc3`)
**Проблема:** `discover.py` фильтровал новинки **только по рейтингу и голосам**
(IMDb ≥ 7.0, KP ≥ 8.5 для РФ; голосов ≥ 1000 / ≥ 500). Проверки «есть ли фильм
в TMDB» не было → в `## Новинки` попадало вышедшее кино, которого TMDB не знает,
и в каталоге Lampa оно давало «здесь пусто».
**Изменения:**
- `discover.py`: `_resolvable(movie, tmdb_key, kp_key, cache)` — резолв через
`watchlist_sync.resolver` (тот умеет cross-reference RU → `alternativeName` (EN)
→ TMDB). Кэш промахов, чтобы не долбить API.
- `run()`: новый параметр `tmdb_key`. Проверка идёт **после** фильтра по рейтингу,
голосам, году и дедупликации `_all_watchlist_titles()`.
- `upcoming` (`🗓️`) **не проверяется** — календарь информативен, в каталог такие
позиции всё равно не идут.
- `cli.py`: команда `discover` передаёт `TMDB_KEY` из окружения. Без него проверка
не выполняется.
**Живой dry-run** (`cd ~/Developer/watchlist-sync && . ./.env && .venv/bin/python -m watchlist_sync.cli discover`):
из 150 released + 50 upcoming прошло 5 released / 16 upcoming, отсеяно 6 позиций, которых
нет в TMDB: `Одиссея`, `Майкл`, `Убежище`, `Обитель зла`, `Приглашение`, `Властелины вселенной`.
🔴 **Грабли:** правка тела `run()` и правка его сигнатуры — **разные места**. Патч добавил
`tmdb_key` в тело, но не в сигнатуру; Pyright показал `No parameter named "tmdb_key"`
в `cli.py`. Сигнатуру править отдельно и проверять LSP-диагностикой.
Полный контекст задачи (автопоиск, три уровня резолва, `missing.json`, маркер ❓) —
[[personal/plans/watchlist-tmdb-missing-marker]].
---
## Архитектура
```
+113 -13
View File
@@ -2,11 +2,110 @@
# Watchlist — автопоиск TMDB + маркер «нет в базе» (без ручных правок)
**Дата:** 2026-09-22
**Статус:** Шаги 1 и 2 ВЫПОЛНЕНЫ И ПРОВЕРЕНЫ. Шаг 1.3 переделывается. Шаг 3 — в работе.
**Статус:** Шаги 1, 1.3, 2, 3 ВЫПОЛНЕНЫ И ПРОВЕРЕНЫ, оба репо запушены.
Осталось только: деплой на Кра́кен + имя в карточке из TMDB/КП.
**Связано:** [[personal/plans/lampac-watchlist-plugin]], [[family/projects/watchlist-automation]], [[family/projects/watchlist-sync]], [[personal/tech/lampac-deployment]]
---
## ✅ ФИНАЛ 2026-09-22: шаги 1, 1.3, 2, 3 закрыты
### Коммиты (оба запушены, SHA сверены с remote)
| Репо | Remote | SHA | Что |
|---|---|---|---|
| `lampac-scripts` | gitea `git_admin/lampac-scripts` | `1765c05` | генератор + деплой + маркер + приёмки |
| `watchlist-sync` | github `mallexxx/watchlist-sync` | `396acc3` | Шаг 3: TMDB-фильтр в `discover.py` + `cli.py` |
Проверка: `git rev-parse HEAD` == `git ls-remote origin refs/heads/main` — совпало обоих.
### 📊 Результат прогона генератора (сырьё, не оценка)
| Метрика | Было (задеплоено) | Стало |
|---|---|---|
| Позиций в каталоге | 403 | **373** |
| Без TMDB-ID | 48 | **0** |
| Автопоиск по названию (2b) | — | **17** |
| Автопоиск по kp_id (2c) | — | **4** |
| Нерезолвленных (`missing.json`) | — | **30** |
| Источники | — | Radarr 9, TMDB 343, карточек 373 |
Разбивка: films 331 (постеров 327) · new 32 · cartoons 3 · anime 7.
Скрыто без TMDB: films **29**, new **1**.
### Шаг 1.3 — ПЕРЕДЕЛАН, теперь верен
**Было (неверно):** `mark_missing.py` решал «нет TMDB» **по тексту `.md`** → 344 пометки
вместо 48. Помечал заведомо рабочие позиции («Лило и Стич», «28 лет спустя» — в каталоге
они с постерами). В прод не пошёл.
**Стало (верно):** генератор пишет снимок нерезолвленных → `missing.json` рядом с JSON
каталога (`{"generated":true,"count":30,"missing":[...]}`), маркер ставится **по факту
промаха генератора**:
```bash
python3 scripts/gen_watchlist.py <путь.md> <путь/watchlist.json> # пишет missing.json
python3 scripts/mark_missing.py --missing <путь>/missing.json <путь.md> [--apply]
```
**Dry-run на реальном файле (.md с Кра́кена, 596 позиций):**
| Категория | Кол-во |
|---|---|
| всего позиций | 596 |
| не в `missing.json` | 350 |
| `🗓️` (не тронуты) | 39 |
| `[x]` просмотрено (не тронуты) | 179 |
| уже с `❓` | 10 |
| **будет помечено** | **18** |
Помеченные — черновые заметки с опечатками и подсказками в скобках, ровно то, что
требует ручной правки названия: `Джеймс Кемерон морские приключения?`,
`Разговорник/Квартетник`, `Аэроплан (Нильсен)`, `Солярис Тарковский`, `Идиот Бортко`,
`Спрут сериал`, `бердманн`, `Hemlock Grove (сериал)`, `Pandas 2018/Doc`,
`Some like it hot 1959 в джазе только деву`, `Мулан фильм`,
`Москва слезами не верить`, `Байкал удивительное приключение юмы`,
`Перед почтением сжечь`, `Курасава скрытая крепость`, `Клетка (джей ло)`,
`Ирана`, `Лена 2012`.
⚠️ Сопоставление — по **нормализованному** названию (нижний регистр, `ё→е`, без
пунктуации/эмодзи, без хвостового года), устойчиво к правкам `.md` ночным кроном.
Запись атомарная (tmp + rename), бэкап `.bak-questions`, проверка «файл не стал меньше».
### Шаг 3 — ВЫПОЛНЕН, живой dry-run
**Файлы:** `watchlist_sync/discover.py` (+`_resolvable()`, +`tmdb_key`), `cli.py` (передаёт `TMDB_KEY`).
Проверка идёт **после** фильтра по рейтингу, голосам, году и дедупликации.
`upcoming` (`🗓️`) не проверяется — календарь информативен, в каталог они не идут.
```bash
cd ~/Developer/watchlist-sync && . ./.env && .venv/bin/python -m watchlist_sync.cli discover
```
**Отсеяно 6 позиций** (ровно те, что дали бы «здесь пусто»):
`Одиссея` · `Майкл` · `Убежище` · `Обитель зла` · `Приглашение` · `Властелины вселенной`
Из 150 released + 50 upcoming прошло: **5 released, 16 upcoming**.
### Грабли сессии (не повторять)
1. 🔴 **Патчи по якорю не находили место** и тихо пропускались: импорт
`SequenceMatcher`, константы `KP_BASE`/`UA`/`KP_SIM_THRESHOLD`, блок `stats` в
`enrich()`, блок 2b, отчёт `main()`. Сообщения «= … (уже есть)» маскировали промах.
**Итог:** функции вставились, а зависимости — нет; файл падал на `NameError`.
**Правило:** после патча проверять не только «синтаксис валиден», но и наличие
каждой константы/импорта; критичные правки — через `patch`, а не строковую замену.
2. 🔴 **`run()` получил `tmdb_key` в тело, но не в сигнатуру** — Pyright сразу показал
`No parameter named "tmdb_key"` в `cli.py`. Сигнатуру править ОТДЕЛЬНО.
3. 🔴 **Откат из бэкапа вернул более старую версию.** `cp gen_watchlist_v2.py.bak-step1b …`
— снимок делался ДО записи, поэтому «бэкап» был старше правок.
**Правило: сначала снимок, потом правки.**
4. 🔴 **Подстановка `$( … | sed … )` вырезается** и в `write_file`, и в `patch`.
Строка собирается программно (питфолл №2 из [[family/how-to/gitea-config.md]]).
---
## 🔴 Прогресс 2026-09-22 (вечер): Шаг 1 пройден 3/3
**Итог автопоиска — проверено сырьём (`tests/accept_step1_order.py`):**
@@ -143,18 +242,18 @@ Alex: *«В developer. Пуш на gitea.»* Репо на gitea (private, HTTP
и в `write_file`, и в `patch` — строка собирается программно
(питфолл №2 из [[family/how-to/gitea-config.md]]).
### Осталось
### Осталось (после финала — только 2 пункта)
1. **Коммит + push** `lampac-scripts` на gitea.
1. **Деплой** нового `gen_watchlist.py` на Кра́кен (`lampac/scripts/`) + обновить
`~/scripts/watchlist-catalog.sh`, чтобы после генерации гонялся `mark_missing.py`.
⚠️ Проверить, что на Кра́кене есть `KP_KEY` в `/srv/.../watchlist-sync/.env` —
без него уровни 2b/2c не работают.
2. **Имя в карточке из TMDB/КП**, не из `.md` — требование Alex
(«Отображать как в tmdb/kp», «Ничего не укорачивать»). Сейчас генератор пишет
в карточку `it["name"]` = название из `.md`.
3. **Шаг 1.3 переделать:** список нерезолвленных из генератора → маркер `❓`.
4. **Шаг 3:** фильтр в `discover.py`. Уже прочитано: `discover.run(watchlist, kp_key, dry_run)`,
вызов в `cli.py:215`. Резолвер: `resolve(title, tmdb_key, kp_key)` → `ResolveResult`,
`AMBIG_SCORE_THRESHOLD = 0.75` (resolver.py:12).
5. **Деплой** `gen_watchlist.py` на Кра́кен (`lampac/scripts/`).
6. Доки.
Выполнено: шаги 1, 1.3, 2, 3; репо `lampac-scripts` (gitea `1765c05`),
`watchlist-sync` (github `396acc3`).
---
@@ -430,14 +529,15 @@ def tmdb_via_kp(title, year, tmdb_key, kp_key, cache):
| Файл | Роль |
|---|---|
| `~/Developer/lampac-scripts/scripts/gen_watchlist.py` | **ПРОД-генератор** каталога (репо, gitea) |
| `~/Developer/lampac-scripts/scripts/gen_watchlist.py` | **ПРОД-генератор** каталога (репо, gitea `1765c05`). Пишет `watchlist.json` + `cards/` + `missing.json` |
| `~/Developer/lampac-scripts/scripts/deploy_lampac.py` | заливка на Кра́кен |
| `~/Developer/lampac-scripts/scripts/mark_missing.py` | маркер ❓ — **в прод НЕ ставить** (см. Шаг 1.3) |
| `~/Developer/lampac-scripts/scripts/mark_missing.py` | маркер ❓ по `missing.json` (переделан, `--missing`) |
| `~/Developer/lampac-scripts/tests/accept_step1*.py` | приёмки автопоиска (3/3) |
| `~/lampac-tmp/gen_watchlist_v2.py` | рабочая копия + все патч-скрипты сессии |
| `~/lampac-tmp/` | рабочая свалка + все патч-скрипты сессии (НЕ git) |
| Кра́кен: `lampac/scripts/gen_watchlist.py` | **ЗАДЕПЛОЕННАЯ старая версия** — ждёт обновления |
| `~/Developer/watchlist-sync/watchlist_sync/resolver.py` | ЭТАЛОН cross-reference |
| `~/Developer/watchlist-sync/watchlist_sync/discover.py` | фильтр новинок (Шаг 3) |
| `~/Developer/watchlist-sync/watchlist_sync/discover.py` | TMDB-фильтр (Шаг 3, `_resolvable()`) |
| `~/Developer/watchlist-sync/watchlist_sync/cli.py` | `discover` передаёт `TMDB_KEY` |
| `~/.lampac_tmdb_cache.json` | кэш поиска TMDB (`.bak-20260922-step1`, `.bak-step1b`) |
| `~/.lampac_tmdb_details.json` | кэш деталей карточки (`.bak-20260922-step1`) |
+50 -1
View File
@@ -71,14 +71,63 @@ related:
/Users/admin/Developer/lampac-scripts/
├── scripts/gen_watchlist.py (из gen_watchlist_v2.py — ПРОД-генератор)
├── scripts/deploy_lampac.py
├── scripts/mark_missing.py (в прод НЕ ставить — см. план, Шаг 1.3)
├── scripts/mark_missing.py (маркер ❓, вход — missing.json)
└── tests/accept_step1.py, tests/accept_step1_order.py
```
**Первый коммит:** `1765c05` (gitea `git_admin/lampac-scripts`, private).
Проверка push: `git rev-parse HEAD` == `git ls-remote origin refs/heads/main`.
Создание репо через API — `~/lampac-tmp/create_gitea_repo.py` (Python, а не bash:
подстановка `$( … | sed … )` вырезается фильтром безопасности — питфолл №2 из
[[family/how-to/gitea-config.md]]).
## 🔎 Резолв позиций в каталоге — три уровня (2026-09-22)
Генератор ищет фильм по цепочке, от точного к эвристике:
```
1) Radarr по названию
2) TMDB по названию как есть
2c) Кинопоиск по KP_ID ИЗ ССЫЛКИ .md -> имя/EN-алиас -> TMDB ← машинный ключ
2b) Кинопоиск по НАЗВАНИЮ -> имя/EN-алиас -> TMDB ← эвристика
3) miss -> позиция в JSON каталога НЕ попадает, имя в missing.json -> ❓ в .md
```
🔴 **2c ВЫШЕ 2b намеренно.** `kp_id` из ссылки — машинный ключ, он не зависит от
переименований. Пример: в `.md` «Астрал. Могильный ритуал 2», КП по id `11825624`
отдаёт актуальное имя «Могильный ритуал. Зло вернулось» → TMDB **1576953**.
Если бы 2b шёл первым, он привязал бы **1-ю часть** (1264397) по общему префиксу.
🔴 **`KP_SIM_THRESHOLD = 0.75`** — порог близости документа КП к запросу.
Документ с `sim ≥ 0.75`, но нерезолвенный в TMDB = **окончательный промах**,
дальше перебирать нельзя. Без порога перебор уходил в мусор: по «WeWork»
релевантный документ (sim=1.00) не резолвился из-за **латинской `C`** в слове
«Cоздание» (дефект данных КП), и скрипт брал `We Work for the Dead` (1135465)
вместо `795622`.
⚠️ Порог и `kp_id` — **числовые/машинные критерии**, не ручной словарь соответствий.
Инвариант «никаких ручных правок» не нарушен.
### `missing.json` и маркер ❓
Генератор пишет снимок нерезолвленных рядом с каталогом:
```json
{"generated": true, "count": 30, "missing": ["All systems red", "..."]}
```
Маркер ставится **по факту промаха**, а не по тексту `.md`:
```bash
python3 scripts/mark_missing.py --missing <wwwroot>/missing.json <путь.md> [--apply]
```
🔴 **Первая версия `mark_missing.py` решала «нет TMDB» по тексту строки** и дала
**344 пометки вместо 18** (помечала рабочие позиции с постерами). В прод не пошла.
Сопоставление теперь по нормализованному названию (нижний регистр, `ё→е`, без
пунктуации/эмодзи, без хвостового года). `🗓️` и `[x]` не трогаются, `❓` не дублируется.
## 🔄 Ночной крон на Кра́кене — что делает автоматика
`~/scripts/watchlist-nightly.sh`, cron **01:00**: