https://github.com/lorenbll/youtubedownloader
REST API for downloading YouTube videos/audio with batch support and background job queue.
https://github.com/lorenbll/youtubedownloader
api-server background-jobs batch-processing flask local-server media-download python rest-api utilities video-download youtube youtube-downloader
Last synced: 5 days ago
JSON representation
REST API for downloading YouTube videos/audio with batch support and background job queue.
- Host: GitHub
- URL: https://github.com/lorenbll/youtubedownloader
- Owner: LorenBll
- License: unlicense
- Created: 2026-05-11T10:21:12.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-24T19:10:29.000Z (13 days ago)
- Last Synced: 2026-06-24T21:04:09.628Z (13 days ago)
- Topics: api-server, background-jobs, batch-processing, flask, local-server, media-download, python, rest-api, utilities, video-download, youtube, youtube-downloader
- Language: Python
- Homepage:
- Size: 60.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# YoutubeDownloader
YoutubeDownloader is a local YouTube download service. It solves the problem of queuing single-video or batch downloads and returning the downloaded media in MP4 or MP3 form through an HTTP API.
## About
YoutubeDownloader is scoped to request validation, background download jobs, and file delivery on the local machine. The service binds to `127.0.0.1` on port `49156`, keeps task state in memory, and uses a cleanup thread to remove finished jobs after a retention period.
## Setup
1. Install the Python dependencies with `pip install -r requirements.txt`.
2. Install `ffmpeg` if you want to merge adaptive MP4 streams above 720p.
3. Review `resources/configuration.json` to configure `port`, `allowed_roots`, and `blacklisted_roots`.
- `allowed_roots`: list of root paths the API is allowed to write downloads into. If this list is non-empty, ONLY these roots are permitted and the blacklist is ignored.
- `blacklisted_roots`: list of root paths that are forbidden when `allowed_roots` is empty. If `allowed_roots` is empty and `blacklisted_roots` is non-empty, any path inside a blacklisted root is forbidden.
- Behavior summary:
- If `allowed_roots` is non-empty → only those roots are permitted (blacklist ignored).
- Else if `blacklisted_roots` is non-empty → all paths are permitted except any inside a blacklisted root.
- Else (both lists empty) → all paths on the system are permitted.
4. Leave the project structure intact so the service can find `resources/` and `src/`.
## Run
1. Windows: run `scripts\run.bat`.
2. Unix-like systems: run `bash scripts/run.sh`.
3. Manual: run `python src/main.py` from the project root.
## API Endpoints
All endpoints also support `OPTIONS`; `GET` endpoints additionally support `HEAD`.
### `POST /api/download` (also `OPTIONS`)
Queues a single or batch download task and returns a task ID.
- Body (JSON object):
- Single-download mode (required fields):
- `video_link` (string, required): valid YouTube URL (`youtube.com` or `youtu.be`), playlists are rejected.
- `format` (string, required): `mp4` or `mp3`.
- `quality` (string, required):
- mp4: values like `720`, `720p`, `1080`, `1080p`
- mp3: values like `128`, `128kbps`, `160`, `160kbps`
- `folder` (string, required): destination folder path (created if missing).
- The folder must be allowed by `resources/configuration.json`.
- `name` (string, optional): preferred file name stem.
- `file_name` (string, optional alias): alternative to `name`.
- Batch mode:
- `videos` (array, required): non-empty array of video objects. Each item must include single-download required fields.
- Returns:
- `202` single -> `{ "task_id": "", "status": "queued" }`
- `202` batch -> `{ "task_id": "", "status": "queued", "video_count": }`
- `400` -> `{ "error": "Request body must be valid JSON." }`
- `400` -> `{ "error": "Missing required fields.", "missing_fields": ["..."] }`
- `400` -> `{ "error": "format must be either 'mp4' or 'mp3'" }`
- `400` -> `{ "error": "video_link must be a valid YouTube URL (youtube.com or youtu.be)." }`
- `400` -> `{ "error": "Playlist download is not supported. Please provide a single video URL." }`
- `400` batch validation ->
```json
{
"error": "Invalid videos payload.",
"video_errors": [
{ "index": 0, "error": "..." }
]
}
```
- `500` ->
```json
{
"error": "Could not start download worker. The server may be under heavy load.",
"task_id": ""
}
```
### `GET /api/task/` (also `HEAD`, `OPTIONS`)
Returns current task status and final result/error.
- Path parameters:
- `task_id` (string, required): task identifier returned by `POST /api/download`.
- Returns:
- `200` queued/in progress -> `{ "task_id": "", "status": "queued|in_progress" }`
- `200` completed (single) ->
```json
{
"task_id": "",
"status": "completed",
"result": {
"name": "",
"format": "mp4|mp3",
"requested_quality": "",
"actual_quality": "",
"save_path": "",
"merge": "ffmpeg"
}
}
```
Note: `merge` appears only for adaptive mp4 merges.
- `200` completed (batch) ->
```json
{
"task_id": "",
"status": "completed",
"result": {
"items": [
{ "index": 0, "status": "completed", "result": { "...": "..." } },
{ "index": 1, "status": "failed", "error": "..." }
],
"summary": { "total": 2, "completed": 1, "failed": 1 }
}
}
```
- `200` failed -> `{ "task_id": "", "status": "failed", "error": "" }`
- `404` -> `{ "error": "Task not found." }`
### `GET /api/health` (also `HEAD`, `OPTIONS`)
Service and queue health snapshot.
- Body: none
- Returns:
- `200` ->
```json
{
"status": "ok",
"service": "YoutubeDownloader",
"bind_address": "127.0.0.1",
"port": 49156,
"task_counts": {
"queued": 0,
"in_progress": 0,
"completed": 0,
"failed": 0,
"total": 0
},
"task_retention_minutes": 60,
"task_cleanup_interval_seconds": 60,
"youtube_client": "WEB",
"hostname": "...",
"primary_ip": "...",
"local_ips": ["..."]
}
```
## License
- [LICENSE](LICENSE)
## Author
- [LorenBll](https://github.com/LorenBll)