From 2e7c6388e2b99f80b68a4407ee601fbfbb39ae9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=9E=AC=ED=98=84?= <231584193+kimdzhekhon@users.noreply.github.com> Date: Sun, 19 Jul 2026 09:46:01 +0900 Subject: [PATCH] fix(transcribe): normalize www.-prefixed URLs before validation is_url() classifies a scheme-less "www.youtube.com/watch?v=..." input as a URL (it's in URL_PREFIXES), but download_audio() passed that raw string straight to security.validate_url(), which rejects it for having no http/https scheme. transcribe_all() only logs the resulting ValueError as a warning and silently drops the file, so any www.-form URL failed unconditionally. Add _normalize_url(): prepends https:// to www.-prefixed input before it reaches validate_url()/yt-dlp. Already-scheme'd URLs and plain file paths pass through unchanged. Found via codex-assisted code review, verified against the actual code before delegating the fix. --- graphify/transcribe.py | 8 ++++++++ tests/test_transcribe.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/graphify/transcribe.py b/graphify/transcribe.py index 8e45bcaa2..a13dbd7e6 100644 --- a/graphify/transcribe.py +++ b/graphify/transcribe.py @@ -47,6 +47,13 @@ def is_url(path: str) -> bool: return any(path.startswith(p) for p in URL_PREFIXES) +def _normalize_url(url: str) -> str: + """Add an HTTPS scheme to URLs that start with ``www.``.""" + if url.startswith('www.'): + return f'https://{url}' + return url + + def download_audio(url: str, output_dir: Path) -> Path: """Download audio-only stream from a URL using yt-dlp. @@ -54,6 +61,7 @@ def download_audio(url: str, output_dir: Path) -> Path: Uses cached file if already downloaded. """ from graphify.security import validate_url + url = _normalize_url(url) validate_url(url) # blocks private IPs, bad schemes before yt-dlp runs yt_dlp = _get_yt_dlp() output_dir.mkdir(parents=True, exist_ok=True) diff --git a/tests/test_transcribe.py b/tests/test_transcribe.py index 8e35f2aaf..1f52df154 100644 --- a/tests/test_transcribe.py +++ b/tests/test_transcribe.py @@ -9,7 +9,9 @@ from graphify.transcribe import ( VIDEO_EXTENSIONS, + _normalize_url, build_whisper_prompt, + is_url, transcribe, transcribe_all, ) @@ -27,6 +29,33 @@ def test_video_extensions_set(): assert ".py" not in VIDEO_EXTENSIONS +# --------------------------------------------------------------------------- +# URL handling +# --------------------------------------------------------------------------- + +def test_normalize_url_adds_https_to_www_url(): + assert _normalize_url("www.youtube.com/watch?v=xyz") == "https://www.youtube.com/watch?v=xyz" + + +@pytest.mark.parametrize("url", [ + "https://www.youtube.com/watch?v=xyz", + "http://www.youtube.com/watch?v=xyz", + "videos/lecture.mp4", +]) +def test_normalize_url_preserves_other_inputs(url): + assert _normalize_url(url) == url + + +@pytest.mark.parametrize("value, expected", [ + ("www.youtube.com/watch?v=xyz", True), + ("http://youtube.com/watch?v=xyz", True), + ("https://youtube.com/watch?v=xyz", True), + ("videos/lecture.mp4", False), +]) +def test_is_url_preserves_prefix_detection(value, expected): + assert is_url(value) is expected + + # --------------------------------------------------------------------------- # build_whisper_prompt # ---------------------------------------------------------------------------