Compare commits
No commits in common. "cfe72195ed535c21ea4f355c9e13d92590e95f25" and "77679b26bbb611677b4d5b0f8c42defd93faddca" have entirely different histories.
cfe72195ed
...
77679b26bb
21 changed files with 10376 additions and 49 deletions
|
|
@ -1,40 +0,0 @@
|
||||||
"""tag_vertical_counts bez klucza obcego do tags (zakleszczenie o 05:00, Sentry GOON-7N).
|
|
||||||
|
|
||||||
Codziennie o 05:00 rusza naraz refresh_taxonomy_counts (co 3 h) i tag_tail_trim (raz
|
|
||||||
dziennie). Przycinanie kasuje przypisania rule34video, a potem osierocone tagi; DELETE na
|
|
||||||
tags schodzi kaskada do tag_vertical_counts i czeka na refresh, ktory te wiersze wlasnie
|
|
||||||
podmienia. Refresh wstawia liczniki z migawki, w ktorej skasowane przypisania jeszcze sa,
|
|
||||||
a sprawdzenie klucza obcego czeka na blokade tego samego wiersza tags. Od 2026-09-29
|
|
||||||
Postgres co rano zabija tag_tail_trim.
|
|
||||||
|
|
||||||
Tabela to kopia liczona od zera co 3 h, wiec klucz obcy nic nie daje: wiersz po
|
|
||||||
skasowanym tagu znika przy nastepnym odswiezeniu, a /tags i tak zaczyna od tabeli tags.
|
|
||||||
|
|
||||||
Revision ID: 0050_tag_vertical_counts_no_fk
|
|
||||||
Revises: 0049_playback_origin_sort_idx
|
|
||||||
Create Date: 2026-09-30
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
|
|
||||||
revision = "0050_tag_vertical_counts_no_fk"
|
|
||||||
down_revision: str | None = "0049_playback_origin_sort_idx"
|
|
||||||
branch_labels = None
|
|
||||||
depends_on = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
op.execute("SET LOCAL lock_timeout = '5s'")
|
|
||||||
op.execute(
|
|
||||||
"ALTER TABLE tag_vertical_counts DROP CONSTRAINT IF EXISTS fk_tag_vertical_counts_tag_id_tags"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
op.execute("SET LOCAL lock_timeout = '5s'")
|
|
||||||
op.execute("DELETE FROM tag_vertical_counts v WHERE NOT EXISTS (SELECT 1 FROM tags t WHERE t.id = v.tag_id)")
|
|
||||||
op.execute(
|
|
||||||
"ALTER TABLE tag_vertical_counts ADD CONSTRAINT fk_tag_vertical_counts_tag_id_tags "
|
|
||||||
"FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE"
|
|
||||||
)
|
|
||||||
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
Wypelnia `refresh_taxonomy_counts`, czyta /tags?vertical=. Ta sama definicja co dawny
|
Wypelnia `refresh_taxonomy_counts`, czyta /tags?vertical=. Ta sama definicja co dawny
|
||||||
agregat na zywo: sceny z >= 1 zywym zrodlem z originow wertykalu, bez progu dlugosci.
|
agregat na zywo: sceny z >= 1 zywym zrodlem z originow wertykalu, bez progu dlugosci.
|
||||||
Patrz migracja 0047. Bez klucza obcego do tags (0050): z nim kasowanie tagow w
|
Patrz migracja 0047.
|
||||||
tag_tail_trim zakleszczalo sie z odswiezaniem tej tabeli.
|
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy import Integer, String
|
from sqlalchemy import ForeignKey, Integer, String
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
|
@ -20,5 +19,7 @@ class TagVerticalCount(Base):
|
||||||
__tablename__ = "tag_vertical_counts"
|
__tablename__ = "tag_vertical_counts"
|
||||||
|
|
||||||
vertical: Mapped[str] = mapped_column(String(32), primary_key=True)
|
vertical: Mapped[str] = mapped_column(String(32), primary_key=True)
|
||||||
tag_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True)
|
tag_id: Mapped[uuid.UUID] = mapped_column(
|
||||||
|
UUID(as_uuid=True), ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True
|
||||||
|
)
|
||||||
scene_count: Mapped[int] = mapped_column(Integer, nullable=False)
|
scene_count: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
|
|
|
||||||
302
landing/index.html
Normal file
302
landing/index.html
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
<!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 & 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 & 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> — 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 & 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 » ♥.
|
||||||
|
</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>
|
||||||
|
|
@ -122,12 +122,9 @@ export function ClipCreatorScreen() {
|
||||||
}
|
}
|
||||||
renderItem={({ item, index }) => (
|
renderItem={({ item, index }) => (
|
||||||
<Pressable style={styles.tile} onPress={() => setPlaying(index)}>
|
<Pressable style={styles.tile} onPress={() => setPlaying(index)}>
|
||||||
{/* Najpierw thumb_url: RedGifs -mobile.jpg to 480x854 (~37 KB), a plakat
|
{item.clip.poster_url || item.clip.thumb_url ? (
|
||||||
1080x1920 (~120 KB) na kafelek szerokosci 1/3 ekranu; tuzin plakatow
|
|
||||||
ladowal sie tak dlugo, ze ktos zglosil pusta siatke (a2fad93f). */}
|
|
||||||
{item.clip.thumb_url || item.clip.poster_url ? (
|
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: (item.clip.thumb_url || item.clip.poster_url) as string }}
|
source={{ uri: (item.clip.poster_url || item.clip.thumb_url) as string }}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
transition={100}
|
transition={100}
|
||||||
|
|
|
||||||
29
scripts/theporndude_coverage_check.py
Normal file
29
scripts/theporndude_coverage_check.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
"""Coverage check: ile tube'ów z theporndude.com mamy już w bazie."""
|
||||||
|
from app.db import SessionLocal
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with SessionLocal() as s:
|
||||||
|
# Wszystkie distinct origins (canonical + tube: + pornapp:)
|
||||||
|
rows = s.execute(text("""
|
||||||
|
SELECT origin, COUNT(*) AS n,
|
||||||
|
COUNT(*) FILTER (WHERE dead_at IS NULL) AS live,
|
||||||
|
COUNT(*) FILTER (WHERE dead_at IS NOT NULL) AS dead
|
||||||
|
FROM playback_sources
|
||||||
|
GROUP BY origin
|
||||||
|
ORDER BY origin
|
||||||
|
""")).all()
|
||||||
|
print(f"distinct origins: {len(rows)}")
|
||||||
|
by_kind = {}
|
||||||
|
for r in rows:
|
||||||
|
kind = r.origin.split(":")[0] if ":" in r.origin else "other"
|
||||||
|
by_kind.setdefault(kind, []).append((r.origin, r.n, r.live, r.dead))
|
||||||
|
for kind, items in by_kind.items():
|
||||||
|
print(f"\n=== {kind} ({len(items)} origins) ===")
|
||||||
|
for origin, n, live, dead in sorted(items, key=lambda x: -x[2]):
|
||||||
|
print(f" {origin:<35} n={n:>7,} live={live:>7,} dead={dead:>5,}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
118
scripts/theporndude_coverage_match.py
Normal file
118
scripts/theporndude_coverage_match.py
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
"""Cross-check 166 resolved theporndude domains vs nasze 25 tube origins."""
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Origins z DB (live + dead) + extractor REGISTRY w app/extractors/__init__.py
|
||||||
|
OUR_ORIGINS = [
|
||||||
|
# DB live + dead
|
||||||
|
"tube:0dayxxcom", "tube:epornercom", "tube:fpoxxx", "tube:freshpornoorg",
|
||||||
|
"tube:hqpornercom", "tube:latestpornvideocom", "tube:mypornerleakcom",
|
||||||
|
"tube:perverzijacom", "tube:porn00org", "tube:porndishcom", "tube:porndittcom",
|
||||||
|
"tube:pornhatcom", "tube:pornhubcom", "tube:porntrexcom", "tube:pornxpph",
|
||||||
|
"tube:redtubecom", "tube:sxylandcom", "tube:sxyprncom", "tube:xhamstercom",
|
||||||
|
"tube:xnxxcom", "tube:xvideoscom", "tube:youporncom", "tube:latestleaksco",
|
||||||
|
"tube:siskavideo", "tube:hdporn92com",
|
||||||
|
# REGISTRY only (extractor known, brak playback w live DB)
|
||||||
|
"tube:xmoviesforyoucom", "tube:watchporn", "tube:porn4dayspw",
|
||||||
|
"tube:paradisehillcc",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Tylko realne TLD-y. NIE "tube"/"porn"/"xxx" bo to często części nazwy (redtube, pornhub, fpoxxx).
|
||||||
|
_TLD_RE = __import__("re").compile(r"(com|net|org|tv|cc|pw|co|to|ws|me|sx|info|biz)$")
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_tld(s: str) -> str:
|
||||||
|
"""xvideoscom -> xvideos; pornhubcom -> pornhub; hdporn92com -> hdporn92"""
|
||||||
|
return _TLD_RE.sub("", s)
|
||||||
|
|
||||||
|
# Build sitetag → matching variants for fuzzy match
|
||||||
|
def origin_to_sitetag(origin: str) -> str:
|
||||||
|
return origin.replace("tube:", "")
|
||||||
|
|
||||||
|
|
||||||
|
def domain_to_sitetag(domain: str) -> str:
|
||||||
|
"""xvideos.com -> xvideoscom, porntrex.com -> porntrexcom"""
|
||||||
|
return domain.lower().replace(".", "").replace("-", "")
|
||||||
|
|
||||||
|
|
||||||
|
def match(slug: str, domain: str) -> str | None:
|
||||||
|
"""Match po `slug` (z theporndude review URL) lub `real_domain` (z pdude.link).
|
||||||
|
Slug to nazwa tube'a (np. 'xvideos', 'pornhub', 'paradisehill').
|
||||||
|
Origin format: tube:<sitetag>, gdzie sitetag = domain.replace('.', '').
|
||||||
|
Match na "slug pasuje do sitetag bez TLD" daje dobry recall.
|
||||||
|
"""
|
||||||
|
candidates = []
|
||||||
|
if slug:
|
||||||
|
candidates.append(slug.lower().replace("-", ""))
|
||||||
|
if domain:
|
||||||
|
candidates.append(domain_to_sitetag(domain))
|
||||||
|
if not candidates:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for o in OUR_ORIGINS:
|
||||||
|
st = origin_to_sitetag(o)
|
||||||
|
st_no_tld = _strip_tld(st)
|
||||||
|
for c in candidates:
|
||||||
|
c_no_tld = _strip_tld(c)
|
||||||
|
if c_no_tld == st_no_tld and len(c_no_tld) >= 3:
|
||||||
|
return o
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
data = json.loads(Path("theporndude_resolved.json").read_text())
|
||||||
|
have = []
|
||||||
|
new = []
|
||||||
|
error = []
|
||||||
|
for r in data:
|
||||||
|
if "error" in r and not r.get("real_domain"):
|
||||||
|
error.append(r)
|
||||||
|
continue
|
||||||
|
domain = r.get("real_domain", "")
|
||||||
|
our = match(r.get("slug", ""), domain)
|
||||||
|
r["our_origin"] = our
|
||||||
|
if our:
|
||||||
|
have.append(r)
|
||||||
|
else:
|
||||||
|
new.append(r)
|
||||||
|
|
||||||
|
print(f"=== Coverage ===")
|
||||||
|
print(f"Total theporndude top-porn-tubes: {len(data)}")
|
||||||
|
print(f" Already in our DB: {len(have)}")
|
||||||
|
print(f" NEW (potential candidates): {len(new)}")
|
||||||
|
print(f" Errors: {len(error)}")
|
||||||
|
print()
|
||||||
|
print(f"=== Already have (matched) — top 30 by theporndude rank ===")
|
||||||
|
for r in sorted(have, key=lambda x: x["rank"])[:30]:
|
||||||
|
print(
|
||||||
|
f" #{r['rank']:>3} score={r.get('theporndude_score') or '?':>4} "
|
||||||
|
f"{r['real_domain']:<28} -> {r['our_origin']}"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
print(f"=== NEW candidates (not in DB) — top 60 by theporndude rank ===")
|
||||||
|
for r in sorted(new, key=lambda x: x["rank"])[:60]:
|
||||||
|
print(
|
||||||
|
f" #{r['rank']:>3} score={r.get('theporndude_score') or '?':>4} "
|
||||||
|
f"{r.get('real_domain') or '?':<30} ({r['slug']})"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Output detailed
|
||||||
|
summary = {
|
||||||
|
"total": len(data),
|
||||||
|
"already_have": [{"rank": r["rank"], "slug": r["slug"], "domain": r["real_domain"],
|
||||||
|
"score": r.get("theporndude_score"), "our_origin": r["our_origin"]}
|
||||||
|
for r in sorted(have, key=lambda x: x["rank"])],
|
||||||
|
"new_candidates": [{"rank": r["rank"], "slug": r["slug"], "domain": r.get("real_domain"),
|
||||||
|
"score": r.get("theporndude_score"),
|
||||||
|
"final_url": r.get("final_url", "")}
|
||||||
|
for r in sorted(new, key=lambda x: x["rank"])],
|
||||||
|
"errors": [{"rank": r["rank"], "slug": r["slug"], "error": r.get("error")}
|
||||||
|
for r in error],
|
||||||
|
}
|
||||||
|
Path("theporndude_coverage.json").write_text(json.dumps(summary, indent=2))
|
||||||
|
print(f"\n-> theporndude_coverage.json")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
203
scripts/theporndude_curl_triage.py
Normal file
203
scripts/theporndude_curl_triage.py
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
"""Batch curl triage 144 nowych theporndude tubes:
|
||||||
|
- HEAD root domain (200/4xx/5xx/timeout?)
|
||||||
|
- GET / → check landing markers: video listing, sceny, login wall, redirect
|
||||||
|
- GET /latest, /videos, /tube/recent → check które listing path działa
|
||||||
|
- Wynik: per-slug status + landing markers + scene_url_pattern guess
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
COVERAGE_FILE = Path("theporndude_coverage.json")
|
||||||
|
OUT_FILE = Path("theporndude_triage.json")
|
||||||
|
|
||||||
|
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0 Safari/537.36"
|
||||||
|
|
||||||
|
# Common candidate landing paths
|
||||||
|
LANDING_PATHS = ["/", "/latest", "/latest-videos", "/recent", "/new", "/videos", "/category/new", "/top-rated"]
|
||||||
|
|
||||||
|
# Markers w HTML
|
||||||
|
SCENE_LINK_PATTERNS = [
|
||||||
|
r'<a[^>]+href="(/(?:video|videos|watch|v|scene|scenes|stream|movie|movies|view|play|porn|tube)/[^"]+)"',
|
||||||
|
r'<a[^>]+href="((?:https?:)?//[^/"]+/(?:video|videos|watch|v|scene|scenes|stream|movie|movies|view|play|porn|tube)/[^"]+)"',
|
||||||
|
]
|
||||||
|
META_MARKERS = [
|
||||||
|
(r'jsonld|json-ld|"@type"\s*:\s*"VideoObject"', "jsonld_video"),
|
||||||
|
(r'<meta\s+property="og:type"\s+content="video', "og_video"),
|
||||||
|
(r'<meta\s+name="description"\s+content="([^"]+)"', "meta_desc"),
|
||||||
|
(r'class="[^"]*\b(?:video|scene|episode)-?(?:item|card|tile|thumb)\b', "video_card"),
|
||||||
|
(r'class="[^"]*\b(?:performer|actress|model|pornstar)\b', "performer_marker"),
|
||||||
|
(r'class="[^"]*\b(?:studio|production|brand|channel)\b', "studio_marker"),
|
||||||
|
(r'class="[^"]*\b(?:duration|runtime|length)\b|<time\s+datetime=', "duration_marker"),
|
||||||
|
(r'\b(?:HLS|hls|m3u8|application/x-mpegURL)\b', "hls_marker"),
|
||||||
|
(r'(?:hlsmanifest|videoUrl|video_url|stream_url|streamUrl)\s*[:=]\s*["\']', "stream_url_marker"),
|
||||||
|
(r'login\s*required|create\s+account|sign\s+(?:in|up)|members\s+only|join\s+now\s+to\s+watch', "auth_wall"),
|
||||||
|
(r'<title>[^<]*\b(?:404|not\s+found|gone|domain)\b[^<]*</title>', "dead_404"),
|
||||||
|
(r'<meta[^>]+http-equiv="refresh"[^>]+url=', "meta_refresh"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Slug w feedzie to slug RECENZJI ("youperv-website-review"), nie domena. Bez odcięcia
|
||||||
|
# sufiksu fallback budował "youperv-website-review.com" → conn_refused → wpis lądował
|
||||||
|
# jako "dead/skip". Tak wypadło 15 ze 144 kandydatów, w tym youperv i pornbusy, które
|
||||||
|
# potem niezależnie oceniliśmy na 4.5-5/5 i dodaliśmy. Fix 2026-07-27.
|
||||||
|
_REVIEW_SUFFIX_RE = re.compile(r"-(?:website-)?review$", re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
|
def _slug_to_domain(slug: str) -> str:
|
||||||
|
return _REVIEW_SUFFIX_RE.sub("", slug.lower().strip())
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_one(cli: httpx.AsyncClient, url: str) -> tuple[int, str]:
|
||||||
|
"""Fetch z Chrome TLS (curl_cffi), NIE gołym httpx.
|
||||||
|
|
||||||
|
FIX 2026-07-27: pierwotna wersja szła czystym httpx, którego JA3 fingerprint
|
||||||
|
większość tubów za Cloudflare odrzuca. Efekt: 8 timeoutów + 4× 403 w wynikach,
|
||||||
|
czytane potem jako "dead/skip", choć strony działają w przeglądarce. `browser_get`
|
||||||
|
(curl_cffi, chrome120) przechodzi, to ta sama ścieżka, której używają nasze
|
||||||
|
scrapery, więc triage mierzy wreszcie to samo co produkcja.
|
||||||
|
"""
|
||||||
|
import asyncio as _asyncio
|
||||||
|
|
||||||
|
from app.extractors._fetch import browser_get
|
||||||
|
|
||||||
|
def _sync():
|
||||||
|
return browser_get(url, timeout=20.0, headers={"User-Agent": UA})
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = await _asyncio.to_thread(_sync)
|
||||||
|
return r.status_code, (r.text or "")[:200_000]
|
||||||
|
except Exception as e:
|
||||||
|
name = type(e).__name__
|
||||||
|
if "Timeout" in name:
|
||||||
|
return -2, "timeout"
|
||||||
|
if "Connect" in name or "Resolve" in name or "DNS" in name:
|
||||||
|
return -1, "conn_refused"
|
||||||
|
return -9, f"{name}: {e}"[:120]
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_html(html: str) -> dict:
|
||||||
|
found = {}
|
||||||
|
for pattern, name in META_MARKERS:
|
||||||
|
if re.search(pattern, html, re.IGNORECASE):
|
||||||
|
found[name] = True
|
||||||
|
# Scene link patterns
|
||||||
|
scene_links = []
|
||||||
|
for p in SCENE_LINK_PATTERNS:
|
||||||
|
for m in re.finditer(p, html, re.IGNORECASE):
|
||||||
|
scene_links.append(m.group(1)[:120])
|
||||||
|
if len(scene_links) >= 5:
|
||||||
|
break
|
||||||
|
if len(scene_links) >= 5:
|
||||||
|
break
|
||||||
|
if scene_links:
|
||||||
|
found["scene_link_samples"] = scene_links[:3]
|
||||||
|
# Unique pattern (path prefix po slash)
|
||||||
|
prefixes = set()
|
||||||
|
for link in scene_links:
|
||||||
|
parts = link.lstrip("/").split("/", 2)
|
||||||
|
if parts:
|
||||||
|
prefixes.add("/" + parts[0])
|
||||||
|
found["scene_path_prefixes"] = sorted(prefixes)
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
async def audit_one(cli: httpx.AsyncClient, slug: str, domain: str) -> dict:
|
||||||
|
"""Audit pojedynczego tube'a."""
|
||||||
|
out = {"slug": slug, "domain": domain}
|
||||||
|
|
||||||
|
# Próbuj https://<domain>/ root
|
||||||
|
if not domain or not re.match(r"^[\w\.-]+\.\w+$", domain):
|
||||||
|
out["error"] = "no_valid_domain"
|
||||||
|
return out
|
||||||
|
|
||||||
|
root_url = f"https://{domain}/"
|
||||||
|
status, html = await fetch_one(cli, root_url)
|
||||||
|
out["root_status"] = status
|
||||||
|
if status not in (200, 301, 302):
|
||||||
|
out["root_error"] = html[:80] if isinstance(html, str) else None
|
||||||
|
return out
|
||||||
|
|
||||||
|
out["root_findings"] = analyze_html(html)
|
||||||
|
# Heurystyka score 0-3
|
||||||
|
f = out["root_findings"]
|
||||||
|
score = 0
|
||||||
|
reasons = []
|
||||||
|
if f.get("jsonld_video"):
|
||||||
|
score += 1
|
||||||
|
reasons.append("jsonld_video")
|
||||||
|
if f.get("og_video"):
|
||||||
|
score += 1
|
||||||
|
reasons.append("og_video")
|
||||||
|
if f.get("video_card"):
|
||||||
|
score += 1
|
||||||
|
reasons.append("video_card")
|
||||||
|
if f.get("performer_marker"):
|
||||||
|
score += 1
|
||||||
|
reasons.append("performer_marker")
|
||||||
|
if f.get("studio_marker"):
|
||||||
|
score += 1
|
||||||
|
reasons.append("studio_marker")
|
||||||
|
if f.get("duration_marker"):
|
||||||
|
score += 0.5
|
||||||
|
if f.get("hls_marker") or f.get("stream_url_marker"):
|
||||||
|
score += 0.5
|
||||||
|
if f.get("scene_path_prefixes"):
|
||||||
|
score += 1
|
||||||
|
reasons.append(f"scene_paths={f['scene_path_prefixes']}")
|
||||||
|
if f.get("auth_wall"):
|
||||||
|
score -= 2
|
||||||
|
reasons.append("auth_wall")
|
||||||
|
if f.get("dead_404"):
|
||||||
|
score -= 5
|
||||||
|
reasons.append("dead_404")
|
||||||
|
if f.get("meta_refresh"):
|
||||||
|
score -= 1
|
||||||
|
reasons.append("meta_refresh")
|
||||||
|
out["heuristic_score"] = round(score, 1)
|
||||||
|
out["reasons"] = reasons
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
cov = json.loads(COVERAGE_FILE.read_text())
|
||||||
|
new_candidates = cov["new_candidates"]
|
||||||
|
print(f"audytuję {len(new_candidates)} nowych kandydatów…")
|
||||||
|
|
||||||
|
timeout = httpx.Timeout(15.0, connect=8.0)
|
||||||
|
limits = httpx.Limits(max_keepalive_connections=20, max_connections=50)
|
||||||
|
async with httpx.AsyncClient(timeout=timeout, limits=limits, http2=False) as cli:
|
||||||
|
sem = asyncio.Semaphore(12)
|
||||||
|
|
||||||
|
async def worker(r):
|
||||||
|
async with sem:
|
||||||
|
# Use slug or guess domain (most slug.com)
|
||||||
|
domain = r.get("domain") or ""
|
||||||
|
# Jeśli pdude.link daje porndudecams.com (interstitial), użyj <slug>.com
|
||||||
|
if not domain or "porndudecams" in domain:
|
||||||
|
domain = f"{_slug_to_domain(r['slug'])}.com"
|
||||||
|
return {**r, **(await audit_one(cli, r["slug"], domain))}
|
||||||
|
|
||||||
|
results = await asyncio.gather(*[worker(r) for r in new_candidates])
|
||||||
|
|
||||||
|
OUT_FILE.write_text(json.dumps(results, indent=2))
|
||||||
|
|
||||||
|
# Stats
|
||||||
|
by_score = {}
|
||||||
|
for r in results:
|
||||||
|
s = r.get("heuristic_score", 0)
|
||||||
|
bucket = "5+" if s >= 5 else "3-5" if s >= 3 else "1-3" if s >= 1 else "<1"
|
||||||
|
by_score.setdefault(bucket, []).append(r)
|
||||||
|
print("\n=== Heurystyczny rozkład (canonical-fit) ===")
|
||||||
|
for b in ["5+", "3-5", "1-3", "<1"]:
|
||||||
|
if b in by_score:
|
||||||
|
print(f" {b:<5} {len(by_score[b])} tubów")
|
||||||
|
print(f"\n-> {OUT_FILE}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
234
scripts/theporndude_movies_pipeline.py
Normal file
234
scripts/theporndude_movies_pipeline.py
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
"""Pełny pipeline dla theporndude /full-porn-movies-sites (94 tubes):
|
||||||
|
1. Resolve real domains (pdude.link follow, ale follow only 1 hop)
|
||||||
|
2. Coverage match vs nasze 25+ origins
|
||||||
|
3. Curl triage HTML markers
|
||||||
|
4. Per-tube scorecard
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0 Safari/537.36"
|
||||||
|
|
||||||
|
OUR_ORIGINS = [
|
||||||
|
"tube:0dayxxcom", "tube:epornercom", "tube:fpoxxx", "tube:freshpornoorg",
|
||||||
|
"tube:hqpornercom", "tube:latestpornvideocom", "tube:mypornerleakcom",
|
||||||
|
"tube:perverzijacom", "tube:porn00org", "tube:porndishcom", "tube:porndittcom",
|
||||||
|
"tube:pornhatcom", "tube:pornhubcom", "tube:porntrexcom", "tube:pornxpph",
|
||||||
|
"tube:redtubecom", "tube:sxylandcom", "tube:sxyprncom", "tube:xhamstercom",
|
||||||
|
"tube:xnxxcom", "tube:xvideoscom", "tube:youporncom", "tube:latestleaksco",
|
||||||
|
"tube:siskavideo", "tube:hdporn92com",
|
||||||
|
"tube:xmoviesforyoucom", "tube:watchporn", "tube:porn4dayspw",
|
||||||
|
"tube:paradisehillcc",
|
||||||
|
]
|
||||||
|
|
||||||
|
_TLD_RE = re.compile(r"(com|net|org|tv|cc|pw|co|to|ws|me|sx|info|biz)$")
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_tld(s: str) -> str:
|
||||||
|
return _TLD_RE.sub("", s)
|
||||||
|
|
||||||
|
|
||||||
|
def match(slug: str, domain: str) -> str | None:
|
||||||
|
candidates = []
|
||||||
|
if slug:
|
||||||
|
candidates.append(slug.lower().replace("-", ""))
|
||||||
|
if domain:
|
||||||
|
candidates.append(domain.lower().replace(".", "").replace("-", ""))
|
||||||
|
for o in OUR_ORIGINS:
|
||||||
|
st = o.replace("tube:", "")
|
||||||
|
st_no_tld = _strip_tld(st)
|
||||||
|
for c in candidates:
|
||||||
|
c_no_tld = _strip_tld(c)
|
||||||
|
if c_no_tld == st_no_tld and len(c_no_tld) >= 3:
|
||||||
|
return o
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
SCENE_PATH_RE = re.compile(
|
||||||
|
r'<a[^>]+href="((?:https?:)?//?[^"]*?/(?:video|videos|watch|v|scene|movie|movies|play|view|stream)/[^"]+)"',
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
META_MARKERS = [
|
||||||
|
(r'"@type"\s*:\s*"VideoObject"', "jsonld_video"),
|
||||||
|
(r'<meta\s+property="og:type"\s+content="video', "og_video"),
|
||||||
|
(r'class="[^"]*\b(?:video|scene|movie|episode)-?(?:item|card|tile|thumb|block)\b', "video_card"),
|
||||||
|
(r'class="[^"]*\b(?:performer|actress|model|pornstar|cast)\b|href="[^"]*/pornstar', "performer_marker"),
|
||||||
|
(r'class="[^"]*\b(?:studio|production|brand|channel|network)\b|href="[^"]*/studio', "studio_marker"),
|
||||||
|
(r'class="[^"]*\b(?:duration|runtime|length)\b|itemprop="duration"', "duration_marker"),
|
||||||
|
(r'\b(?:HLS|m3u8|application/x-mpegURL)\b', "hls_marker"),
|
||||||
|
(r'(?:videoUrl|video_url|stream_url|streamUrl)\s*[:=]\s*["\']', "stream_url_marker"),
|
||||||
|
(r'(?:login\s+required|create\s+account|members\s+only|join\s+now)', "auth_wall"),
|
||||||
|
(r'<title>[^<]*\b(?:404|not\s+found|domain\s+for\s+sale|gone)\b', "dead_404"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_one(cli: httpx.AsyncClient, url: str, *, max_redirects: int = 5) -> tuple[int, str, str]:
|
||||||
|
try:
|
||||||
|
r = await cli.get(url, headers={"User-Agent": UA}, follow_redirects=False)
|
||||||
|
# Follow up to max_redirects but stop on cross-domain redirect-out (to detect pdude.link → ad)
|
||||||
|
hops = 0
|
||||||
|
first_external_domain = None
|
||||||
|
cur = r
|
||||||
|
cur_url = url
|
||||||
|
while cur.status_code in (301, 302, 303, 307, 308) and hops < max_redirects:
|
||||||
|
loc = cur.headers.get("location")
|
||||||
|
if not loc:
|
||||||
|
break
|
||||||
|
if loc.startswith("/"):
|
||||||
|
p = urlparse(cur_url)
|
||||||
|
loc = f"{p.scheme}://{p.netloc}{loc}"
|
||||||
|
cur_url = loc
|
||||||
|
hops += 1
|
||||||
|
# Track first external (non-pdude, non-theporndude)
|
||||||
|
host = urlparse(loc).hostname or ""
|
||||||
|
if first_external_domain is None and not host.endswith("pdude.link") and not host.endswith("theporndude.com"):
|
||||||
|
first_external_domain = host.replace("www.", "")
|
||||||
|
cur = await cli.get(loc, headers={"User-Agent": UA}, follow_redirects=False)
|
||||||
|
return cur.status_code, cur.text[:200_000] if hasattr(cur, "text") else "", first_external_domain or (urlparse(cur_url).hostname or "").replace("www.", "")
|
||||||
|
except httpx.ConnectError:
|
||||||
|
return -1, "conn_refused", ""
|
||||||
|
except httpx.TimeoutException:
|
||||||
|
return -2, "timeout", ""
|
||||||
|
except Exception as e:
|
||||||
|
return -9, str(e)[:120], ""
|
||||||
|
|
||||||
|
|
||||||
|
async def resolve_domain(cli: httpx.AsyncClient, slug: str) -> str:
|
||||||
|
"""Pdude.link follow z early-exit dla first external."""
|
||||||
|
try:
|
||||||
|
r = await cli.get(f"https://pdude.link/{slug}", headers={"User-Agent": UA}, follow_redirects=False)
|
||||||
|
loc = r.headers.get("location", "")
|
||||||
|
if loc:
|
||||||
|
host = urlparse(loc).hostname or ""
|
||||||
|
host = host.replace("www.", "")
|
||||||
|
# Jeśli pdude.link redirectuje na affiliate (anexo.link/awejmp.com/etc), wyciągnij subAffId
|
||||||
|
if "anexo.link" in host or "awejmp.com" in host or "porndudecams" in host:
|
||||||
|
# Try slug.com fallback
|
||||||
|
return ""
|
||||||
|
return host
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_html(html: str) -> dict:
|
||||||
|
found = {}
|
||||||
|
for pattern, name in META_MARKERS:
|
||||||
|
if re.search(pattern, html, re.IGNORECASE):
|
||||||
|
found[name] = True
|
||||||
|
prefixes = set()
|
||||||
|
sample = []
|
||||||
|
for m in SCENE_PATH_RE.finditer(html):
|
||||||
|
link = m.group(1)
|
||||||
|
sample.append(link[:100])
|
||||||
|
# Wyciągnij prefix
|
||||||
|
# Normalize: //host/path → /path; otherwise full match
|
||||||
|
if link.startswith("//"):
|
||||||
|
link = "/" + link.split("/", 3)[3] if "/" in link[2:] else "/"
|
||||||
|
if link.startswith("/"):
|
||||||
|
parts = link.lstrip("/").split("/", 2)
|
||||||
|
if parts:
|
||||||
|
prefixes.add("/" + parts[0])
|
||||||
|
if len(sample) >= 5:
|
||||||
|
break
|
||||||
|
if prefixes:
|
||||||
|
found["scene_path_prefixes"] = sorted(prefixes)
|
||||||
|
if sample:
|
||||||
|
found["scene_link_samples"] = sample[:3]
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
def score_findings(f: dict) -> tuple[float, list]:
|
||||||
|
score, reasons = 0.0, []
|
||||||
|
if f.get("jsonld_video"):
|
||||||
|
score += 1.5; reasons.append("jsonld_video")
|
||||||
|
if f.get("og_video"):
|
||||||
|
score += 0.5; reasons.append("og_video")
|
||||||
|
if f.get("video_card"):
|
||||||
|
score += 1; reasons.append("video_card")
|
||||||
|
if f.get("performer_marker"):
|
||||||
|
score += 1; reasons.append("performer_marker")
|
||||||
|
if f.get("studio_marker"):
|
||||||
|
score += 1; reasons.append("studio_marker")
|
||||||
|
if f.get("duration_marker"):
|
||||||
|
score += 0.5; reasons.append("duration_marker")
|
||||||
|
if f.get("hls_marker") or f.get("stream_url_marker"):
|
||||||
|
score += 0.5
|
||||||
|
if f.get("scene_path_prefixes"):
|
||||||
|
score += 1; reasons.append(f"paths={f['scene_path_prefixes']}")
|
||||||
|
if f.get("auth_wall"):
|
||||||
|
score -= 2; reasons.append("auth_wall")
|
||||||
|
if f.get("dead_404"):
|
||||||
|
score -= 5; reasons.append("dead_404")
|
||||||
|
return round(score, 1), reasons
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
movies = json.loads(Path("theporndude_movies.json").read_text())["all"]
|
||||||
|
print(f"audyt {len(movies)} tubów z full-porn-movies-sites…")
|
||||||
|
|
||||||
|
timeout = httpx.Timeout(15.0, connect=8.0)
|
||||||
|
async with httpx.AsyncClient(timeout=timeout, http2=False) as cli:
|
||||||
|
sem = asyncio.Semaphore(12)
|
||||||
|
|
||||||
|
async def worker(r):
|
||||||
|
async with sem:
|
||||||
|
slug = r["slug"]
|
||||||
|
# Resolve real domain z pdude.link first hop
|
||||||
|
domain = await resolve_domain(cli, slug)
|
||||||
|
if not domain or any(x in domain for x in ["anexo.link", "awejmp.com", "porndudecams"]):
|
||||||
|
domain = f"{slug.lower()}.com"
|
||||||
|
# Curl root + scene path heurystyka
|
||||||
|
status, html, _ = await fetch_one(cli, f"https://{domain}/")
|
||||||
|
findings = analyze_html(html) if status == 200 else {}
|
||||||
|
score, reasons = score_findings(findings)
|
||||||
|
our = match(slug, domain)
|
||||||
|
return {
|
||||||
|
**r,
|
||||||
|
"domain": domain,
|
||||||
|
"root_status": status,
|
||||||
|
"findings": findings,
|
||||||
|
"score": score,
|
||||||
|
"reasons": reasons,
|
||||||
|
"our_origin": our,
|
||||||
|
}
|
||||||
|
|
||||||
|
results = await asyncio.gather(*[worker(r) for r in movies])
|
||||||
|
|
||||||
|
# Aggregate
|
||||||
|
have = [r for r in results if r["our_origin"]]
|
||||||
|
new_promising = [r for r in results if not r["our_origin"] and r["score"] >= 2.5]
|
||||||
|
new_low = [r for r in results if not r["our_origin"] and 1 <= r["score"] < 2.5]
|
||||||
|
new_zero = [r for r in results if not r["our_origin"] and 0 < r["score"] < 1]
|
||||||
|
new_dead = [r for r in results if not r["our_origin"] and (r["root_status"] <= 0 or r["score"] < 0)]
|
||||||
|
new_no_signal = [r for r in results if not r["our_origin"] and r["score"] == 0 and r["root_status"] == 200]
|
||||||
|
|
||||||
|
print(f"\n=== Coverage /full-porn-movies-sites ({len(results)} tubes) ===")
|
||||||
|
print(f" already have: {len(have):>3}")
|
||||||
|
print(f" promising: {len(new_promising):>3}")
|
||||||
|
print(f" low value: {len(new_low):>3}")
|
||||||
|
print(f" no signal: {len(new_no_signal):>3}")
|
||||||
|
print(f" dead: {len(new_dead):>3}")
|
||||||
|
print()
|
||||||
|
print("ALREADY HAVE:")
|
||||||
|
for r in have:
|
||||||
|
print(f" {r['slug']:<20} -> {r['our_origin']}")
|
||||||
|
print()
|
||||||
|
print("PROMISING (score >= 2.5):")
|
||||||
|
for r in sorted(new_promising, key=lambda x: -x["score"]):
|
||||||
|
print(f" score={r['score']:>4} {r['domain']:<25} ({r['slug']:<20}) reasons={','.join(r['reasons'])[:60]}")
|
||||||
|
print()
|
||||||
|
print("LOW VALUE (1-2.5):")
|
||||||
|
for r in sorted(new_low, key=lambda x: -x["score"]):
|
||||||
|
print(f" score={r['score']:>4} {r['domain']:<25} ({r['slug']:<20}) reasons={','.join(r['reasons'])[:60]}")
|
||||||
|
|
||||||
|
Path("theporndude_movies_scorecard.json").write_text(json.dumps(results, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
87
scripts/theporndude_resolve_domains.py
Normal file
87
scripts/theporndude_resolve_domains.py
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
"""Per 166 review slugs z top-porn-tube-sites:
|
||||||
|
1. Fetch review page → extract pdude.link Visit URL + rating + score badges
|
||||||
|
2. Follow pdude.link → real tube domain
|
||||||
|
3. Cross-check vs nasze 25 tube origins
|
||||||
|
4. Output JSON: { slug, name, theporndude_rank, theporndude_score, real_domain, in_our_db, our_origin }
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
REVIEWS_FILE = Path("theporndude_free_tubes.json")
|
||||||
|
OUT_FILE = Path("theporndude_resolved.json")
|
||||||
|
|
||||||
|
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0 Safari/537.36"
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_review(cli: httpx.AsyncClient, review: dict, rank: int) -> dict:
|
||||||
|
url = f"https://theporndude.com/{review['id']}/{review['slug']}"
|
||||||
|
try:
|
||||||
|
r = await cli.get(url, headers={"User-Agent": UA})
|
||||||
|
html = r.text
|
||||||
|
except Exception as e:
|
||||||
|
return {**review, "rank": rank, "error": f"fetch_review: {e}"}
|
||||||
|
|
||||||
|
# Wyciągnij score
|
||||||
|
score_m = re.search(r'class="rate__num">\s*(\d+(?:\.\d+)?)\s*<', html)
|
||||||
|
# Wyciągnij pdude.link visit URL
|
||||||
|
pdude_m = re.search(r'href="(https://pdude\.link/[\w\-\.]+)"', html)
|
||||||
|
# Wyciągnij <title> + meta description
|
||||||
|
title_m = re.search(r"<title>([^<]+)</title>", html)
|
||||||
|
desc_m = re.search(r'<meta\s+name="description"\s+content="([^"]+)"', html)
|
||||||
|
|
||||||
|
out = {
|
||||||
|
**review,
|
||||||
|
"rank": rank,
|
||||||
|
"theporndude_score": float(score_m.group(1)) if score_m else None,
|
||||||
|
"page_title": (title_m.group(1) if title_m else "")[:120],
|
||||||
|
"page_desc": (desc_m.group(1) if desc_m else "")[:200],
|
||||||
|
}
|
||||||
|
if not pdude_m:
|
||||||
|
out["error"] = "no_pdude_link"
|
||||||
|
return out
|
||||||
|
pdude_url = pdude_m.group(1)
|
||||||
|
|
||||||
|
# Follow pdude.link
|
||||||
|
try:
|
||||||
|
r2 = await cli.get(pdude_url, headers={"User-Agent": UA})
|
||||||
|
# Final URL po wszystkich redirectach
|
||||||
|
final_url = str(r2.url)
|
||||||
|
host = urlparse(final_url).hostname or ""
|
||||||
|
host = host.replace("www.", "")
|
||||||
|
out["real_domain"] = host
|
||||||
|
out["final_url"] = final_url[:200]
|
||||||
|
except Exception as e:
|
||||||
|
out["error"] = f"pdude_follow: {e}"
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
reviews = json.loads(REVIEWS_FILE.read_text())["reviews"]
|
||||||
|
|
||||||
|
timeout = httpx.Timeout(20.0, connect=10.0)
|
||||||
|
limits = httpx.Limits(max_keepalive_connections=10, max_connections=20)
|
||||||
|
async with httpx.AsyncClient(
|
||||||
|
timeout=timeout, limits=limits, follow_redirects=True, http2=False
|
||||||
|
) as cli:
|
||||||
|
sem = asyncio.Semaphore(8)
|
||||||
|
|
||||||
|
async def worker(rev, rank):
|
||||||
|
async with sem:
|
||||||
|
return await fetch_review(cli, rev, rank)
|
||||||
|
|
||||||
|
tasks = [worker(r, i + 1) for i, r in enumerate(reviews)]
|
||||||
|
results = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
OUT_FILE.write_text(json.dumps(results, indent=2))
|
||||||
|
ok = sum(1 for r in results if r.get("real_domain"))
|
||||||
|
print(f"resolved {ok}/{len(results)} ({ok*100/len(results):.0f}%)")
|
||||||
|
print(f"out -> {OUT_FILE}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
104
scripts/theporndude_scorecard.py
Normal file
104
scripts/theporndude_scorecard.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
"""Generuje końcowy scorecard JSON dla wszystkich 166 theporndude top-porn-tube-sites:
|
||||||
|
- coverage status (already_have/new/dead/low_value)
|
||||||
|
- canonical_value_score 0-5 (heurystyka + nasz ranking)
|
||||||
|
- recommendation: skip / consider / pilot / integrate
|
||||||
|
|
||||||
|
Plus markdown summary dla człowieka.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
COVERAGE = json.loads(Path("theporndude_coverage.json").read_text())
|
||||||
|
TRIAGE = json.loads(Path("theporndude_triage.json").read_text())
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
triage_by_slug = {r["slug"]: r for r in TRIAGE}
|
||||||
|
|
||||||
|
scorecards = []
|
||||||
|
for r in COVERAGE["already_have"]:
|
||||||
|
scorecards.append({
|
||||||
|
"rank": r["rank"],
|
||||||
|
"slug": r["slug"],
|
||||||
|
"domain": r["domain"],
|
||||||
|
"status": "already_have",
|
||||||
|
"our_origin": r["our_origin"],
|
||||||
|
"canonical_value_score": None,
|
||||||
|
"recommendation": "skip — already integrated",
|
||||||
|
})
|
||||||
|
for r in COVERAGE["new_candidates"]:
|
||||||
|
t = triage_by_slug.get(r["slug"], {})
|
||||||
|
score = t.get("heuristic_score", 0)
|
||||||
|
findings = t.get("root_findings", {})
|
||||||
|
reasons = t.get("reasons", [])
|
||||||
|
root_status = t.get("root_status", 0)
|
||||||
|
domain = t.get("domain") or r.get("domain") or f"{r['slug']}.com"
|
||||||
|
|
||||||
|
if root_status <= 0 or findings.get("dead_404"):
|
||||||
|
status = "dead"
|
||||||
|
rec = "skip — dead/unreachable"
|
||||||
|
elif findings.get("auth_wall") and score < 2:
|
||||||
|
status = "auth_wall"
|
||||||
|
rec = "skip — login required, no public scenes"
|
||||||
|
elif score >= 2.5:
|
||||||
|
status = "promising"
|
||||||
|
rec = "pilot — deep audit + write extractor"
|
||||||
|
elif score >= 1:
|
||||||
|
status = "low_value"
|
||||||
|
rec = "consider — basic metadata only, low priority"
|
||||||
|
else:
|
||||||
|
status = "no_value"
|
||||||
|
rec = "skip — no canonical-fit signal in HTML"
|
||||||
|
|
||||||
|
scorecards.append({
|
||||||
|
"rank": r["rank"],
|
||||||
|
"slug": r["slug"],
|
||||||
|
"domain": domain,
|
||||||
|
"status": status,
|
||||||
|
"our_origin": None,
|
||||||
|
"canonical_value_score": score,
|
||||||
|
"heuristic_reasons": reasons,
|
||||||
|
"findings": findings,
|
||||||
|
"recommendation": rec,
|
||||||
|
})
|
||||||
|
|
||||||
|
scorecards.sort(key=lambda x: x["rank"])
|
||||||
|
|
||||||
|
out = {
|
||||||
|
"source": "theporndude.com/top-porn-tube-sites",
|
||||||
|
"fetched_at": "2026-05-20",
|
||||||
|
"total": len(scorecards),
|
||||||
|
"summary": {
|
||||||
|
"already_have": sum(1 for s in scorecards if s["status"] == "already_have"),
|
||||||
|
"promising": sum(1 for s in scorecards if s["status"] == "promising"),
|
||||||
|
"low_value": sum(1 for s in scorecards if s["status"] == "low_value"),
|
||||||
|
"no_value": sum(1 for s in scorecards if s["status"] == "no_value"),
|
||||||
|
"auth_wall": sum(1 for s in scorecards if s["status"] == "auth_wall"),
|
||||||
|
"dead": sum(1 for s in scorecards if s["status"] == "dead"),
|
||||||
|
},
|
||||||
|
"scorecards": scorecards,
|
||||||
|
}
|
||||||
|
Path("theporndude_scorecard.json").write_text(json.dumps(out, indent=2))
|
||||||
|
|
||||||
|
# Pretty print summary
|
||||||
|
print("=" * 70)
|
||||||
|
print(f"THEPORNDUDE.COM CANONICAL-FIT SCORECARD ({out['total']} tubes)")
|
||||||
|
print("=" * 70)
|
||||||
|
for k, v in out["summary"].items():
|
||||||
|
print(f" {k:<15} {v:>4} ({100*v/out['total']:.0f}%)")
|
||||||
|
print()
|
||||||
|
print("PROMISING (score >= 2.5) — pilot candidates:")
|
||||||
|
for s in scorecards:
|
||||||
|
if s["status"] == "promising":
|
||||||
|
r = ",".join(s.get("heuristic_reasons", []))[:60]
|
||||||
|
print(f" #{s['rank']:>3} score={s['canonical_value_score']:>4} {s['domain']:<25} ({s['slug']}) {r}")
|
||||||
|
print()
|
||||||
|
print("LOW_VALUE (1-2.5) — defer:")
|
||||||
|
for s in scorecards:
|
||||||
|
if s["status"] == "low_value":
|
||||||
|
r = ",".join(s.get("heuristic_reasons", []))[:50]
|
||||||
|
print(f" #{s['rank']:>3} score={s['canonical_value_score']:>4} {s['domain']:<25} ({s['slug']}) {r}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
54
streamtape_sample.md
Normal file
54
streamtape_sample.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Streamtape sample do testu (50 URL, najświeższy last_seen_at)
|
||||||
|
|
||||||
|
Otwórz w przeglądarce, zaznacz w komentarzu które działają. Wszystkie zostały sprobed dziś z VPS Hetzner + lokalnego Win residential — `Video not found`.
|
||||||
|
|
||||||
|
1. Naughty Weddings 7 — https://streamtape.com/e/OA43pkp3Q3IZbJZ
|
||||||
|
2. Creampie Lover — https://streamtape.com/e/PLXrxXQ6wgIbwj
|
||||||
|
3. La Sextape — https://streamtape.com/e/8WRDwZDR81Iooqg
|
||||||
|
4. Lost And Found — https://streamtape.com/e/lr9YKv62MeI7QXk
|
||||||
|
5. Let's Post It 7 — https://streamtape.com/e/xl2xJ8QRejCkO9y
|
||||||
|
6. Let's Post It 7 (alt) — https://streamtape.com/e/3oVm799lxDIdQoZ
|
||||||
|
7. Sirens 5 — https://streamtape.com/e/dZljQpaWvoFkX7D
|
||||||
|
8. He's In Charge 5 — https://streamtape.com/e/8w4drQAGPZTlDP
|
||||||
|
9. Deep Into Blu — https://streamtape.com/e/ykb6r92gv1C1lKW
|
||||||
|
10. Anal Savages 10 — https://streamtape.com/e/BBbJXMX08bU2mA
|
||||||
|
11. Home Sweet Home — https://streamtape.com/e/xmaLM93WaJcogA
|
||||||
|
12. All Or Nothing: A Hailey Rose Showcase — https://streamtape.com/e/vPVmAK7rYMCJ7D
|
||||||
|
13. The Horny Babysitter — https://streamtape.com/e/Kzpqv0eLg1TJAV
|
||||||
|
14. Temple Of Flesh 5 — https://streamtape.com/e/6bO0P2wKAmU9Xlw
|
||||||
|
15. Come Home — https://streamtape.com/e/Bekv3DrORGSyL2O
|
||||||
|
16. My Husband Likes To Watch — https://streamtape.com/e/vQrJ0GRjyzubmv
|
||||||
|
17. Second Chances — https://streamtape.com/e/8dpQy06AG3I23Q
|
||||||
|
18. Brazzers 20th Anniversary — https://streamtape.com/e/JWbj4GqqRVCe7V
|
||||||
|
19. My Friend's Hot Mom 110 — https://streamtape.com/e/DoDbYAYaDgiLgW
|
||||||
|
20. The Best of Forbidden Scenes 10 — https://streamtape.com/e/QXD7yPGo4qI0mJk
|
||||||
|
21. Blacks On Cougars 19 — https://streamtape.com/e/aRWmG7BajqFxx97
|
||||||
|
22. Horny Hotwife 5 — https://streamtape.com/e/0Z64qYQ9oWTbPDL
|
||||||
|
23. Stepfamilies Exposed — https://streamtape.com/e/M9zqorkA3LIm7DK
|
||||||
|
24. I Love My Hotwife's Big Ass — https://streamtape.com/e/oZ9ygBBVRLSJa7q
|
||||||
|
25. Money Talks 6 — https://streamtape.com/e/QWkgrZA1mOt0040
|
||||||
|
26. Tight European Beauties 4 — https://streamtape.com/e/mPaD1Z6mkOfXg8
|
||||||
|
27. The Hunger — https://streamtape.com/e/D4jZYAYedLTk7Ow
|
||||||
|
28. The Swing Life — https://streamtape.com/e/kP9JrooyYXfr29
|
||||||
|
29. My Younger Lover 2 — https://streamtape.com/e/wGVyPGjB7lfJka2
|
||||||
|
30. Rest In Pieces — https://streamtape.com/e/KgAWJD3BlWtDra
|
||||||
|
31. The Lady In Charge Likes Girls — https://streamtape.com/e/G37Mj3kkXYHZAJ
|
||||||
|
32. Internal Love 4 — https://streamtape.com/e/JoBGBwDxB6HjDWX
|
||||||
|
33. Nuru Fixes Everything 2 — https://streamtape.com/e/2o8Pv3okdKTAAM
|
||||||
|
34. Let's Post It 7 (alt2) — https://streamtape.com/e/Mk9verWlGoU0MB
|
||||||
|
35. DP Granny! — https://streamtape.com/e/K0gy9kdk8Gi08Qj
|
||||||
|
36. Love Stories 3 — https://streamtape.com/e/oZBBQyWRlatJ6dm
|
||||||
|
37. Anne A Taboo Parody — https://streamtape.com/e/wVbPWMgdxxhJ1QZ
|
||||||
|
38. Pornogeist — https://streamtape.com/e/JY8JyqW2Opcj0vz
|
||||||
|
39. All Stacked All Natural X-Cut 20 — https://streamtape.com/e/dOqK9x0vJzhkmZY
|
||||||
|
40. The Wedding — https://streamtape.com/e/94meOvYPQkSDy2
|
||||||
|
41. Swingers Getaway 4 — https://streamtape.com/e/8DR666jbdXcGMK
|
||||||
|
42. Not So Innocent 3 — https://streamtape.com/e/9lKYWv84glhyoX
|
||||||
|
43. That Time I Got My Stepmom Pregnant 2 — https://streamtape.com/e/7X4OByaDoGTRxD
|
||||||
|
44. Il Gladiatore XXX Parody — https://streamtape.com/e/7ovJBY9p4ZFBa3
|
||||||
|
45. Mission XXX Impossible — https://streamtape.com/e/gRQQ7DyW4wuqW4X
|
||||||
|
46. Squirt Game Part: 2 — https://streamtape.com/e/PZqBZp4OomF0Q61
|
||||||
|
47. Father Figure 4 — https://streamtape.com/e/PvZL0jzVK6I0pLG
|
||||||
|
48. Homeward Bound — https://streamtape.com/e/pagAXXDlP0Sr39B
|
||||||
|
49. Gold Diggers — https://streamtape.com/e/GMbxK2v6kXH1wJR
|
||||||
|
50. Erotic Threesomes — https://streamtape.com/e/Dz0Ae4AvmjFkyr3
|
||||||
1170
theporndude_coverage.json
Normal file
1170
theporndude_coverage.json
Normal file
File diff suppressed because it is too large
Load diff
1
theporndude_dump.json
Normal file
1
theporndude_dump.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"catsWithContent":24,"totalSlugs":180,"cats":{"top-porn-tube-sites":{"title":"Free Porn Tube Sites","n":2,"slugs":["xvideos","cumlouder"]},"free-onlyfans-accounts":{"title":"Free OnlyFans Accounts","n":2,"slugs":["skylar-mae-free-onlyfans-review","luna-star-onlyfans-review-3"]},"tiktok-porn-sites":{"title":"TikTok Porn Sites","n":1,"slugs":["kwiky"]},"asian-porn-premium-sites":{"title":"Premium Asian Porn Sites","n":5,"slugs":["javhd","erito","japanhdv","asiansexdiary","zenra"]},"top-asian-sex-cams":{"title":"Live Asian Sex Cams","n":2,"slugs":["stripchatasian","livesexasian"]},"adult-vod-sites":{"title":"Adult VOD Sites","n":1,"slugs":["adulttime"]},"premium-nude-celebrities":{"title":"Premium Nude Celebrities","n":1,"slugs":["bannedsextapes"]},"ai-porn-sites":{"title":"AI Porn Sites","n":17,"slugs":["candyai","ourdream","gptgirlfriend-aipornsites-review","spicychat","playbox-aiporn-review","juicychatai-review","fapify-review","edenaiworld","camsodaai-review","undressappai","clothoff-review","trynectar","goloveai","pornworks","createporn","dondiai-review","createhottie-aipornsites-review"]},"nsfw-ai-fantasy-sites":{"title":"NSFW AI Fantasy Sites","n":16,"slugs":["playbox-undressai-review","fapify-undressaisites-review","clothoff","nudeai-review","xundress-review","createhottie-undress-review","nudiva","nudifyai-review","undressxxx-review","nudefab-review","fapwise-review","facyai","ainudesite","leakifyhub-review","nodress","aiundresscc-review"]},"best-premium-hentai-sites":{"title":"Premium Hentai Sites","n":3,"slugs":["hentaipros","hentaiedpro","createhentai"]},"top-sex-cam-sites":{"title":"Live Sex Cam Sites","n":28,"slugs":["camsoda","stripchat","sinparty","livejasmin","streamate","bongacams","jerkmate","cam4","myfreecams","flirtify-review","flirtbate","amateurtven","xlovecam","imlive","skyprivate","coomeetchat","xcams","sextpanther","sexytalktv-review","cams","flirt4free","sakuralive","slutroulette","luckycrush","babestation","xtease-review","xpanded","sospoiltcams"]},"ai-porn-generator-sites-reviews":{"title":"AI Porn Generator Sites","n":16,"slugs":["playbox-review","createhottie","fapify-aiporngenerators-review","sugargenbox-review","lusychat-generator-review","kissyvid-review","seducedai","dpnode-review","createporn","leakifyhub-aiporngenerators-review","pornworks-aigenerator-review","musebox-review","clothoff-aiporngenerator-review","trynectar-aiporngenerator-review","pornface-review","pornmakerai-aiporngenerators-review"]},"best-vr-porn-sites":{"title":"VR Porn Sites","n":5,"slugs":["vrporn","povrcom","vrbangers","sexlikereal","virtualrealporn"]},"premium-cartoon-porn-sites":{"title":"Premium Cartoon Porn Sites","n":2,"slugs":["animatria","adulttimeanime"]},"free-cam-girl-video-sites":{"title":"Free Cam Girl Video Sites","n":1,"slugs":["recurbate"]},"free-sex-cam-sites":{"title":"Free Sex Cam Sites","n":5,"slugs":["stripchat","sinparty","myfreecams","bongacams","camsoda"]},"best-adult-chat-sites":{"title":"Sex Chat Sites","n":10,"slugs":["coomeet","luckycrush","porndudegirls","flirtify-review","candyai","sextpanther","sexytalktv-review","mirami-review","flirtbees","stripchat-cam2cam-review"]},"ai-sex-chat-sites":{"title":"AI Sex Chat Sites","n":5,"slugs":["candyaisexchat-review","ourdream-review","gptgirlfriend","spicychat-aisexchat-review","lusychat-aisexchat-review"]},"top-premium-sites":{"title":"Top Premium Porn Sites","n":33,"slugs":["brazzersnetwork","bangbrosnetwork","spicevids","iknowthatgirl","letsdoeit","adulttime","faphouse","candyaipremium","hentaiedpro-premiumpornsites-review","fapify-premiumpornsites-review","teamskeet","mylf","puretaboo","naughtyamerica","xvideosred","gptgirlfriendpremium","mofosnetwork","evilangel","vixen","adultprime","pornbox","familystrokes","swappz","czechav-review","freeuse","realitykings","pornplus","vixenplus","pervz-review","nubilesporn","digitalplayground","julesjordan","blacked"]},"best-dating-sites":{"title":"Hookup Sites","n":5,"slugs":["adultfriendfinder","benaughty","ashleymadison","fling","fuckbook"]},"amateur-premium-sites":{"title":"Premium Amateur Porn Sites","n":5,"slugs":["exploitedcollegegirls","netvideogirls","trueamateurs","backroomcastingcouch","czechcasting-review"]},"best-porn-games":{"title":"Best Porn Games","n":10,"slugs":["nutaku","hentaiheroes","lustgoddess","pornstarharem","lifeselector","erolabs","kingofkinks","projectqt","bootycalls","kamihime"]},"betting-sites":{"title":"Betting Sites","n":4,"slugs":["betonline","betus","bcgame","slotocash"]},"best-escort-sites":{"title":"Escort Sites","n":1,"slugs":["slixa"]}}}
|
||||||
65
theporndude_final_report.md
Normal file
65
theporndude_final_report.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Theporndude.com canonical-fit audit (2026-05-20)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Source: `theporndude.com/top-porn-tube-sites` (kategoria "Free Porn Tube Sites")
|
||||||
|
- Method: 166 review-page slugs → resolve real domain (pdude.link follow) →
|
||||||
|
coverage check vs naszych 25 origins + 4 REGISTRY → curl HTML triage 144 nowych
|
||||||
|
→ Chrome DevTools deep audit dla 4 z heurystycznym score ≥ 2.5
|
||||||
|
- Files generated:
|
||||||
|
- `theporndude_resolved.json` — 166 tubes z real domains
|
||||||
|
- `theporndude_coverage.json` — already-have vs new
|
||||||
|
- `theporndude_triage.json` — curl HTML heurystyka per-tube
|
||||||
|
- `theporndude_scorecard.json` — final per-tube scorecard
|
||||||
|
|
||||||
|
## Final coverage
|
||||||
|
|
||||||
|
| Status | N | % | |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Already have** | 22 | 13% | xvideos, xnxx, pornhub, xhamster, eporner, hqporner, porntrex, redtube, youporn, hdporn92 (DELISTED), pornhat, porn00, porndish, paradisehill, watchporn, xmoviesforyou, perverzija, latestpornvideo, latestleaks, mypornerleak, sxyland, porn4days |
|
||||||
|
| **Promising** (score≥2.5) | 4 | 2% | porndoe, fullporner, porn300, vrsmash |
|
||||||
|
| **Low value** (1-2.5) | 13 | 8% | tnaflix, hitprn, porntop, pornxp, fakings, justfullporn, empflix, taxi69, sexu, definebabe, hd-easyporn, bigbuttshub, zhornyhub |
|
||||||
|
| **No value** | 46 | 28% | Generic landing/JS-rendered, brak strukturalnych markerów |
|
||||||
|
| **Auth wall** | 46 | 28% | Login required / member-only / premium |
|
||||||
|
| **Dead** | 35 | 21% | 404, conn refused, timeout |
|
||||||
|
|
||||||
|
## Deep audit 4 promising
|
||||||
|
|
||||||
|
| Tube | Title | Performers | Studio | Date | Duration | Tags | JSON-LD | Verdict |
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
| **porndoe.com** | ✅ rich | ✅ 4 named | ⚠️ stub | ✅ "May 20, 2026" | ❓ | ✅ 10 named | ✅ @graph | **HIGH — pilot candidate** |
|
||||||
|
| fullporner.com | ✅ | ✅ 3 named | ❌ | ❌ | ❌ | ⚠️ 3 hashtags | ❌ | LOW — title+performer only |
|
||||||
|
| porn300.com | ⚠️ broken h1 | ⚠️ stub "Pornstars" | ❌ | ⚠️ "27:05" niejasne | ⚠️ 0:00 | ⚠️ 8 z headerem | ❌ | VERY LOW — anti-bot / broken DOM |
|
||||||
|
| vrsmash.com | ✅ rich | ⚠️ 1 named | ❌ | ❌ | ❌ | ✅ 10 VR genre | ⚠️ WebPage | LOW — VR niche, brak studio |
|
||||||
|
|
||||||
|
## Werdykt
|
||||||
|
|
||||||
|
**Z 166 top free tubes na theporndude.com, mamy realnie 1 verified pilot candidate (porndoe.com).**
|
||||||
|
|
||||||
|
### Dlaczego tak mało?
|
||||||
|
|
||||||
|
1. **High-value mainstream tubes mamy już wszystkie** (xvideos/xnxx/pornhub/xhamster/eporner/redtube/youporn etc.).
|
||||||
|
2. **Wiele theporndude rekomendacji to ad-network / premium / cam / dating** — mimo że są w kategorii "Free Tubes". Theporndude monetyzuje partnerships, więc featured ≠ canonical-friendly.
|
||||||
|
3. **Reszta to:**
|
||||||
|
- dead/expired domains (35) — theporndude updates pozostają w tyle
|
||||||
|
- auth wall (46) — paysite teasers, scenes wymagają konta
|
||||||
|
- no-metadata SEO shells (46) — popup ads, no scene metadata
|
||||||
|
- low value (13) — basic title+thumb, brak studio/performer/date
|
||||||
|
|
||||||
|
### Recommendation
|
||||||
|
|
||||||
|
**Don't expand sources from theporndude beyond `porndoe`** (pre-public bandwidth/anonymity priorytet — patrz [[goon_bandwidth_strategy]]). Marginalny ROI vs ryzyko nowych extractor-bugów.
|
||||||
|
|
||||||
|
### Optional next steps
|
||||||
|
|
||||||
|
- **porndoe pilot** (~2-3h dev): rich metadata + named performers + dates + 10 tags + JSON-LD. Najlepszy candidate w całej liście. Warto napisać extractor jeśli kiedyś będzie potrzeba expansion.
|
||||||
|
- Skip pozostałe — niski ROI vs effort.
|
||||||
|
|
||||||
|
## Surowe pliki
|
||||||
|
|
||||||
|
```
|
||||||
|
theporndude_resolved.json — 166 entries z real domains
|
||||||
|
theporndude_coverage.json — split already-have/new
|
||||||
|
theporndude_triage.json — curl HTML heurystyka per-tube
|
||||||
|
theporndude_scorecard.json — final per-tube scorecard z status+recommendation
|
||||||
|
```
|
||||||
1
theporndude_free_tubes.json
Normal file
1
theporndude_free_tubes.json
Normal file
File diff suppressed because one or more lines are too long
92
theporndude_full_audit_report.md
Normal file
92
theporndude_full_audit_report.md
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
# Theporndude.com FULL audit — 2 kategorie (2026-05-20)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
| Kategoria | URL | Tubes |
|
||||||
|
|---|---|---|
|
||||||
|
| Free Porn Tube Sites | `/top-porn-tube-sites` | 166 |
|
||||||
|
| Free Porn Video Sites | `/full-porn-movies-sites` | 94 |
|
||||||
|
| **Unique total** | — | **172** |
|
||||||
|
| Overlap między kategoriami | — | 88 |
|
||||||
|
|
||||||
|
## Coverage combined
|
||||||
|
|
||||||
|
| Status | N | % | |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Already have | 26 | 15% | mainstream + REGISTRY tubes (xvideos, xnxx, pornhub, hqporner, porntrex, eporner, xmoviesforyou, paradisehill, watchporn, latestpornvideo, latestleaks, mypornerleak, sxyland, perverzija, porndish, porn00, porn4days, pornhat, redtube, youporn, xhamster, hdporn92, watchxxxfree...) |
|
||||||
|
| **Promising (verified ≥2.5)** | **1** | **0.6%** | **porndoe.com** |
|
||||||
|
| Low value (1-2.5) | 13 | 8% | tnaflix, pornxp, sexu, fakings, taxi69, definebabe, hd-easyporn, bigbuttshub, zhornyhub, hitprn, porntop, empflix, justfullporn, pornhd3x, veporn |
|
||||||
|
| No value / auth wall / dead | 132 | 77% | — |
|
||||||
|
|
||||||
|
## porndoe.com deep audit verdict
|
||||||
|
|
||||||
|
**Status: ❤️ HIGH-VALUE metadata-only source**
|
||||||
|
|
||||||
|
### Co ma w JSON-LD VideoObject (per scene)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@type": "VideoObject",
|
||||||
|
"name": "Alexx Bbc Craving Asian",
|
||||||
|
"uploadDate": "2026-05-20T14:55:13+00:00",
|
||||||
|
"duration": "T8M0S",
|
||||||
|
"thumbnailUrl": "https://p.cdnc.porndoe.com/image/movie/...",
|
||||||
|
"contentUrl": "https://porndoe.com/watch/pd7a3o0e8v2b",
|
||||||
|
"producer": {"@type": "Organization", "name": "Fantasy Girl Pass",
|
||||||
|
"url": "https://porndoe.com/channel-profile/fantasy-girl-pass"},
|
||||||
|
"publisher": {"@type": "Organization", "name": "Fantasy Girl Pass", ...},
|
||||||
|
"actor": [{"@type": "Person", "name": "Alexx Zen",
|
||||||
|
"url": "https://porndoe.com/pornstars-profile/alexx-zen"}],
|
||||||
|
"description": "Alexx Bbc Craving Asian"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Co potwierdza wartość
|
||||||
|
- ✅ **Schema.org compliant** (JSON-LD VideoObject)
|
||||||
|
- ✅ **Named studios** ("Fantasy Girl Pass", "Japan Lust" etc.) — cross-matchowalne z TPDB
|
||||||
|
- ✅ **Named performers** ("Alexx Zen") z stable URL `/pornstars-profile/<slug>`
|
||||||
|
- ✅ **Precyzyjny timestamp** uploadDate (ISO 8601)
|
||||||
|
- ✅ **Duration ISO 8601** (parsowanie trywialne)
|
||||||
|
- ✅ **Stable scene URL pattern**: `/watch/<8-12 char id>`
|
||||||
|
- ✅ **Listing endpoint**: `/videos/most-recent?page=N` (paginacja działa)
|
||||||
|
- ⚠️ Stream URL: NIE inline w SSR HTML (player JS init po Play click) — trzeba follow embed lub player config
|
||||||
|
|
||||||
|
### Recommendation porndoe
|
||||||
|
|
||||||
|
**Dodaj jako metadata-only source (jak TPDB/StashDB)**, NIE jako playable tube.
|
||||||
|
|
||||||
|
| Aspekt | Verdict |
|
||||||
|
|---|---|
|
||||||
|
| **Canonical match** | ✅ HIGH — JSON-LD daje wszystko |
|
||||||
|
| **Bandwidth cost** | ✅ LOW — tylko HTML JSON-LD scrape (~50KB/page, ~30 scenes = ~1.7KB/scene) |
|
||||||
|
| **VPS anonimowość** | ✅ Bezpieczne — request standardowy curl/httpx |
|
||||||
|
| **Playback** | ❌ NIE — stream JS-rendered po click, lepiej skip |
|
||||||
|
| **Effort** | ~3-4h dev: nowy connector `app/connectors/porndoe.py` (paginacja + JSON-LD parser) + scene/performer/studio mapping |
|
||||||
|
|
||||||
|
**Value-add do bazy:** porndoe agreguje hundreds of named studios (Fantasy Girl Pass, Japan Lust, etc.). Może wypełnić luki TPDB dla mniejszych studios + amateur/niche content.
|
||||||
|
|
||||||
|
## /full-porn-movies-sites werdykt
|
||||||
|
|
||||||
|
**Brutto 0 nowych high-value kandydatów.** Z 94 tubes:
|
||||||
|
- 88 overlap z `/top-porn-tube-sites` (już zaaudytowane)
|
||||||
|
- 6 unique → wszyscy 6 redirectują pdude.link → theporndude.com (delisted/dead)
|
||||||
|
|
||||||
|
## Final recommendation
|
||||||
|
|
||||||
|
| Priorytet | Akcja |
|
||||||
|
|---|---|
|
||||||
|
| **(opcjonalne) Pilot porndoe** | Napisz `app/connectors/porndoe.py` — paginacja `/videos/most-recent?page=N` + JSON-LD parser → SceneDTO. **Metadata-only**, weight=0.6. Idealny dla orphan TPDB scenes. ~3-4h dev. |
|
||||||
|
| **Skip reszta theporndude** | Marginalny ROI. Mainstream już mamy, reszta to ad-network/dead/auth-wall. |
|
||||||
|
| **(pre-public)** | Zostań w obecnym scope. Bandwidth/anonymity > expansion. |
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
```
|
||||||
|
theporndude_resolved.json — 166 z top-porn-tubes
|
||||||
|
theporndude_movies.json — 94 z full-porn-movies-sites
|
||||||
|
theporndude_coverage.json — top-porn-tubes split have/new
|
||||||
|
theporndude_triage.json — curl heurystyka per-tube
|
||||||
|
theporndude_scorecard.json — final scorecard top-porn-tubes
|
||||||
|
theporndude_movies_scorecard.json — final scorecard movies
|
||||||
|
theporndude_final_report.md — raport top-porn-tubes
|
||||||
|
theporndude_full_audit_report.md — TEN raport (combined)
|
||||||
|
```
|
||||||
1
theporndude_movies.json
Normal file
1
theporndude_movies.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"h1":"Free Porn Videos & XXX Sex Movies - Reviews","n":94,"all":[{"id":"1514","slug":"yourporn"},{"id":"2356","slug":"hqporner"},{"id":"6138","slug":"porntrex"},{"id":"6157","slug":"eporner"},{"id":"8849","slug":"pornhoarder"},{"id":"8746","slug":"yespornvip"},{"id":"3192","slug":"xmoviesforyou"},{"id":"6248","slug":"pornhits"},{"id":"3228","slug":"porn00"},{"id":"1761","slug":"pornhd3x"},{"id":"4740","slug":"xxxfiles"},{"id":"4423","slug":"porngo"},{"id":"2378","slug":"pornktube"},{"id":"1813","slug":"pornovideoshub"},{"id":"5065","slug":"brazzers3x"},{"id":"3517","slug":"paradisehill"},{"id":"3380","slug":"freeomovie"},{"id":"1423","slug":"xtapes"},{"id":"1736","slug":"porndish"},{"id":"7936","slug":"fullporner"},{"id":"6209","slug":"trendyporn"},{"id":"2507","slug":"pornhd8k"},{"id":"3415","slug":"xfreehd"},{"id":"21367","slug":"pornslash"},{"id":"1875","slug":"pornobae"},{"id":"2801","slug":"netfapx"},{"id":"2684","slug":"joysporn"},{"id":"5111","slug":"pornmz"},{"id":"7191","slug":"xmegadrive"},{"id":"2328","slug":"porn4days"},{"id":"5969","slug":"justfullporn"},{"id":"1412","slug":"watchxxxfree"},{"id":"6185","slug":"hdporn92"},{"id":"6311","slug":"yespornpleasexxx"},{"id":"7432","slug":"hitprn"},{"id":"5232","slug":"czechvideo"},{"id":"9062","slug":"fuxnxx"},{"id":"8610","slug":"watchporn"},{"id":"8281","slug":"latestpornvideo"},{"id":"7374","slug":"pornxp"},{"id":"16954","slug":"latestleaks"},{"id":"1528","slug":"pandamovie"},{"id":"16957","slug":"bananamovies"},{"id":"2884","slug":"letsjerk"},{"id":"3388","slug":"streamporn"},{"id":"1796","slug":"fullxxxmovies"},{"id":"1748","slug":"xxvideoss"},{"id":"7778","slug":"iceporncasting"},{"id":"1757","slug":"pornvibe"},{"id":"3401","slug":"mangoporn"},{"id":"4541","slug":"swingerpornfun"},{"id":"6206","slug":"perverzija"},{"id":"13692","slug":"pornx"},{"id":"3549","slug":"siska"},{"id":"1429","slug":"yourdailypornvideos"},{"id":"9060","slug":"whereismyporn"},{"id":"17940","slug":"allpornstream"},{"id":"8384","slug":"thepornarea"},{"id":"9885","slug":"pornvideobb"},{"id":"9057","slug":"teenagepornx"},{"id":"29266","slug":"speedporn-review"},{"id":"9107","slug":"pornoflix"},{"id":"15889","slug":"neporn"},{"id":"11245","slug":"pornezcam"},{"id":"10281","slug":"americass"},{"id":"10411","slug":"sextu"},{"id":"10954","slug":"galaxyporn"},{"id":"11698","slug":"pornseed"},{"id":"17542","slug":"ogporn"},{"id":"15967","slug":"bingato"},{"id":"18998","slug":"bibamax"},{"id":"13362","slug":"xfuntaxy"},{"id":"19323","slug":"xvgold"},{"id":"14555","slug":"brazz"},{"id":"14784","slug":"bigbuttshub"},{"id":"15284","slug":"cheemsporno"},{"id":"17485","slug":"mypornerleak"},{"id":"17546","slug":"coralxxx"},{"id":"24262","slug":"youperv-website-review"},{"id":"18347","slug":"porneec"},{"id":"18171","slug":"freepornsex"},{"id":"18338","slug":"amigosporn"},{"id":"18315","slug":"vepornnet"},{"id":"1548","slug":"veporn"},{"id":"20299","slug":"xleech"},{"id":"20745","slug":"sxyland"},{"id":"21910","slug":"brazzpw-review"},{"id":"21358","slug":"zhornyhub"},{"id":"24377","slug":"pornhd4k-website-review"},{"id":"24397","slug":"pornbusy-website-review"},{"id":"24698","slug":"8kporner-website-review"},{"id":"25496","slug":"saintporn-website-review"},{"id":"27856","slug":"4k69-review"},{"id":"28502","slug":"jerkmotion-review"}]}
|
||||||
954
theporndude_movies_scorecard.json
Normal file
954
theporndude_movies_scorecard.json
Normal file
|
|
@ -0,0 +1,954 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "1514",
|
||||||
|
"slug": "yourporn",
|
||||||
|
"domain": "yourporn.com",
|
||||||
|
"root_status": -1,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2356",
|
||||||
|
"slug": "hqporner",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:hqpornercom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6138",
|
||||||
|
"slug": "porntrex",
|
||||||
|
"domain": "porntrex.com",
|
||||||
|
"root_status": -2,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:porntrexcom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6157",
|
||||||
|
"slug": "eporner",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:epornercom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8849",
|
||||||
|
"slug": "pornhoarder",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8746",
|
||||||
|
"slug": "yespornvip",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3192",
|
||||||
|
"slug": "xmoviesforyou",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:xmoviesforyoucom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6248",
|
||||||
|
"slug": "pornhits",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3228",
|
||||||
|
"slug": "porn00",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:porn00org"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1761",
|
||||||
|
"slug": "pornhd3x",
|
||||||
|
"domain": "pornhd3x.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {
|
||||||
|
"video_card": true
|
||||||
|
},
|
||||||
|
"score": 1.0,
|
||||||
|
"reasons": [
|
||||||
|
"video_card"
|
||||||
|
],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4740",
|
||||||
|
"slug": "xxxfiles",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4423",
|
||||||
|
"slug": "porngo",
|
||||||
|
"domain": "my.cams.bar",
|
||||||
|
"root_status": -1,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2378",
|
||||||
|
"slug": "pornktube",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1813",
|
||||||
|
"slug": "pornovideoshub",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5065",
|
||||||
|
"slug": "brazzers3x",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3517",
|
||||||
|
"slug": "paradisehill",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:paradisehillcc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3380",
|
||||||
|
"slug": "freeomovie",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1423",
|
||||||
|
"slug": "xtapes",
|
||||||
|
"domain": "xtapes.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1736",
|
||||||
|
"slug": "porndish",
|
||||||
|
"domain": "porndish.com",
|
||||||
|
"root_status": 403,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:porndishcom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7936",
|
||||||
|
"slug": "fullporner",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6209",
|
||||||
|
"slug": "trendyporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2507",
|
||||||
|
"slug": "pornhd8k",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3415",
|
||||||
|
"slug": "xfreehd",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21367",
|
||||||
|
"slug": "pornslash",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1875",
|
||||||
|
"slug": "pornobae",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2801",
|
||||||
|
"slug": "netfapx",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2684",
|
||||||
|
"slug": "joysporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5111",
|
||||||
|
"slug": "pornmz",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7191",
|
||||||
|
"slug": "xmegadrive",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2328",
|
||||||
|
"slug": "porn4days",
|
||||||
|
"domain": "porn4days.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {
|
||||||
|
"studio_marker": true
|
||||||
|
},
|
||||||
|
"score": 1.0,
|
||||||
|
"reasons": [
|
||||||
|
"studio_marker"
|
||||||
|
],
|
||||||
|
"our_origin": "tube:porn4dayspw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5969",
|
||||||
|
"slug": "justfullporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1412",
|
||||||
|
"slug": "watchxxxfree",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6185",
|
||||||
|
"slug": "hdporn92",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:hdporn92com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6311",
|
||||||
|
"slug": "yespornpleasexxx",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7432",
|
||||||
|
"slug": "hitprn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5232",
|
||||||
|
"slug": "czechvideo",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9062",
|
||||||
|
"slug": "fuxnxx",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8610",
|
||||||
|
"slug": "watchporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:watchporn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8281",
|
||||||
|
"slug": "latestpornvideo",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:latestpornvideocom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7374",
|
||||||
|
"slug": "pornxp",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "16954",
|
||||||
|
"slug": "latestleaks",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:latestleaksco"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1528",
|
||||||
|
"slug": "pandamovie",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "16957",
|
||||||
|
"slug": "bananamovies",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2884",
|
||||||
|
"slug": "letsjerk",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3388",
|
||||||
|
"slug": "streamporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1796",
|
||||||
|
"slug": "fullxxxmovies",
|
||||||
|
"domain": "fullxxxmovies.com",
|
||||||
|
"root_status": -1,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1748",
|
||||||
|
"slug": "xxvideoss",
|
||||||
|
"domain": "xxvideoss.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7778",
|
||||||
|
"slug": "iceporncasting",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1757",
|
||||||
|
"slug": "pornvibe",
|
||||||
|
"domain": "pornvibe.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3401",
|
||||||
|
"slug": "mangoporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4541",
|
||||||
|
"slug": "swingerpornfun",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6206",
|
||||||
|
"slug": "perverzija",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:perverzijacom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13692",
|
||||||
|
"slug": "pornx",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3549",
|
||||||
|
"slug": "siska",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1429",
|
||||||
|
"slug": "yourdailypornvideos",
|
||||||
|
"domain": "yourdailypornvideos.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9060",
|
||||||
|
"slug": "whereismyporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "17940",
|
||||||
|
"slug": "allpornstream",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "8384",
|
||||||
|
"slug": "thepornarea",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9885",
|
||||||
|
"slug": "pornvideobb",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9057",
|
||||||
|
"slug": "teenagepornx",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "29266",
|
||||||
|
"slug": "speedporn-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9107",
|
||||||
|
"slug": "pornoflix",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "15889",
|
||||||
|
"slug": "neporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "11245",
|
||||||
|
"slug": "pornezcam",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10281",
|
||||||
|
"slug": "americass",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10411",
|
||||||
|
"slug": "sextu",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "10954",
|
||||||
|
"slug": "galaxyporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "11698",
|
||||||
|
"slug": "pornseed",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "17542",
|
||||||
|
"slug": "ogporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "15967",
|
||||||
|
"slug": "bingato",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18998",
|
||||||
|
"slug": "bibamax",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13362",
|
||||||
|
"slug": "xfuntaxy",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "19323",
|
||||||
|
"slug": "xvgold",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "14555",
|
||||||
|
"slug": "brazz",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "14784",
|
||||||
|
"slug": "bigbuttshub",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "15284",
|
||||||
|
"slug": "cheemsporno",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "17485",
|
||||||
|
"slug": "mypornerleak",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:mypornerleakcom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "17546",
|
||||||
|
"slug": "coralxxx",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "24262",
|
||||||
|
"slug": "youperv-website-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18347",
|
||||||
|
"slug": "porneec",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18171",
|
||||||
|
"slug": "freepornsex",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18338",
|
||||||
|
"slug": "amigosporn",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "18315",
|
||||||
|
"slug": "vepornnet",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1548",
|
||||||
|
"slug": "veporn",
|
||||||
|
"domain": "veporn.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {
|
||||||
|
"performer_marker": true
|
||||||
|
},
|
||||||
|
"score": 1.0,
|
||||||
|
"reasons": [
|
||||||
|
"performer_marker"
|
||||||
|
],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20299",
|
||||||
|
"slug": "xleech",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "20745",
|
||||||
|
"slug": "sxyland",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": "tube:sxylandcom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21910",
|
||||||
|
"slug": "brazzpw-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21358",
|
||||||
|
"slug": "zhornyhub",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "24377",
|
||||||
|
"slug": "pornhd4k-website-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "24397",
|
||||||
|
"slug": "pornbusy-website-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "24698",
|
||||||
|
"slug": "8kporner-website-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "25496",
|
||||||
|
"slug": "saintporn-website-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "27856",
|
||||||
|
"slug": "4k69-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "28502",
|
||||||
|
"slug": "jerkmotion-review",
|
||||||
|
"domain": "theporndude.com",
|
||||||
|
"root_status": 200,
|
||||||
|
"findings": {},
|
||||||
|
"score": 0.0,
|
||||||
|
"reasons": [],
|
||||||
|
"our_origin": null
|
||||||
|
}
|
||||||
|
]
|
||||||
1994
theporndude_resolved.json
Normal file
1994
theporndude_resolved.json
Normal file
File diff suppressed because it is too large
Load diff
2552
theporndude_scorecard.json
Normal file
2552
theporndude_scorecard.json
Normal file
File diff suppressed because it is too large
Load diff
2408
theporndude_triage.json
Normal file
2408
theporndude_triage.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue