Commit graph

40 commits

Author SHA1 Message Date
goon-foss
0fae99f67c fix(mobile): show Refresh thumbnail when the hero image actually fails to load
The button keyed on thumbnail_url presence, but a URL can be present yet broken (hqfap
404 → blank hero, no button — report ef0c6a5a). Tie it to the hero Image load state
(onLoad ok / onError broken / no url none) and show Refresh only when the image is
broken or missing. Reconciles 26c114ed (hidden for good previews) with ef0c6a5a.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 14:18:44 +02:00
goon-foss
1c021daf33 feat(mobile): auto-accept age-gate modal in WebView fallback
4k69 (and similar) show an "Are you 18 or above? Yes/No" modal that blocks the jwplayer
from initialising, so the WebView fallback never extracts a stream. Click the age-gate
accept button by id (#pop_up_18_yes and id*=18_yes/age_yes variants) on the same loop as
the consent/play-poster auto-clickers. Verified on emulator: 4k69 age-gate clears and the
player initialises (ExoPlayer hands off). A VAST preroll is still grabbed instead of the
okcdn content for 4k69 specifically (report 5de3fbc5 stays open) - separate ad-filter work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 11:31:12 +02:00
goon-foss
5d359cd319 fix(mobile): show Refresh thumbnail only when preview missing or broken
The Refresh thumbnail button appeared on every scene, which is noise for the majority
that already have a good preview (report 26c114ed). Show it only when no source has a
usable thumbnail or the only thumbnails are rotting (sxyprn/trafficdeposit), which is
exactly when a manual refresh helps (the original d3376a71 case).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-14 11:17:18 +02:00
goon-foss
5bac6d9c16 feat(mobile): source-code link in Settings + Refresh thumbnail button
- AppLockSettings: a "Source code" row linking the public OSS repo (report 4c5066b8) -
  a trust signal for a sideloaded FOSS app (audit / self-host / contribute).
- SceneDetail: a "Refresh thumbnail" button (force) for scenes whose preview is broken
  or stale (report d3376a71).
- changelog: new What's New entry for this batch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 19:04:11 +02:00
goon-foss
dbeef3deb1 fix(mobile): remount Favorites lists on numColumns change (GOON-11)
The new Scenes tab uses a 2-column FlatList while Performers/Studios/Movies are
1-column. Switching tabs reused the same FlatList instance, so numColumns changed on
the fly and RN threw "Changing numColumns on the fly is not supported" (5 users).
Give the Scenes list a distinct key ("fav-scenes") from the shared single-column key
("fav-list") so React remounts a fresh FlatList across the 1<->2 boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:19:19 +02:00
goon-foss
f1c1e60007 feat(mobile): "Your messages" inbox on bug FAB + geo-block playback hint
Bug FAB now has two tabs: "Report a bug" (existing) and "Your messages", which lists
this device's reports with any admin reply in a highlighted box. A badge dot on the FAB
shows unread replies; opening the tab marks them seen. Polls every 90s and on open.

PlayerScreen: when the WebView fallback (residential IP) cannot extract a stream within
25s and there is no 404/410, show a one-time hint that the source may be blocked in the
user's region or by their ISP (try another source or a VPN) - so a geo/network block on
the user's side does not read as a broken app.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:35:44 +02:00
goon-foss
05f7cee92d feat(mobile): Favorites "Scenes" tab to view saved scenes
Users could heart individual scenes from SceneDetail, but the Favorites screen only
had Performers/Studios/Movies tabs, so saved scenes were invisible (bug report: got a
bunch of scenes saved but no way to see them). Add a Scenes tab (now the default)
listing favorited scenes as tiles via GET /scene-favorites, long-press to remove.
Adds client.listSceneFavorites().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 10:06:12 +02:00
goon-foss
1b8ff0197b feat(mobile): phone-side resolvers for IP-bound tubes (sxyprn, eporner, voe)
These CDNs bind their signed video URL to the IP that fetched the page, so a
server-side resolve hands the phone a URL bound to the server IP -- the device then
gets a placeholder/403 and falls back through the proxy, streaming the whole video
through the server. Resolve on the device instead (token binds to the phone IP) so
playback goes direct with zero proxy bandwidth.

Ports of the existing backend extractors:
- sxyprnResolver.ts: data-vnfo + boo/ssut51 transform
- epornerResolver.ts: vid+hash -> /xhr/video mp4 sources
- voeResolver.ts: mirror redirect + 7-step payload decoder

Wired into SceneDetailScreen.onPress (sxyprn/eporner) and MovieDetailScreen.playVoe (voe).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 16:14:25 +02:00
goon-foss
7222f3dcf9 fix(performer): tag chips → in-place horizontal filter selector
Follow-up to 1a4bf258 feedback (a627637b + 0264a3ff): the flexWrap chip list ate
too much vertical space and tapping navigated away to TagScenes. Rework: single-row
horizontal scroll of toggle-chips that filter the performer's scenes IN-PLACE
(performer_ids + tags in one listScenes query, no navigation). Selected chip is
highlighted with a ✕ affordance; tap again clears. One line tall instead of N rows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 09:25:02 +02:00
goon-foss
99e9dae4d1 feat(performer): replace dev Re-scrape button with top-tag chips
bug-report 1a4bf258: "Re-scrape mógłby zniknąć, za to tagi/kategorie by mogły".
Re-scrape was a dev-only bulk thumbnail/tag enrich — noise on the performer page
(per-scene enrich already happens on SceneDetail). Removed it; kept Search.

New GET /performers/{id}/tags aggregates scene_tags across the performer's
live-playback scenes (top N). PerformerScenes renders them as chips → tap navigates
to TagScenes. Search button widened to full row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 11:56:26 +02:00
goon-foss
49ebba2aa8 fix(mobile): movie part picker as scrollable modal — Android showed only 3 of N
paradisehill multipart movies passed all N parts to Alert.alert, but Android's
native AlertDialog renders at most 3 buttons → a 35-part movie showed 3 (bug-report
2ebd0690 2026-06-07). Backend correctly returns all 35; the cap was client-side.
Reuse PlaybackQualityModal (now scrollable + title + preserveOrder props, hides
bogus "1p" for non-resolution labels). Also add the missing `raw` field to the
StreamLink type (backend sends it; part_label lives there).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:25:03 +02:00
goon-foss
fbecd4cd50 fix(mobile): removeClippedSubviews=false on grids — stop thumbnails vanishing on scroll
Android FlatList defaults removeClippedSubviews=true, which detaches off-viewport
subviews; expo-image frequently fails to re-render them when they scroll back in →
blank thumbnails (bug-report f181d382 2026-06-07, recurring). Disable on all heavy
image grids: scene grids (Scenes/Site/Studio/Tag/Performer) + movie poster grids.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 10:18:48 +02:00
goon-foss
1d8ede6201 fix(player): drop "Tap for sound" pill — speaker toggle is enough
User feedback (2026-06-07, report 4bdca61e) on the prior mute change: the always-
visible "Tap for sound" pill is redundant — the 🔇/🔊 toggle in the top controls
is enough. Removed the pill (+ its styles); video still starts muted and the
speaker toggle unmutes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 09:20:42 +02:00
goon-foss
2dcc470f4d feat(playback): native pornxp.ph via phone-side resolver (kills black screen)
pornxp.ph serves direct <source> mp4 (360/720/1080p) on st.pornxp.sh whose path
token is IP-bound to whoever fetched the PAGE (verified 2026-06-07: VPS-resolved
URL → 403 cross-IP). Backend resolve was therefore impossible, so pornxpph fell
to the WebView fallback which black-screened (bug-report fd06cd86).

Fix: resolve on-device (same pattern as getfileResolver/doodstream) — the phone
fetches the page, so tokens bind to the phone IP and play natively. New
pornxpResolver.ts extracts the <source> mp4s into multi-quality StreamLinks;
SceneDetail short-circuits tube:pornxpph to it before backend resolve, feeding
the existing quality-picker + native player.

Verified on emulator (live OTA): pornxpph scene → quality picker (1080/720/360)
→ native playback PLAYING (no WebView, no ads, no black screen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:58:40 +02:00
goon-foss
0cedd513f9 feat(player): start muted, unmute via button (autoplay-friendly)
Scenes/movies now start with sound OFF; user enables audio via a control
(UX request). NativeVideoPlayer: useVideoPlayer starts muted=true + speaker
toggle in top controls + always-visible "Tap for sound" pill while muted.
WebView path: injected autoplay sets muted=true (also makes muted autoplay
reliable per browser policy → faster CDN extraction); host player controls
handle unmute when the WebView is the actual surface.

Verified on emulator against the live runtime-1.1 OTA bundle: video starts
muted (pill shown), tap unmutes (pill clears).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:03:52 +02:00
goon-foss
9d7e0eca94 perf(movies+scenes): direct-play #hash movie hosters; skip empty blacklist filters
Movies: the seekplayer-engine family (easyvidplayer/player4me/seekplayer/
embedseek/upns, ~322k sources) returns a time-bound master.m3u8 on a CDN with a
valid IP-SAN cert that plays cross-IP. Mark it mobile_direct in resolve, and make
MovieDetailScreen prefer direct_url with a proxy fallback (mirrors the scene
path) — previously every movie streamed through the VPS proxy. Paradisehill
multipart parts now go direct too. Device-verified: ExoPlayer plays the raw CDN
direct, zero proxy traffic, no flicker.

Scenes: the three blacklist NOT EXISTS clauses were appended to every filtered
list and evaluated per-row even when all blacklist tables are empty (~3.4s tax on
a deep mega-tag walk). Skip them when the tables are empty (cached check) —
mega-tag list 6.7s -> 3.3s, and every filtered list benefits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 19:44:41 +02:00
goon-foss
18bd635dfa mobile: page-side get_file resolve for hdporngg/fullmovies (native, no proxy/flicker)
Device logs (not assumptions) pinned the real cause of the hdporngg/fullmovies
flicker: the backend returns a get_file URL, but get_file is bound to the IP that
loaded the *page*. The backend (VPS) loads the page, so the get_file is VPS-bound;
the phone fetching that get_file gets HTTP 410 -> ExoPlayer errors -> falls back to
the proxy via nav.replace (the "flicker"), and ends up streaming through the proxy.
(My earlier "stateless/portable" test was from the VPS — same IP as the page load —
so it wrongly showed 206.)

Fix: when the direct_url is a get_file, the phone re-fetches the *page* itself
(resolveGetFilePage on source.page_url) so the get_file is bound to the phone IP,
picks the requested quality skipping 4K (dead on fpvcdn), follows to the CDN, and
hands ExoPlayer a working URL. On failure it keeps the original (proxy fallback).

Verified on device: [getfile] page-resolve -> get_file 206 -> ExoPlayer PLAYING,
position advancing, no error/proxy/flicker, real video frame rendered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:16:19 +02:00
goon-foss
dca47e9d11 mobile: resolve get_file redirect client-side (kills hdporngg flicker)
hdporn.gg/fullmovies.xxx return an unresolved get_file direct_url that 302-redirects
to fpvcdn.com with the requester IP baked in. The backend can't resolve it (would
bind fpvcdn to the VPS IP -> mobile 403), so the phone must follow the redirect. But
ExoPlayer errors on that cross-domain get_file->fpvcdn redirect (drops Referer / won't
complete it) -> the native player falls back to the proxy via nav.replace, which the
user sees as a screen-reload "flicker" before playback (and means it's actually playing
through the VPS proxy, not direct).

Fix: resolve the get_file 302 in JS on the phone (so fpvcdn binds to the phone IP)
before navigating to the player, and hand ExoPlayer the final fpvcdn URL directly —
no redirect, no error, no flicker, no proxy. Uses the same redirect:'manual' +
Location-header pattern as the doodstream resolver (works on RN Android). On resolve
failure it keeps the original get_file URL (current behaviour with proxy fallback).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 23:49:40 +02:00
goon-foss
77b4916083 mobile: skeleton grid while scene lists load (perceived perf)
Scene-list screens showed a small spinner while waiting on the API, so a slow
list read felt like a blank stall. Replace the initial-load spinner on
ScenesScreen and TagScenesScreen with a SceneGridSkeleton — a 2-col grid of
pulsing placeholder tiles laid out 1:1 with SceneTile (16:9 thumb + title + meta
lines). It paints instantly with zero data, so the screen feels responsive even
when the query takes a moment, and the skeleton->content swap doesn't reflow.

Pairs with the backend list-count fix (most filtered lists are now ~0.1s); the
skeleton also masks the residual slow path (enormous tags) so it no longer reads
as a freeze.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:03:33 +02:00
goon-foss
d468d0a750 chore(mobile/login): drop dead brandDot/title styles from old design
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:25:09 +02:00
goon-foss
7cabd553b7 fix(mobile/login): one-tap public pairing, manual URL/key behind Advanced
Pairing is automatic (App.tsx auto-connects to the public instance when no creds are
stored); the login screen only appears after an explicit Sign out. It defaulted to
localhost + empty key, forcing manual entry that no longer reflects how pairing works.
Now it prefills the public backend + shipped key (one-tap 'Connect to public instance')
and tucks the URL/API-key fields under an 'Advanced · self-hosted backend' toggle for
power users.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:22:50 +02:00
goon-foss
948f62cb65 mobile: recover from mid-playback decode/seek errors (doply NAL)
Bug f6c86847/b1b5e1a2: doply/playmogo plays fine but seeking throws
"source error, invalid NAL length" in ExoPlayer. Investigation (cross-IP,
2026-06-01) showed the stream is well-formed — faststart MP4 (moov before
mdat) on cloudatacdn.com which fully supports HTTP range (206, correct
content-range, repeatable token, no redirect). So it is an ExoPlayer-internal
seek failure, not an HTTP/container problem, and expo-video exposes no
extractor/MIME hint to influence it.

Mitigation: when the native player errors *after* it had already loaded
(i.e. a mid-playback/seek failure, not an initial-load failure) and the error
is not a 404/410, recreate the source via player.replace() and resume at the
last known position — this opens a fresh connection and re-parses moov, which
typically clears the transient decode error. Hard-capped at 2 attempts per
mount to avoid any auto-reload loop; if it still fails it falls through to the
existing proxy/WebView fallback and error UI. Initial-load errors are
untouched, so the resolver and the ~59k working doply sources are unaffected.

Also thread playbackId/entityKind through the resolved-hoster and proxy/WebView
nav.replace calls so those paths get the 404 "Mark broken" affordance too, and
complete the local RouteParams type with headers/fallbackProxyUrl.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:15:21 +02:00
goon-foss
fb59d5fdfa mobile: let players identify & report 404/410 sources
When a host returns 404/410 at playback time (CDN gone, video removed) the
player previously showed only a raw error and a Back button — the user could
not tell it was a dead source or report it without going back to the detail
screen (bug a78cc3b6: "fpo i sxyprn to 404, którego apka nie potrafi
zidentyfikować").

- Thread playback_source.id into Player route params (scenes + movies).
- Native player error overlay: detect 404/410 in the ExoPlayer error, show
  "Source no longer available" and a "Mark broken" button that marks the
  source dead and returns. 403 is excluded (proxy/WebView fallback may save it).
- WebView player: add onHttpError; on a main-document 404/410 show the same
  overlay (Mark broken / Try anyway / Back) instead of the host's 404 page.
  Guarded to the loaded document (host+path) so same-host ad/subresource 404s
  don't false-trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:04:58 +02:00
goon-foss
fdf81ab4be fix(player): skip VAST preroll ad mp4s in WebView stream scrape
WebView-fallback hosts (pornditt, xhamster, 0dayxx, sxyland, fpoxxx, porndoe)
inject a VAST preroll ad video (trafostatic.com / bkcdn.net / gripi.online / ...)
that loads before the real content. The INJECTED_JS performance scrape grabbed
that ad mp4 and handed it to ExoPlayer, so the native player showed the 30s ad
instead of the video (user bug: "pornditt łapie reklamę zamiast video").

report() now calls isAdHost() and skips ad-network video URLs; extended AD_HOSTS
with the video-ad CDNs. Content CDNs (sacdnssedge etc.) still pass through.
Shipped via OTA runtime 1.1 (update ea4b9901).

NOTE: this fixes ad-scraping for the WebView class generally, but pornditt itself
is separately broken — its content get_file fails to load even in a real desktop
browser from a residential IP (MEDIA_ERR code 4; only the ad mp4 loads) and its
player config is dynamic/obfuscated (no inline flashvars to resolve server-side).
pornditt effectively unplayable for now — see task; deprioritize / fall back to
other sources. yespornvip (clean backend resolve) is unaffected by this.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 10:24:17 +02:00
goon-foss
1f32d5eccb fix(player): hide ad-heavy WebView behind opaque cover until stream scraped
User bug: opening a WebView-fallback scene (yespornvip etc.) shows the host's
ad-heavy page while INJECTED_JS auto-plays + scrapes the stream url in the
background. User sees ads instead of a loading state.

Render an opaque cover (theme.bg + spinner "Loading video…") over the WebView
while !extractedUrl. The WebView is still laid out and painted underneath, so
media keeps playing (autoplay via mediaPlaybackRequiresUserAction=false) and the
performance-scan picks up the CDN url — but the user only ever sees a loading
screen, then the native player. Applies to every WebView-fallback host.

Safety: if no stream is scraped within 15s (host needs a real tap to start),
reveal the WebView so the user can interact manually — no worse than before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:47:35 +02:00
goon-foss
9f8d64032a fix(player): scrape real CDN url for KVS hosts, not session-bound get_file
yespornvip (and other KVS / kt_player tubes) play via the WebView fallback:
INJECTED_JS scrapes <video>.src and hands it to ExoPlayer. For KVS, <video>.src
is a get_file/N/<hash>/... intermediate that 302-redirects to the CDN, but that
redirect is bound to the WebView's cookies/session (and is effectively one-shot).
ExoPlayer's separate request gets "Source error: response code 410" (user bug
2026-05-31, scenes Delicious Dulce / Alexis Fawx).

The actual playable CDN url (e.g. tsvideo.sacdnssedge.com/video/ol_<hash>.mp4) is
portable (206 with no cookies/referer) but never appears in <video>.src or
XHR/fetch — only in Performance resource timing (the native media loader fetches
it after the 302). Verified live in Chromium on the exact broken scene.

INJECTED_JS now:
- skips get_file intermediates (INTERMEDIATE_RE) so they're never sent to ExoPlayer
- skips scrubber preview/heatmap/sprite mp4s (PREVIEW_RE)
- scans performance.getEntriesByType('resource') each tick and reports the real
  CDN media url — cross-origin entries expose .name even without Timing-Allow-Origin

Pure JS → shipped via OTA runtime 1.1 (update d4708fed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:07:53 +02:00
goon-foss
981a7a15c4 perf(scenes): bounded count + has_more for filtered scene lists
Filtered /scenes (tag/origin/q/studio/performer) ran exhaustive COUNT with
stub-filter EXISTS over 1.7M rows: TAG 5.1s, ORIGIN 4.9s, SEARCH 3.1s.
Mobile relied on `loaded < total` for infinite-scroll, making exact count
mandatory and ruling out approximate shortcuts.

Backend:
- SceneListOut gains has_more (bool) and total_capped (bool), both optional
  for backward compat with old mobile
- Filtered count uses LIMIT _COUNT_CAP+1 (1000) subquery — cost is
  O(min(matches, cap)) instead of O(all). Measured: TAG 5.1s→664ms,
  SEARCH 3.1s→138ms, ORIGIN 4.9s→1.07s (also fixes SiteScenes showing
  global count ~1M instead of per-site count)
- has_more from fetching per_page+1 rows (essentially free); extra row
  stripped before serialisation
- Pure-default list (no filters at all) keeps TTL-cached full count

Mobile:
- getNextPageParam uses has_more ?? fallback to loaded<total
- Display shows "{total}+" when total_capped=true (5 screens)

Verified on emulator: tag "Big Tits" → "1000 scenes" loaded, no 500s,
backward compat confirmed (old APK works against new backend).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 19:24:26 +02:00
goon-foss
eead9f159c i18n(mobile): polish UI strings → English
Tłumaczenie wszystkich user-facing stringów PL→EN (bug-report 2026-05-31
"dalej wszystko po polsku"). Alerty, przyciski, placeholdery, labelki w 12
ekranach/komponentach: BugReportFAB, AppLock(Screen/Settings/PinEntry),
applock biometric prompts, doodstream error msgs, MovieDetail, PlaybackQuality,
Player, SceneDetail, ScenesFilter, SiteScenes. Komentarze w kodzie zostają PL.

Zmiany były WIP drugiego okna (uncommitted); wjechały do bundla 0.2.1 przy
buildzie (były w working tree) — apka zainstalowana już ma EN. Ten commit
utrwala je w gicie żeby nie zginęły. Czysto stringi, zero zmian logiki.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:27:55 +02:00
goon-foss
8d272acf64 feat(brand): oo logo — app icons, landing, in-app mark (Gemini-generated)
Wybrane logo: interlocked double-o "oo" (off-white ringi + oxblood doty na warm
charcoal). Wygenerowane przez Gemini 3 Pro Image (Nano Banana), wybrane z 8
wariantow przez Jana.

Native icons (wejda przy nast. APK buildzie, nie OTA):
- assets/icon.png, adaptive-icon.png, splash.png, favicon.png — zlozone z mastera
  przez PIL (mark wyciety po alpha-masce, osadzony na dokladnym #15110D, rozne
  fill-ratio per asset).
- app.json: adaptiveIcon + splash backgroundColor #0E1018 (stary navy) -> #15110D.

In-app (OTA):
- GoonMark SVG przerobiony z pojedynczego ring+dot na double-o (2 ringi + doty),
  spojny z app-icon. Login: mark 92 + wordmark 40.

Landing goon-foss.org (osobny deploy na caddy volume):
- /logo.png + /favicon.png upload, <link rel=icon/apple-touch-icon>, hero brand
  block: dot+text -> <img logo> + dwutonowy "g[oo]n".

OTA: a81ea98c-723d-4b39-9dc0-bbe8736c104e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 22:38:52 +02:00
goon-foss
d5f4fc3f84 feat(mobile): custom fonts (General Sans + Geist Mono) + logo rework
FONTY:
- Dodane assets/fonts/: GeneralSans Regular/Medium/Semibold (Fontshare, free
  commercial) + GeistMono Regular (Vercel OFL). Pobrane jako .ttf.
- expo-font ~13.0.4 (matchuje SDK 52). Native module jest w APK bo `expo`
  ciagnie expo-font jako bezposrednia zaleznosc -> useFonts dziala przez OTA
  bez rebuildu.
- App.tsx: useFonts() gate (blokuje render do zaladowania, .ttf z bundla <100ms)
  + globalny Text.defaultProps fontFamily=GeneralSans-Regular dla body.
- theme.ts: fonts = { body, medium, display, mono }. RN nie syntezuje weightow
  dla custom fontow, wiec 4 osobne rodziny per-weight (gotcha udokumentowany).
- Jawne fonty na high-traffic: SceneTile (title=display, meta+dur=mono),
  MoviePosterCard (j.w.), navigation (taby display/medium, header display).

LOGO:
- GoonWordmark przepisany: zamiast krzywych recznych SVG path (o-ka jako
  nachodzace elipsy, zniekształcone n) renderuje PRAWDZIWY tekst w General Sans
  Semibold. Dwutonowy twist: "g[oo]n" ze srodkowym "oo" w oxblood.
- GoonMark (monogram): czysty SVG koncentryczny ring + dot (oxblood) — motyw
  soczewki/oka. Dla app-icon/splash gdzie font niedostepny.
- Wpiety na AgeGate (wordmark 40), Login (mark 44 + wordmark 44), nav header.

OTA: c986c911-0868-44f7-9f4a-fc2a74e53095 live (23 assets, 4 fonty serwuja 200).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 22:25:26 +02:00
goon-foss
32ebccb1ea style(mobile): SceneTile shared component, 2-col grid w 5 ekranach scen
Jan feedback po pierwszym overhaulu: layout 2-col tile pasuje, ale aktywnie
tylko na ScenesScreen - reszta ekranow scen (SiteScenes, PerformerScenes,
StudioScenes, TagScenes) dalej w full-width row layoucie.

Wyciagniety SceneTile do mobile/src/components/SceneTile.tsx ze wsparciem:
- secondLine: 'studio' | 'performers' | 'date' | 'none' - per-ekran dobor
  metadanej (Studio na SiteScenes/Performer, performers na Studio, etc)
- seenSince: ISO timestamp - pokazuje NEW badge gdy scene.created_at > seen
  (uzywane na Performer/Studio screens dla NEW od ostatniego markFavoriteSeen)
- onLongPress: opcjonalny custom handler (default = animated preview)

Refaktor 5 ekranow:
- ScenesScreen: usuwa lokalna kopie SceneTile, import shared
- SiteScenesScreen: SceneRow -> SceneTile (numColumns=2, secondLine='studio')
- PerformerScenesScreen: FavoriteSceneRow -> SceneTile (numColumns=2)
- StudioScenesScreen: FavoriteSceneRow -> SceneTile (numColumns=2, performers)
- TagScenesScreen: lokalna SceneRow -> SceneTile

FavoriteSceneRow component zostaje (legacy import w PerformerScenes - nie
ruszamy bo moze byc uzyty w innym kontekscie). gridRow style scaffold (gap+
marginBottom) dodany w kazdym StyleSheet osobno bo te ekrany maja rozne
paddingHorizontal w container.

OTA: 9eea7ac6-df72-460e-9660-22bf6c39c3ac live, runtime 1.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 15:16:09 +02:00
goon-foss
1f93e5ee81 style(mobile): UI overhaul - warm dark, oxblood, wordmark, 2-col grid
Audit przez impeccable.style/slop: aktualny theme byl literal "AI default
palette" - deep navy #08090F + purple #8B5CF6 + glow #A78BFA + brak custom
typography. Plus user feedback "wieksze miniaturki, mniej tekstu - to portal
video".

theme.ts:
- Warm dark: bg #15110D (charcoal z orange undertone), card #26201A, fg
  warm off-white #F5EDE0
- Accent: oxblood #B23A48 + amber secondary #D89B4A (brak purple, brak glow)
- type + space scale (1.25 ratio, 8/16/24/32 spacing) eksportowane
- Backwards-compat: accentDeep/Glow/Secondary/good/warn/bad zachowane
- Font scaffold: komentarz z instrukcja jak dodac General Sans + Geist Mono
  (Fontshare/Vercel free) - czeka na expo-font install

GoonWordmark + GoonMark: custom letterform SVG (4 litery jako path geometry,
flat ellipses + descender hook). Monogram standalone dla icon/splash.
Wstrzykniety do TopTabs (header) zamiast plain "" title.

ScenesScreen:
- 2-col 16:9 grid (SceneTile) zamiast full-width SceneRow (6 lini tekstu)
- Title 1 linijka, studio uppercase micro
- Wyrzucone z listy: performers, release_date, sources count, "watched"
  string - replaced check badge + dim
- Duration badge bottom-right thumb, fav badge top-left

OTA: faad1f92-541a-4241-81fd-9cf159173b7e live, runtime 1.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 15:06:34 +02:00
goon-foss
fd6808304f feat(mobile): bug-report context capture + SiteScenes tag filter
BugReportFAB (bug-report #4 / bda4383a 2026-05-26 "sceny z tej strony nie
dzialaja"): zbiera siteId/studioId/performerId/movieId/tagId z route params
i appenduje [auto-context: ...] do message body. Bez tego ekrany takie jak
SiteScenes/PerformerScenes/StudioScenes raportowaly bez kontekstu - admin
widzial tylko screen_name. Bez DB schema migration.

SiteScenesScreen (bug-report #13 / 43f81a46 2026-05-26 "przydalyby sie
kategorie na stronach Sites"): toolbar z Filter button (counter aktywnych
tagow) + Clear button. TagPickerModal: search + multi-select chipy z
popular tags (only_with_content=true). Selected slugs -> listScenes
({tags: [...]}) - backend juz wspiera AND. React Query keyed na (origin,
selected.sort().join(',')).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:24:20 +02:00
goon-foss
a4457d98d2 feat(movies): watched/continue-watching tracking end-to-end
Bug-report b207ff17 2026-05-26 ("przydaloby sie oznaczenie filmow juz
obejrzanych" - sceny mialy watched badge + dim, filmom brakowalo).

Backend:
- alembic 0018_movie_play_progress: nowa tabela (mirror scene_play_progress)
- MoviePlayProgress SQLAlchemy model
- MovieOut schema dolane finished/position_sec/last_played_at
- POST+DELETE /movies/{id}/progress endpointy (upsert via pg ON CONFLICT)
- _movie_to_out wstrzykuje progress z DB

Mobile:
- RouteParams.entityKind: 'scene'|'movie' (default scene dla back-compat)
- PlayerScreen NativeVideoPlayer + EmbedWebViewPlayer dispatchuja
  upsertProgress vs upsertMovieProgress po entityKind
- MovieDetailScreen przekazuje entityKind='movie' do nav
- MoviePosterCard renderuje dim + check badge + progress bar
  (parity ze ScenesScreen pattern)

Wczesniej MovieDetail przekazywal movieId jako sceneId -> backend
/scenes/<movieId>/progress zwracal 404 (silently caught). Po dodaniu
dedykowanego movie endpoint proper routing dziala.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:24:06 +02:00
goon-foss
d586d45e5b session work: bug-report fixes + WIP cleanup
User-facing bugs resolved (per bug_reports table 2026-05-25):
- 40cd28aa (short-scene filter): mobile api.ts default min_duration_sec=60
  hides 6519 sub-60s scenes across all list endpoints (Performer/Site/Tag/
  Browse). Caller may override with explicit 0.
- 5e89ef7e (porndoe needs cookies/play click): INJECTED_JS in PlayerScreen
  now auto-clicks player-poster overlay (player-poster-play, big-play-button,
  vjs-big-play-button, jw-icon-display, btn-big-play, mejs__overlay-button,
  play-button, btn-play, videoPlayButton). Triggered same interval as
  consent-dismiss + ad-iframe removal.
- b1b5e1a2 (Mixdrop czarny ekran): re-enable mixdrop direct stream via VPS
  curl_cffi proxy (was: skip → WebView fallback → blank screen). Backend
  pipeline (mixdrop.py extract + stream_proxy._curl_cffi_stream with JA3 +
  auto-refetch on token expire) was already complete; just removed the skip
  in app/api/playback.py.

Plus ongoing WIP (paradisehill multi-part extraction, stream_proxy refetch
logic, gesture race fix for long-press 2x speed, anti-adblock INJECTED_JS
defenses, scripts for freshporno backfill, new sources API).
2026-05-25 22:02:52 +02:00
goon-foss
f2737d7e60 filemoon: resurrect via mobile-side resolver (Byse SPA RE)
filemoon (+ mirrory kerapoxy/lvturbo/emturbovid/bysezoxexe/bysezejataos)
nie umarł — ~2026-05 zrobił rebrand na Vite SPA "Byse Frontend". Stary
P.A.C.K.E.R.-JWPlayer embed zniknął, więc backend uznał go za martwego i
wpisał na DEAD_HOSTER_RE. RE bundla index-ChwZgmXV.js (2026-05-22):

  POST /api/videos/<code>/embed/playback  body {"fingerprint":{}}
  → {"playback":{"key_parts":[..],"iv":..,"payload":..}}
  → key=concat(b64url(key_parts)); AES-256-GCM(key,iv,payload) → JSON
  → sources[*].url = HLS master.m3u8

Browser-attestation jest opcjonalny — pusty fingerprint wystarcza.
Stream URL jest IP-bound (token wiąże się z IP requestera), więc resolve
musi iść z urządzenia użytkownika (jak doodstream.ts / packerHoster.ts).

- mobile/src/lib/aesGcm.ts — pure-JS AES-256-GCM decrypt (RN/Hermes nie
  ma Web Crypto); S-box liczony z GF(2^8), GHASH weryfikuje tag.
  Zweryfikowane przeciw cryptography (Python) na 2 payloadach.
- mobile/src/lib/filemoonHoster.ts — resolver: POST playback → decrypt →
  pick best source. E2E test: filemoon.to/e + /d + bysezoxexe.com mirror.
- PlayerScreen: filemoon w resolve useEffect obok doodstream/packer.
- backend: filemoon poza DEAD_HOSTER_RE; hoster.py early-return → przelot
  jako type='hoster' do mobile resolvera (server-side resolve bezcelowy,
  bo URL IP-bound do VPS).
- direct_scrapers: poprawiony błędny komentarz "filemoon shutdown".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:18:26 +02:00
goon-foss
5dc8c8c80e Origin/hoster filter w /scenes + Filter modal
Dotąd nie dało się docelować sceny konkretnego hostera — search faworyzuje
xnxx/xvideos (dominują bazę), brak filtra po źródle. Diagnostyka per-hoster
(test cookie-fix, luluvid, porntrex) wymagała trafienia sceny danego tube'a.

- /scenes?origin=<substr> — exists() na PlaybackSource.origin ilike, np.
  'hqporner' łapie tube:hqpornercom
- ScenesFilterModal: sekcja "Source / hoster" (TextInput) w FilterState.origin
- ScenesScreen: filter.origin → listScenes; liczone do activeCount

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 12:12:50 +02:00
goon-foss
61e25cd963 Mobile: P.A.C.K.E.R. hoster resolver (luluvid/streamwish)
Backend (VPS IP) dostaje CAPTCHA od luluvid/streamwish → try_extract
zwraca type='hoster' → WebView fallback ze stroną+reklamami. Mobile IP
usera renderuje pełny embed z packed JWPlayer config.

- packerHoster.ts: port unpack_packer (hoster.py) do TS — eval-unpack
  P.A.C.K.E.R. → JWPlayer sources file URL, ad-roll filter
- PlayerScreen: resolve useEffect probuje DoodStream LUB P.A.C.K.E.R.
  → sukces = NativeVideoPlayer bez reklam, fail = WebView fallback

Naprawia latestpornvideo (luluvid) — bug 02444895 "Luluvid czarny ekran".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 11:40:03 +02:00
goon-foss
d8c44a0d1e Mobile 0.1.9: OTA enable, WebView cookie-dismiss fix, porndoe connector
Mobile / OTA:
- Enable Expo Updates (app.json + AndroidManifest) → api.goon-foss.org
- Bump 0.1.6 → 0.1.9 (build.gradle, app.json, appVersion.ts, main.py /version)
- backend.ts: default public backend auto-connect (no manual login)

WebView fallback fix (PlayerScreen INJECTED_JS):
- Auto-dismiss cookie/consent gates (hqporner et al. blocked kt_player init)
- Context-scoped: only clicks consent buttons inside cookie/gdpr containers
- Retry window for <source>.src polling raised 5→15 ticks (post-dismiss init)

Resolver:
- Series-position + modifier mismatch detector (Episode 2≠4, BTS/unedited)
  → composite_score hard-reject / cap; wired into scene_score + bulk_dedup
- aggregator-mode candidate query: LIMIT 500 + title-match ordering

Connectors:
- porndoe.com browse scraper (JSON-LD VideoObject) — theporndude audit pilot

landing: APK links → goon-v0.1.9.apk

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 11:20:57 +02:00
goon-foss
ad0284585b Initial commit
Goon — self-hosted aggregator for adult-content scene metadata.

Indexes scenes from TPDB, StashDB, and 30+ public adult tube sites.
Cross-source deduplication via perceptual hash + Levenshtein distance.
FastAPI backend + APScheduler worker + React Native (Expo) mobile client.

FOSS, ad-free, donation-funded. See README for details.
2026-05-20 10:10:22 +02:00