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

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

wordcloud

Podcast

Sit back and let the article speak for itself!

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.
Mind map of the project

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/extract and /api/v1/download run 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_FILE or cookies_from_browser) to survive bot detection.
  • CLI surface: Argparse commands (fetchyt/cli.py)—download, info, server, and cookies—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

    1. Input: User submits a YouTube video or playlist URL from the SPA, CLI, or Python call.
    1. Extract: /api/v1/extract (or CLI info) calls YouTubeDownloader.extract_info, which uses yt-dlp with headers/cookies to fetch metadata safely.
    1. Confirm: UI/CLI lists titles, durations, and counts so users can pick format/quality and decide if they want the whole playlist.
    1. Enqueue: /api/v1/download (or CLI download) schedules a background task and returns a task_id immediately; the server remains responsive.
    1. Download & transcode: YouTubeDownloader.download drives yt-dlp + FFmpeg to emit MP3/M4A/WAV at the requested bitrate.
    1. Persist: Files are stored in the configured downloads directory with paths tracked per task.
    1. Monitor: SPA polls /api/v1/status/{task_id}; CLI streams progress (percent, ETA, file path) using callbacks.
    1. Cleanup: Optional /api/v1/task/{task_id} removes task metadata (files stay) to keep state lean for the next run.

Key Components

  • YouTubeDownloader.extract_info(url, browser=None): Uses yt-dlp with browser-like headers and optional cookies (file or cookies_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/extract to preview info, /api/v1/download to 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: download and info wrap the same downloader core; server starts FastAPI; cookies --browser chrome|firefox --output cookies.txt shells 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 set COOKIES_FILE and rerun your command.
  • Under the hood: Downloader sets realistic headers, retries transient blocks, and can attempt cookies_from_browser when 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; macOS brew install ffmpeg; Ubuntu/Debian sudo apt install ffmpeg.
  • Fast path for local use (recommended):
    1. Install uv if missing: Windows PowerShell irm https://astral.sh/uv/install.ps1 | iex; macOS/Linux curl -LsSf https://astral.sh/uv/install.sh | sh
    2. Create and activate a virtualenv: uv venv then .venv\Scripts\activate (Windows) or source .venv/bin/activate (macOS/Linux)
    3. Install in editable mode: uv pip install -e .
  • From PyPI (no repo clone): uv pip install fetchyt or pip install fetchyt
  • Smoke test: fetchyt --version then fetchyt info "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  • If blocked by YouTube: fetchyt cookies --browser chrome --output cookies.txt, set COOKIES_FILE to 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 (or pip install fetchyt).
  • Step 3: Try a download: fetchyt download "https://www.youtube.com/watch?v=VIDEO" --yes and find the audio in ./downloads.
  • Step 4 (if blocked): fetchyt cookies --browser chrome --output cookies.txt then set COOKIES_FILE=./cookies.txt and rerun.
  • Step 5: Open the web UI: fetchyt server then visit http://localhost:8098.
Screenshot – Home page of web UI showing URL input and download options

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_FILE and can call yt-dlp’s cookies_from_browser to 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/extract to preview, /api/v1/download to 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 8098 then open http://localhost:8098; everything (static + API) comes from that server.
Screenshot – Web UI showing playlist info

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 --yes to grab audio with chosen format/bitrate; omit --yes to confirm playlists interactively.
  • Bypass bot checks: fetchyt cookies --browser chrome --output cookies.txt then set COOKIES_FILE so future commands automatically send cookies.
  • Serve the UI/API: fetchyt server --host 0.0.0.0 --port 8098 if 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 pytest after 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 ...), set COOKIES_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

Last updated 2026-01-11 18:58:22.307709 IST

[^top]

Comments