A simple local Social media downloader with a React front-end and a Flask backend that uses yt-dlp to download videos. The UI lets you queue multiple URLs, set filenames, start downloads, and monitor live progress.
- Web UI (React) served as a single
index.htmlfile - Background downloads using
yt-dlp(non-blocking via Pythonthreading) - Live progress polling from backend (
/progress/<id>) - Custom file naming and queue management
- Download folder auto-created on the Desktop (
YT-Downloads)
project-root/
├─ app.py # Flask backend (server + yt-dlp integration)
├─ index.html # React UI (single-file, loaded with Babel)
├─ run.bat # Optional: script to start the server on Windows
├─ requirements.txt # Python dependencies
└─ README.md # (this file)
- Python 3.8+ (3.10+ recommended)
pippackage manager- Windows / macOS / Linux (tested on Windows)
- Internet connection (for downloading videos)
Python packages (install below):
- Flask
- Flask-CORS
- yt-dlp
You can install with:
pip install -r requirements.txtor install individually:
pip install flask flask-cors yt-dlpIf you don't have a requirements.txt, create one with:
flask
flask-cors
yt-dlp
- The React UI (
index.html) runs in your browser and lets you paste one or more YouTube URLs. - The UI POSTs to
/downloadon the Flask backend with{ url, filename, id }. - Flask starts
yt-dlpin a background thread and returns immediately. - The UI polls
/progress/<id>to get live updates about download status and percentage. - Completed files are saved to
~/Desktop/YT-Downloadsby default.
-
Clone or copy the repository to your machine.
-
Install dependencies (see Requirements).
-
Start the Flask server:
On Windows you may use a run.bat file with content like:
@echo off
python app.py
pauseOr run directly from terminal:
python app.py-
When the server starts it should print the download folder path and open
http://localhost:5000in your default browser. -
In the browser: paste one or more YouTube URLs (one per line), set a description (used for naming), click Add to Queue, then Start Downloads.
-
Files will appear in
Desktop/YT-Downloads(or the folder printed by the backend).
GET /or/index.html— serves the frontend filePOST /download— start a download; expects JSON{ url, filename, id }GET /progress/<id>— returns download progress for an item idGET /download-folder— returns JSON{ folder: '<path>' }GET /health— health check; returns{ status: 'ok' }
- Download folder: by default the backend sets
DOWNLOAD_FOLDERto~/Desktop/YT-Downloads. Change this inapp.py:
DOWNLOAD_FOLDER = os.path.join(os.path.expanduser("~"), "Desktop", "YT-Downloads")-
Port: Flask runs on port
5000by default inapp.py. Changeapp.run(..., port=5000)if needed. -
CORS:
CORS(app)is enabled so the UI (served from same machine) can fetch the API. If you host the frontend on another domain, configureflask_cors.CORSaccordingly.
- This project is intended to run locally (localhost). Do NOT expose the server to the public internet without adding authentication and access controls.
yt-dlpcan download from many sources — only use it where you have permission to download content.
- Server not connected: Confirm Flask is running and listening at
http://localhost:5000. Check terminal for errors. - CORS errors: Ensure
CORS(app)inapp.py. If frontend served from a different origin, allow that origin specifically. - Progress stuck: Inspect Flask logs for exceptions in
progress_hook.yt-dlpuses_percent_strin hooks — if absent, progress may remain at 0. - Permission errors saving files: Ensure the user running the script has write permission to the download folder.
- Build the frontend with a proper bundler (Vite/webpack) instead of Babel in browser for production.
- Replace polling with WebSocket or Server-Sent Events for real-time progress.
- Add authentication and role checks if hosting on a network.
- Improve concurrency control (limit parallel downloads)
- Sanitize filenames and validate input URLs.
PRs are welcome. Please open an issue first to discuss major changes.
This project is provided under the MIT License. Feel free to adapt and reuse.
If you want changes to this README (more details, examples, or add badges), tell me what to include and I will update it.