You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.5 KiB
4.5 KiB
Plan
Status: ready_for_implement
Goal: implement a containerised YouTube downloader web app per ROADMAP.md.
Scope
- Single Docker container running a FastAPI server (Python 3.12-slim + ffmpeg + yt-dlp).
- Minimal browser UI: one URL text field + Download MP3 and Download Video buttons.
- MP3 download: yt-dlp extracts best audio, ffmpeg re-encodes to MP3 at highest quality (
-q:a 0), file streamed back to browser. - Video download: yt-dlp fetches
bestvideo+bestaudio, merges to MP4 via ffmpeg, file streamed back to browser. - Invalid/empty URL returns a JSON error; the UI displays it without crashing.
- No persistent storage — temp dir per request, cleaned up after response.
Acceptance Criteria
docker build -t yt-dl .exits 0.docker run --rm -p 8080:8080 yt-dlstarts the server andcurl http://localhost:8080/returns 200.- A valid YouTube URL + MP3 button delivers a
.mp3file to the browser. - A valid YouTube URL + Video button delivers a
.mp4file to the browser. - An empty URL or garbage URL returns an error visible in the UI.
Implementation Phases
Phase 1 — T-001: FastAPI backend + HTML/JS frontend
Files to create:
app.py— entire server + inline HTMLrequirements.txt
app.py structure:
-
HTML constant (
HTML) — inline single-page UI:- Text input for URL, placeholder "Paste YouTube URL…"
- Two buttons:
Download MP3/Download Video - Status
<div>for errors/progress text - JS
downloadFile(mode):- Reads URL from input, validates non-empty client-side
fetch('/download', { method:'POST', body: FormData{url, mode} })- On success (content-type not application/json): creates object URL →
<a download>click → revoke URL - On error or JSON response: parse JSON and show
.messagein status div
-
FastAPI app (
app):GET /— returnsHTMLResponse(HTML)POST /download—url: str = Form(...),mode: Literal["mp3","video"] = Form(...)- Validate URL non-empty and starts with
http; if not, raiseHTTPException(400) - Create
tempfile.TemporaryDirectory() - Build
ydl_opts:- MP3:
format='bestaudio/best', postprocessorFFmpegExtractAudiowithpreferredcodec='mp3',preferredquality='0' - Video:
format='bestvideo+bestaudio/best',merge_output_format='mp4' outtmpl=tmpdir/%(title)s.%(ext)s
- MP3:
- Run
yt_dlp.YoutubeDL(ydl_opts).download([url])insideasyncio.get_event_loop().run_in_executor(None, ...)to avoid blocking the event loop - Find the downloaded file in tmpdir (glob the single file present)
- Return
FileResponse(path, filename=..., background=BackgroundTask(cleanup_tmpdir))— cleanup runs after response is sent - Wrap yt-dlp call in try/except; on any exception raise
HTTPException(500, detail=str(e))
- Validate URL non-empty and starts with
-
requirements.txt:fastapi>=0.111.0 uvicorn[standard]>=0.29.0 yt-dlp>=2024.4.9 python-multipart>=0.0.9
Key decisions:
- Inline HTML keeps the project a two-file app (plus Dockerfile) — no template engine, no static asset serving needed.
run_in_executorkeeps uvicorn's event loop unblocked while yt-dlp runs (which can take 30–120 s).FileResponsewithBackgroundTaskcleanup is the standard FastAPI pattern for temp file responses.- yt-dlp is called via its Python API (not subprocess) to avoid shell escaping issues with URLs.
Phase 2 — T-002: Dockerfile + README
Files to create/update:
DockerfileREADME.md(update with build/run instructions)
Dockerfile:
FROM python:3.12-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends ffmpeg \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8080
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]
README.md must document:
- Prerequisites: Docker
- Build:
docker build -t yt-dl . - Run:
docker run --rm -p 8080:8080 yt-dl - Usage: open
http://localhost:8080in a browser - Note on video size / download time expectations
Validation
# Build
docker build -t yt-dl .
# Smoke test — server starts and home page returns 200
docker run --rm -d -p 8080:8080 --name yt-dl-test yt-dl
sleep 3
curl -sf http://localhost:8080/ | grep -q "Download MP3"
docker stop yt-dl-test
No unit-test framework is introduced; the app is small enough that the docker smoke test is the validation gate. The reviewer will perform a live download test.