{"id":45811508,"url":"https://github.com/lorenbll/youtubedownloader","last_synced_at":"2026-07-02T19:01:12.900Z","repository":{"id":360210584,"uuid":"1235437132","full_name":"LorenBll/YoutubeDownloader","owner":"LorenBll","description":"REST API for downloading YouTube videos/audio with batch support and background job queue.","archived":false,"fork":false,"pushed_at":"2026-06-24T19:10:29.000Z","size":62,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T21:04:09.628Z","etag":null,"topics":["api-server","background-jobs","batch-processing","flask","local-server","media-download","python","rest-api","utilities","video-download","youtube","youtube-downloader"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LorenBll.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T10:21:12.000Z","updated_at":"2026-06-24T19:03:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"75f8aafa-e545-4dda-b3c3-3899b569552c","html_url":"https://github.com/LorenBll/YoutubeDownloader","commit_stats":null,"previous_names":["lorenbll/youtubedownloader"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/LorenBll/YoutubeDownloader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LorenBll%2FYoutubeDownloader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LorenBll%2FYoutubeDownloader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LorenBll%2FYoutubeDownloader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LorenBll%2FYoutubeDownloader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LorenBll","download_url":"https://codeload.github.com/LorenBll/YoutubeDownloader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LorenBll%2FYoutubeDownloader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35059251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-server","background-jobs","batch-processing","flask","local-server","media-download","python","rest-api","utilities","video-download","youtube","youtube-downloader"],"created_at":"2026-02-26T16:53:54.032Z","updated_at":"2026-07-02T19:01:12.892Z","avatar_url":"https://github.com/LorenBll.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# YoutubeDownloader\n\nYoutubeDownloader 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.\n\n## About\nYoutubeDownloader 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.\n\n## Setup\n1. Install the Python dependencies with `pip install -r requirements.txt`.\n2. Install `ffmpeg` if you want to merge adaptive MP4 streams above 720p.\n3. Review `resources/configuration.json` to configure `port`, `allowed_roots`, and `blacklisted_roots`.\n\t\t- `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.\n\t\t- `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.\n\t\t- Behavior summary:\n\t\t\t- If `allowed_roots` is non-empty → only those roots are permitted (blacklist ignored).\n\t\t\t- Else if `blacklisted_roots` is non-empty → all paths are permitted except any inside a blacklisted root.\n\t\t\t- Else (both lists empty) → all paths on the system are permitted.\n4. Leave the project structure intact so the service can find `resources/` and `src/`.\n\n## Run\n1. Windows: run `scripts\\run.bat`.\n2. Unix-like systems: run `bash scripts/run.sh`.\n3. Manual: run `python src/main.py` from the project root.\n\n## API Endpoints\n\nAll endpoints also support `OPTIONS`; `GET` endpoints additionally support `HEAD`.\n\n### `POST /api/download` (also `OPTIONS`)\nQueues a single or batch download task and returns a task ID.\n\n- Body (JSON object):\n\t- Single-download mode (required fields):\n\t\t- `video_link` (string, required): valid YouTube URL (`youtube.com` or `youtu.be`), playlists are rejected.\n\t\t- `format` (string, required): `mp4` or `mp3`.\n\t\t- `quality` (string, required):\n\t\t\t- mp4: values like `720`, `720p`, `1080`, `1080p`\n\t\t\t- mp3: values like `128`, `128kbps`, `160`, `160kbps`\n\t\t- `folder` (string, required): destination folder path (created if missing).\n\t\t\t- The folder must be allowed by `resources/configuration.json`.\n\t\t- `name` (string, optional): preferred file name stem.\n\t\t- `file_name` (string, optional alias): alternative to `name`.\n\t- Batch mode:\n\t\t- `videos` (array, required): non-empty array of video objects. Each item must include single-download required fields.\n- Returns:\n\t- `202` single -\u003e `{ \"task_id\": \"\u003cuuid\u003e\", \"status\": \"queued\" }`\n\t- `202` batch -\u003e `{ \"task_id\": \"\u003cuuid\u003e\", \"status\": \"queued\", \"video_count\": \u003cn\u003e }`\n\t- `400` -\u003e `{ \"error\": \"Request body must be valid JSON.\" }`\n\t- `400` -\u003e `{ \"error\": \"Missing required fields.\", \"missing_fields\": [\"...\"] }`\n\t- `400` -\u003e `{ \"error\": \"format must be either 'mp4' or 'mp3'\" }`\n\t- `400` -\u003e `{ \"error\": \"video_link must be a valid YouTube URL (youtube.com or youtu.be).\" }`\n\t- `400` -\u003e `{ \"error\": \"Playlist download is not supported. Please provide a single video URL.\" }`\n\t- `400` batch validation -\u003e\n\t\t```json\n\t\t{\n\t\t\t\"error\": \"Invalid videos payload.\",\n\t\t\t\"video_errors\": [\n\t\t\t\t{ \"index\": 0, \"error\": \"...\" }\n\t\t\t]\n\t\t}\n\t\t```\n\t- `500` -\u003e\n\t\t```json\n\t\t{\n\t\t\t\"error\": \"Could not start download worker. The server may be under heavy load.\",\n\t\t\t\"task_id\": \"\u003cuuid\u003e\"\n\t\t}\n\t\t```\n\n### `GET /api/task/\u003ctask_id\u003e` (also `HEAD`, `OPTIONS`)\nReturns current task status and final result/error.\n\n- Path parameters:\n\t- `task_id` (string, required): task identifier returned by `POST /api/download`.\n- Returns:\n\t- `200` queued/in progress -\u003e `{ \"task_id\": \"\u003cuuid\u003e\", \"status\": \"queued|in_progress\" }`\n\t- `200` completed (single) -\u003e\n\t\t```json\n\t\t{\n\t\t\t\"task_id\": \"\u003cuuid\u003e\",\n\t\t\t\"status\": \"completed\",\n\t\t\t\"result\": {\n\t\t\t\t\"name\": \"\u003cfile-name-stem\u003e\",\n\t\t\t\t\"format\": \"mp4|mp3\",\n\t\t\t\t\"requested_quality\": \"\u003cnormalized-request\u003e\",\n\t\t\t\t\"actual_quality\": \"\u003cresolved-stream-quality\u003e\",\n\t\t\t\t\"save_path\": \"\u003cfinal-file-path\u003e\",\n\t\t\t\t\"merge\": \"ffmpeg\"\n\t\t\t}\n\t\t}\n\t\t```\n\t\tNote: `merge` appears only for adaptive mp4 merges.\n\t- `200` completed (batch) -\u003e\n\t\t```json\n\t\t{\n\t\t\t\"task_id\": \"\u003cuuid\u003e\",\n\t\t\t\"status\": \"completed\",\n\t\t\t\"result\": {\n\t\t\t\t\"items\": [\n\t\t\t\t\t{ \"index\": 0, \"status\": \"completed\", \"result\": { \"...\": \"...\" } },\n\t\t\t\t\t{ \"index\": 1, \"status\": \"failed\", \"error\": \"...\" }\n\t\t\t\t],\n\t\t\t\t\"summary\": { \"total\": 2, \"completed\": 1, \"failed\": 1 }\n\t\t\t}\n\t\t}\n\t\t```\n\t- `200` failed -\u003e `{ \"task_id\": \"\u003cuuid\u003e\", \"status\": \"failed\", \"error\": \"\u003creason\u003e\" }`\n\t- `404` -\u003e `{ \"error\": \"Task not found.\" }`\n\n### `GET /api/health` (also `HEAD`, `OPTIONS`)\nService and queue health snapshot.\n\n- Body: none\n- Returns:\n\t- `200` -\u003e\n\t\t```json\n\t\t{\n\t\t\t\"status\": \"ok\",\n\t\t\t\"service\": \"YoutubeDownloader\",\n\t\t\t\"bind_address\": \"127.0.0.1\",\n\t\t\t\"port\": 49156,\n\t\t\t\"task_counts\": {\n\t\t\t\t\"queued\": 0,\n\t\t\t\t\"in_progress\": 0,\n\t\t\t\t\"completed\": 0,\n\t\t\t\t\"failed\": 0,\n\t\t\t\t\"total\": 0\n\t\t\t},\n\t\t\t\"task_retention_minutes\": 60,\n\t\t\t\"task_cleanup_interval_seconds\": 60,\n\t\t\t\"youtube_client\": \"WEB\",\n\t\t\t\"hostname\": \"...\",\n\t\t\t\"primary_ip\": \"...\",\n\t\t\t\"local_ips\": [\"...\"]\n\t\t}\n\t\t```\n\n## License\n- [LICENSE](LICENSE)\n\n## Author\n- [LorenBll](https://github.com/LorenBll)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Florenbll%2Fyoutubedownloader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Florenbll%2Fyoutubedownloader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Florenbll%2Fyoutubedownloader/lists"}