LeyckerS/moondownloaderPublic

Bulk file downloader for datanodes.to and fuckingfast.co — real-Chrome extraction over CDP, pure-HTTP extraction with a Chrome TLS fingerprint, aiohttp streaming, WebView2 GUI and a headless CLI. Windows, Python 3.10+, MIT.

AI summary: High-performance bulk file downloader utilizing Playwright extraction and aiohttp streaming.

Stars
1.6K
+1 today
Forks
45
Watchers
1
Open issues
16
Open PRs
3
Contributors
~37
Commits
170
Branches
6

PythonMITCreated Oct 2, 2024Last push 1d agoLatest release v4.0+4 stars this week+4 this month

Star history

since May 17, 2026
05001K1.5KMay 2026Jun 2026Jul 2026Aug 2026
1.6K stars as of Aug 7, 2026, tracked back to May 17, 2026. Historical curve reconstructed from public GitHub event archives, calibrated to the current total.

Contribution activity

commits per day, last 52 weeks
AugSepOctNovDecJanFebMarAprMayJunJulAugMonWedFri2025-08-10: 0 commits2025-08-11: 0 commits2025-08-12: 0 commits2025-08-13: 0 commits2025-08-14: 0 commits2025-08-15: 0 commits2025-08-16: 0 commits2025-08-17: 0 commits2025-08-18: 0 commits2025-08-19: 0 commits2025-08-20: 0 commits2025-08-21: 0 commits2025-08-22: 0 commits2025-08-23: 0 commits2025-08-24: 0 commits2025-08-25: 0 commits2025-08-26: 0 commits2025-08-27: 0 commits2025-08-28: 0 commits2025-08-29: 0 commits2025-08-30: 0 commits2025-08-31: 0 commits2025-09-01: 0 commits2025-09-02: 0 commits2025-09-03: 0 commits2025-09-04: 0 commits2025-09-05: 0 commits2025-09-06: 0 commits2025-09-07: 0 commits2025-09-08: 0 commits2025-09-09: 0 commits2025-09-10: 0 commits2025-09-11: 0 commits2025-09-12: 0 commits2025-09-13: 0 commits2025-09-14: 0 commits2025-09-15: 0 commits2025-09-16: 0 commits2025-09-17: 0 commits2025-09-18: 0 commits2025-09-19: 0 commits2025-09-20: 0 commits2025-09-21: 0 commits2025-09-22: 0 commits2025-09-23: 0 commits2025-09-24: 0 commits2025-09-25: 0 commits2025-09-26: 0 commits2025-09-27: 0 commits2025-09-28: 0 commits2025-09-29: 0 commits2025-09-30: 0 commits2025-10-01: 0 commits2025-10-02: 0 commits2025-10-03: 0 commits2025-10-04: 0 commits2025-10-05: 0 commits2025-10-06: 0 commits2025-10-07: 0 commits2025-10-08: 0 commits2025-10-09: 0 commits2025-10-10: 0 commits2025-10-11: 0 commits2025-10-12: 0 commits2025-10-13: 0 commits2025-10-14: 0 commits2025-10-15: 0 commits2025-10-16: 0 commits2025-10-17: 0 commits2025-10-18: 0 commits2025-10-19: 0 commits2025-10-20: 0 commits2025-10-21: 0 commits2025-10-22: 0 commits2025-10-23: 0 commits2025-10-24: 0 commits2025-10-25: 0 commits2025-10-26: 0 commits2025-10-27: 0 commits2025-10-28: 0 commits2025-10-29: 0 commits2025-10-30: 0 commits2025-10-31: 0 commits2025-11-01: 0 commits2025-11-02: 0 commits2025-11-03: 0 commits2025-11-04: 0 commits2025-11-05: 0 commits2025-11-06: 0 commits2025-11-07: 0 commits2025-11-08: 0 commits2025-11-09: 0 commits2025-11-10: 0 commits2025-11-11: 0 commits2025-11-12: 0 commits2025-11-13: 0 commits2025-11-14: 0 commits2025-11-15: 0 commits2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 0 commits2026-01-28: 0 commits2026-01-29: 0 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 0 commits2026-02-03: 0 commits2026-02-04: 0 commits2026-02-05: 0 commits2026-02-06: 0 commits2026-02-07: 0 commits2026-02-08: 0 commits2026-02-09: 0 commits2026-02-10: 0 commits2026-02-11: 0 commits2026-02-12: 0 commits2026-02-13: 0 commits2026-02-14: 0 commits2026-02-15: 0 commits2026-02-16: 0 commits2026-02-17: 0 commits2026-02-18: 0 commits2026-02-19: 0 commits2026-02-20: 0 commits2026-02-21: 0 commits2026-02-22: 0 commits2026-02-23: 0 commits2026-02-24: 0 commits2026-02-25: 0 commits2026-02-26: 0 commits2026-02-27: 0 commits2026-02-28: 0 commits2026-03-01: 0 commits2026-03-02: 0 commits2026-03-03: 0 commits2026-03-04: 0 commits2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 0 commits2026-03-08: 0 commits2026-03-09: 0 commits2026-03-10: 0 commits2026-03-11: 0 commits2026-03-12: 0 commits2026-03-13: 0 commits2026-03-14: 0 commits2026-03-15: 0 commits2026-03-16: 0 commits2026-03-17: 0 commits2026-03-18: 0 commits2026-03-19: 0 commits2026-03-20: 0 commits2026-03-21: 0 commits2026-03-22: 0 commits2026-03-23: 0 commits2026-03-24: 0 commits2026-03-25: 0 commits2026-03-26: 0 commits2026-03-27: 0 commits2026-03-28: 0 commits2026-03-29: 0 commits2026-03-30: 0 commits2026-03-31: 0 commits2026-04-01: 0 commits2026-04-02: 0 commits2026-04-03: 0 commits2026-04-04: 0 commits2026-04-05: 0 commits2026-04-06: 0 commits2026-04-07: 0 commits2026-04-08: 0 commits2026-04-09: 0 commits2026-04-10: 0 commits2026-04-11: 0 commits2026-04-12: 0 commits2026-04-13: 0 commits2026-04-14: 0 commits2026-04-15: 0 commits2026-04-16: 0 commits2026-04-17: 0 commits2026-04-18: 0 commits2026-04-19: 0 commits2026-04-20: 0 commits2026-04-21: 0 commits2026-04-22: 0 commits2026-04-23: 0 commits2026-04-24: 0 commits2026-04-25: 0 commits2026-04-26: 0 commits2026-04-27: 0 commits2026-04-28: 0 commits2026-04-29: 0 commits2026-04-30: 0 commits2026-05-01: 0 commits2026-05-02: 0 commits2026-05-03: 0 commits2026-05-04: 0 commits2026-05-05: 0 commits2026-05-06: 0 commits2026-05-07: 0 commits2026-05-08: 0 commits2026-05-09: 0 commits2026-05-10: 0 commits2026-05-11: 0 commits2026-05-12: 0 commits2026-05-13: 0 commits2026-05-14: 0 commits2026-05-15: 0 commits2026-05-16: 0 commits2026-05-17: 1 commit2026-05-18: 4 commits2026-05-19: 0 commits2026-05-20: 0 commits2026-05-21: 0 commits2026-05-22: 0 commits2026-05-23: 0 commits2026-05-24: 0 commits2026-05-25: 0 commits2026-05-26: 0 commits2026-05-27: 0 commits2026-05-28: 0 commits2026-05-29: 0 commits2026-05-30: 0 commits2026-05-31: 0 commits2026-06-01: 0 commits2026-06-02: 0 commits2026-06-03: 0 commits2026-06-04: 0 commits2026-06-05: 0 commits2026-06-06: 0 commits2026-06-07: 0 commits2026-06-08: 0 commits2026-06-09: 0 commits2026-06-10: 0 commits2026-06-11: 0 commits2026-06-12: 0 commits2026-06-13: 0 commits2026-06-14: 0 commits2026-06-15: 0 commits2026-06-16: 0 commits2026-06-17: 0 commits2026-06-18: 0 commits2026-06-19: 0 commits2026-06-20: 0 commits2026-06-21: 0 commits2026-06-22: 0 commits2026-06-23: 0 commits2026-06-24: 0 commits2026-06-25: 0 commits2026-06-26: 0 commits2026-06-27: 0 commits2026-06-28: 0 commits2026-06-29: 0 commits2026-06-30: 0 commits2026-07-01: 0 commits2026-07-02: 0 commits2026-07-03: 0 commits2026-07-04: 0 commits2026-07-05: 0 commits2026-07-06: 0 commits2026-07-07: 0 commits2026-07-08: 0 commits2026-07-09: 0 commits2026-07-10: 0 commits2026-07-11: 0 commits2026-07-12: 0 commits2026-07-13: 3 commits2026-07-14: 2 commits2026-07-15: 17 commits2026-07-16: 0 commits2026-07-17: 2 commits2026-07-18: 0 commits2026-07-19: 0 commits2026-07-20: 0 commits2026-07-21: 0 commits2026-07-22: 0 commits2026-07-23: 0 commits2026-07-24: 1 commit2026-07-25: 0 commits2026-07-26: 0 commits2026-07-27: 0 commits2026-07-28: 0 commits2026-07-29: 0 commits2026-07-30: 34 commits2026-07-31: 23 commits2026-08-01: 21 commits2026-08-02: 11 commits2026-08-03: 3 commits2026-08-04: 0 commits2026-08-05: 11 commits2026-08-06: 2 commits2026-08-07: 1 commit2026-08-08: 0 commits
136 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Actively maintained

    Pushed within 48 hours

  • Well documented

    High community health score

  • Permissive license

    MIT

What moondownloader does

Moon Downloader is a Python-based utility engineered for extremely fast bulk file downloading. It combines Playwright for dynamic link extraction and browser flows with aiohttp for asynchronous streaming. Designed to maximize bandwidth utilization, it can reach peak speeds of 253 MB/s on gigabit fiber connections. It features pluggable support for various file-hosting providers like datanodes.to and fuckingfast.co via regex or automated browser interactions.

Power users and data engineers who need to download large volumes of files quickly from web-based hosting services. It requires familiarity with Python environments and basic command-line usage.

  • Asynchronous streaming: leverages aiohttp for high-throughput, non-blocking downloads.
  • Browser automation: uses Playwright to navigate complex extraction flows and captchas.
  • High performance: capable of saturating multi-gigabit connections up to 253 MB/s.
  • Pluggable architecture: supports custom providers via simple regex or dedicated browser scripts.
  • Bulk management: efficiently handles dozens of large files in a single session.

Where teams use it

Mass file retrieval

Download massive datasets or media collections rapidly from supported file hosts.

Automated extraction

Bypass manual link clicking on sites that require browser interaction before downloading.

Bandwidth saturation

Fully utilize multi-gigabit internet connections that standard downloaders fail to max out.

Custom provider scraping

Write custom regex or Playwright scripts to support new, unlisted file hosting platforms.

Getting started: git clone https://github.com/LeyckerS/moondownloader.git

README

main branch

🌙 Moon Downloader

V4

Bulk file downloader — real-Chrome extraction for datanodes.to, pure-HTTP extraction for fuckingfast.co, aiohttp streaming, and a GUI that runs on Edge WebView2.

Supported providers: datanodes.to · fuckingfast.co

Built with Python · Playwright · aiohttp · curl_cffi

Python Playwright WebView2 License CI Stars CodeTriage



Best tested: ~250 MB/s on a 2.5 Gbps fiber — 23.5 GB across 47 files in ~3 minutes

Contributions welcomethe roadmap ranks everything open by size and by whether it needs Windows. Most of it doesn't. Three issues are held for anyone who has never had a pull request merged anywhere.



📸 The interface

The rate is the figure being watched, so it gets the left side and the plot underneath; completed, downloaded and ETA are reference and sit small. Every row shows which host it came from. Rendered from the page's own mock engine — the numbers are synthetic, which is what the demo chip in the corner says.



The cold open: the mark draws itself on, the wordmark resolves out of a blur, and the card cascade is held until it hands over. Under two seconds, and any key or click ends it.



Live transfers Engine log

Filter box and state chips over the list; what is moving gets room and a brighter name, what has finished tightens and recedes.


🔧 Engineering highlights

One browser decision, made once. BrowserGate in moon_extract.py owns the question of whether Chrome is needed at all. A batch of fuckingfast.co links never launches it — not even the Playwright driver's node process. A batch containing one datanodes.to link opens exactly one shared Chrome, on demand, no matter how many extractors are running. The GUI and the CLI import the same gate, so they cannot diverge, and tests/test_no_chrome.py asserts it for both in CI.

The UI pulls, Python never pushes. The page requests snapshot(cursor) about twelve times a second instead of the engine writing into it. Every DOM write stays on the page's own timeline, so a late snapshot costs a dropped frame instead of a stalled interface.

The log ring has a cursor. A bounded 6000-line ring plus a monotonic counter. The page asks for "everything after N"; if it fell behind further than the ring holds, it receives the oldest line still present rather than a silent gap it has no way to detect.

Untrusted input is clamped at one boundary. Everything the page sends passes through Engine.apply_cfg(), which coerces and clamps every number before it can reach a semaphore. There is one place to audit, not one per setting.

Measured, not asserted. 195.7 MB/s across 124 links on 8 download streams — 12.63 GB in 1m23s, 29 files done, 0 failed, no browser window opened. Method and instrumentation in docs/ENGINEERING_NOTES.md; the full design writeup is in docs/ARCHITECTURE.md.


🌗 What V4 is

Both providers broke, and this is the repair. The interface is unchanged since V3.

  • datanodes removed step 1. The share URL used to return a form whose submit carried name="method_free"; it now answers with step 2 directly, tokens already minted, and says "STEP 2 OF 2" on screen. The extractor waited 22s for markup that no longer exists and gave up before reaching the trigger chain that was already working.
  • fuckingfast added Turnstile to POST /f/<id>/go, so the pure-HTTP path returns 403 on its own.

Both are fixed and measured: datanodes 5/5 end to end, fuckingfast 3/3 consecutive at 6.8–9.7s with auto-solve only, both together 4/4, 24 tests passing. The full account — including three defects introduced while building the fuckingfast path and caught in testing — is in CHANGELOG.md.


🖥️ What V3 was

The interface, rebuilt. The engine is untouched — extraction, downloading and the CLI are the same code they were in 2.1, and that is deliberate: this release changes what you look at, not what it does.

before V3 V3
Launch the window simply appeared a cold open under two seconds, skippable, removed from the DOM when done
Mark a 512px PNG an SVG whose crescent and orbit move independently
Headline four equal stat cards one hero band; the rate is a 49px figure, the rest is reference
Settings column five floating cards one surface, hairline sections, Start built in
Transfer rows all identical weight provider chip per row, weight follows state
Finding a file scrolling filter box, state chips, / to focus
Getting links in the Load button drag and drop anywhere, appended not replacing
After a run the list, as it was a summary that can copy every failed link

Keyboard: Ctrl+Enter start or stop · Ctrl+O load a .txt · / filter · ? the shortcut panel · Esc close or clear.

On motion. The GUI animates for everyone; there is no prefers-reduced-motion exception, and that is a stated choice rather than an oversight. Nothing flashes and every loop is slow. If you want a motion preference, open an issue — it is a small change.


⚡ How the two providers differ

The two providers stopped having anything in common, so the app stopped pretending they did.

datanodes.to fuckingfast.co
Extraction real Chrome over CDP + persistent profile plain HTTPS first, the shared Chrome when refused
Browser yes — pages on one window, one identity only for links /go refuses
Captcha Turnstile: auto-solve, manual fallback Turnstile since Aug 2026, same auto-solve
Cost per link seconds ~0.25 s over HTTPS, ~7 s through the browser
Settings Pages, Captcha wait, Chrome path, API key nothing to tune

This changed in V4. fuckingfast put Cloudflare Turnstile in front of POST /f/<id>/go in August 2026: without a cf-turnstile-response token the endpoint answers 403 captcha verification failed, and no TLS fingerprint can mint one. Links it refuses now go through the same Chrome datanodes uses. A batch fuckingfast still serves over plain HTTPS opens no window at all — the HTTP path is tried first, every time, and the browser is only reached for after a refusal.

A batch that contains one datanodes link opens exactly one shared Chrome, on demand, no matter how many extractors are running.

One BrowserGate in moon_extract.py owns that decision, so the GUI and the CLI behave the same way — tests/test_no_chrome.py asserts it for both.


⚖️ Scope and responsible use

Moon Downloader is a client for links you already have. It automates the retrieval step — it does not search, index, or discover content, and it has no catalogue of any kind.

  • What you download is your responsibility. Copyright, licensing and the terms of the hosts you point it at are yours to respect. This tool does not check any of that for you.
  • The datanodes.to path automates a challenge the site presents to visitors. That challenge is an anti-bot control, and automating it may be contrary to that host's terms of service. Decide whether that is acceptable for your use before you run it.
  • The defaults are deliberately conservative — 8 download streams, one shared browser, one identity. They can be raised. Do not use this to hammer a host.
  • No affiliation. This project is not affiliated with, endorsed by, or supported by datanodes.to, fuckingfast.co, Cloudflare, or Microsoft. All trademarks belong to their respective owners.
  • Provided as-is under the MIT licence, without warranty of any kind. See LICENSE.

Rightsholders and providers: if you want a path changed or removed, open an issue or use the contact in SECURITY.md.


🚀 Quick start

One-click (Windows)

  1. Install Python 3.10+ — check ✅ "Add Python to PATH"
  2. Double-click start.bat
  3. Done. The first run installs the dependencies and Chromium.

start.bat starts a loopback HTTP server and opens the GUI in Edge (or Chrome) with --app: a window with no tabs and no address bar. No native GUI dependency, nothing to guess.

Manual

pip install -r requirements.txt
playwright install chromium
python moon_bridge.py            # GUI
python moon_bridge.py --serve    # server only, prints the URL
python moon_cli.py --urls links.txt --output ./downloads   # headless CLI

Just want to look at the interface?

Open web/index.html in Chrome or Edge. It boots in demo mode with a synthetic engine.


🖥️ GUI features

  • Links — per-host colouring while you paste, live count, datanodes / fuckingfast / others split
  • Per-method panels — common knobs in one card, datanodes in its own, fuckingfast declaring it has nothing to tune
  • Live transfers — one row per file: progress ring, state, percentage, instantaneous speed. Active transfers sort above the finished tail
  • Stats — speed (3 s rolling window + sparkline), completed, downloaded, byte-based ETA
  • Pipeline — extraction and download tracked separately, because they run at the same time
  • Log — the engine's own lines, tagged and coloured, capped at 2000
  • English / Italian — switchable at runtime, English by default
  • Settings and pasted links persist in settings.json (atomic write)

⚙️ Settings

Setting Range Default Applies to Description
Extractors 2 – 32 16 both Parallel extraction workers
DL streams 2 – 48 48 both Concurrent download connections
Retries 0 – 5 3 both Extraction retries per URL (network retries are separate)
Pages 1 – 8 8 datanodes Tabs on the shared Chrome window — not separate windows
Captcha 30 – 600 s 30 datanodes Manual Turnstile wait
Chrome path autodetect datanodes chrome.exe to drive over CDP
API key string datanodes Premium key → direct JSON, no browser, no captcha

Fewer DL streams means more bandwidth per file; the pipe is still the ceiling.

Environment variables, the dedicated Chrome profile and the API-key limits are in docs/CONFIGURATION.md.


🏗️ Architecture

start.bat
   └── moon_bridge.py          loopback HTTP + token, launches Edge --app, OS dialogs
         ├── web/              index.html · styles.css · app.js   (the GUI)
         └── moon_engine.py    headless engine: start/stop/snapshot
               └── moon_extract.py   datanodes (Chrome+CDP) · fuckingfast (curl_cffi)
                                     BrowserGate: the launch, deferred

moon_cli.py       headless CLI, same engine, same extraction layer

Both front-ends import the same moon_extract, so extraction, the Chrome lifecycle and the launch decision exist once.

Pull model. The page asks for snapshot(cursor) ~12 times per second instead of Python pushing at it: every DOM write stays on the page's own timeline and a late snapshot is a dropped frame, not a stall.

The log has a cursor. A bounded 6000-line ring plus a monotonic counter; the page asks for "everything after N" and, if it fell behind further than the ring, gets the oldest line still held rather than a gap it cannot detect.

Rows read live FileRecords. download_file publishes rec.done_bytes and rec.live_mbs about four times a second on its own window, kept separate from the stall detector's 60 s history.

Untrusted input. Everything the page sends passes through Engine.apply_cfg(), which coerces and clamps every number before it reaches a semaphore.

Full write-up: docs/ARCHITECTURE.md.


📚 Documentation

Document What's in it
Quick start install, first run, what each setting does
CLI every flag, exit codes, scripting examples
Configuration every setting, every environment variable, the Chrome profile
Providers how each host is extracted, and how to add another
Architecture how the engine is built, feature by feature
Engineering notes the measurements behind the design decisions
Troubleshooting 403s, Turnstile failures, CDP conflicts, stalls
FAQ why curl_cffi is mandatory, what --extractors means
Contributing architecture, what counts as a contribution, the verification commands
Changelog every version since 14.0

🤝 Contributing

→ The roadmap — everything open, ranked by how hard it is and whether it needs Windows.

Most of the open work does not require a Windows machine. Documentation, CI, tests and dependency work all run on Linux and macOS, and the test suite stubs Chrome and the network at the moon_extract boundary, so it runs anywhere. Each issue says up front which it is.

good first issue scoped small, with the files to touch and the acceptance criteria already written out
help wanted everything open to outside contributors, including the larger items

Claim one by commenting on it in your own words — no need to ask permission first. What gets a pull request merged, and what gets one sent back, is written out in CONTRIBUTING.md.

Everyone who has shipped a change is named in AUTHORS.md with what they did.


🔒 The local server

Binds 127.0.0.1 only, on a kernel-chosen port, and every /api/ call must carry the token minted at startup — without it, 403. The API starts downloads and reads paths, so this is not a formality. The process exits by itself after 12 s with no requests: the page polls every 80 ms, so "no requests" means "window closed".


📂 Output files

File Description
moontech_*.log Human-readable performance report
moontech_*.json Per-file metrics (machine-readable)
output_links.txt Extracted direct links (Links-only mode)
failed_links.txt URLs that failed every retry
settings.json GUI settings, pasted links, language

📁 Optional files

Place next to the scripts:

File Purpose
proxies.txt Proxy list — ip:port:user:pass or http://user:pass@ip:port. Downloads only — see below

Proxies wrap the download session and nothing else. Link extraction always connects directly, from your own address: the shared Chrome instance datanodes needs for its Turnstile challenge, and the curl_cffi session fuckingfast uses, are never routed through the pool. An unreachable proxy list therefore stalls downloads while pages keep opening normally — that is the design, not a broken proxy.


✅ Verification

pytest tests/ -q       # engine + CLI: no browser for fuckingfast, exactly one for datanodes
python integration_http.py     # browser → loopback HTTP → engine (the path start.bat takes)
python integration_web.py      # pywebview path
python render_gui.py out/           # renders at 2554x1400 and 1440x900 + overflow audit
python moon_engine.py          # headless engine: prints a snapshot and exits

tests/test_no_chrome.py stubs Chrome and the network at the moon_extract boundary, so it needs no browser, no display and no Playwright install — it runs in CI on every push (.github/workflows/lint.yml).


📋 Requirements

  • OS: Windows 10 / 11 (the GUI needs Edge or Chrome — both ship with Chromium)
  • Python: 3.10+
  • Disk: ~150 MB for the Playwright Chromium (datanodes only)
  • Packages: aiohttp, playwright, curl_cffi; pywebview optional

Made with 🖤 and cold coffee

View on GitHub

Recent activity

commits and pull requests

Recent open issues

view all

Releases and announcements

6 total
  1. Both providers changed how they hand out links and every download stopped. This release is the repair. Both providers changed the way they hand out links and every download stopped. This release is the repair. The GUI is unchanged since 3.0. ### Fixed - **datanodes.to removed step 1.** The share URL used to return a form whose submit carried `name="method_free" value="Free Download >>"`; the page now answers with step 2 directly, tokens already minted, and says so on screen ("STEP 2 OF 2"). The extractor waited 22s for `#downloadReveal` and `#method_free`, timed out and gave up before ever reaching the trigger chain that was already working — the failing runs are 22.5s per link against a 22.0s budget. It now waits for whichever shape the server sends and branches on the page rather than on an assumption; the old two-step path is still taken when the old markup arrives. Skipping the obsolete POST also matters on its own: a second `POST /download` re-runs SecSave and invalidates the token step 2 is holding, so the run was destroying its own session. - **The wait now covers the operator's reaction time.** On the new shape the controls only app

  2. The interface was rebuilt. The engine was not: extraction, downloading and the CLI are the same code they were in 2.1. ### Added - **A cold open.** The mark draws itself on as a vector, the wordmark resolves out of a blur, a raking beam crosses the field, and the card cascade is *held* — not merely delayed — until it hands over. Under two seconds, ended instantly by any key or click, and removed from the DOM when it finishes. - **The mark is an SVG and it moves.** Crescent and orbit are separate shapes, so the ring tilts continuously: the minor radius is what animates, which reads as a rotation in depth. The same drawing now serves the topbar, the cold open and the empty state, and the PNG survives only as the favicon. - **A hero band replaces the four stat cards.** The rate is a 49px figure with the plot spanning the card underneath; completed, downloaded and ETA are reference and sit small. - **The settings column is one surface.** Five floating cards became four sections of a single panel divided by a hairline, with Start built into the same object. - **Transfer rows carry their provider**, read off the source URL, and their weight follows thei

  3. **Nine people outside the project shipped changes in this release.** Two days ago that number was zero. Everything below came from someone who read the code, disagreed with it, and sent a patch. ## Fixed **The elapsed-time clock kept counting after a run finished.** `snapshot()` computed the elapsed time from `now - t0` unconditionally, so the display carried on ticking after the last file landed. The engine now records the end time at the single point where a run terminates — covering both a normal finish and a crash — and freezes the value there. *(#73, @RubenSanosh)* **A discarded partial download said nothing.** When a server answers `200` instead of `206` it is refusing to resume, and the code correctly restarted from zero — silently. A multi-gigabyte transfer appeared to begin again for no reason. The restart is now recorded and appears in `moontech_*.log`, with a regression test that fakes the response without touching the network. *(#98, @XEDAB)* **`--proxies` failed silently.** A misspelled path, or a file the parser did not recognise, loaded zero proxies and printed nothing — so a run started specifically to avoid direct connections made them anyway, with no indicatio

  4. No code changed in this release. This is documentation and contributor onboarding. ## What's new - **Scope and responsible use** section in the README — what the tool does and does not do, who owns the copyright question, and a plain statement that the datanodes path automates an anti-bot challenge - **Engineering highlights** moved above the provider table — `BrowserGate`, the pull-model UI, the cursored log ring, the single clamping boundary - CI and stars badges - `CONTRIBUTING.md` now has a **Where to start** section - Screenshots redacted ## Contributors This release exists because of work from outside the maintainer. Everyone who has contributed is named in [AUTHORS.md](https://github.com/LeyckerS/moondownloader/blob/main/AUTHORS.md), with what they did. Four contributions landed on top of this documentation work: - **@pollychen-lab** moved the download engine into a shared `moon_download.py` (#41) and moved the no-Chrome regression into a pytest suite (#38). `download_file`, `Telemetry` and `ProxyPool` existed as two copies before this, so every fix in the download path was a two-file change - **@kushin25** wrote `docs/CLI.md`, the

  5. # V2 — datanodes rebuilt, fuckingfast rebuilt, new GUI Everything since **V1** (tag `v14.1`). Both providers changed how they hand out links, so both extraction methods were rewritten from scratch, the interface left tkinter, and the settings panel was split per method — because the two methods no longer share one. <img src="https://raw.githubusercontent.com/LeyckerS/moondownloader/main/docs/gui_speed.png" width="920"/> *195.7 MB/s · 124 fuckingfast links · 8 DL streams · 12.63 GB in 1m23s · 29 done, 0 failed — and not one browser window, because no datanodes link was in the batch.* --- ## 🔧 datanodes.to — rebuilt on real Chrome The old flow stopped working end to end. It now runs on a **real Chrome/Edge** the app spawns itself with `--remote-debugging-port` and attaches to over CDP, on a **persistent profile**. - **Cloudflare Turnstile is handled** — auto-solve, with a manual fallback and a configurable wait. The old blocklist contained `challenges.cloudflare`, so the widget could never even load, and it blocked stylesheets, which collapses every `getBoundingClientRect()` to 0×0 and left the button finder blind. - **Not Playwright's Chromium an

Commits per week

last 52 weeks
780Week of 2025-08-10: 0 commitsWeek of 2025-08-17: 0 commitsWeek of 2025-08-24: 0 commitsWeek of 2025-08-31: 0 commitsWeek of 2025-09-07: 0 commitsWeek of 2025-09-14: 0 commitsWeek of 2025-09-21: 0 commitsWeek of 2025-09-28: 0 commitsWeek of 2025-10-05: 0 commitsWeek of 2025-10-12: 0 commitsWeek of 2025-10-19: 0 commitsWeek of 2025-10-26: 0 commitsWeek of 2025-11-02: 0 commitsWeek of 2025-11-09: 0 commitsWeek of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 0 commitsWeek of 2026-02-01: 0 commitsWeek of 2026-02-08: 0 commitsWeek of 2026-02-15: 0 commitsWeek of 2026-02-22: 0 commitsWeek of 2026-03-01: 0 commitsWeek of 2026-03-08: 0 commitsWeek of 2026-03-15: 0 commitsWeek of 2026-03-22: 0 commitsWeek of 2026-03-29: 0 commitsWeek of 2026-04-05: 0 commitsWeek of 2026-04-12: 0 commitsWeek of 2026-04-19: 0 commitsWeek of 2026-04-26: 0 commitsWeek of 2026-05-03: 0 commitsWeek of 2026-05-10: 0 commitsWeek of 2026-05-17: 5 commitsWeek of 2026-05-24: 0 commitsWeek of 2026-05-31: 0 commitsWeek of 2026-06-07: 0 commitsWeek of 2026-06-14: 0 commitsWeek of 2026-06-21: 0 commitsWeek of 2026-06-28: 0 commitsWeek of 2026-07-05: 0 commitsWeek of 2026-07-12: 24 commitsWeek of 2026-07-19: 1 commitsWeek of 2026-07-26: 78 commitsWeek of 2026-08-02: 28 commitsAug 10, 2025Aug 2, 2026
136 commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 1 commitsSun 1:00 — 0 commitsSun 2:00 — 3 commitsSun 3:00 — 4 commitsSun 4:00 — 0 commitsSun 5:00 — 0 commitsSun 6:00 — 0 commitsSun 7:00 — 0 commitsSun 8:00 — 0 commitsSun 9:00 — 0 commitsSun 10:00 — 0 commitsSun 11:00 — 0 commitsSun 12:00 — 0 commitsSun 13:00 — 0 commitsSun 14:00 — 2 commitsSun 15:00 — 0 commitsSun 16:00 — 0 commitsSun 17:00 — 0 commitsSun 18:00 — 0 commitsSun 19:00 — 0 commitsSun 20:00 — 1 commitsSun 21:00 — 0 commitsSun 22:00 — 0 commitsSun 23:00 — 1 commitsMon 0:00 — 4 commitsMon 1:00 — 2 commitsMon 2:00 — 0 commitsMon 3:00 — 0 commitsMon 4:00 — 1 commitsMon 5:00 — 0 commitsMon 6:00 — 0 commitsMon 7:00 — 0 commitsMon 8:00 — 0 commitsMon 9:00 — 0 commitsMon 10:00 — 0 commitsMon 11:00 — 0 commitsMon 12:00 — 2 commitsMon 13:00 — 0 commitsMon 14:00 — 0 commitsMon 15:00 — 0 commitsMon 16:00 — 0 commitsMon 17:00 — 0 commitsMon 18:00 — 0 commitsMon 19:00 — 1 commitsMon 20:00 — 0 commitsMon 21:00 — 0 commitsMon 22:00 — 0 commitsMon 23:00 — 0 commitsTue 0:00 — 0 commitsTue 1:00 — 0 commitsTue 2:00 — 0 commitsTue 3:00 — 0 commitsTue 4:00 — 0 commitsTue 5:00 — 0 commitsTue 6:00 — 0 commitsTue 7:00 — 0 commitsTue 8:00 — 0 commitsTue 9:00 — 0 commitsTue 10:00 — 0 commitsTue 11:00 — 0 commitsTue 12:00 — 0 commitsTue 13:00 — 0 commitsTue 14:00 — 0 commitsTue 15:00 — 0 commitsTue 16:00 — 0 commitsTue 17:00 — 0 commitsTue 18:00 — 0 commitsTue 19:00 — 0 commitsTue 20:00 — 0 commitsTue 21:00 — 0 commitsTue 22:00 — 0 commitsTue 23:00 — 2 commitsWed 0:00 — 2 commitsWed 1:00 — 2 commitsWed 2:00 — 5 commitsWed 3:00 — 6 commitsWed 4:00 — 1 commitsWed 5:00 — 1 commitsWed 6:00 — 1 commitsWed 7:00 — 2 commitsWed 8:00 — 1 commitsWed 9:00 — 0 commitsWed 10:00 — 1 commitsWed 11:00 — 0 commitsWed 12:00 — 0 commitsWed 13:00 — 0 commitsWed 14:00 — 0 commitsWed 15:00 — 1 commitsWed 16:00 — 1 commitsWed 17:00 — 1 commitsWed 18:00 — 0 commitsWed 19:00 — 0 commitsWed 20:00 — 1 commitsWed 21:00 — 0 commitsWed 22:00 — 3 commitsWed 23:00 — 0 commitsThu 0:00 — 0 commitsThu 1:00 — 0 commitsThu 2:00 — 0 commitsThu 3:00 — 0 commitsThu 4:00 — 0 commitsThu 5:00 — 0 commitsThu 6:00 — 0 commitsThu 7:00 — 0 commitsThu 8:00 — 0 commitsThu 9:00 — 0 commitsThu 10:00 — 0 commitsThu 11:00 — 0 commitsThu 12:00 — 0 commitsThu 13:00 — 0 commitsThu 14:00 — 0 commitsThu 15:00 — 0 commitsThu 16:00 — 0 commitsThu 17:00 — 10 commitsThu 18:00 — 12 commitsThu 19:00 — 9 commitsThu 20:00 — 4 commitsThu 21:00 — 0 commitsThu 22:00 — 1 commitsThu 23:00 — 0 commitsFri 0:00 — 4 commitsFri 1:00 — 1 commitsFri 2:00 — 4 commitsFri 3:00 — 1 commitsFri 4:00 — 0 commitsFri 5:00 — 0 commitsFri 6:00 — 0 commitsFri 7:00 — 0 commitsFri 8:00 — 0 commitsFri 9:00 — 1 commitsFri 10:00 — 0 commitsFri 11:00 — 0 commitsFri 12:00 — 0 commitsFri 13:00 — 1 commitsFri 14:00 — 1 commitsFri 15:00 — 0 commitsFri 16:00 — 0 commitsFri 17:00 — 0 commitsFri 18:00 — 0 commitsFri 19:00 — 7 commitsFri 20:00 — 2 commitsFri 21:00 — 0 commitsFri 22:00 — 2 commitsFri 23:00 — 4 commitsSat 0:00 — 0 commitsSat 1:00 — 0 commitsSat 2:00 — 1 commitsSat 3:00 — 0 commitsSat 4:00 — 3 commitsSat 5:00 — 1 commitsSat 6:00 — 1 commitsSat 7:00 — 1 commitsSat 8:00 — 3 commitsSat 9:00 — 0 commitsSat 10:00 — 1 commitsSat 11:00 — 3 commitsSat 12:00 — 3 commitsSat 13:00 — 1 commitsSat 14:00 — 1 commitsSat 15:00 — 0 commitsSat 16:00 — 0 commitsSat 17:00 — 0 commitsSat 18:00 — 1 commitsSat 19:00 — 0 commitsSat 20:00 — 0 commitsSat 21:00 — 1 commitsSat 22:00 — 0 commitsSat 23:00 — 0 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.

Who is committing

last 52 weeks
Maintainer commits111 (66%)
Community commits58 (34%)

169 commits in total over the last year.

DateListRankStars gained
Jul 8, 2026daily#21+4