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> |
||
|---|---|---|
| .github/workflows | ||
| alembic | ||
| app | ||
| landing | ||
| mobile | ||
| scripts | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| alembic.ini | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| DISCLAIMER.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| streamtape_sample.md | ||
| theporndude_coverage.json | ||
| theporndude_dump.json | ||
| theporndude_final_report.md | ||
| theporndude_free_tubes.json | ||
| theporndude_full_audit_report.md | ||
| theporndude_movies.json | ||
| theporndude_movies_scorecard.json | ||
| theporndude_resolved.json | ||
| theporndude_scorecard.json | ||
| theporndude_triage.json | ||
Goon
Self-hosted aggregator for adult-content scene metadata. Indexes scenes from TheporndB, StashDB, and 30+ public adult tube sites; deduplicates across sources; serves an API + mobile (React Native) client for browsing and linking out to playback.
18+ ONLY · Self-hosted only · See DISCLAIMER.md before hosting an instance.
What it does
- Multi-source ingest: pulls canonical scene/performer/studio metadata from TPDB and StashDB on a delta cron, merges duplicates by performer + title + date heuristics (perceptual hash + Levenshtein title distance).
- Tube discovery: per-performer search across 30+ public adult tube sites (mainstream + aggregators). Each tube is scraped directly via HTTP — no proprietary API dependencies.
- Stream resolution on demand: when a user clicks Watch, the API extracts a fresh m3u8/mp4 URL from the tube's page (or falls back to embed link for WebView playback). Mainstream tubes use yt-dlp; aggregator tubes use a generic P.A.C.K.E.R. unpacker for JWPlayer-based hosters (StreamWish/doodporn/mixdrop/...).
- Mobile client (Expo / React Native): scene grid, performer pages, watch history, favorites, hold-to-preview animated thumbnails.
- Performer-driven backfill: a continuous worker walks performers ordered
by
last_searched_at NULLS FIRSTand back-fills tube scenes for the longest-stale performer first.
What it doesn't do
- Host or store any media. Scene metadata + thumbnail URLs only.
- Bypass paywalls, authentication, geo-blocks, or DRM.
- Provide age verification, ToS gating, or moderation for public deployments. See DISCLAIMER.md.
- Phone home. Sentry telemetry is opt-in (env var, empty by default).
Quick start
1. Run the backend (Docker)
git clone <repo-url> goon
cd goon
cp .env.example .env
# Edit .env:
# - TPDB_API_TOKEN (theporndb.net account → API tokens)
# - STASHDB_API_KEY (stashdb.org account → API keys)
# - API_KEYS (generate one: python -c "import secrets; print(secrets.token_urlsafe(32))")
docker compose up -d
Three services come up: db (Postgres 16), api (FastAPI on :8000,
auto-applies migrations on startup), worker (APScheduler running TPDB/StashDB
delta + performer-driven backfill).
Verify: curl localhost:8000/health → {"status":"ok"}.
2. Install the mobile app (Android)
Download the latest debug APK from
GitHub Releases → goon-vX.Y.Z-debug.apk, install on
your Android device (allow "Install from unknown sources" for the browser /
file manager you used to download).
On first launch the app shows the age-gate disclaimer (must be accepted), then a login screen. Enter:
- Backend URL:
http://<your-backend-host>:8000(e.g. your LAN IP, orhttp://localhost:8000if running on the device — uncommon) - API key: one of the values you put in
API_KEYSin.env
That's it.
Local Python (no Docker)
python -m venv .venv && . .venv/bin/activate # or .\.venv\Scripts\activate on Windows
pip install -e .[dev]
cp .env.example .env # edit creds
alembic upgrade head
uvicorn app.main:app --port 8000
Worker (manual one-shot ingest)
# Foreground APScheduler with all jobs
python -m app.scheduler.worker
# One-shot:
python -m app.scheduler.worker --once --source=tpdb --limit=200
python -m app.scheduler.worker --once --strategy=performer-driven --top-n=20
python -m app.scheduler.worker --once --strategy=performer-driven \
--performers="Lola Noir,Mia Malkova"
Building the APK locally
cd mobile
npm install
cd android
./gradlew assembleDebug
# output: mobile/android/app/build/outputs/apk/debug/app-debug.apk
Or just push a v* tag — GitHub Actions builds and attaches the APK to the
Release (.github/workflows/build-apk.yml).
Sentry telemetry (optional)
Default behavior: no telemetry. Sentry only initializes when a DSN is present at runtime/build time.
To enable Sentry for your instance (errors only, no PII, no replay):
- Backend: set
SENTRY_DSN=https://...in.env(gitignored). OptionallySENTRY_ENVIRONMENT=productionandSENTRY_TRACES_SAMPLE_RATE=0.1. - Mobile (local builds): create
mobile/.env(gitignored) withEXPO_PUBLIC_SENTRY_DSN=https://.... Expo SDK 49+ auto-inlinesEXPO_PUBLIC_*vars into the JS bundle at build time. - Mobile (CI builds): add a GitHub repository secret named
SENTRY_DSN. The APK workflow exports it asEXPO_PUBLIC_SENTRY_DSNto gradle. Without the secret, the APK ships with telemetry disabled (forks of this repo don't inherit your DSN).
Sentry init is gated by if (SENTRY_DSN) { Sentry.init(...) } — empty DSN
means the SDK is loaded as dead code but never sends a single request.
Configuration
All runtime config is environment variables (see .env.example for the full list). Highlights:
| Var | Default | Required? | Notes |
|---|---|---|---|
DATABASE_URL |
postgresql+psycopg://goon:goon@localhost:5432/goon |
Yes | Postgres 14+ |
TPDB_API_TOKEN |
empty | For TPDB ingest | Get from theporndb.net account |
STASHDB_API_KEY |
empty | For StashDB ingest | Get from stashdb.org account |
API_KEYS |
empty | Recommended | CSV of allowed API keys; empty = no auth (localhost-only) |
SENTRY_DSN |
empty | No | Empty = no telemetry. Use your own DSN if you want crash reports. |
LOG_LEVEL |
INFO |
No | DEBUG for verbose tube scraping logs |
Scheduler tuning (set to 0 to disable a job):
| Var | Default | Description |
|---|---|---|
GOON_SCHED_TPDB_HOURS |
6 |
TPDB delta interval |
GOON_SCHED_STASHDB_HOURS |
6 |
StashDB delta interval |
GOON_SCHED_PERFORMER_DRIVEN_HOURS |
12 |
Top-N performer ingest |
GOON_SCHED_PERFORMER_CONTINUOUS_SECONDS |
15 |
Continuous backfill tick |
Architecture (high level)
┌──────────┐ delta cron ┌────────────┐
│ TPDB │────────────────▶│ │
└──────────┘ │ │
┌──────────┐ │ ingest │
│ StashDB │────────────────▶│ pipeline │──┐
└──────────┘ │ │ │ cross-source
┌──────────┐ performer- │ │ ▼ dedup +
│ ~25 tube │ driven ┌───▶│ │ ┌─────────┐
│ sites │ search │ └────────────┘ │ Postgres│
└──────────┘────────────┘ └─────────┘
│
┌─────────────────────────────────────────────┤
▼ ▼
┌──────────────┐ ┌──────────────┐
│ FastAPI │ │ Worker │
│ /scenes │◀────── on Watch click ───│ scheduler │
│ /performers │ resolve stream URL │ (APScheduler)│
│ /playback │ (yt-dlp / hoster └──────────────┘
└──────────────┘ packer)
│
▼
┌──────────────┐
│ Expo mobile │
│ (Android) │
└──────────────┘
Key modules:
app/connectors/— TPDB, StashDB, dooplay (movies), paradisehill (movies),direct_scrapers/(25 tube discovery scrapers).app/extractors/— stream URL resolution per tube. yt-dlp wrapper + custom + generic embed-iframe + P.A.C.K.E.R. unpacker.app/resolve/— cross-source scene merging (phash, title similarity, performer overlap, release date window).app/scheduler/— APScheduler jobs +performer_driven.py(the core ingest strategy: completeness > recency).mobile/— Expo / React Native client.
Tube coverage
Discovery + stream resolution registered for ~33 sources:
Mainstream tubes: pornhub, redtube, xhamster, xvideos, xnxx, youporn, eporner, hqporner, sxyprn, porntrex, pornhat.
Aggregators / mirrors: xmoviesforyou, watchporn, siska, porn4days, porndish, xxxfreewatch, latestleaks, latestpornvideo, mypornerleak, porndittcom, hdporn92, sxyland, 0dayxx, perverzija, fpoxxx, porn00, pornxp, hdporngg, fullmovies, freshporno, shyfap.
Movie sites: paradisehill (primary) + dooplay mirrors (mangoporn, streamporn, pandamovies).
If you want to add another tube, see CONTRIBUTING.md.
Support the project
Goon is free, open-source, and ad-free. It stays that way because donations cover the VPS, the TPDB/StashDB tokens, and the time. Crypto only — mainstream processors refuse adult projects, even FOSS tooling.
In-app: Scenes → ♥ opens a screen with QR codes for Monero, Bitcoin, and USDT (TRC-20).
Addresses are hardcoded in
mobile/src/lib/donate.ts so a compromised
server cannot swap them mid-donation. Verify the value on-screen against the
copy in this repo before sending.
Roadmap
Near-term:
- Browse-by-performer + sort-by-studio
- Multi-tag filter (AND / OR)
- Continue-watching rail (position sync across devices)
- Stash local-server bridge — sync favorites/watchlist with a self-hosted Stash
- iOS sideload via TestFlight invite
Mid-term:
- Web companion (read-only browser frontend over the same API)
- BTCPay Server invoicing for one-time / recurring donations
- Performer-alert notifications (server push when a favorited performer drops a new scene)
License
MIT — see LICENSE.