Zgloszenia 2026-08-20 ("hentaimama dalej 0 scen", "porndish zaprzestal ingestu",
"czemu sexu ma tylko 38 scen"). Trzy razy ten sam pierwotny powod, i to NIE ingest.
`api.ts` doklejal `min_duration_sec=60` do KAZDEGO listScenes bez wlasnej wartosci.
W SQL `duration_sec >= 60` jest nieprawda takze dla NULL, wiec prog wycinal nie tylko
krotkie sceny, ale wszystkie o NIEZNANEJ dlugosci. Zmierzone na produkcji:
supjav, hentaigasm, hentaimama, hentaiplay, javflix, xanimeporn 100% NULL
xhamster 90,3% (360 709) porndish 88,9% (37 736) porntrex 76,8% (616 345)
latestpornvideo 68,4% xnxx 65,2% (226 217) xvideos 63,8% (258 711)
Czyli wejscie w konkretny serwis ukrywalo ponad 1,5 mln scen i wygladalo jak martwy
albo zatrzymany tube. Ekrany drill-down (SiteScenes, PerformerScenes, StudioScenes,
TagScenes) przekazuja teraz 0: user wszedl JAWNIE w te pozycje, wiec ma zobaczyc jej
zawartosc. Glowny feed zostaje z progiem 60s, bo tam chroni przed teaserami.
Zweryfikowane na produkcji: hentaimama / supjav / xanimeporn 0 -> 50 pozycji.
Pulapka NULL opisana w api.ts, zeby ktos nie "uproscil" tego z powrotem.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Domkniecie tej samej rodziny bledu co przy tagach i studiach. Pomiar rozstrzygnal,
ze oba wertykaly wymagaja INNEGO rozwiazania, wiec nie ma tu jednego filtra:
animacja 12 698 scen -> 0 performerow
JAV 54 888 scen -> 1 392 performerow
JAV: /performers dostaje parametr `vertical`, liczony na zywo, bo zdenormalizowany
`performers.scene_count` z zalozenia opisuje wylacznie glowny feed (job taxonomy_counts
wyklucza originy wertykalowe). Bez tego zakladka pokazywala zachodnia obsade, a wlasne
nazwiska mialy licznik 0 i ladowaly na koncu listy. Po zmianie: Satomi Suzuki (137),
Kei Mizushima (128), yui hatano (68).
Animacja: przycisk Performers jest chowany. Zero obsady to nie brak danych, tylko
wlasciwosc tresci: dla postaci rysowanych nie istnieje kanon (TPDB/StashDB ich nie maja),
wiec zaden scraper nie ma czego przypisac i zaden nigdy nie bedzie mial. Filtrowanie
dawaloby gwarantowanie pusta liste, czyli dokladnie ten sam objaw, ktory naprawiamy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. WERTYKALY. Hentai i 3D szly do glownego feedu wymieszane z zachodnim
katalogiem, mimo ze argument za odgrodzeniem byl ten sam co przy JAV.
Zamiast drugiej kopii mechanizmu JAV wprowadzam wspolne pojecie wertykalu
(app/verticals.py): mapa nazwa -> originy, jeden predykat, `?vertical=`.
Brama po originie jest DOKLADNA, zmierzone: 9507 scen animowanych, ZERO
z originem spoza listy (nie maja sie z czym zdeduplikowac). Dlatego osobna
baza nie jest potrzebna, rozdzielam logicznie.
Tagi to samodzielny powod: z 3116 tagow przy scenach animowanych tylko 452
wystepuja tez poza animacja, czyli 85% to slownictwo wylacznie animacyjne.
`/tags?vertical=` liczy je NA ZYWO (male zbiory, zero migracji), a
zdenormalizowany Tag.scene_count opisuje juz WYLACZNIE glowny feed.
Przy okazji naprawione zasmiecenie JAV-em istniejace od lipca:
taxonomy_counts wliczal wertykaly do licznikow, ktore opisuja feed ich
nie pokazujacy. Refresh: 1476 tagow, 1402 performerow, 154 studia.
Stary `?jav=true` DZIALA dalej (instalacje sprzed sierpnia go wysylaja).
2. FILMY NA EKRANIE STUDIA. Zgloszenie: polubione studio od filmow nie
pokazuje nowych filmow i nie ma "+1". Backend liczyl je poprawnie
(_merged_new_counts scala sceny i filmy od 2026-07-28). Blad byl w apce:
tap na ulubione studio ZEROWAL licznik (markStudioSeen) i wchodzil na
ekran wylacznie ze scenami, wiec "+N" z nowych filmow znikalo, a filmy
nie mialy sie gdzie pokazac. StudioMoviesScreen istnial, ale wchodzilo
sie do niego tylko z detalu filmu. StudioScenes dostaje zakladki
Scenes/Movies, jak PerformerScenes.
Pasek zakladek w headerze przewija sie w poziomie, bo piata pozycja
(Anime) nie miescila sie na waskich telefonach.
1745 wystapien w 291 plikach (backend, scripts, mobile). Dominowal jeden ksztalt,
' — ' laczace zdania, wiec poszedl przecinek; myslnik otwierajacy mysl zdjety;
zakresy 0–5 i A–Z na dywiz.
Podzial komentarz/kod robil skaner stanow, nie regex, zeby nie ruszyc myslnika
stojacego w stringu albo w wyrazeniu regularnym. Potwierdzone maszynowo: po wycieciu
komentarzy z wersji HEAD i z dysku obie sa identyczne we wszystkich 291 plikach,
czyli poza komentarzami nie zmienilo sie nic. 233 pliki .py parsuja sie bez bledu,
tsc ma te same 11 bledow w tych samych 3 plikach co przed zmiana.
Dwie rzeczy wymagaly poprawki po automacie: wyrownane listy definicji, gdzie myslnik
byl separatorem kolumny (37 miejsc, kolumna przywrocona), i trzy przeniesienia mysli
do nowej linii, gdzie zostawal wiszacy przecinek na poczatku (poprawione recznie).
Bundla nie republikuje: Hermes i tak nie niesie komentarzy, wiec dla uzytkownika
to zero zmiany.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Backend GET /scenes/{id}/similar: ranking po WSPÓLNYCH performerach (waga 3) i
tagach (waga 1). Performerzy selektywni → mała pula kandydatów (cap 1500); tagi
liczone tylko w tej puli. Fill z tagów (bounded 400, HAVING>=2 wspólne) tylko gdy
performerów mało/brak (anonimowe tube sceny) — inaczej szeroki tag skanowałby
setki tysięcy scen. Filtr widoczności jak lista: grywalne + brama JAV + blacklist
+ nie-stub + nie-self. Ranking/sort w Pythonie nad ograniczonymi zbiorami.
Zmierzone: 210-944ms wg liczby performerów seeda.
Mobile: SimilarRail (poziomy) na dole SceneDetail, lazy przez react-query, znika
gdy brak dopasowań, tap → nav.push kolejnego SceneDetail.
No-accounts app, so favorites/saved-searches/hidden-list/watch-progress live
per-device (keyed by X-Device-Id). Users asked how to move them to a new phone.
Backend: POST /me/import/{source_device} copies all device-scoped tables from a
source device to the caller (dedup via NOT EXISTS, new UUIDs for id-PK tables like
saved_searches). Copy not move, so the old phone keeps everything; idempotent. The
device_id is a random UUID so knowing it is the authorization (fine for a keyless app).
Mobile: Settings -> "Backup & sync" shows this device's sync code (copy button) and a
Restore field to paste the other device's code; invalidates favorites queries after.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
JAV is a distinct vertical (user decision): Asian codes/titles that do not dedup
against the western catalog, so they must not flood the main feed.
Backend (scenes.py): JAV_ORIGINS = {tube:javflix, tube:javguru, tube:vjav,
tube:supjav}; list_scenes gains a `jav` param. Default (jav=false) excludes any
scene with a live JAV-origin source; jav=true returns only those. The cached
default-count and _is_pure_default also exclude JAV so the main feed count matches.
JavflixScraper is now registered in ALL_BROWSE_SCRAPERS (scheduled ingest lands in
the JAV section, gated). Scraper hardened: requires a real server button
(class="myLink") so static pages (Terms/FAQ) are skipped, and unescapes HTML
entities in the title.
Mobile: a "JAV" top tab reuses ScenesScreen with { jav: true } (route param ->
listScenes jav=true). The 60s minimum-duration default is disabled in the JAV tab
because javflix does not expose duration (NULL >= 60 would hide the whole section).
Verified on prod: 16 javflix scenes appear only in the JAV feed and are excluded
from the 2.29M main feed; playback resolves to voe/doodstream/emturbovid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The zload CDN URL (remote_control.php) is a valid faststart progressive mp4 that
ExoPlayer plays fine when fresh: 206, no IP/referer binding, ~2h token TTL,
verified from a residential IP. Real-device failures (player_error/gone
telemetry) come from the static URL going stale: a rotated or dead CDN node, or
an expired token.
Instead of falling back to the tube page in a WebView (removed, it papered over
the real issue), PlayerScreen now re-resolves the scene fresh via the backend on
initial-load error for backend-native KVS tubes (watchporn). That yields a live
node plus a fresh token and stays on the native direct stream (0 VPS bandwidth).
For these origins a CDN 'gone' (404) means stale URL, not deleted, so we
re-resolve on gone too; a genuinely deleted post raises HosterDead (410) and
falls through to the normal fallback chain.
Also: add playback_events.error_detail (raw ExoPlayer message) to pin down the
exact failure of tubes we cannot reproduce on the emulator. Keep FLAG_SECURE on
release builds only (!__DEV__) so debug builds stay screenshottable for local UI
verification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rates each source on three axes the user asked for:
- freshness: how recently/often new content arrives (newest age + 7d volume)
- richness: metadata coverage (thumbnail/tags/performers/description/studio/duration)
- plays: does it actually play — from real playback telemetry when available,
else a proxy from the resolve mechanism. 0★ = offline (gates the overall stars,
so a fresh+rich source that doesn't play still ranks bottom — the hqfap/4k69 case)
Backend:
- playback_events: fire-and-forget telemetry POST from the app per playback attempt
(origin + success/error + time-to-first-frame), append-only, 30d retention
- source_stats: per-origin computed scores, refreshed by a scheduler job (6h);
/sources joins it and sorts by stars
- models + local migration 0025; new GOON_SCHED_SOURCE_STATS_HOURS setting
Mobile:
- Sites rows show ★ rating; tap the stars for a breakdown (axes + metadata %, plus
whether "plays" is measured or estimated)
- PlayerScreen reports playback success/failure per source (native path only —
symmetric, conservative); origin threaded through Scene/Movie play callsites
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Batch from user feedback: (1) Grid columns 1/2/3 setting (PreferencesContext, persisted) across all scene grids — default 2 was too small on phones. (2) Min-duration filter chips (5/10/20/30+ min) to hide ad-clips. (3) Saved-search chips + Save button (backed by /saved-searches). (4) Re-enabled screen-capture protection (Recents hide + screenshot block) for distributed users — verified active on emulator (screencap returns 0 bytes). (5) 'Checking for updates' gate before the PIN screen so a background OTA restart no longer causes a double PIN prompt. Changelog entry added. Published OTA runtime 1.1 (a9620b12).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 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>
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>
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>
GoonClient attaches a stable per-install device id (SecureStore, lazy UUID) on
every request so server-side user state is scoped per device. On first launch
after update, call /me/adopt-legacy once (SecureStore flag) to claim the previous
shared state onto this device — the instance owner should relaunch first.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bug-report 5a6844db: the hold-to-preview animated gesture did nothing useful.
Replace it with a long-press action menu on scene tiles:
- Ukryj scenę → POST /scenes/{id}/hide
- Oznacz jako duplikat → enter selection mode; tapping another tile merges the
long-pressed scene INTO the tapped one (POST /scenes/{keep}/merge/{drop}).
SceneActionsProvider holds the selection state + a bottom banner, so it works across
all 5 scene-list screens via the shared SceneTile (no per-screen wiring). Selecting
mode highlights tappable tiles and badges the pending duplicate. Animated thumbnails
kept only as a still-fallback image; has_animated_thumbnail filter removed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
The generic request<T>() always called res.json(), which throws on a 204 No
Content body. mark-dead endpoints (scene + movie "Mark as invalid"/broken)
return 204, so the call threw AFTER the backend had already marked the source
dead → user saw a "Failed" alert and the list didn't refresh, even though the
mark succeeded server-side (bug-reports 2026-05-28 Voe, 2026-06-03 scene
1e8dc190). Return undefined for 204 before parsing JSON.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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.