| Validation | `python3 -m py_compile app.py`, `docker build -t yt-dl .`, homepage button check, and a live `video720` MP4 job passed; the delivered video was 640x360, confirming the cap and lower-quality fallback path. |
| Commit | Pending reviewer approval |
| Next Role | review |
---
### T-007 — implement — 2026-09-11T14:28:37Z
| Field | Value |
|-------|-------|
| Agent | codex |
| Summary | Squashed the reviewed 720p video download option into the task commit. |
| Commit | Created by this handoff's task commit |
| Next Role | none |
---
### T-007 — review — 2026-09-11T14:27:56Z
| Field | Value |
|-------|-------|
| Agent | claude |
| Summary | Reviewed and verified the 720p video mode: rebuilt the Docker image, proved the format-selector cap+fallback correct against yt-dlp's real selection engine with synthetic 720p/sub-720p format sets (since real sources reachable in this environment currently top out at 360p due to YouTube's SABR rollout), and drove live video720/MP3/Video/playlist/error-path jobs through the container — all passed. |
@ -230,3 +230,43 @@ No blocker, major, minor, or nit findings.
**Risks:**
**Risks:**
- This is an explicitly best-effort mitigation (as documented in `README.md` and `ROADMAP.md`): YouTube's anti-bot posture changes over time, and the `android`/`ios`/`tv` clients could themselves become restricted later, requiring the client list to be retuned. No action needed now; this is a known, accepted risk per the plan's scope.
- This is an explicitly best-effort mitigation (as documented in `README.md` and `ROADMAP.md`): YouTube's anti-bot posture changes over time, and the `android`/`ios`/`tv` clients could themselves become restricted later, requiring the client list to be retuned. No action needed now; this is a known, accepted risk per the plan's scope.
- The live test observed `WARNING: ... Some android/ios client https formats have been skipped ... SABR-only streaming experiment` — a yt-dlp/YouTube-side warning unrelated to this change (it did not block the download, format selection fell through to a working format) — noted for awareness, not a regression introduced by T-006.
- The live test observed `WARNING: ... Some android/ios client https formats have been skipped ... SABR-only streaming experiment` — a yt-dlp/YouTube-side warning unrelated to this change (it did not block the download, format selection fell through to a working format) — noted for awareness, not a regression introduced by T-006.
---
## T-007 — `app.py` (`video720` mode: `Literal` type extension, `bestvideo[height<=720]+bestaudio/best[height<=720]` format branch, third HTML button) + `README.md` update
**Verdict:** PASS
### Findings
No blocker, major, minor, or nit findings.
### Verification
**Steps performed:**
- Re-read `.ai/PLAN.md` (Priority 5 / T-007 spec) and diffed it against `app.py` / `README.md`. Implementation matches exactly: `Literal["mp3", "video", "video720"]` extended on both the `/download` route's `mode``Form(...)` parameter and `JobState.mode`; a `elif mode == "video720":` branch setting `format: "bestvideo[height<=720]+bestaudio/best[height<=720]"` and `merge_output_format: "mp4"`, placed before the existing unrestricted-`video` `else` branch; a third HTML button `Download Video 720p` calling `downloadFile('video720')`, reusing the mode-agnostic `downloadFile` JS unchanged. `README.md` documents the new button and its cap/fallback behavior.
- `GET /` on the running container confirmed the "Download Video 720p" button is present in the HTML.
- **Format-selector correctness, verified directly against the format-selection engine of the installed yt-dlp (2026.08.19) inside the container**, since real YouTube sources reachable from this environment currently only exposed up to 360p (YouTube's ongoing SABR-only-streaming rollout was blocking higher-resolution `https` formats on every test video tried across `android`/`ios`/`tv`/`web` clients — confirmed via `docker logs` warnings and a direct `extract_info` format dump; not something this app or task controls). To verify the cap and fallback logic itself rather than being blocked by an unrelated environmental limitation:
- Constructed synthetic format lists inside the container and ran them through `yt_dlp.YoutubeDL.build_format_selector` with the app's exact selector string, after the required `ydl.sort_formats(info)` pre-sort step (confirmed this step matters: without it the selector silently picks the wrong entry, which the initial harness attempt caught and was corrected).
- Source with formats at 1080p/720p/480p → `bestvideo[height<=720]+bestaudio/best[height<=720]` selected the muxed pair `(v720, audio)` — confirms the cap actually restricts above-720p sources.
- Source with formats only at 480p/360p (no 720p) → the same selector selected `v480` (best available) — confirms graceful fallback to the best sub-720p quality with no error, matching the "no upscaling, no error" requirement.
- `bestvideo` with no filter on the 1080p/720p/480p set correctly selected `v1080`, confirming the harness itself (once correctly pre-sorted) reflects real yt-dlp selection behavior, not an artifact of the test setup.
- Ran the container and drove the real job flow live for `video720`:
- `POST /download` (mode `video720`, `7MrdyaSlOfI`) → completed, `status: "finished"`, delivered a valid MP4 (`file` confirms ISO Media MP4) with a correctly sanitized `Content-Disposition` filename; progress percent/speed/downloaded/total advanced correctly through completion (this source's real available formats topped out at 360p per the SABR limitation above, so this run exercises the fallback path live, complementing the synthetic-selector proof of the capping path).
- Regression: plain `mp3` and plain unrestricted `video` mode against a different video (`jNQXAC9IVRw`) both completed and delivered correct, correctly-named files (`Me at the zoo.mp3` / `.mp4`) — unaffected by the new mode.
- Regression: `video720` against a playlist-context URL (`&list=...`) — `docker logs` showed `Downloading just the video ... because of --no-playlist`, confirming T-003's single-video behavior extends to the new mode.
- Error path: `video720` against a non-existent URL → job reached `status: "error"` with a clear message, no server crash, server remained responsive — confirming existing error handling is unaffected.
- Stopped and removed the test container, deleted downloaded test files.
**Findings from verification:** All acceptance criteria for T-007 hold:
- The UI shows three buttons: Download MP3, Download Video, Download Video 720p.
- The format selector caps at 720p when higher-resolution sources exist (verified directly against yt-dlp's selection engine) and falls back to the best available quality with no error when the source is below 720p (verified both synthetically and live).
- The 720p mode shows the same live progress bar and auto-delivery behavior as the existing modes (verified live).
- The delivered filename follows the same sanitized-title convention with the correct extension (verified live).
- Existing Download MP3 and Download Video behavior is unchanged (verified live, both regressions passed).
- `python -m py_compile app.py` passes.
**Risks:**
- Direct live confirmation that a real >720p YouTube source gets *downscaled* to exactly 720p (rather than just "the selector logic caps correctly," which was verified against synthetic data) was not obtainable in this environment: every real video URL tried during this review only exposed up to 360p via the available player clients, due to YouTube's current SABR-only-streaming rollout affecting `https`-protocol adaptive formats broadly (unrelated to this task's code). This is an environmental/platform limitation at review time, not a defect in the implementation — the format-selector logic itself was verified correct against the installed yt-dlp's real selection engine. Re-running the live ">720p → capped to 720p" check opportunistically (e.g. against a video/client combination that regains higher-resolution `https` formats as YouTube's rollout evolves) would close this residual gap, but is not a required fix.
| T-004 | `app.py` (job-based `POST /download` + `GET /progress/{job_id}` + `GET /download/{job_id}/file`, in-memory job store, yt-dlp `progress_hooks`, frontend polling + progress bar UI) + `README.md` update | done | Progress bar appears below the buttons on download start and updates with percent/speed/downloaded-total size; file auto-delivers to the browser on completion with the correct sanitized filename; server-side failure shows an error in place of the bar with no crash; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile` and drove the full job lifecycle live through the built Docker image against real YouTube URLs (progress polling, auto-delivered file, 404/409 semantics, server-side error path with no crash) — all passed; see `.ai/REVIEW.md` | none |
| T-004 | `app.py` (job-based `POST /download` + `GET /progress/{job_id}` + `GET /download/{job_id}/file`, in-memory job store, yt-dlp `progress_hooks`, frontend polling + progress bar UI) + `README.md` update | done | Progress bar appears below the buttons on download start and updates with percent/speed/downloaded-total size; file auto-delivers to the browser on completion with the correct sanitized filename; server-side failure shows an error in place of the bar with no crash; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile` and drove the full job lifecycle live through the built Docker image against real YouTube URLs (progress polling, auto-delivered file, 404/409 semantics, server-side error path with no crash) — all passed; see `.ai/REVIEW.md` | none |
| T-005 | `Dockerfile` (install Deno JS runtime) + `app.py` (point yt-dlp `ydl_opts` at the installed Deno binary and permit official EJS solver retrieval) + `README.md` update | done | `docker build` exits 0; `docker exec <container> deno --version` succeeds; a video URL previously failing with `This video is not available` now downloads successfully; server logs no longer show `No supported JavaScript runtime could be found` during a download; playlist-single-video/progress-bar/filename behavior from T-003/T-004 is unaffected; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile`, `docker build`, verified the `js_runtimes`/`remote_components` `ydl_opts` shape against the installed yt-dlp source, and drove live MP3 downloads (previously-failing URL + playlist-link regression) through the built Docker image — no missing-runtime warning, EJS solver fetched and used, single-video/progress/filename behavior intact; see `.ai/REVIEW.md` | none |
| T-005 | `Dockerfile` (install Deno JS runtime) + `app.py` (point yt-dlp `ydl_opts` at the installed Deno binary and permit official EJS solver retrieval) + `README.md` update | done | `docker build` exits 0; `docker exec <container> deno --version` succeeds; a video URL previously failing with `This video is not available` now downloads successfully; server logs no longer show `No supported JavaScript runtime could be found` during a download; playlist-single-video/progress-bar/filename behavior from T-003/T-004 is unaffected; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile`, `docker build`, verified the `js_runtimes`/`remote_components` `ydl_opts` shape against the installed yt-dlp source, and drove live MP3 downloads (previously-failing URL + playlist-link regression) through the built Docker image — no missing-runtime warning, EJS solver fetched and used, single-video/progress/filename behavior intact; see `.ai/REVIEW.md` | none |
| T-006 | `app.py` (`extractor_args.youtube.player_client` ordering in shared `ydl_opts`) + `README.md` update | done | A video URL previously failing with `Missing required Visitor Data` / `This video is not available` now downloads successfully via an alternate player client; existing playlist-single-video/progress-bar/filename/Deno behavior unaffected; failures still surface a clean UI error with no crash; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile`, `docker build`, confirmed the `player_client` key is read by the installed yt-dlp extractor, and drove live downloads (previously-failing `LV-NXucnyrc`, plain MP3/Video, playlist-link, error-path) through the built Docker image — all passed; see `.ai/REVIEW.md` | none |
| T-006 | `app.py` (`extractor_args.youtube.player_client` ordering in shared `ydl_opts`) + `README.md` update | done | A video URL previously failing with `Missing required Visitor Data` / `This video is not available` now downloads successfully via an alternate player client; existing playlist-single-video/progress-bar/filename/Deno behavior unaffected; failures still surface a clean UI error with no crash; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile`, `docker build`, confirmed the `player_client` key is read by the installed yt-dlp extractor, and drove live downloads (previously-failing `LV-NXucnyrc`, plain MP3/Video, playlist-link, error-path) through the built Docker image — all passed; see `.ai/REVIEW.md` | none |
| T-007 | `app.py` (`video720` mode: `Literal` type extension, `bestvideo[height<=720]+bestaudio/best[height<=720]` format branch, third HTML button) + `README.md` update | ready_for_implement | UI shows a third "Download Video 720p" button; downloads are capped at 720p with graceful fallback to lower quality when the source is below 720p; progress bar, auto-delivery, and sanitized filename behavior match existing modes; existing MP3/Video buttons unchanged; `python -m py_compile app.py` passes | pending | implement |
| T-007 | `app.py` (`video720` mode: `Literal` type extension, `bestvideo[height<=720]+bestaudio/best[height<=720]` format branch, third HTML button) + `README.md` update | done | UI shows a third "Download Video 720p" button; downloads are capped at 720p with graceful fallback to lower quality when the source is below 720p; progress bar, auto-delivery, and sanitized filename behavior match existing modes; existing MP3/Video buttons unchanged; `python -m py_compile app.py` passes | Reviewer re-ran `py_compile`/`docker build`, verified the format-selector cap+fallback directly against yt-dlp's real selection engine with synthetic 720p/sub-720p format sets, and drove live `video720`/MP3/Video/playlist/error-path jobs through the built Docker image — all passed; see `.ai/REVIEW.md` for the note on real >720p sources being unreachable in this environment due to YouTube's current SABR rollout | none |
Open [http://localhost:8080](http://localhost:8080) in a browser, paste a YouTube URL, then select **Download MP3** or **Download Video**.
Open [http://localhost:8080](http://localhost:8080) in a browser, paste a YouTube URL, then select **Download MP3**, **Download Video**, or **Download Video 720p**. The 720p option caps the video at 720p and falls back to the best available lower quality when the source has no 720p stream; it otherwise uses the same progress, automatic delivery, and sanitized filename behavior as the unrestricted Video option.
Downloads run as temporary in-memory jobs, so a progress bar shows percentage, speed, and downloaded size while the server prepares the file. When it finishes, the file is delivered to your browser automatically. Large videos and higher-quality formats can take several minutes, depending on the source video and your network connection.
Downloads run as temporary in-memory jobs, so a progress bar shows percentage, speed, and downloaded size while the server prepares the file. When it finishes, the file is delivered to your browser automatically. Large videos and higher-quality formats can take several minutes, depending on the source video and your network connection.