diff --git a/family/how-to/gitea-config.md b/family/how-to/gitea-config.md index fa776c34..10b1974c 100644 --- a/family/how-to/gitea-config.md +++ b/family/how-to/gitea-config.md @@ -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:@git.mallexxx.duckdns.org/...` → токен светится в каждом `git remote -v` и в выводах команд/логах. **Правильно:** чистый URL + токен в `~/.git-credentials` (chmod 600) + `credential.helper=store`. Кандидат на **ротацию токена**. diff --git a/family/projects/watchlist-sync.md b/family/projects/watchlist-sync.md index ca5713cd..16ae9801 100644 --- a/family/projects/watchlist-sync.md +++ b/family/projects/watchlist-sync.md @@ -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]]. + +--- + ## Архитектура ``` diff --git a/personal/plans/watchlist-tmdb-missing-marker.md b/personal/plans/watchlist-tmdb-missing-marker.md index b3e53f2d..856d603e 100644 --- a/personal/plans/watchlist-tmdb-missing-marker.md +++ b/personal/plans/watchlist-tmdb-missing-marker.md @@ -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`) | diff --git a/personal/tech/lampac-deployment.md b/personal/tech/lampac-deployment.md index 53e8f09c..b404aaa9 100644 --- a/personal/tech/lampac-deployment.md +++ b/personal/tech/lampac-deployment.md @@ -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 /missing.json <путь.md> [--apply] +``` + +🔴 **Первая версия `mark_missing.py` решала «нет TMDB» по тексту строки** и дала +**344 пометки вместо 18** (помечала рабочие позиции с постерами). В прод не пошла. +Сопоставление теперь по нормализованному названию (нижний регистр, `ё→е`, без +пунктуации/эмодзи, без хвостового года). `🗓️` и `[x]` не трогаются, `❓` не дублируется. + ## 🔄 Ночной крон на Кра́кене — что делает автоматика `~/scripts/watchlist-nightly.sh`, cron **01:00**: