HeshamHaroon Claude Opus 4.6 (1M context) commited on
Commit
4e77f57
·
1 Parent(s): 91765a4

fix: validate URLs upfront — reject invalid/non-YouTube URLs immediately

Browse files

ZeroGPU blocks all external DNS except HuggingFace services, so only
YouTube URLs (via proxy) and uploaded files work. Previously:
- Invalid text ("hello") was treated as a file path → FileNotFoundError
- Non-YouTube URLs tried download.download_video → hung on DNS
- YouTube URLs without scheme ("youtube.com/...") → treated as file
- YouTube URLs with invalid video ID → confusing proxy error

Now validates source before the pipeline starts:
- Auto-adds https:// for youtube.com/youtu.be URLs missing scheme
- Rejects non-YouTube URLs with clear explanation
- Validates YouTube video ID format (11 chars from URL)
- Rejects garbage input with helpful guidance
- Simplifies Stage 1 download logic (only YouTube proxy or local file)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. pipeline_runner.py +52 -20
pipeline_runner.py CHANGED
@@ -322,23 +322,56 @@ def run_pipeline(
322
  """
323
  from pathlib import Path
324
  import soundfile as sf
325
-
326
- # Resolve slug — YouTube DNS is blocked on ZeroGPU so skip yt-dlp for
327
- # YouTube URLs entirely (avoids 30-60s of pointless DNS retries).
328
  import hashlib
329
- if _is_youtube_url(source):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  match = _YT_RE.search(source)
331
- slug = match.group(1) if match else hashlib.md5(source.encode()).hexdigest()[:12]
332
- elif download.is_url(source):
333
- try:
334
- slug = download.resolve_slug(source)[0]
335
- except Exception:
336
- slug = hashlib.md5(source.encode()).hexdigest()[:12]
337
- else:
 
338
  try:
339
  slug = download.slug_from_path(source)
340
  except Exception:
341
  slug = hashlib.md5(source.encode()).hexdigest()[:12]
 
 
 
342
  proj = ProjectPaths(slug, base_dir=BASE_DIR, target_language=target_language)
343
  proj.ensure_dirs()
344
  tracker = LLMUsageTracker()
@@ -351,18 +384,17 @@ def run_pipeline(
351
  yield stage, f"[{STAGE_NAMES[stage - 1]}] Downloading: {source}", {}
352
 
353
  try:
354
- if download.is_url(source):
355
- if _is_youtube_url(source):
356
- # YouTube: use proxy Space (ZeroGPU blocks youtube.com DNS)
357
- yield stage, f"[{STAGE_NAMES[stage - 1]}] Downloading via YouTube proxy...", {}
358
- _download_via_proxy(source, proj.video)
359
- else:
360
- download.download_video(source, proj.video)
361
  download.extract_audio(proj.video, proj.audio)
362
- elif download.is_audio_file(source):
363
  download.ingest_local_audio(source, proj.audio)
364
- else:
365
  download.ingest_local_video(source, proj.video, proj.audio)
 
 
 
366
  except Exception as exc:
367
  yield stage, f"[{STAGE_NAMES[stage - 1]}] FAILED: {exc}", {"error": str(exc)}
368
  return
 
322
  """
323
  from pathlib import Path
324
  import soundfile as sf
 
 
 
325
  import hashlib
326
+
327
+ # ── Upfront source validation ──────────────────────────────────────
328
+ # ZeroGPU blocks all external DNS except HuggingFace services.
329
+ # Only YouTube URLs (via proxy) and uploaded files work.
330
+ source = source.strip()
331
+ is_local = os.path.exists(source)
332
+ is_url = download.is_url(source)
333
+ is_yt = _is_youtube_url(source)
334
+
335
+ if not is_local and not is_url:
336
+ # Might be a URL without scheme — try adding https://
337
+ if "youtube.com" in source or "youtu.be" in source:
338
+ source = "https://" + source
339
+ is_url = True
340
+ is_yt = True
341
+ else:
342
+ yield 1, (
343
+ f"[ERROR] Invalid source: '{source}'\n"
344
+ "Please provide a YouTube URL (e.g. https://youtube.com/watch?v=...) "
345
+ "or upload a video/audio file."
346
+ ), {"error": "invalid source"}
347
+ return
348
+
349
+ if is_url and not is_yt:
350
+ yield 1, (
351
+ f"[ERROR] Non-YouTube URLs are not supported on this Space.\n"
352
+ f"URL: {source}\n"
353
+ "ZeroGPU blocks external network access. Only YouTube URLs work "
354
+ "(downloaded via proxy). Please use a YouTube link or upload your file directly."
355
+ ), {"error": "unsupported URL"}
356
+ return
357
+
358
+ if is_yt:
359
  match = _YT_RE.search(source)
360
+ if not match:
361
+ yield 1, (
362
+ f"[ERROR] Could not find a valid YouTube video ID in: {source}\n"
363
+ "Expected format: https://youtube.com/watch?v=VIDEO_ID or https://youtu.be/VIDEO_ID"
364
+ ), {"error": "invalid YouTube URL"}
365
+ return
366
+ slug = match.group(1)
367
+ elif is_local:
368
  try:
369
  slug = download.slug_from_path(source)
370
  except Exception:
371
  slug = hashlib.md5(source.encode()).hexdigest()[:12]
372
+ else:
373
+ slug = hashlib.md5(source.encode()).hexdigest()[:12]
374
+
375
  proj = ProjectPaths(slug, base_dir=BASE_DIR, target_language=target_language)
376
  proj.ensure_dirs()
377
  tracker = LLMUsageTracker()
 
384
  yield stage, f"[{STAGE_NAMES[stage - 1]}] Downloading: {source}", {}
385
 
386
  try:
387
+ if is_yt:
388
+ yield stage, f"[{STAGE_NAMES[stage - 1]}] Downloading via YouTube proxy...", {}
389
+ _download_via_proxy(source, proj.video)
 
 
 
 
390
  download.extract_audio(proj.video, proj.audio)
391
+ elif is_local and download.is_audio_file(source):
392
  download.ingest_local_audio(source, proj.audio)
393
+ elif is_local:
394
  download.ingest_local_video(source, proj.video, proj.audio)
395
+ else:
396
+ yield stage, f"[{STAGE_NAMES[stage - 1]}] FAILED: unsupported source", {"error": "unsupported"}
397
+ return
398
  except Exception as exc:
399
  yield stage, f"[{STAGE_NAMES[stage - 1]}] FAILED: {exc}", {"error": str(exc)}
400
  return