Download manager with native HLS/DASH streaming and browser extension
Download • Screenshots • What is this • Why fork • Features • Quick Start • GUI • Browser Extension • HTTP API • Architecture • Credits
FluxGet is a vibe-coded download manager built in Go with a browser extension that captures video streams from YouTube, Vimeo, Twitch, and 30+ platforms. It handles HLS, DASH, and direct video downloads natively — no third-party download helper required for most sites.
It is forked from Surge, a fast multi-connection TUI downloader. FluxGet keeps the entire Surge engine and adds a full video/stream interception layer on top.
Vibe development — this project was built through conversation with Claude (Anthropic). The goal was to build a download manager that intercepts browser video streams the same way professional download managers do, using open standards and public browser APIs.
The project is working end-to-end. Here is a summary of what's fully functional:
- Native Linux desktop app wrapping the full FluxGet engine
- Dark-themed dashboard with live speed graph (60s rolling network history)
- Live queue: active, paused, queued, errored, and completed tabs with per-item actions
- Stats bar: total throughput, active count, queue ETA, active connections heatmap
- Per-item controls: pause, resume, retry, open file, open folder, info panel, remove
- All operations (pause/resume/delete) persist correctly across restarts
- Runs the HTTP engine on port 1700 automatically on startup
- Resolved a Go 1.25 + WebKit2GTK/JSC signal-handler incompatibility that caused crashes
when JavaScript executed — fixed by polling and patching
SA_ONSTACKon JSC's signal handlers
- Floating FluxGet button injects on every
<video>element in the page - Clicking it opens a quality picker: 1080p HD / 720p HD / 480p / 360p / Audio only / Subtitles only
- Subtitles-only download from the picker, the popup ("💬 Subs" on HLS streams), and the right-click menu
- Extension toolbar popup lists all streams detected on the current tab
- Shows format badge (HLS / DASH / MP4 / WebM), quality dropdown, Download button, Copy URL
- Download All batch download from popup
- YouTube adaptive streams captured directly from
ytInitialPlayerResponse— no yt-dlp - HLS/DASH manifest URLs captured by patching
fetch()andXHRin MAIN world - JWPlayer and Video.js setups hooked before player initialization
- Connection status indicator in the popup (green Connected / red Not running)
- Native HLS parser: master + media playlists, best-variant selection, AES-128-CBC decryption
- Fragmented-MP4 / CMAF (av1, h265, …): parses
#EXT-X-MAP, prepends the init segment, and remuxes — handles streams the concat demuxer can't - Subtitle download: reads
#EXT-X-MEDIA:TYPE=SUBTITLESrenditions, downloads every language, embeds them into the video (.mkv) and writes.srtsidecars — or grabs subtitles only (no video) - Native DASH parser: MPD XML,
$Number$/$Time$/$Bandwidth$template expansion - YouTube adaptive: picks best video + audio pair, muxes with ffmpeg
-c copy - yt-dlp fallback for Vimeo, Twitch, TikTok, and 30+ known platforms
- Multi-connection parallel chunk engine (up to 32 workers) from Surge, fully intact
- Tunable HLS/DASH segment concurrency from the GUI (1–32) to dodge CDN rate-limits
- Full download history persisted in SQLite
- Subtitles — HLS subtitle renditions (
#EXT-X-MEDIA:TYPE=SUBTITLES) are now downloaded for every language, embedded into the video as soft tracks (.mkv) and saved as.srtsidecars. A new "Download subtitles only" action (floating picker, popup, right-click menu) grabs just the captions. Toggle in Settings → HLS/DASH engine → Download subtitles. - Fragmented-MP4 / CMAF — av1/h265 streams that use a separate
#EXT-X-MAPinit segment now download correctly (previously failed withcould not find corresponding trex). - Fake-image segment prefix — segments that some CDNs disguise with a leading PNG/JPEG header are transparently stripped.
- GUI fixes — the dashboard no longer gets stuck "offline" when idle (SSE heartbeat); the auto-start no longer spawns a rival background daemon that downloaded with no UI; single-instance lock prevents duplicate tray icons; open-file / open-folder work from the GUI again.
- Richer tray — shows active downloads with progress and per-item pause / resume / cancel.
- Tunable HLS/DASH connections (1–32) and a build version + commit shown in the titlebar.
Surge is an excellent multi-connection file downloader with a beautiful TUI. We forked it because our additions are too different in scope for a PR:
- Surge is a file downloader with a TUI — keyboard-driven, terminal-native
- FluxGet adds a video stream interception layer, browser extension, HLS/DASH native engine, and an HTTP API designed to be called from a browser extension
The Surge engine (parallel chunks, mirrors, speed graphs, TUI) is entirely intact. FluxGet just wraps it with a video-capture interface on top.
If you just want a fast terminal downloader, use Surge. If you want to capture and download video streams from your browser, use FluxGet.
| Feature | Details |
|---|---|
| Native HLS downloader | Parses .m3u8 master + media playlists, picks best variant, downloads segments in parallel |
| Native DASH downloader | Parses MPD XML, handles $Number$ / $Time$ / $Bandwidth$ template expansion |
| fMP4 / CMAF support | Parses #EXT-X-MAP, prepends the init segment, and remuxes — fixes av1/h265 "could not find trex" failures |
| AES-128-CBC decryption | Reads #EXT-X-KEY tags, fetches key, derives IV from sequence number when not explicit |
| Subtitle download | Reads #EXT-X-MEDIA:TYPE=SUBTITLES, fetches every language, embeds them into the .mkv and writes .srt sidecars |
| Subtitles-only download | Grab just the caption tracks as .srt — from the floating picker, popup, or right-click menu |
| Tunable stream concurrency | GUI setting for HLS/DASH segment connections (1–32) to trade speed against CDN rate-limiting |
| YouTube adaptive streams | Reads ytInitialPlayerResponse directly from the page — no API key, no yt-dlp for YouTube |
| yt-dlp fallback | For Vimeo, Twitch, TikTok, and 30+ known platforms where no manifest URL is captured |
| ffmpeg mux | Lossless -c copy container remux — HLS segments → MP4, video+audio → MKV |
| Browser extension | MV3 Chrome/Edge extension: intercepts downloads, captures video streams from any site |
| Floating download button | Appears on <video> elements with quality picker (Best / 1080p / 720p / 480p / Audio) |
| Popup stream list | Shows all detected streams per tab with format badges, thumbnails, quality dropdown |
| Desktop GUI | Native Wails + WebKit2GTK app: dark dashboard, live speed graph, queue management, per-item controls |
| Settings REST API | GET /settings / PUT /settings — engine config over HTTP, persisted to ~/.config/fluxget/settings.json |
| Chrome TLS fingerprint | utls client with HelloChrome_Auto — bypasses Cloudflare Bot Management on CDN-protected streams |
| Web dashboard | SSE-connected dark UI at http://127.0.0.1:1700/ui — no token needed from localhost |
| Referer passthrough | CDN-protected streams: captures documentUrl and sends it as Referer header |
| JWPlayer interception | Hooks jwplayer().setup() in MAIN world to capture HLS/DASH before blob: conversion |
| Video.js interception | Same for videojs() player setup |
| Windows support | CLI cross-compiles to Windows natively; GUI cross-compilable via mingw-w64 |
- Multi-connection parallel chunk download (up to 32 workers)
- Mirror support with automatic failover
- Sequential/streaming mode for media preview while downloading
- Beautiful TUI (Bubble Tea + Lip Gloss)
- Headless server mode + CLI
- System service install
Surge powers FluxGet's download engine — if it's useful to you, consider supporting the original authors.
| File | Platform | Notes |
|---|---|---|
fluxget-linux-amd64 |
Linux | CLI / TUI / headless server |
fluxget-gui-linux-amd64 |
Linux | Desktop app (requires libwebkit2gtk-4.1) |
fluxget_2.1.7_amd64.deb |
Debian / Ubuntu | Installs both binaries + desktop entry |
fluxget-windows-amd64.exe |
Windows | CLI / headless server |
fluxget-extension.zip |
Chrome / Edge | Load unpacked in chrome://extensions |
# Linux .deb install — easiest
sudo apt install ffmpeg
sudo dpkg -i fluxget_2.1.7_amd64.deb
fluxget-gui # desktop app
fluxget server start # OR headless on port 1700- Go 1.25+ — to build from source
- ffmpeg — for HLS/DASH mux (
sudo apt install ffmpeg) - yt-dlp — for fallback platform support (
pip install yt-dlp) - Node.js — required by yt-dlp for some platforms (
nvm install node)
# Clone
git clone https://github.com/msh2050/fluxget
cd fluxget
# Build
go build -o fluxget .
# Run with TUI
./fluxget
# OR headless server
./fluxget server start --port 1700
# Verify
curl http://127.0.0.1:1700/health
# → {"status":"ok","port":1700}GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o fluxget.exe .- Open Chrome/Edge →
chrome://extensions - Enable Developer mode (top right)
- Click Load unpacked
- Select the
extension-nexload/folder - The FluxGet icon appears in your toolbar — it shows Connected when the backend is running
No auth token needed — the extension talks to
127.0.0.1:1700directly, and loopback connections bypass authentication.
# Requirements: libwebkit2gtk-4.1-dev, wails
sudo apt install libwebkit2gtk-4.1-dev build-essential
go install github.com/wailsapp/wails/v2/cmd/wails@latest
# Build the native desktop app
cd gui
wails build -tags webkit2_41 -o ../fluxget-gui
# Run
./fluxget-guiThe GUI automatically starts the backend engine on port 1700. Features:
- Dashboard — live queue with speed graph, connection heatmap, 60s network history
- Completed — full download history with avg speed, duration, file size
- Settings — per-engine config (Surge, HLS/DASH, yt-dlp, Browser Extension), saved to
~/.config/fluxget/settings.json - Per-item actions — pause/resume, retry (on error), open file, open folder, info panel (source URL + dest path), remove
http://127.0.0.1:1700/ui
Same UI served as a webpage. Works from localhost without a token.
# Install as a system service
./fluxget service install
./fluxget service start
./fluxget service stop
./fluxget service statusThe extension has three layers:
document.js (MAIN world)
Reads ytInitialPlayerResponse, patches fetch/XHR/MediaSource,
hooks JWPlayer and Video.js setup calls.
Sends data via window.postMessage(__fluxget)
↓
content.js (extension world)
Relays postMessage to background, injects floating ▶ button
on <video> elements with quality picker panel.
↓
background.js (service worker)
Intercepts chrome.downloads, captures network requests,
routes to /download or /stream based on 5-level priority:
1. YouTube adaptive formats (ytInitialPlayerResponse)
2. Captured HLS/DASH manifest URL
3. Captured direct video URL
4. Known platform → yt-dlp
5. Notify user — nothing found
↓
Backend http://127.0.0.1:1700
Go engine, ffmpeg mux, SSE events, web UI
- YouTube:
document.jsreadswindow.ytInitialPlayerResponsewhich contains signed CDN URLs for every quality level — video and audio separately. Sent directly to/streamasformats[]; no yt-dlp involved. - HLS sites:
fetch()andXMLHttpRequestare patched in MAIN world. Any.m3u8or.mpdresponse is captured and forwarded to the backend. - JWPlayer sites:
jwplayer().setup(cfg)is hooked before the player initializes, extracting the playlist source URLs. - Unknown sites: Falls back to webRequest capture — any video-like response (by MIME type or extension) is captured.
Click the FluxGet toolbar icon to see all video streams detected on the current tab:
- Format badge (HLS / DASH / MP4 / WebM)
- Quality dropdown (Best / 1080p / 720p / 480p / 360p / Audio)
- Download button → sends to backend
- Copy URL button (⋮)
- Download All button for batch downloads
The backend runs on port 1700 by default.
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
{"status":"ok","port":1700} |
| GET | /events |
SSE stream of all download events |
| POST | /download |
Queue file download (multi-connection engine) |
| POST | /stream |
Queue video/stream (native HLS/DASH or yt-dlp fallback) |
| POST | /ytdlp |
Queue explicitly via yt-dlp |
| GET | /ui |
Web dashboard |
| GET | /list |
Active downloads |
| GET | /history |
Completed downloads |
| POST | /pause?id= |
Pause download |
| POST | /resume?id= |
Resume download |
| POST | /delete?id= |
Remove download |
| PUT | /update-url?id= |
Update stale URL |
| POST | /open-file?id= |
Open file (loopback only) |
| POST | /open-folder?id= |
Open folder (loopback only) |
| GET | /settings |
Read engine config as JSON |
| PUT | /settings |
Write engine config (persisted to ~/.config/fluxget/settings.json) |
1. formats[] present → native adaptive (YouTube CDN direct → video + audio → ffmpeg mux)
2. .m3u8 / mpegurl → native HLS (parse → parallel segments → AES-128 decrypt → ffmpeg concat)
3. .mpd / dash+xml → native DASH (parse MPD → parallel segments → ffmpeg mux)
4. known platform → yt-dlp subprocess
5. direct video URL → single-connection download
{
"url": "https://example.com/master.m3u8",
"title": "My Video",
"ytFormat": "bestvideo[height<=1080]+bestaudio/best[height<=1080]",
"headers": {
"Referer": "https://example.com/watch"
},
"formats": [],
"subtitles": false
}formats[] is the YouTube adaptive format array from ytInitialPlayerResponse. When present, the backend picks the best video+audio pair and muxes them with ffmpeg.
subtitles: true downloads only the subtitle tracks (every language in the HLS master, or a direct .vtt) as .srt files — the video is skipped.
fluxget/
├── main.go
├── cmd/
│ ├── http_api.go HTTP routes — all endpoints + SSE
│ ├── embedded.go StartEmbedded / ShutdownEmbedded — Wails GUI integration point
│ ├── settings_api.go GET /settings · PUT /settings — REST config over HTTP
│ ├── root_downloads.go /download handler
│ ├── root_http_server.go Port 1700, loopback auth bypass, ?token= support
│ └── server.go server start/stop/status subcommands
├── internal/
│ ├── stream/
│ │ ├── hls.go HLS m3u8 parser (master + media, best-variant selection)
│ │ ├── dash.go DASH MPD XML parser ($Number$/$Time$/$Bandwidth$ expansion)
│ │ ├── downloader.go Parallel segment fetcher, AES-128-CBC decrypt, ffmpeg mux
│ │ └── chrome_transport.go utls client with HelloChrome_Auto — bypasses Cloudflare JA3/JA4
│ ├── webui/
│ │ ├── ui.go go:embed wrapper
│ │ └── ui.html Dark SSE-connected dashboard
│ ├── ytdlp/
│ │ └── ytdlp.go yt-dlp subprocess runner, NeedsYtDlp() dispatch logic
│ ├── config/
│ │ ├── settings.go Settings struct, load/save ~/.config/fluxget/settings.json
│ │ ├── settings_schema.go JSON schema for the settings REST API
│ │ └── paths.go Platform-aware config/data directory resolution
│ ├── download/
│ │ ├── pool.go WorkerPool — goroutine pool for concurrent downloads
│ │ └── manager.go Download state machine
│ ├── engine/
│ │ └── network.go HTTP chunk fetcher with mirror failover (from Surge)
│ └── processing/
│ ├── manager.go Download lifecycle coordination
│ ├── pause_resume.go Pause/resume/cancel lifecycle hooks
│ └── probe.go HEAD probe for file size and range support
├── gui/ ★ Wails v2 desktop app (WebKit2GTK on Linux, WebView2 on Windows)
│ ├── main.go Wails entry point
│ ├── app.go startup/shutdown; JSC signal-handler polling goroutine
│ ├── signal_fix_linux.go CGo: patches SA_ONSTACK on JSC handlers (Go 1.25 + WebKit2GTK)
│ ├── signal_fix_other.go No-op stub for Windows / macOS builds
│ └── frontend/ Vite + vanilla JS dashboard (no framework)
└── extension-nexload/ Browser extension (MV3, load unpacked — no build step)
├── manifest.json
├── document.js MAIN world: ytInitialPlayerResponse, fetch/XHR/JWPlayer hooks
├── background.js Service worker: intercept, capture, route to /download or /stream
├── content.js Injected: floating button, quality picker, postMessage relay
└── popup.html / popup.js Toolbar popup: stream list per tab
FluxGet builds on the work of many great open source projects:
- Surge — the Go download engine this project is forked from. Multi-connection HTTP, TUI, CLI, service management — all from Surge. Go give them a star.
- yt-dlp — used as a subprocess fallback for platforms like Vimeo, Twitch, TikTok, and others where native manifest capture is not enough.
- ffmpeg — lossless container remux for HLS/DASH segments (
-c copy). - Bubble Tea and Lip Gloss — the TUI framework powering the terminal interface (via Surge).
- Wails — Go + WebView desktop framework powering the native GUI on Linux (WebKit2GTK) and Windows (WebView2).
- utls — Chrome TLS fingerprint spoofing used in the Chrome transport layer for Cloudflare-protected CDNs.
MIT — see LICENSE.
FluxGet is an independent fork and is not affiliated with SurgeDM or Tonec Inc.



