DownMan
DownMan
A self-hosted download manager with a FastAPI backend, a Vue 3 web UI, and a Firefox extension that hands your browser downloads off to it. Files are fetched in parallel chunks, organised into category folders, and the whole queue survives a server restart.
Features
- Parallel, chunked downloads — each file is split into byte ranges
(
CHUNK_THREADS, default 4) and downloaded concurrently using HTTPRangerequests. - Concurrent queue — up to
MAX_CONCURRENT(default 3) downloads run at once; the rest wait in an async queue. - Pause / resume / cancel — control individual downloads while they run.
- Resumable across restarts — chunk progress is checkpointed to the OS temp dir, and the queue state is persisted in SQLite, so interrupted downloads are re-queued when the server starts again.
- Automatic categorisation — files are sorted into
Videos,Music,Apps,Documents,Compressed, orOtherby extension, each saved to its own folder (folders are configurable per category). - Live updates — progress, speed, and status changes stream to the UI over Server-Sent Events (SSE); multiple browser tabs stay in sync.
- Browser integration — the Firefox extension intercepts downloads and forwards them to the service, falling back to the browser's own downloader (with a notification) if DownMan isn't running.
- Smart filenames — duplicate destinations get
(1),(2), … appended rather than overwriting.
Architecture
┌──────────────────┐
Firefox ───► │ Firefox extension│ ──► POST /api/downloads
└──────────────────┘ │
▼
┌──────────────────────────────────────────────────┐
│ FastAPI server (server.py, port 6500) │
│ ├─ DownloadQueue — async queue + worker pool │
│ ├─ DownloadManager — threaded chunked downloader │
│ ├─ SQLite (~/.downman/downman.db) — persistence │
│ └─ SSE /api/events — live progress to the UI │
└──────────────────────────────────────────────────┘
│ serves
▼
Vue 3 SPA (backend/static)
Backend modules (backend/)
| File | Responsibility |
|---|---|
server.py | FastAPI app: REST API, SSE endpoint, serves the built UI |
queue_manager.py | DownloadQueue + DownloadItem — async scheduling, pause/resume/cancel, progress broadcasting |
downloader.py | DownloadManager — multi-threaded chunked download, retries, resume, merge |
database.py | SQLite persistence for downloads and per-category folder overrides |
categories.py | Extension → category mapping and default folder resolution |
config.py | Port, concurrency limits, download/data directories |
Frontend (frontend/)
A Vue 3 + Pinia + Vite single-page app (icons via lucide-vue-next). The
built output is committed under backend/static/, so the backend serves the
UI without a build step. You only need the frontend/ toolchain to modify the UI.
Requirements
- Python 3.10+ (uses
str | Nonesyntax andset[...]generics) - Backend dependencies in
backend/requirements.txt:fastapi,uvicorn[standard],requests - (UI development only) Node.js 18+ for the Vue frontend
Getting started
1. Run the backend
cd backend
pip install -r requirements.txt
python server.pyThe server listens on http://localhost:6500 — open it in your browser to use
the web UI. Application data (the SQLite DB) lives in ~/.downman/, and
downloads default to category subfolders under your system Downloads directory.
2. Install the Firefox extension (optional)
Load extensions/firefox in Firefox via
about:debugging → This Firefox → Load Temporary Add-on, then select
manifest.json. Once active, any download you
start in Firefox is cancelled and forwarded to the DownMan service. If the
service is down, the browser downloads the file normally and shows a notification.
3. Develop the UI (optional)
cd frontend
npm install
npm run dev # Vite dev server with hot reload
npm run build # outputs to backend/static/ for the server to serveConfiguration
Edit backend/config.py:
| Setting | Default | Description |
|---|---|---|
PORT | 6500 | Port the server listens on |
MAX_CONCURRENT | 3 | Max downloads running at once |
CHUNK_THREADS | 4 | Parallel chunks per download |
Per-category download folders can be overridden at runtime from the UI's
settings dialog (stored in the database, not in config.py).
API reference
The backend exposes a REST API under /api plus an SSE stream:
| Method | Path | Description |
|---|---|---|
POST | /api/downloads | Add a download (url, optional dest/folder/filename/category) |
GET | /api/downloads | List all downloads |
DELETE | /api/downloads/{id} | Cancel an active download or remove a finished one |
DELETE | /api/downloads?status=… | Bulk-remove all downloads with a status |
POST | /api/downloads/{id}/pause | Pause a download |
POST | /api/downloads/{id}/resume | Resume a download |
GET | /api/categories | List categories with their current folders |
POST | /api/categories/{category}/folder | Set a custom folder for a category |
GET | /api/detect-category?filename=… | Detect a file's category |
GET | /api/events | SSE stream of live download events |
Interactive API docs are available at http://localhost:6500/docs while the
server is running.
Notes & limitations
- Servers must support
Rangerequests. Chunked downloading relies on the server honouring byte ranges (responding206 Partial Content). A200response is treated as a hard error rather than downloading the file single-threaded. - The server binds to
127.0.0.1and CORS is open (*) so the browser extension'smoz-extension://origin can reach it. It is intended for local use, not exposure to a network. - Do not run the server with
uvicorn --reload— a reload kills in-progress downloads.