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.
6.5 KiB
6.5 KiB
ROADMAP
Goal: deliver a containerised YouTube downloader web app — one URL input, two download buttons (MP3 / Video), always best available quality.
Priority 1
Objective: Containerised YouTube downloader with a minimal browser UI.
- User pastes a YouTube URL into a single input field.
- Clicking Download MP3 triggers a server-side yt-dlp run that extracts audio at the highest available bitrate (converted to MP3 via ffmpeg) and streams the file back to the browser for download.
- Clicking Download Video triggers a server-side yt-dlp run that fetches the best video+audio quality and streams the file back to the browser for download.
- The entire app runs in a single Docker container (
docker build/docker run -p 8080:8080 yt-dl) with no external dependencies beyond Docker. - No login, no database, no persistent storage required — stateless request/response.
Acceptance Criteria
docker buildcompletes without errors.docker run -p 8080:8080 yt-dlstarts the server.- Pasting a valid YouTube URL and clicking Download MP3 delivers an
.mp3file to the browser. - Pasting a valid YouTube URL and clicking Download Video delivers a best-quality video file (
.mp4/.mkv) to the browser. - Invalid or empty URLs show a clear error message in the UI (no server crash).
Out of Scope
- Playlist batch downloads (only the single video referenced by the pasted link is ever downloaded — see Priority 2).
- User accounts / history.
Priority 2
Objective: single-video-only downloads from playlist links, plus live download progress in the UI.
- If the pasted URL is a playlist link or a video link that also carries a playlist context (e.g.
watch?v=...&list=...), only the one referenced video is downloaded — the rest of the playlist is ignored. Implemented via yt-dlp'snoplaylistoption. - Below the two download buttons, a progress bar area appears once a download starts, showing for the video currently being processed:
- percentage complete
- current download speed
- downloaded size / total size
- Progress transport: the server runs each download as a background job identified by a
job_idand records live progress (from yt-dlp'sprogress_hooks) in server-side memory. The browser polls aGET /progress/{job_id}endpoint (~every 500ms) and updates the bar/text from the response. - File delivery: when a job reaches 100%, the page automatically fetches
GET /download/{job_id}/fileand triggers the browser's normal save behavior — no extra click required, preserving today's one-click UX. - Errors surfaced during the background job (invalid URL, yt-dlp failure) are reported through the same progress endpoint/UI status area used today, without a server crash.
- Still single-container, no external dependencies beyond Docker, no persistent storage — job state is in-memory only and is cleaned up once the file has been delivered (or after a reasonable failure/timeout window).
- The delivered filename is derived from the video's real title, not a random/opaque id (currently the browser sometimes ends up saving with a UUID-like name):
- Take the video title as reported by yt-dlp.
- Strip characters that aren't plain text — emoji, pictograms/icons, and other symbols outside normal letters/digits/punctuation — rather than replacing the whole title.
- Keep letters (any language), digits, spaces, and common punctuation (
- _ . ( ) , '); collapse repeated whitespace left behind by stripped characters and trim the ends. - If stripping leaves an empty name (e.g. an emoji-only title), fall back to the video id so a filename always exists.
- Keep the correct extension for the mode (
.mp3for audio,.mp4/.mkvfor video) after the cleaned title.
Acceptance Criteria (Priority 2)
- Pasting a playlist URL (or a watch URL with a
list=param) and clicking MP3 or Video downloads only that one video, never the rest of the playlist. - Starting a download shows a progress bar below the buttons that updates with percentage, speed, and downloaded/total size while the server is processing.
- When the download finishes, the file is automatically delivered to the browser (save dialog) without an extra click.
- The saved file's name matches the video's title (with emoji/icons/symbols removed, normal characters kept) instead of a UUID or other opaque id, and still ends in the correct extension.
- If the download fails server-side, the UI shows a clear error in place of the progress bar (no server crash, no stuck spinner).
Out of Scope
- Playlist batch downloads (downloading every video in a playlist in one action).
- User accounts / history.
- Persisting job/progress state across server restarts.
Priority 3
Objective: fix downloads failing due to yt-dlp's missing JavaScript runtime.
- Bug: some videos fail with
ERROR: [youtube] <id>: This video is not available, preceded byWARNING: [youtube] No supported JavaScript runtime could be found. Only deno is enabled by default.... yt-dlp falls back to an alternate player client (e.g.visionos) when it can't run JS-based signature/PO-token deciphering, and that fallback path incorrectly reports some videos as unavailable. - Root cause: the Docker image installs no JS runtime, so yt-dlp can't perform the JS-dependent extraction steps modern YouTube extraction increasingly requires.
- Fix: install Deno (yt-dlp's documented lightweight JS runtime) in the
Dockerfile, configure yt-dlp (viaydl_optsand/or a--js-runtimesequivalent) to use it, and allow retrieval of the official GitHub-hosted EJS challenge-solver scripts required by current yt-dlp releases, so normal extraction succeeds without falling back to a degraded client. - Confirm the
WARNING: No supported JavaScript runtime could be foundmessage no longer appears in server logs during a download.
Acceptance Criteria (Priority 3)
docker buildstill completes without errors after adding Deno.- Re-running a download of a video URL that previously failed with
This video is not available(due to the missing JS runtime) succeeds and delivers the file. - Server logs no longer show
No supported JavaScript runtime could be foundduring a normal MP3 or Video download. - Existing Priority 1/2 behavior (single-video-from-playlist, progress bar, filename sanitization) is unaffected.
Out of Scope
- Fixing videos that are genuinely unavailable (age-restricted with no workaround, region-blocked, deleted, private) — this priority only addresses the missing-JS-runtime-induced false negative.
- General yt-dlp version upgrade policy beyond what's needed to pair with the new JS runtime.