A production-ready REST API service that downloads single YouTube videos as MP4 or MP3, with asynchronous job tracking and batch support.
- Features
- Architecture
- Project Structure
- Installation
- Configuration
- Service Modes
- API Reference
- Customization Guide
- Deployment
- Troubleshooting
- Best Practices
- License
- Contributing
- Support
- MP4 (video) and MP3 (audio) support
- Batch request support via
videosarray - YouTube URL validation (youtube.com/youtu.be only)
- Playlist URLs rejected (single videos only)
- Permission and disk space error handling
- In-memory task retention with automatic cleanup worker
- Uses
pytubefixfirst, falls back topytube - Auto-startup configuration for Windows, Linux, and macOS
- Asynchronous task processing with non-blocking API responses
The service uses a simple but effective architecture:
- Flask Web Server: Handles HTTP requests and responses
- Background Workers: Separate threads process downloads asynchronously
- In-Memory Storage: Tasks are stored in a thread-safe dictionary
- Cleanup Worker: Daemon thread removes old completed/failed downloads
- Configuration-Driven: All settings loaded from JSON file
┌─────────────┐
│ Client │
└──────┬──────┘
│ HTTP Request
▼
┌──────────────────────┐
│ Flask Routes │
│ - POST /api/download│
│ - GET /api/download/id │
│ - GET /api/health │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ Task Queue │
│ (In-Memory Dict) │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ Worker Threads │
│ - Download videos │
│ - Update status │
└──────────────────────┘
YoutubeDownloader/
├─ src/
│ └─ main.py # Main Flask application
├─ scripts/
│ ├─ run.bat # Windows run script
│ ├─ run.sh # Unix run script
│ ├─ setup.bat # Windows setup script
│ └─ setup.sh # Unix setup script
├─ deployment/
│ ├─ startup-windows.vbs # Windows auto-startup wrapper
│ ├─ service.service # Linux systemd service file
│ └─ com.service.plist # macOS launchd configuration
├─ resources/
│ └─ configuration.json # Service configuration
├─ requirements.txt # Python dependencies
├─ LICENSE
└─ README.md
- Python 3.10 or newer
- Windows, macOS, or Linux
-
Clone the repository:
git clone https://github.com/LorenBll/youtube-downloader.git cd youtube-downloader -
Make the run script executable (macOS/Linux only):
chmod +x scripts/run.sh
-
Run the application:
- Windows:
scripts\run.bat - macOS/Linux:
./scripts/run.sh
- Windows:
By default, the service starts in the background. To see real-time output and debug information, use the --verbose flag:
- Windows:
scripts\run.bat --verbose - macOS/Linux:
./scripts/run.sh --verbose
The run scripts create a virtual environment and install dependencies on first run.
If you prefer to manage the environment yourself:
-
Create and activate a virtual environment:
python -m venv .venv # Windows .venv\Scripts\activate # macOS/Linux source .venv/bin/activate
-
Install dependencies:
pip install -r requirements.txt
-
Run the API:
python src/main.py
The API binds to 127.0.0.1:49153 by default.
Configuration is loaded from resources/configuration.json. It supports three modes:
- private: Local-only service with no authentication
- unprivate: Requires API keys for protected endpoints
- public: No authentication and intended for LAN access
Example structure:
{
"defaultMode": "private",
"private": {"ip": "127.0.0.1", "port": 49153},
"unprivate": {"ip": "127.0.0.1", "port": 49153, "keylist": ["your-key"]},
"public": {"ip": "0.0.0.0", "port": 49153}
}Optional environment variables:
TASK_RETENTION_MINUTES(default30): How long completed/failed tasks stay in memoryTASK_CLEANUP_INTERVAL_SECONDS(default60): Cleanup worker intervalFFMPEG_PATH(optional): Full path to the ffmpeg executable
The run scripts start the service in the background without displaying a terminal window.
- Background mode (default): Terminal closes after startup
- Verbose mode: Use
--verboseto keep the terminal open
Verify the service is running:
curl http://127.0.0.1:49153/api/healthUse the files in deployment/ to start the service automatically on boot:
- Windows:
deployment/startup-windows.vbs(place in Startup folder) - Linux:
deployment/service.service(systemd) - macOS:
deployment/com.service.plist(launchd)
Provide the API key in the request body or query string:
- POST requests: include
api_keyin the JSON body - GET requests: add
?api_key=<api-key>to the URL
Example (POST):
{
"api_key": "your-key",
"video_link": "https://www.youtube.com/watch?v=...",
"format": "mp4",
"quality": "720p",
"folder": "C:/Downloads",
"name": "optional-file-name"
}Example (GET):
GET /api/download/<task_id>?api_key=your-key
Create a new async download task.
Single-item payload:
{
"api_key": "your-key",
"video_link": "https://www.youtube.com/watch?v=...",
"format": "mp4",
"quality": "720p",
"folder": "C:/Downloads",
"name": "optional-file-name"
}Batch payload:
{
"api_key": "your-key",
"videos": [
{
"video_link": "https://www.youtube.com/watch?v=...",
"format": "mp3",
"quality": "128kbps",
"folder": "C:/Downloads"
}
]
}Response (202):
{
"task_id": "uuid",
"status": "queued"
}Batch response (202):
{
"task_id": "uuid",
"status": "queued",
"video_count": 1
}Notes:
qualityis normalized: mp4 expects values like720p(or digits like720), mp3 expects128kbps(or digits like128).- Playlist URLs are rejected.
Returns task status (queued, in_progress, completed, failed) and result/error payload.
Success response (single item, completed):
{
"task_id": "uuid",
"status": "completed",
"result": {
"name": "My Video",
"format": "mp4",
"requested_quality": "720p",
"actual_quality": "720p",
"save_path": "C:/Downloads/My Video.mp4"
}
}Success response (batch, completed):
{
"task_id": "uuid",
"status": "completed",
"result": {
"items": [
{
"index": 0,
"status": "completed",
"result": {
"name": "Track",
"format": "mp3",
"requested_quality": "128kbps",
"actual_quality": "128kbps",
"save_path": "C:/Downloads/Track.mp3"
}
}
],
"summary": {
"total": 1,
"completed": 1,
"failed": 0
}
}
}Failed response (failed):
{
"task_id": "uuid",
"status": "failed",
"error": "Invalid API key."
}Health report including bind/port, task counts, retention settings, and active YouTube client.
- Playlist URLs are intentionally rejected.
- MP4 uses progressive streams up to 720p. Higher qualities use separate video/audio streams that are merged with ffmpeg.
- Task data is in-memory and cleared on process restart.
High-quality MP4 downloads (above 720p) require ffmpeg to merge video and audio streams.
Windows
- Download the Release full build from https://www.gyan.dev/ffmpeg/builds/
- Extract the zip (for example:
C:\ffmpeg) - Add
C:\ffmpeg\binto your PATH - Open a new terminal and run:
ffmpeg -version
macOS (Homebrew)
brew install ffmpeg
ffmpeg -versionLinux (Ubuntu/Debian)
sudo apt update
sudo apt install ffmpeg
ffmpeg -version"Address already in use"
- Another process is using the configured port
- Update
resources/configuration.jsonto use a different port
"Permission denied"
- On Linux/macOS, ports below 1024 require root privileges
- Use a port >= 1024 or run with
sudoif necessary
"Cannot create or access download folder"
- The folder does not exist or is not writable
- Ensure the folder exists and your user account has write access
- Check available disk space
"Cannot write file"
- Verify the output folder path is valid and accessible
- Check disk space; YouTube videos can require large temporary space
- On Windows, avoid paths over 260 characters
curl -X POST http://localhost:49153/api/download \
-H "Content-Type: application/json" \
YoutubeDownloader/
├── src/
│ └── main.py # Main Flask application
├── scripts/
│ ├── run.bat # Windows run script
│ ├── run.sh # Unix run script
│ ├── setup.bat # Windows setup script
│ └── setup.sh # Unix setup script
├── deployment/
│ ├── startup-windows.vbs # Windows auto-startup wrapper
│ ├── service.service # Linux systemd service file
│ └── com.service.plist # macOS launchd configuration
├── resources/
│ └── configuration.json # Service configuration
├── requirements.txt # Python dependencies
├── LICENSE
├── SECURITY.md
└── README.md
curl -X POST http://localhost:49153/api/download \
-H "Content-Type: application/json" \
-d '{
"videos": [
{
"video_link": "https://www.youtube.com/watch?v=VIDEO1",
"format": "mp4",
"quality": "720p",
"folder": "/Users/username/Downloads"
},
{
"video_link": "https://www.youtube.com/watch?v=VIDEO2",
"format": "mp3",
"quality": "128kbps",
"folder": "/Users/username/Downloads"
}
]
}'The Unlicense - See LICENSE file for details
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
For issues, questions, or contributions:
- Open an issue on GitHub
- Check existing issues for solutions
- Provide detailed information about your environment
- Include logs from
--verbosemode when reporting issues