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

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-dl starts the server and curl http://localhost:8080/ returns 200.
  • A valid YouTube URL + MP3 button delivers a .mp3 file to the browser.
  • A valid YouTube URL + Video button delivers a .mp4 file 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 HTML
  • requirements.txt

app.py structure:

  1. 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 .message in status div
  2. FastAPI app (app):

    • GET / — returns HTMLResponse(HTML)
    • POST /downloadurl: str = Form(...), mode: Literal["mp3","video"] = Form(...)
      • Validate URL non-empty and starts with http; if not, raise HTTPException(400)
      • Create tempfile.TemporaryDirectory()
      • Build ydl_opts:
        • MP3: format='bestaudio/best', postprocessor FFmpegExtractAudio with preferredcodec='mp3', preferredquality='0'
        • Video: format='bestvideo+bestaudio/best', merge_output_format='mp4'
        • outtmpl=tmpdir/%(title)s.%(ext)s
      • Run yt_dlp.YoutubeDL(ydl_opts).download([url]) inside asyncio.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))
  3. 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_executor keeps uvicorn's event loop unblocked while yt-dlp runs (which can take 30–120 s).
  • FileResponse with BackgroundTask cleanup 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:

  • Dockerfile
  • README.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:8080 in 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.