goon/landing/index.html
https://github.com/goon-foss/goon 642f1ab8b8 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

302 lines
13 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="description" content="Goon — self-hosted adult content metadata aggregator. Mobile-first, FOSS, ad-free." />
<meta name="robots" content="noindex, nofollow" />
<title>goon · self-hosted adult content aggregator</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
bg: '#08090F',
bgElevated: '#11131C',
card: '#1A1D2A',
border: '#262A3D',
accent: '#8B5CF6',
accentGlow: '#A78BFA',
accentDeep: '#5B21B6',
good: '#10B981',
},
fontFamily: {
sans: ['-apple-system', 'BlinkMacSystemFont', 'Inter', 'system-ui', 'sans-serif'],
mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'monospace'],
},
},
},
};
</script>
<style>
body { background-color: #08090F; color: #F4F4F8; }
.glow { box-shadow: 0 0 60px -10px rgba(139, 92, 246, 0.4); }
.grad { background: radial-gradient(60% 60% at 50% 0%, rgba(139,92,246,0.18) 0%, rgba(8,9,15,0) 70%); }
.card-hover { transition: transform 200ms ease, border-color 200ms ease; }
.card-hover:hover { transform: translateY(-2px); border-color: #8B5CF6; }
.pulse-dot { animation: pulse 2.5s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
</style>
</head>
<body class="font-sans antialiased">
<!-- AGE GATE -->
<div id="agegate" class="fixed inset-0 z-50 bg-bg flex items-center justify-center px-6">
<div class="max-w-md text-center">
<div class="inline-block w-3 h-3 rounded-full bg-accent glow pulse-dot mb-6"></div>
<h1 class="text-3xl font-extrabold tracking-tight mb-3">18+ Adults Only</h1>
<p class="text-sm text-gray-400 mb-8 leading-relaxed">
This site discusses software for indexing adult content. You must be of legal
age in your jurisdiction to enter.
</p>
<div class="flex gap-3 justify-center">
<button onclick="document.getElementById('agegate').style.display='none'"
class="px-6 py-3 rounded-xl bg-accent text-white font-bold hover:bg-accentDeep transition">
I'm 18 or older
</button>
<a href="https://www.google.com"
class="px-6 py-3 rounded-xl bg-bgElevated border border-border text-gray-300 font-semibold">
Take me out
</a>
</div>
</div>
</div>
<!-- HERO -->
<header class="grad relative pt-20 pb-24 px-6 overflow-hidden">
<div class="max-w-5xl mx-auto">
<div class="flex items-center gap-2 mb-8">
<div class="w-3 h-3 rounded-full bg-accent glow"></div>
<span class="text-sm font-bold tracking-widest text-gray-300 uppercase">goon</span>
</div>
<h1 class="text-5xl md:text-7xl font-extrabold tracking-tight leading-[0.95] mb-6">
Self-hosted<br>
<span class="text-accent">scene catalog</span><br>
for grown-ups.
</h1>
<p class="text-lg md:text-xl text-gray-400 max-w-2xl leading-relaxed mb-10">
Goon indexes scene metadata from TPDB &amp; StashDB, deduplicates across
30+ public tubes, and serves a fast mobile client. Zero ads. Zero tracking.
Download, open, browse — no account, no setup.
</p>
<div class="flex flex-wrap gap-3">
<a href="/goon-v0.1.9.apk"
class="px-6 py-4 rounded-xl bg-accent text-white font-bold hover:bg-accentDeep transition glow">
Download APK
</a>
<a href="#donate"
class="px-6 py-4 rounded-xl bg-transparent border border-border text-gray-400 font-semibold hover:text-accent hover:border-accent transition">
♥ Support project
</a>
</div>
<p class="text-xs text-gray-500 mt-6 font-mono">
Android 7+ · no setup, no login · 18+
</p>
</div>
</header>
<!-- FEATURES -->
<section class="px-6 py-20 bg-bgElevated/30 border-y border-border">
<div class="max-w-5xl mx-auto">
<h2 class="text-3xl font-extrabold mb-2 tracking-tight">What it does</h2>
<p class="text-gray-500 mb-12 max-w-xl">
Goon is not a tube. It does not host, transcode, or proxy content.
It is a metadata aggregator + mobile UI for finding scenes that are
already publicly available.
</p>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card-hover bg-card border border-border rounded-2xl p-6">
<div class="text-accent text-2xl mb-3"></div>
<h3 class="text-lg font-bold mb-2">Multi-source ingest</h3>
<p class="text-sm text-gray-400 leading-relaxed">
TPDB &amp; StashDB metadata + 30+ public tubes. Cross-source
deduplication via perceptual-hash thumbnails + title-Levenshtein matching.
</p>
</div>
<div class="card-hover bg-card border border-border rounded-2xl p-6">
<div class="text-accent text-2xl mb-3"></div>
<h3 class="text-lg font-bold mb-2">Per-performer backfill</h3>
<p class="text-sm text-gray-400 leading-relaxed">
A continuous worker walks performers by staleness and back-fills tube scenes
for the longest-stale first. Completeness over recency.
</p>
</div>
<div class="card-hover bg-card border border-border rounded-2xl p-6">
<div class="text-accent text-2xl mb-3"></div>
<h3 class="text-lg font-bold mb-2">Smart stream resolution</h3>
<p class="text-sm text-gray-400 leading-relaxed">
yt-dlp for mainstream tubes + P.A.C.K.E.R. unpacker for JWPlayer hosters
+ WebView fallback for IP-bound CDNs. Streams direct from source, no transcoding.
</p>
</div>
<div class="card-hover bg-card border border-border rounded-2xl p-6">
<div class="text-accent text-2xl mb-3"></div>
<h3 class="text-lg font-bold mb-2">Mobile-first UI</h3>
<p class="text-sm text-gray-400 leading-relaxed">
React Native + Expo. Scene grid, performer pages, watch history,
favorites, hold-to-preview thumbnails. Built for thumb scrolling.
</p>
</div>
<div class="card-hover bg-card border border-border rounded-2xl p-6">
<div class="text-accent text-2xl mb-3"></div>
<h3 class="text-lg font-bold mb-2">Privacy by default</h3>
<p class="text-sm text-gray-400 leading-relaxed">
App lock (PIN + biometrics), FLAG_SECURE screenshot block, age gate.
No analytics. No telemetry unless YOU configure Sentry with your own DSN.
</p>
</div>
<div class="card-hover bg-card border border-border rounded-2xl p-6">
<div class="text-accent text-2xl mb-3"></div>
<h3 class="text-lg font-bold mb-2">Works out of the box</h3>
<p class="text-sm text-gray-400 leading-relaxed">
Download the APK and it connects automatically — no account, no
config. Power users can point it at their own self-hosted backend.
</p>
</div>
</div>
</div>
</section>
<!-- SCREENSHOTS -->
<section class="px-6 py-20">
<div class="max-w-5xl mx-auto">
<h2 class="text-3xl font-extrabold mb-2 tracking-tight">In the app</h2>
<p class="text-gray-500 mb-12">Screen-shots from a real install — censored where needed.</p>
<!-- Placeholder grid: replace with real screen-shots when ready -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<div class="aspect-[9/19.5] rounded-2xl bg-card border border-border flex items-center justify-center text-gray-600 text-xs font-mono">
Scenes grid
</div>
<div class="aspect-[9/19.5] rounded-2xl bg-card border border-border flex items-center justify-center text-gray-600 text-xs font-mono">
Scene detail
</div>
<div class="aspect-[9/19.5] rounded-2xl bg-card border border-border flex items-center justify-center text-gray-600 text-xs font-mono">
Performer page
</div>
<div class="aspect-[9/19.5] rounded-2xl bg-card border border-border flex items-center justify-center text-gray-600 text-xs font-mono">
Favorites
</div>
</div>
</div>
</section>
<!-- GET STARTED -->
<section class="px-6 py-20 bg-bgElevated/30 border-y border-border">
<div class="max-w-3xl mx-auto">
<h2 class="text-3xl font-extrabold mb-3 tracking-tight">Get started</h2>
<p class="text-gray-500 mb-8">Three steps. No account, no server, no config.</p>
<ol class="space-y-4">
<li class="bg-card border border-border rounded-2xl p-5 flex gap-4">
<span class="text-accent font-extrabold text-xl">1</span>
<span class="text-sm text-gray-300 leading-relaxed">
<a href="/goon-v0.1.9.apk" class="text-accent font-bold hover:underline">Download the APK</a>
and open it. Allow "install from unknown sources" for your browser if Android asks.
</span>
</li>
<li class="bg-card border border-border rounded-2xl p-5 flex gap-4">
<span class="text-accent font-extrabold text-xl">2</span>
<span class="text-sm text-gray-300 leading-relaxed">
Open the app, accept the 18+ gate. It connects automatically — no login.
</span>
</li>
<li class="bg-card border border-border rounded-2xl p-5 flex gap-4">
<span class="text-accent font-extrabold text-xl">3</span>
<span class="text-sm text-gray-300 leading-relaxed">
Browse. That's it.
</span>
</li>
</ol>
</div>
</section>
<!-- DONATE -->
<section id="donate" class="px-6 py-20">
<div class="max-w-3xl mx-auto">
<h2 class="text-3xl font-extrabold mb-3 tracking-tight">Support the project</h2>
<p class="text-gray-400 mb-8 leading-relaxed max-w-xl">
Goon stays free and ad-free because donations cover the VPS, the
TPDB/StashDB tokens, and the time. <strong class="text-gray-200">Crypto
only</strong> &mdash; mainstream processors won't touch adult-adjacent
FOSS projects.
</p>
<div class="grid sm:grid-cols-3 gap-3">
<div class="bg-card border border-border rounded-2xl p-5">
<div class="text-orange-500 text-xs font-mono font-bold mb-1">XMR</div>
<div class="text-sm font-bold mb-3">Monero</div>
<p class="text-xs text-gray-500 leading-relaxed">
Privacy by default. Recommended. Get the address from the app or the project README.
</p>
</div>
<div class="bg-card border border-border rounded-2xl p-5">
<div class="text-yellow-500 text-xs font-mono font-bold mb-1">BTC</div>
<div class="text-sm font-bold mb-3">Bitcoin</div>
<p class="text-xs text-gray-500 leading-relaxed">
Mainstream &amp; easy to send. Use a fresh wallet if you care about on-chain anonymity.
</p>
</div>
<div class="bg-card border border-border rounded-2xl p-5">
<div class="text-blue-400 text-xs font-mono font-bold mb-1">LTC</div>
<div class="text-sm font-bold mb-3">Litecoin</div>
<p class="text-xs text-gray-500 leading-relaxed">
Fast and cheap (~$0.01 fee). Lower adoption than BTC but friendlier for small donations.
</p>
</div>
</div>
<p class="text-xs text-gray-500 mt-6">
Addresses + QR codes are shown in the app under Scenes &raquo; ♥.
</p>
</div>
</section>
<!-- FOOTER -->
<footer class="px-6 py-12 border-t border-border">
<div class="max-w-5xl mx-auto flex flex-col sm:flex-row gap-6 justify-between text-xs text-gray-500">
<div>
<div class="flex items-center gap-2 mb-2">
<div class="w-2 h-2 rounded-full bg-accent"></div>
<span class="font-bold tracking-widest uppercase">goon</span>
</div>
<p>Adult content metadata aggregator. FOSS, ad-free.</p>
<p>MIT license. No warranty. 18+ jurisdictions only.</p>
</div>
<div class="flex flex-col gap-1 text-right">
<a href="/goon-v0.1.9.apk" class="hover:text-accent transition">Download APK</a>
<a href="#donate" class="hover:text-accent transition">Support</a>
</div>
</div>
<p class="max-w-5xl mx-auto mt-6 text-[10px] text-gray-600 leading-relaxed">
Goon does not host, transcode, store, or distribute any media. It scrapes
publicly-available metadata and links out to the source. Users are
responsible for complying with local law.
</p>
</footer>
<script>
// Persist age-gate acknowledgement so it doesn't reappear every visit.
if (localStorage.getItem('agegate_v1') === 'ok') {
document.getElementById('agegate').style.display = 'none';
}
document.querySelector('#agegate button').addEventListener('click', function () {
localStorage.setItem('agegate_v1', 'ok');
});
</script>
</body>
</html>