Download videos and extract audio from YouTube, Facebook, Twitter/X, TikTok, Instagram, and 1800+ more.
Auto-detects media on any page. Floating download pills. Zero configuration.
3-tier failover · hidden background server · real-time progress · 7-layer Facebook extraction
🚀 Quick Install
✨ Features
🌍 Sites
🏗️ Architecture
🔧 Troubleshoot
One command. Everything configured. No manual setup.
irm https://raw.githubusercontent.com/SysAdminDoc/MediaDL/refs/heads/main/Install-MediaDL.ps1| iexRun in an elevated PowerShell window. The installer downloads yt-dlp, ffmpeg, registers protocol handlers, deploys the background download server, creates a Scheduled Task for auto-start, and installs the userscript.
MediaDL has optimized detection for these platforms, including Instagram Reels, Stories, and Highlights, plus generic <video> detection for any site yt-dlp supports:
| Platform | Video | Audio | Platform | Video | Audio | ||
|---|---|---|---|---|---|---|---|
| YouTube | ✅ | ✅ | 🔵 | Vimeo | ✅ | ✅ | |
| 📘 | ✅ | ✅ | 🔊 | SoundCloud | — | ✅ | |
| 🐦 | Twitter / X | ✅ | ✅ | 🎸 | Bandcamp | — | ✅ |
| 🎵 | TikTok | ✅ | ✅ | 📺 | Dailymotion | ✅ | ✅ |
| 📷 | ✅ | ✅ | Bilibili | ✅ | ✅ | ||
| 🟣 | Twitch | ✅ | ✅ | 🏴 | Rumble / Odysee | ✅ | ✅ |
| 🔴 | ✅ | ✅ | 🍿 | Crunchyroll / Nebula | ✅ | ✅ |
+ Kick, Floatplane, Streamable, Imgur, Arte, Tagesschau, and 1800+ more via yt-dlp
Scans every page for video and audio elements. Attaches floating download pills automatically — no clicking required. TikTok uses server-rendered play URLs first for watermark-free MP4, with X Spaces discovers live Live YouTube and Twitch players expose a HTTP server → protocol handler → GM_download. If one method fails, the next activates seamlessly. Video pills include a click-or-hold quality picker for Best, 720p, 1080p, and 4K. The server also applies editable per-host presets for format, quality, codec, and audio fallback. Playlist and channel URLs are handled as collection downloads with one queue item, playlist-aware folders, the configured rate limit, and the download archive for resume-safe retries. API clients can force this behavior with | Lightweight HTTP server on Queue requests and interrupted states persist in a SQLite WAL journal at Server token negotiated automatically via | In-page progress bars with download speed and ETA. Appear at the bottom-right of the browser window. MutationObserver + URL change detection handles single-page navigation on YouTube, Facebook, Twitter, and all modern SPAs. |
Title and thumbnail fetched in background jobs. The progress popup renders instantly and never freezes. Content identity lock (site + video ID + channel when available) prevents equivalent page/CDN URLs from being queued twice; a scoped URL hash remains the fallback for sites without stable IDs. Animated bar with eased interpolation. Reads only last 4KB via | Cancelling kills both the PowerShell wrapper and child YouTube via direct API. All other sites via Enable chapter splitting in the server settings to emit one file per YouTube Enable the opt-in post-processing switches to extract audio after a video completes, tag it through MusicBrainz, and move it to a collision-safe Music folder filename. Configure host presets in the server settings JSON. The built-in examples request 1080p AV1 MP4 for YouTube, best MP4 for X/Twitter, and FLAC with an MP3 retry for SoundCloud. Subtitle control can optionally download manual and automatic captions as SRT and mux them into an MKV output. Hardware transcode is opt-in in the same settings panel: choose NVENC or QSV to re-encode completed video with FFmpeg while copying audio, subtitle, and data streams unchanged. Click "Complete!" to open Explorer with the downloaded file pre-selected. | DWM rounded corners via The background server checks yt-dlp and ffmpeg once per day, downloads only the expected GitHub release assets, and replaces them only after their SHA-256 release digests match. Missing digests or unexpected download origins fail closed and leave the installed tools untouched. Completion toasts are opt-in and fail safely when WinRT toast registration is unavailable. 4-layer exception handling: closing flag, nuclear try/catch, control guards, and global |
The extension/ directory contains unsigned MV3 packages for Chrome and Firefox, plus an Edge package with a native side-panel queue viewer. Run node extension/build.mjs after changing MediaDL.user.js to regenerate the shared content/background adapters. Firefox AMO submission/signing is intentionally not performed; load the unsigned package as a development extension.
MediaDL.mobile.user.js is the lightweight Kiwi Browser/Orion variant. It contains no Windows paths, local protocol, or PowerShell assumptions and downloads direct MP4/audio URLs; page-managed blobs and HLS playlists are reported as desktop-only limitations.
┌─ Browser ───────────────────────┐ ┌─ Windows ──────────────────────────┐
│ │ │ │
│ MediaDL Userscript │ │ ┌─ Tier 1: Download Server ────┐ │
│ ├─ Auto-detect <video> │HTTP│ │ ytdl-server.ps1 │ │
│ ├─ Floating download pills │◀──▶│ │ 127.0.0.1:9751 │ │
│ ├─ Facebook 7-layer extraction │ │ │ ├─ Concurrent downloads (3x) │ │
│ ├─ In-page progress toasts │ │ │ ├─ Real-time progress │ │
│ └─ SPA navigation handling │ │ │ └─ Auto-start on login │ │
│ │ │ └─────────────────────────────── │
│ ┌──────────────┐ ytdl:// │ │
│ │ Click pill │────────▶│ ┌─ Tier 2: Protocol Handler ────┐ │
│ └──────────────┘ │ │ │ ytdl-handler.ps1 │ │
│ │ │ │ ├─ Progress popup │ │
│ ┌──────────────┐ GM_dl│ │ │ ├─ Thumbnail + title │ │
│ │ CDN direct │───────▶│ │ └─ Auto-retry (3x) │ │
│ └──────────────┘ │ │ └────────────────────────────── │
└─────────────────────────────────┘ └────────────────────────────────────┘
│
Scheduled Task: MediaDL-Server
(auto-start on login, hidden)
| Tier | Method | How | Progress | Activates When |
|---|---|---|---|---|
| 1 | HTTP Server | GM_xmlhttpRequest to 127.0.0.1:9751 | ✅ Real-time polling | Server is running (default) |
| 2 | Protocol Handler | ytdl:// URL triggers ytdl-handler.ps1 | ✅ Popup window | Server is offline |
| 3 | Browser Direct | GM_download for CDN URLs | ❌ None | Both 1 & 2 fail, URL is direct CDN |
Facebook aggressively obfuscates video URLs. MediaDL defeats this with seven extraction layers tried in priority order:
| # | Layer | Technique |
|---|---|---|
| 1 | XHR/Fetch Intercept | Hooks window.fetch and XMLHttpRequest at document-start to capture playable_url_quality_hd from GraphQL responses |
| 2 | Performance Resource Timing | Scans performance.getEntriesByType('resource') for fbcdn.net video entries, sorted by transfer size |
| 3 | React Fiber Tree Walk | Traverses __reactFiber from the <video> element upward through memoizedProps searching for browser_native_hd_url |
| 4 | Embedded JSON Scrape | Searches <script type="application/json"> blocks for HD video URL patterns |
| 5 | DOM Permalink Walk | Climbs the DOM tree from the video element to find a /videos/, /watch/, or /reel/ link |
| 6 | Page URL Candidate | Keeps the current Facebook video/reel/story URL as a fallback candidate |
| 7 | MQTT / GraphQL WebSocket | Hooks page-context WebSocket frames and extracts HD URLs from text, JSON, Blob, and ArrayBuffer payloads |
The download server runs on 127.0.0.1:9751 (localhost only, not exposed to network).
| Method | Endpoint | Auth | Response |
|---|---|---|---|
GET | /health | — | Server status. Returns auth token when X-MDL-Client: MediaDL header is present |
GET | /ui | — | Local queue viewer with drag handles and download controls |
GET | /plugins | 🔐 | Loaded extractor plugin names, versions, and directory |
POST | /download | 🔐 | Start download. Body: {url, title, audioOnly, referer, format, quality, channelMode}. Returns {id} |
GET | /status/:id | 🔐 | {status, progress, speed, eta, filename} |
GET | /queue | 🔐 | Priority-ordered array of downloads with status and site |
POST | /queue/reorder | 🔐 | Persist priority order. Body: {ids: [...]} |
POST | /pause/:id | 🔐 | Suspend a download process tree |
POST | /resume/:id | 🔐 | Resume a paused download |
DELETE | /cancel/:id | 🔐 | Cancel and clean up a download |
GET | /shutdown | 🔐 | Gracefully stop the server |
🔐 = Requires X-Auth-Token header (auto-negotiated by the userscript)
The same local control surface is available on the \\.\pipe\MediaDL named pipe to avoid TCP port collisions. Send one newline-delimited JSON request such as {"method":"GET","path":"/health","headers":{"X-MDL-Client":"MediaDL"}}; the response is {"status":200,"body":...}. Pipe requests support health, download, status, queue, pause, resume, cancel, and shutdown operations and use the same auth token.
Drop a trusted .ps1 file in %LOCALAPPDATA%\MediaDL\plugins. It registers a handler with Register-MediaDLPlugin; CanHandle receives the original page URL and Extract returns an absolute media URL plus optional metadata. Plugins are loaded at server start, limited to 256 KiB each, and can be inspected through the authenticated /plugins endpoint.
Register-MediaDLPlugin-Name 'ExampleCdn'-Version '1.0'`-CanHandle { param($url) $url-match'^https://example\.com/video/' } `-Extract {
param($url,$context)
@{ url=$url-replace'/video/','/media/'; title=$context.title }
}The server passes the resolved URL through the normal queue, duplicate, format, subtitle, post-processing, and notification paths. Plugin scripts are local code and therefore run with the server user's permissions.
| File | Description |
|---|---|
Install-MediaDL.ps1 | PowerShell WPF installer wizard (dark-themed GUI) |
MediaDL.user.js | Userscript — auto-detect on all websites |
ytdl-server.ps1 | Hidden HTTP download server (127.0.0.1:9751) |
ytdl-handler.ps1 | Protocol handler with async progress popup |
ytdl-server-launcher.vbs | Windowless server launcher |
ytdl-launcher.vbs | Silent handler launcher |
config.json | Paths, server port/token, preferences |
%LOCALAPPDATA%\MediaDL\plugins\*.ps1 | Optional trusted extractor plugins |
# Open PowerShell as Administrator, then run:
irm https://raw.githubusercontent.com/SysAdminDoc/MediaDL/refs/heads/main/Install-MediaDL.ps1| iexThe installer creates a MediaDL-Server Scheduled Task that auto-starts the background server on login. No console windows will appear.
- Install Tampermonkey or Violentmonkey
- Install the userscript:
- Install yt-dlp and ffmpeg
- Set up protocol handlers manually (see below)
Protocol Handler Registry Entry
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\ytdl]
@="URL:YTDL Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\ytdl\shell\open\command]
@="wscript.exe \"C:\\Path\\To\\ytdl-launcher.vbs\" \"%1\""The installer handles everything:
| If installing without the wizard:
|
| Issue | Solution |
|---|---|
| Download pills don't appear | Refresh the page. Verify userscript is enabled with @match *://*/* |
| Server not running | Check Task Scheduler for MediaDL-Server, or run ytdl-server-launcher.vbs manually |
| Facebook downloads fail | Click the video to trigger playback first (populates CDN URLs for extraction) |
| Download fails silently | Verify yt-dlp and ffmpeg are installed and paths are correct in config.json |
| JIT debugging dialog | Update to the latest handler (4-layer crash prevention) |
| Duplicate downloads | Update to the latest handler (content identity lock with URL fallback) |
Run the installer again — it auto-removes the previous installation before reinstalling.
For full manual removal:
- Task Scheduler → Delete the
MediaDL-Servertask - Delete
%LOCALAPPDATA%\MediaDL - Registry → Remove
HKCU:\Software\Classes\ytdl - Browser → Remove the userscript from Tampermonkey
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License — see the LICENSE file for details.