Two providers, two completely different extraction mechanisms. They stopped having anything in common in the 14.2–14.8 line, which is why the GUI has separate panels for them and why one of them never opens a browser.
datanodes.to |
fuckingfast.co |
|
|---|---|---|
| Extraction | real Chrome over CDP | plain HTTPS, Chrome TLS fingerprint |
| Browser | yes — one shared window | none |
| Captcha | Cloudflare Turnstile | none |
| Cost per link | seconds | ~0.25 s |
| Settings that apply | Pages, Captcha wait, Chrome path, API key |
none |
Both extractors live in moon_extract.py and are shared by the GUI (moon_engine.py)
and the CLI (moon_cli.py).
- Extraction:
POST /f/{id}/goovercurl_cffi, reading thehx-redirectresponse header. - Why not a regex on the HTML: the landing page is Alpine + htmx and the
/dl/URL is not in the markup at all — it only exists in that header. The pre-14.4 regex extractor could not find what was never sent. - Why not aiohttp: Cloudflare fingerprints the TLS ClientHello. aiohttp scores as
a bot and gets
cf-mitigated: challenge→ 403 on every link, whatever headers you send.curl_cffiimpersonates Chrome's ClientHello and sails through, socurl_cffi>=0.7is a hard requirement for this provider. - Downloads still use aiohttp:
dl.fuckingfast.coanswers 206 with a correctContent-Range, so the download engine needs no impersonation. - Filename note: FitGirl-style links carry the name as a URL fragment
(
https://fuckingfast.co/abc123#Game.part01.rar). The file id is parsed off the path, never off the raw string. - Dead links fail fast — 404 or a "not found" body, no retry can recover them.
- No browser, no captcha, nothing to tune.
- Extraction: real Chrome (or Edge), spawned by the app with
--remote-debugging-portand attached over CDP. - Why not Playwright's Chromium: Turnstile rejects it. The launcher adds
automation switches, the binary is not a Google-branded build and the profile is
empty — the widget answers Verification failed / Error 600010. A Chrome we
spawned ourselves has none of those tells and keeps a persistent profile, so
the
cf_clearancecookie survives between links and later files are challenged less. - Why not headless: measured on Chromium 131, the challenge platform answers 401
on
/cdn-cgi/challenge-platform/.../pat/...andcf-turnstile-responsestays empty forever.headless=Falseis deliberate — override withMOON_DN_HEADLESS=1only if you have wired in a solver. - Concurrency:
Pages(1–8) is how many tabs may be open on the one shared window, not how many browsers. Separate contexts were tried in 14.6 and were worse: multiple identities from one IP read as a bot farm and Turnstile hard-failed. - Exactly one
POST /downloadper link. A second one re-runs SecSave server-side and invalidates the token step 2 is holding. - Premium API key: set it in the GUI (or
MOON_DN_API_KEY) and extraction becomes a single JSON GET — no browser, no captcha. Free keys get 403 and fall back to Chrome. - Lane pinning: the CDN pins a session to a lane. A slow lane stays slow; re-extracting is what the stall killer does about it.
moon_extract.BrowserGate launches Playwright and Chrome on the first datanodes
link and never before. A batch of nothing but fuckingfast links opens no browser and
does not even boot the Playwright driver; a batch with one datanodes link opens exactly
one shared instance, however many extractors are running.
pytest tests/ -q asserts both, for the engine and for the CLI.
- Extractor — write it in
moon_extract.py, next to the two that exist. Pure HTTP if the site allows it; a browser only if it genuinely requires one. - Dispatch — add the domain to the
urlparse(url).netlocbranch inmoon_engine.pyandmoon_cli.py(grepdatanodes.to— there are two call sites). AskBrowserGate.get()for a browser inside the branch, never before it, or you reintroduce the launch-for-nothing bug. - Test — 10+ links including one guaranteed-dead one, so dead-link detection is
exercised, plus
pytest tests/ -q.