Skip to content

molo

Molo is a local interface for yt-dlp. It runs on the computer that has the disk and the good connection, and your phone opens a window onto the same queue. You paste a link on whichever device you are holding, and the file lands on the machine that should be holding it.

Good yt-dlp front ends already exist for desktop, Android and servers. None of them share one queue between devices, and that is the gap Molo fills.

The default preset takes the best video and audio the site offers and merges them into an MP4 that plays anywhere, without re-encoding. There is a second preset for the highest bitrate available, and audio-only presets for the sound.

It recovers on its own. A 403 halfway through a large file usually means the signed URL expired. Molo rotates the YouTube client, backs off, and resumes from the partial file instead of starting over.

Copy a link anywhere on the computer and Molo offers to take it. It offers rather than downloading, because downloading on every copy is a way to fill a disk by accident.

Watchers follow a channel or a playlist and queue whatever is new. The first pass only records what is already there, so adding a channel with 800 videos does not cost you 800 videos.

The queue lives in SQLite and survives a restart. Anything interrupted picks up from its partial file when you reopen the app.

There is no circumvention code in the repository. No CDM, no keys, no decryption. On an encrypted stream Molo fails, and no setting adds the capability, because it is absent from the code.

The reasoning is in LEGAL.md with the statutes cited. The short version: reading a stream served in the clear gets past no protection, while breaking encryption is unlawful in itself under Article 6 of EU Directive 2001/29/EC and DMCA section 1201.

Most YouTube failures are session problems rather than DRM. Age restrictions, channel memberships, private videos and the bot check are fixed with your own browser cookies: Settings, “Use cookies from”, pick the browser you are signed in with. When a download fails, Molo says which kind of failure it was and whether anything would help. Genuine DRM is the one case where the answer is no.

ffmpeg is not bundled, for the licensing reasons set out in NOTICE.md. Without it Molo runs, limited to formats that already carry both streams, usually 720p.

Builds are on the Releases page. Nothing is signed with a paid certificate, so the first launch takes one extra step: on macOS right-click the app and choose Open, on Windows SmartScreen wants “More info” then “Run anyway”, on Android you allow installs from unknown sources.

From source:

Terminal window
git clone https://github.com/nerln/molo && cd molo
python3 -m venv .venv && .venv/bin/pip install -e ".[desktop]"
.venv/bin/python -m molo
Terminal window
brew install ffmpeg

Building the packages yourself:

Terminal window
./packaging/build_macos.sh # .app and .dmg
pyinstaller packaging/molo.spec # Windows
cd packaging/android && gradle assembleDebug

Turn on “Reachable from your phone” in settings, copy the link, and open it on the phone. Add it to the home screen and it behaves like an app, because it is a progressive web app. There is also an APK in the releases.

The server listens on 127.0.0.1 until you turn that on. After that, every request from outside the local machine has to carry a token generated for your installation. Nothing is exposed to the internet.

To run Molo on the phone alone, scripts/termux-install.sh sets it up under Termux, where yt-dlp runs natively.

molo/policy.py the download policy; runs before everything else
molo/engine.py yt-dlp as a library: presets, recovery, ffmpeg discovery
molo/jobs.py the queue: persistence, concurrency, events
molo/watch.py channel and playlist watchers
molo/server.py HTTP and WebSocket API, token auth for network access
molo/web/ the interface: hand-written HTML, CSS and JS, no build step

One Python process. The desktop window is the system WebView pointed at localhost instead of a bundled Chromium, which is why the build is 44 MB rather than 200. The interface has no build step on purpose: open app.js, change a line, reload.

Molo replaces its own yt-dlp, in a packaged build as well as from source, because an engine more than a few months old is the most common reason a download stops working. It fetches the py3-none-any wheel from PyPI, checks it against the sha256 PyPI publishes, unpacks it into Molo’s data folder and puts that folder first on sys.path. The engine already running stays loaded; the new one is used at the next launch.

The sha256 catches a truncated or corrupted wheel and is the same protection pip gives you over HTTPS. It is a checksum, so it would not stop a compromised release.

The ban on circumvention code is enforced by a test. A patch that crosses that line fails CI instead of reaching anyone’s inbox.

CI builds macOS, Windows and Android on every push, and attaches them to a release on a v* tag.

Builds are unsigned. packaging/sign_and_notarize.sh covers the whole Developer ID flow, but it has not been run end to end because the machine has no certificate.

Android suspends background processes aggressively. Under Termux, keep the app in the foreground for a long download or hold a wakelock.

The Windows executable and the Android APK compile in CI, and nobody has run them on real hardware yet. Only the macOS build has been launched and used for a full download.