Compare commits
3 commits
77679b26bb
...
cfe72195ed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfe72195ed | ||
|
|
8f8bb1c448 | ||
|
|
8a9fea3eef |
21 changed files with 49 additions and 10376 deletions
40
alembic/versions/20260930_0050_tag_vertical_counts_no_fk.py
Normal file
40
alembic/versions/20260930_0050_tag_vertical_counts_no_fk.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
"""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,13 +2,14 @@
|
||||||
|
|
||||||
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.
|
Patrz migracja 0047. Bez klucza obcego do tags (0050): z nim kasowanie tagow w
|
||||||
|
tag_tail_trim zakleszczalo sie z odswiezaniem tej tabeli.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy import ForeignKey, Integer, String
|
from sqlalchemy import 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
|
||||||
|
|
||||||
|
|
@ -19,7 +20,5 @@ 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(
|
tag_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True)
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -1,302 +0,0 @@
|
||||||
<!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,9 +122,12 @@ export function ClipCreatorScreen() {
|
||||||
}
|
}
|
||||||
renderItem={({ item, index }) => (
|
renderItem={({ item, index }) => (
|
||||||
<Pressable style={styles.tile} onPress={() => setPlaying(index)}>
|
<Pressable style={styles.tile} onPress={() => setPlaying(index)}>
|
||||||
{item.clip.poster_url || item.clip.thumb_url ? (
|
{/* Najpierw thumb_url: RedGifs -mobile.jpg to 480x854 (~37 KB), a plakat
|
||||||
|
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.poster_url || item.clip.thumb_url) as string }}
|
source={{ uri: (item.clip.thumb_url || item.clip.poster_url) as string }}
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
contentFit="cover"
|
contentFit="cover"
|
||||||
transition={100}
|
transition={100}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
"""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()
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
"""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()
|
|
||||||
|
|
@ -1,203 +0,0 @@
|
||||||
"""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())
|
|
||||||
|
|
@ -1,234 +0,0 @@
|
||||||
"""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())
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
"""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())
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
"""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()
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
# 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
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1 +0,0 @@
|
||||||
{"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"]}}}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
# 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
|
|
||||||
```
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,92 +0,0 @@
|
||||||
# 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 +0,0 @@
|
||||||
{"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"}]}
|
|
||||||
|
|
@ -1,954 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue