From 337505a33f914ee0cc7944fd756939665e061928 Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Thu, 24 Sep 2026 01:10:13 +0600 Subject: [PATCH] =?UTF-8?q?watchlist:=20sync-from-lampac=20implemented=20(?= =?UTF-8?q?Lampa=20->=20[x])=20=E2=80=94=20plan=20status,=20automation=20d?= =?UTF-8?q?oc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- family/projects/watchlist-automation.md | 62 +++++++++++++ personal/plans/lampac-watched-to-watchlist.md | 93 +++++++++++++++++-- 2 files changed, 148 insertions(+), 7 deletions(-) diff --git a/family/projects/watchlist-automation.md b/family/projects/watchlist-automation.md index aa059468..e9401c28 100644 --- a/family/projects/watchlist-automation.md +++ b/family/projects/watchlist-automation.md @@ -12,6 +12,68 @@ tags: Полная цепочка: как `family/documents/movies-watchlist.md` превращается в скачанный контент. +## ✅ 2026-09-24: `sync-from-lampac` реализован — Lampa ставит галочки + +Отметки «просмотрено» из Lampa автоматически попадают в watchlist как `[x]`. +Коммит `ef44f3e` (код), план — [[personal/plans/lampac-watched-to-watchlist]] §10. + +**Ночной пайплайн теперь:** +``` +sync-vault (pull) → process-thumbs → resolve --recheck → [sync-down ПРОПУЩЕН] +→ sync-up (Jellyfin, non-fatal) → sync-from-lampac --apply (NEW) +→ sync-vault (push) +``` + +**Как работает матч** (`watchlist_sync/sync_from_lampac.py`): +``` +timecodes.identity ('movie-674' / '674_movie', строгие формы) + → tmdb_id + → 1) .resolve_cache.json (candidate.tmdb_id → ключ = заголовок .md) — ТОЧНО + → 2) фолбэк: watchlist.json (href → name TMDB-ru / original + год ±1) + → _mark_watched ([x], строка уезжает в блок просмотренных) +``` + +**Критерий просмотренного:** `deleted=0` И `percent >= 90` (env +`LAMPAC_PERCENT_THRESHOLD`, CLI `--percent`). `watched_at` — НЕ критерий +(бывает >0 при 18%). Сериалы `tv-*` — скип, этап 2. + +**Ключевые факты:** +- БД `TimeCode.sql` — только внутри контейнера lampac (НЕ на volume!), + читается копией `docker cp`; `sqlite3` в контейнере нет. +- `Sync.sql` (bookmarks) и `Sisi.sql` — ПУСТЫЕ: закладки Lampa не + синхронизируются на Lampac, TimeCode — единственный канал просмотров. +- `timecodes`: UNIQUE(user, identity) — одна строка на юзера+фильм, + percent обновляется на месте. +- Ручной прогон: `uv run watchlist-sync sync-from-lampac` (dry-run по + умолчанию, `--apply` для записи). + +**.env (Kraken, `$WS/.env`, бэкап `.env.bak-lampac-20260924`):** +``` +LAMPAC_TIMECODE_DB=lampac:/lampac/database/TimeCode.sql +LAMPAC_WATCHLIST_JSON=/srv/dev-disk-by-uuid-6194539b-03e0-4f33-986b-4da09834e82f/lampac/wwwroot/watchlist.json +LAMPAC_PERCENT_THRESHOLD=90 +``` + +**nightly:** md5 новой `edf0e34530692dbb7b6fb9644f1b45a7` (было +`4401882e...`), бэкап `~/scripts/watchlist-nightly.sh.bak-lampac-20260924`. +Полный ручной прогон 01:05–01:07 ✓, новый шаг ~2 сек, non-fatal. + +**Побочный фикс:** `_normalize` в `sync_up.py` не делал `.strip()` после +удаления небуквенных — заголовки с хвостовым ⭐ молча проваливали +`_mark_watched` (в т.ч. в Jellyfin-канале). Фикс в `ef44f3e`. + +**Тест:** синтетическая БД (8 сценариев: cache/json матч, 18%, tv, deleted, +уже-[x], левый id, dry-run) — 8/8 ✓; `--apply` на копии .md — diff ровно +2 ожидаемые строки. Бэкап до работы: +`~/backup-watchlist-before-lampac-sync-20260924-005630/`. + +**Осталось:** калибровка порога живым досмотром (досмотреть фильм из +списка в Lampa, вычитать percent, при ≠90 поправить env); сериалы этап 2; +bind-mount для `/lampac/database` (риск потери истории при пересоздании +контейнера — отдельный апрув). Проверка в UI: после первого реального +досмотра фильм пропадает из «Мой список» в Lampa после ночного прогона +(каталог 02:30 выкидывает `[x]`). + ## 🔴 2026-09-23: канал «Lampa → `[x]`» найден; фиксы сирот закоммичены ### Коммит `ffe7974` — закрыт должок по грязному дереву diff --git a/personal/plans/lampac-watched-to-watchlist.md b/personal/plans/lampac-watched-to-watchlist.md index bd3fe80d..be9c318a 100644 --- a/personal/plans/lampac-watched-to-watchlist.md +++ b/personal/plans/lampac-watched-to-watchlist.md @@ -1,15 +1,22 @@ --- title: Отметка «просмотрено» из Lampa → watchlist.md type: plan -status: planned-not-implemented +status: implemented-threshold-tbd namespace: personal -created: 2026-09-23 -updated: 2026-09-23 -tags: [lampac, lampa, watchlist, timecode, torrserver, kraken, sync-up] +created: 2026-09-23T00:00:00.000Z +updated: '2026-09-24T00:00:00.000Z' +tags: + - lampac + - lampa + - watchlist + - timecode + - torrserver + - kraken + - sync-up related: - - "[[family/projects/watchlist-automation]]" - - "[[personal/plans/lampac-watchlist-plugin]]" - - "[[family/documents/movies-watchlist]]" + - '[[family/projects/watchlist-automation]]' + - '[[personal/plans/lampac-watchlist-plugin]]' + - '[[family/documents/movies-watchlist]]' --- # Отметка «просмотрено» из Lampa → `movies-watchlist.md` @@ -188,6 +195,78 @@ KP-ID (`kinopoisk.ru/film/`, 68 штук в `.md`) в цепочке **не уч (см. §7) — без него шаг 6 не закрыть. - ⏳ Не сделано: бэкап (шаг 1), `.env` (шаг 4), крон (шаг 5), доки (шаг 7). +--- + +## 10. ✅ РЕАЛИЗАЦИЯ 2026-09-24 (коммит `ef44f3e`) + +Модуль написан, протестирован, встроен в nightly. Все 8 шагов плана закрыты +(кроме калибровки порога — см. ниже). + +### Что сделано + +| Шаг | Результат | +|---|---| +| 8 (матч) | Проверен синтетикой: обе ветки работают (cache точный + json фолбэк) | +| 1 (бэкап) | `~/backup-watchlist-before-lampac-sync-20260924-005630/` (TimeCode.sql + .md); репо — git push | +| 2 (код) | `watchlist_sync/sync_from_lampac.py` + команда `sync-from-lampac` в cli.py | +| 3 (dry-run) | Синтетическая БД (8 сценариев) + копия .md → diff ровно 2 ожидаемые строки | +| 4 (.env) | `LAMPAC_TIMECODE_DB`, `LAMPAC_WATCHLIST_JSON`, `LAMPAC_PERCENT_THRESHOLD=90` (бэкап `.env.bak-lampac-20260924`) | +| 5 (крон) | Врезан в `~/scripts/watchlist-nightly.sh` после sync-up, до vault push; md5 новой `edf0e34530692dbb7b6fb9644f1b45a7`, бэкап `.bak-lampac-20260924`; полный ручной прогон 01:05–01:07 ✓ | +| 6 (порог) | `percent >= 90` (env/CLI `--percent`); точная калибровка — после живого досмотра | +| 7 (доки) | Этот план + `family/projects/watchlist-automation.md` + скилл `watchlist-sync-resolver` | + +### Матч-цепочка (реализована двухступенчатая) + +``` +timecodes.identity ('movie-674' / '674_movie' — строгие формы, иначе скип) + → tmdb_id + → 1) .resolve_cache.json: candidate.tmdb_id → ключ кэша = заголовок .md (ТОЧНО) + → 2) фолбэк watchlist.json: href '/movie/' → name (TMDB ru) / original (en) + + сверка года (расхождение > 1 → пропуск с логом) + → _mark_watched (как Jellyfin-канал; ⬇️ срезается, эмодзи-префикс сохраняется) +``` + +### Тестовая матрица (все 8/8 ✓) + +| Сценарий | Ожидание | Факт | +|---|---|---| +| cache-матч, percent=95 | `[→WL] via cache` | ✓ («Поступь хаоса», 412656) | +| json-матч, percent=97 | `[→WL] via json:name` | ✓ («Смерть единорога», 1153714) | +| percent=18 | скип ниже порога | ✓ | +| `tv-1234-s1e2` | скип tv | ✓ | +| `deleted=1` | скип | ✓ | +| уже `[x]` фильм | `[ok] ничего не делаю` | ✓ (63958) | +| левый id 999999999 | `[miss] вне watchlist` | ✓ | +| dry-run без `--apply` | 0 записей в .md | ✓ | + +### 🔴 Побочный баг найден и починен: `_normalize` в `sync_up.py` + +`_normalize` не делал `.strip()` после удаления небуквенных символов: +`'Смерть единорога ⭐'` → `'смерть единорога '` (хвостовой пробел) ≠ `'смерть единорога'`. +Из-за этого `_mark_watched` молча проваливался на заголовках с ⭐/✭ на конце — +**включая канал Jellyfin** (там баг жил незамеченным). Фикс: `return t.strip()`. +Симптом до фикса: `[!] _mark_watched не нашёл строку для 'Смерть единорога'`. + +### Новые грабли этой сессии + +| # | Грабля | Обход | +|---|---|---| +| 14 | `timecodes` UNIQUE(user, identity) — повторный INSERT той же пары падает | одна строка на пользователя+фильм, percent обновляется на месте; в тесте — разные `user` | +| 15 | `Sync.sql` (`bookmarks` с `categories`) и `Sisi.sql` — ПУСТЫЕ (проверено 2026-09-24) | Lampa не синхронизирует закладки на Lampac; TimeCode — единственный канал | +| 16 | `json.name` = TMDB ru-заголовок (требование «как в tmdb/kp»), ≠ строке .md | двухступенчатый матч: resolve-cache точный → json name/original + год | +| 17 | длинный inline-ssh с URL-подобными строками уходит на апрув tirith и истекает | файл-скрипт через scp (как всегда) | + +### Осталось (не блокирует работу канала) + +- ⏳ **Калибровка порога**: досмотреть фильм из вотчлиста в Lampa → вычитать + `percent` из `timecodes` → если ≠ 90, поправить `LAMPAC_PERCENT_THRESHOLD` + в `.env` (перезапуск не нужен, читается при каждом запуске). +- ⏳ Сериалы (`tv-*`) — этап 2. +- ⏳ Риск `/lampac/database` не на volume — закрыть bind-mount'ем (отдельный апрув). +- Проверка в UI: после первого реального досмотра фильм должен пропасть из + «Мой список» в Lampa после ночного прогона (каталог 02:30 пересобирается + и выкидывает `[x]`). + **Открытые пункты, ждущие команды Alex** (все три — отдельные апрувы): 1. Лог `nightly`: `/tmp/watchlist-nightly.log` → `/home/kraken/logs/` (одна строка crontab, см. 8.2). 2. `arr.py`: `searchForMovie: False`, `searchForMissingEpisodes: False` (см.