goon/scripts/probe_mangoporn_hosters.py
goon-foss ad0284585b Initial commit
Goon — self-hosted aggregator for adult-content scene metadata.

Indexes scenes from TPDB, StashDB, and 30+ public adult tube sites.
Cross-source deduplication via perceptual hash + Levenshtein distance.
FastAPI backend + APScheduler worker + React Native (Expo) mobile client.

FOSS, ad-free, donation-funded. See README for details.
2026-05-20 10:10:22 +02:00

104 lines
3.5 KiB
Python

"""Probe mangoporn movie embed hosters — wyciąga real stream URLs / patterns
z każdego hostera, decyduje czy mamy yt-dlp support / dedicated extractor /
drop.
Output: tabela hoster | embed status | stream URL pattern | yt-dlp support |
recommended action.
"""
from __future__ import annotations
import json
import re
import sys
try:
from curl_cffi import requests as cf
except ImportError:
print("Install curl_cffi: pip install curl_cffi")
sys.exit(1)
HOSTERS = [
("doply", "https://doply.net/e/wzuz0w61lm7c"),
("mixdrop", "https://mixdrop.my/e/03m9p1kpi7llwl"),
("voe", "https://voe.sx/e/k9j1qhxzbeli"),
("luluvid", "https://luluvid.com/e/67jkt27c33fn"),
("embedseek", "https://embedseek.com/e/abcdef"),
("upns", "https://upns.one/e/abcdef"),
("seekplayer", "https://seekplayer.com/e/abcdef"),
("rpmplay", "https://rpmplay.com/e/abcdef"),
("player4me", "https://player4me.com/e/abcdef"),
("easyvidplayer", "https://easyvidplayer.com/e/abcdef"),
("frdl", "https://frdl.to/abcdef"),
("playmogo", "https://playmogo.com/e/wzuz0w61lm7c"),
]
# Patterns suggesting embedded video
M3U8_RE = re.compile(r'https?://[^"\'\s<>]+\.m3u8[^"\'\s<>]*')
MP4_RE = re.compile(r'https?://[^"\'\s<>]+\.mp4[^"\'\s<>]*')
PACKER_RE = re.compile(r"eval\(function\(p,a,c,k,e,d\)")
JWPLAYER_RE = re.compile(r"jwplayer\(['\"]\w*['\"]\)\.setup")
KVS_RE = re.compile(r"flashvars\s*=\s*\{")
SOURCES_TAG_RE = re.compile(r'<source[^>]+src="[^"]+"')
def probe(name: str, url: str) -> dict:
out = {"hoster": name, "url": url}
try:
r = cf.get(url, impersonate="chrome120", timeout=12, allow_redirects=True)
out["status"] = r.status_code
out["final_url"] = r.url
body = r.text
out["body_size"] = len(body)
out["m3u8_hits"] = len(M3U8_RE.findall(body))
out["mp4_hits"] = len(MP4_RE.findall(body))
out["has_packer"] = bool(PACKER_RE.search(body))
out["has_jwplayer"] = bool(JWPLAYER_RE.search(body))
out["has_kvs"] = bool(KVS_RE.search(body))
out["has_source_tag"] = bool(SOURCES_TAG_RE.search(body))
# Sample one m3u8/mp4 URL if found
m = M3U8_RE.search(body)
if m:
out["m3u8_sample"] = m.group(0)[:120]
m = MP4_RE.search(body)
if m:
out["mp4_sample"] = m.group(0)[:120]
except Exception as e:
out["err"] = f"{type(e).__name__}: {str(e)[:120]}"
return out
def main() -> None:
results = []
for name, url in HOSTERS:
r = probe(name, url)
results.append(r)
keys_to_show = [k for k in r if k != "url"]
line = " | ".join(f"{k}={r[k]}" for k in keys_to_show if r[k] not in (None, "", 0, False))
print(f"\n[{name}] {url}")
print(f" {line}")
print("\n=== Summary ===")
for r in results:
verdict = "?"
if r.get("err"):
verdict = "ERR (dead?)"
elif r.get("status") and r["status"] >= 400:
verdict = f"HTTP {r['status']} (dead)"
elif r.get("m3u8_hits"):
verdict = f"HLS direct ({r['m3u8_hits']} m3u8)"
elif r.get("mp4_hits"):
verdict = f"MP4 direct ({r['mp4_hits']} mp4)"
elif r.get("has_source_tag"):
verdict = "KVS-like <source> tags"
elif r.get("has_packer"):
verdict = "P.A.C.K.E.R. obfuscated"
elif r.get("has_jwplayer"):
verdict = "JWPlayer (needs JS exec)"
else:
verdict = "no streams found"
print(f" {r['hoster']:15s}{verdict}")
if __name__ == "__main__":
main()