From 953068f0db39ef8c83fbec7ed3ef9734b35f6f4e Mon Sep 17 00:00:00 2001 From: jtrzupek Date: Tue, 9 Jun 2026 21:51:29 +0200 Subject: [PATCH] docs(claude): add resolve/playback findings + local debugging guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture the durable Goon facts (phone-side resolve for IP-bound/Turnstile hosters, DoodStream/playmogo pass_md5, _embed_iframe vs _vps_blocked_fallback, stable image proxy tokens, paradisehill multipart, dedup/merge) and a local-debugging section (prod psql/worker patterns, Windows real-Python gotcha, Android emulator AVD `goon` + FLAG_SECURE-off screencap + 2x OTA apply, Chrome DevTools port 9223 + CDP-blanking + hoster network signatures). No secrets/IPs/usernames — env-var forms only. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index c19801f..7488d51 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,6 +30,38 @@ Reguły: --- +## Resolve / playback (kluczowe ustalenia) + +Stream resolve jest **on-demand, per-source**. Nic nie jest pre-resolvowane do DB poza miniaturkami i metadanymi. + +- **Registry ekstraktorów**: `app/extractors/__init__.py` `_REGISTRY` mapuje `sitetag` na funkcję. Natywny ekstraktor zwraca `list[StreamSource]`. `_vps_blocked_fallback.extract` = sygnał "resolve tylko w WebView na telefonie" (sprawdzaj `is_vps_blocked_fallback(sitetag)`). +- **IP-bound / CAPTCHA hosterzy resolwują się PO STRONIE TELEFONU**, nie na VPS. CDN tokeny i Cloudflare Turnstile wiążą się z IP requestera, więc VPS (Hetzner) dostaje 403 albo Turnstile gate, a residential IP telefonu przechodzi. Resolvery phone-side: `mobile/src/lib/{doodstream,packerHoster,filemoonHoster,getfileResolver,pornxpResolver}.ts`. `PlayerScreen.tsx` routuje URL `type='hoster'`: `isDoodStream`→`resolveDoodStream`, `isPackerHoster`→`resolvePackerHoster`, `isFilemoonHoster`→`resolveFilemoonHoster`. +- **DoodStream i klony** (playmogo, doply, dood.*): protokół `pass_md5` + infra `doodcdn.io` + **niewidzialny** Cloudflare Turnstile (przechodzi automatycznie w WebView z residential IP, to NIE interaktywny CAPTCHA). Tube który embeduje playmogo (sxyland, xmoviesforyou, siska) ma iść przez `_embed_iframe.extract` (wyłuskuje iframe, oddaje `type='hoster'`, telefon dood-resolwuje), NIE przez `_vps_blocked_fallback`. +- **Image proxy**: WSZYSTKIE miniaturki idą przez `/proxy/img/{token}/...` (backend dokłada Referer, którego expo-image nie wysyła → CDN by zwracał 403). Token MUSI być stabilny (`make_token(..., stable_bucket_sec=...)`), inaczej URL zmienia się co request → expo-image cache miss → re-download co fetch/launch. +- **Movies multipart** (paradisehill): backend parsuje `var videoList` i zwraca wszystkie części jako osobne `StreamLink`; mobile pokazuje je w **scrollowalnym Modalu** (nie `Alert.alert` — Androidowy AlertDialog renderuje max 3 buttony, stąd dawne "max 3 z 35"). +- **Dedup**: `app/scheduler/bulk_dedup.py` (cross-source performer-shared + `phash_exact`). `merge_scenes` (`app/resolve/scene_merge.py`) przenosi refs/performers/tags/fingerprints/**playback_sources** i kasuje drop. Missing-merge bez phash: `scripts/merge_exact_title_duration.py` (ten sam performer + identyczny znormalizowany tytuł + długość co do sekundy). Over-attribution performerów bierze się z luźnego tube-searcha (hqporner wymaga WSZYSTKICH tokenów query w slug). +- **Bug reports z apki**: tabela `bug_reports` na prodzie (`screen_name`, `scene_id`, `movie_id`, `message`, `screenshot_b64`, `resolved`). To główne źródło triage'u. Screenshot dekodujesz z base64 i oglądasz. + +--- + +## Lokalny debugging + +Hosta VPS, klucze API i wartości env trzymaj w prywatnej pamięci / `.env.local`. NIE commituj ich tu (repo jest publiczne). + +- **DB / worker na prodzie** (przez ssh na VPS): `docker compose exec -T db psql -U goon -d goon -c "..."` (pamiętaj `-U goon`, bez tego "role root does not exist"). Python w workerze: `docker compose exec -T worker python3 -c "..."` albo heredoc. `from app.db import session_scope` (NIE `app.db.session`). +- **Deploy backendu**: `scp app/...` na VPS + `docker compose restart worker api`. Wolumeny `./app` i `./scripts` → restart wystarcza, bez rebuildu. +- **Windows: prawdziwy Python**. `python`/`python3` na PATH to STUB ze Sklepu Microsoft (wypisuje "Python was not found" i wychodzi). Użyj realnego interpretera: `%LOCALAPPDATA%\Programs\Python\Python3XX\python.exe` (uruchamiaj przez PowerShell). `publish_update.py` po sukcesie kończy się czysto (UTF-8 stdout wymuszony 2026-06-08, koniec mylącego exit-1 na polskich znakach). +- **Emulator Android** (podgląd odtwarzania scen i UI): + - adb: `%ANDROID_HOME%\platform-tools\adb.exe` (`ANDROID_HOME` ustawiony; SDK NIE w domyślnym `%LOCALAPPDATA%\Android\Sdk`). AVD: **`goon`**. + - Start: `emulator -avd goon -no-snapshot-load -no-boot-anim -gpu swiftshader_indirect` (w tle). Czekaj na boot: `adb wait-for-device` + poll `adb shell getprop sys.boot_completed` == `1`. + - Pakiet apki: `com.goon.mobile`. Launch: `adb shell monkey -p com.goon.mobile -c android.intent.category.LAUNCHER 1`. + - **Screencap działa bo dev/emulator build ma `FLAG_SECURE` WYŁĄCZONE.** Release build ustawia `FLAG_SECURE` (blokuje screenshoty/nagrywanie ekranu treści NSFW) — w buildzie emulatora zostawiamy off, żeby AI mógł `adb exec-out screencap -p > out.png` i zweryfikować odtwarzanie/UI. + - **OTA na emulatorze**: po publish force-stop + launch **2×** (1. pobiera bundle w tle, 2. aplikuje). + - Gesty przez adb: tap `adb shell input tap X Y` (współrzędne device, sprawdź `wm size`, zwykle 1080x2400). Long-press: `adb shell input swipe X Y X Y 700` (ten sam punkt, >300ms = `delayLongPress`). Bounds elementów: `adb shell uiautomator dump` (RN czasem nie eksponuje tekstu, natywne Alert/dialogi tak). +- **Chrome DevTools (diagnoza hosterów/playbacku — sprawdzaj, nie zgaduj)**: odpal Chrome `--remote-debugging-port=9223 --user-data-dir=`; podłącza się `chrome-devtools` MCP. KVS/dood tubes **blankują się na wykrycie CDP** (strona → about:blank) → używaj `list_network_requests(includePreservedRequests=true)` zamiast DOM. Sygnatury w trace: `pass_md5` + `doodcdn.io` = DoodStream; `kt_player(` + `license_code` = KVS; `cdn-cgi/challenge-platform` + `turnstile` = Cloudflare (sprawdź czy invisible). + +--- + ## Kanban (auto-aggregation) Ten workspace ma własny Kanban (custom tracker type `kanban` w `.nimbalyst/trackers/kanban.yaml`) z kolumnami: