watchlist: sync-from-lampac implemented (Lampa -> [x]) — plan status, automation doc

This commit is contained in:
Alexey Martemyanov committed 2026-09-24 01:10:13 +06:00
1 parent 7af79521ce
commit 337505a33f
2 files changed
+148 -7

No files matched your search

+62
View File
@@ -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` — закрыт должок по грязному дереву
+86 -7
View File
@@ -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/<id>' → 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` (см.