Building FetchYT: A FastAPI + yt-dlp MP3 Downloader with Web UI and CLI

Podcast¶
Sit back and let the article speak for itself!
Contents
- Audience & Goals
- TL;DR
- Introduction
- Architecture Overview
- Data Flow
- Key Components
- Handling YouTube Bot Detection
- Installation & Setup
- Getting Started in 60 Seconds
- Implementing the Downloader
- Web Server & Frontend
- CLI Usage
- Testing & Code Quality
- Performance & Concurrency
- Security Considerations
- Roadmap & Future Enhancements
- Troubleshooting
- Conclusion
- References
Audience & Goals¶
- Practicing backend/frontend engineers who want to see how a small, real app ties FastAPI, yt-dlp, and a vanilla JS SPA together
- DevOps-minded builders looking for production-ready touches: retries, cookies for bot detection, realistic headers, and env-driven config
- Readers comparing approaches to deliver the same capability via three surfaces: REST API, CLI, and browser UI
- Engineers seeking a template for packaging (pyproject/entry points), testing (pytest-asyncio), and docs that stay aligned with features
TL;DR¶
- A FastAPI backend, vanilla JS frontend, and argparse CLI wrap yt-dlp to download single videos and full playlists as MP3/M4A/WAV with quality control, cookies-based bot-bypass, and clean packaging via pyproject.toml. End users get an easy web page and one-liners; developers get a small, readable codebase to extend.

Introduction¶
- Motivation: Hundreds of favorite tracks live in YouTube playlists; I want them as portable audio on any device—no ads, no buffering, no video stream.
- User promise: Paste a video or playlist URL, click Download (or run one CLI command), and get clean MP3/M4A/WAV files locally.
- Developer promise: A compact stack—FastAPI, yt-dlp, vanilla JS, argparse—keeps code understandable and easy to extend.
- Practical hurdles: YouTube bot detection, format/quality choices, and FFmpeg requirements; the app bakes in cookies support, retries, and clear guidance to make this reliable.
Architecture Overview¶
- Backend: FastAPI app (fetchyt/api.py) serves JSON routes and the SPA assets;
/api/v1/extractand/api/v1/downloadrun work in background tasks so the server stays responsive while yt-dlp and FFmpeg operate. - Downloader core:
YouTubeDownloader(fetchyt/downloader.py) wraps yt-dlp with audio-only defaults, format/quality mapping, retries, browser-like headers, and optional cookies (COOKIES_FILEorcookies_from_browser) to survive bot detection. - CLI surface: Argparse commands (fetchyt/cli.py)—
download,info,server, andcookies—expose the same core so users can script downloads, preview metadata, run the server, or export browser cookies without touching the API. - Frontend: Dark-mode SPA under fetchyt/static using vanilla JS (fetchyt/static/js/app.js) to call the API, poll status, and render progress/thumbnails; HTML/CSS keep it lightweight and framework-free.
- Config & wiring: fetchyt/config.py centralizes environment-driven settings (download dir, max concurrency, API host/port, cookies path) so API, CLI, and downloader stay aligned.
Data Flow¶
- Input: User submits a YouTube video or playlist URL from the SPA, CLI, or Python call.
- Extract:
/api/v1/extract(or CLIinfo) callsYouTubeDownloader.extract_info, which uses yt-dlp with headers/cookies to fetch metadata safely.
- Extract:
- Confirm: UI/CLI lists titles, durations, and counts so users can pick format/quality and decide if they want the whole playlist.
- Enqueue:
/api/v1/download(or CLIdownload) schedules a background task and returns atask_idimmediately; the server remains responsive.
- Enqueue:
- Download & transcode:
YouTubeDownloader.downloaddrives yt-dlp + FFmpeg to emit MP3/M4A/WAV at the requested bitrate.
- Download & transcode:
- Persist: Files are stored in the configured downloads directory with paths tracked per task.
- Monitor: SPA polls
/api/v1/status/{task_id}; CLI streams progress (percent, ETA, file path) using callbacks.
- Monitor: SPA polls
- Cleanup: Optional
/api/v1/task/{task_id}removes task metadata (files stay) to keep state lean for the next run.
- Cleanup: Optional
Key Components¶
-
YouTubeDownloader.extract_info(url, browser=None): Uses yt-dlp with browser-like headers and optional cookies (file orcookies_from_browser) to pull playlist/video metadata; retries transient failures and offers clear next steps when bot detection appears. -
YouTubeDownloader.download(...): Maps mp3/m4a/wav to the right yt-dlp/FFmpeg params, enforces quality presets, and emits progress callbacks (percent, ETA, file path) that both CLI and API surface. - API endpoints:
/api/v1/extractto preview info,/api/v1/downloadto enqueue background downloads,/api/v1/status/{task_id}for polling, plus cleanup and health; static assets ship from the same FastAPI app to keep deployment simple. - CLI commands:
downloadandinfowrap the same downloader core;serverstarts FastAPI;cookies --browser chrome|firefox --output cookies.txtshells out to yt-dlp to export browser cookies, making bot bypass a one-liner.
Handling YouTube Bot Detection¶
- Symptom: YouTube replies "Sign in to confirm you’re not a bot" and extraction/download fails.
- Quick fix for end users:
fetchyt cookies --browser chrome --output cookies.txt(or firefox) then setCOOKIES_FILEand rerun your command. - Under the hood: Downloader sets realistic headers, retries transient blocks, and can attempt
cookies_from_browserwhen enabled. - If still blocked: Wait 10–30 minutes, switch network/VPN, or export cookies from another browser profile.
- UX help: CLI/API error messages include actionable steps so users know what to try next.
Installation & Setup¶
- Prereqs: Install Python 3.12+ (add it to PATH) and FFmpeg (needed to produce audio). Quick installs: Windows
choco install ffmpeg; macOSbrew install ffmpeg; Ubuntu/Debiansudo apt install ffmpeg. - Fast path for local use (recommended):
- Install uv if missing: Windows PowerShell
irm https://astral.sh/uv/install.ps1 | iex; macOS/Linuxcurl -LsSf https://astral.sh/uv/install.sh | sh - Create and activate a virtualenv:
uv venvthen.venv\Scripts\activate(Windows) orsource .venv/bin/activate(macOS/Linux) - Install in editable mode:
uv pip install -e .
- Install uv if missing: Windows PowerShell
- From PyPI (no repo clone):
uv pip install fetchytorpip install fetchyt - Smoke test:
fetchyt --versionthenfetchyt info "https://www.youtube.com/watch?v=dQw4w9WgXcQ" - If blocked by YouTube:
fetchyt cookies --browser chrome --output cookies.txt, setCOOKIES_FILEto that path, and retry.
Getting Started in 60 Seconds¶
- Step 1: Install Python 3.12+ and FFmpeg (see above quick commands).
- Step 2: Install FetchYT:
uv pip install fetchyt(orpip install fetchyt). - Step 3: Try a download:
fetchyt download "https://www.youtube.com/watch?v=VIDEO" --yesand find the audio in./downloads. - Step 4 (if blocked):
fetchyt cookies --browser chrome --output cookies.txtthen setCOOKIES_FILE=./cookies.txtand rerun. - Step 5: Open the web UI:
fetchyt serverthen visit http://localhost:8098.

Implementing the Downloader¶
- yt-dlp options: audio-only extraction, format mapping (mp3/m4a/wav), and bitrate presets (128/192/256/320 kbps) so users can trade size for quality; works for single videos and playlists.
- Cookies integration: respects
COOKIES_FILEand can call yt-dlp’scookies_from_browserto export cookies automatically when users ask. - Progress handling: callbacks surface percent, ETA, and destination path so the CLI and API can show live status.
- Error handling: retries transient errors, shows clear messages, and suggests cookies/network/VPN remedies when bot detection appears.
Web Server & Frontend¶
- Single process: FastAPI serves the SPA and JSON routes together—simple to run locally or deploy as one service.
- SPA calls: index.html plus vanilla JS (fetchyt/static/js/app.js) hit
/api/v1/extractto preview,/api/v1/downloadto start, and poll/api/v1/status/{task_id}for progress and thumbnails. - UX: dark theme, responsive layout, progress bars, inline error callouts, and success toasts—no frontend framework to learn or bundle.
- Try it:
fetchyt server --host 0.0.0.0 --port 8098then openhttp://localhost:8098; everything (static + API) comes from that server.

CLI Usage¶
- Preview first:
fetchyt info <URL>(single video or playlist) to see titles/counts before downloading. - Download:
fetchyt download <URL> --format mp3 --quality 192 --output ./downloads --yesto grab audio with chosen format/bitrate; omit--yesto confirm playlists interactively. - Bypass bot checks:
fetchyt cookies --browser chrome --output cookies.txtthen setCOOKIES_FILEso future commands automatically send cookies. - Serve the UI/API:
fetchyt server --host 0.0.0.0 --port 8098if you want the web app running locally.
Testing & Code Quality¶
- Tests: Pytest plus pytest-asyncio cover async downloader/API paths; add coverage reports to see what’s exercised.
- Types & validation: Type hints throughout; Pydantic models validate inputs so bad URLs/options are caught early instead of failing mid-download.
- Lint/format: Ruff and Black keep the code consistent and reviewable.
- Quick check: run
pytestafter changes; keep async tests deterministic and small.
Performance & Concurrency¶
- Async/await keeps the API responsive while downloads run in the background.
- Background tasks handle yt-dlp + FFmpeg so requests return immediately with a
task_id. - Concurrency knobs: set max concurrent downloads via config to balance speed against CPU/network strain.
- Playlists at scale: big lists mean longer poll times; consider chunking or folder-per-playlist if you extend it.
Security Considerations¶
- Validate input: Pydantic guards URLs and options to reduce malformed requests.
- CORS: lock down allowed origins in production if exposing the API publicly.
- File safety: restrict downloads to the configured directory; avoid writing outside intended paths.
- Errors: keep messages informative but not verbose enough to leak internals.
Roadmap & Future Enhancements¶
- Reliability: resume interrupted downloads, add queue management, and surface live progress via WebSockets.
- UX/features: add authentication, download history, batch/CSV inputs, and richer filtering.
- Distribution: publish a Docker image, consider a desktop wrapper, and polish mobile-friendly flows.
Troubleshooting¶
- FFmpeg not found: install via Chocolatey/Homebrew/Apt and retry.
- Bot detection: export cookies (
fetchyt cookies ...), setCOOKIES_FILE, then rerun; if blocked, wait or switch network/VPN. - Port in use: start the server with
--port 8080(or another free port).
Conclusion¶
- For end users: FetchYT turns any YouTube video or playlist into portable audio with a simple web page or one CLI line, even when YouTube pushes bot checks.
- For developers: It’s a compact, well-typed FastAPI + yt-dlp stack with clear separation (API, CLI, downloader, SPA) and pragmatic touches—cookies, retries, headers—that you can extend or embed elsewhere.
References¶
- Repository: https://gitlab.com/allikapub/fetchyt
- FastAPI: https://fastapi.tiangolo.com/
- yt-dlp: https://github.com/yt-dlp/yt-dlp
- FFmpeg: https://ffmpeg.org/
Last updated 2026-01-11 18:58:22.307709 IST
[^top]
Comments