vivekchakraverty commited on
Commit
bcfee56
·
verified ·
1 Parent(s): 0fe3056

Download video via RapidAPI (own-CDN) -> faster-whisper transcript + local screenshots -> delete; retire proxies

Browse files
Files changed (1) hide show
  1. pipeline/downloader.py +83 -0
pipeline/downloader.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Download a YouTube video via the ``youtube-video-fast-downloader-24-7`` RapidAPI.
2
+
3
+ The API returns a link on the provider's OWN server (e.g. ``s5-audio.12388101.xyz``), not
4
+ a ``googlevideo.com`` URL — so the Space downloads the file directly, bypassing both the
5
+ egress DPI and the datacenter-IP block, with no IP-lock. The link is prepared
6
+ asynchronously: it 404s for ~20-300s while the provider fetches it, then is live for about
7
+ 10 minutes. We poll until it's ready, then stream it to disk.
8
+
9
+ Requires the ``RAPIDAPI_KEY`` Space secret. Quality ``18`` is 360p muxed (video+audio) —
10
+ small, and its audio track is enough for faster-whisper.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import os
15
+ import time
16
+
17
+ import requests
18
+
19
+ RAPIDAPI_HOST = "youtube-video-fast-downloader-24-7.p.rapidapi.com"
20
+ DEFAULT_QUALITY = "18" # itag 18 = 360p muxed (has audio, ~10-30 MB for typical videos)
21
+
22
+
23
+ class DownloadError(RuntimeError):
24
+ """Raised when the video can't be obtained from the download API."""
25
+
26
+
27
+ def _headers() -> dict:
28
+ key = os.environ.get("RAPIDAPI_KEY", "").strip()
29
+ if not key:
30
+ raise DownloadError("RAPIDAPI_KEY is not set (required for the video download API).")
31
+ return {"X-RapidAPI-Key": key, "X-RapidAPI-Host": RAPIDAPI_HOST}
32
+
33
+
34
+ def _request_urls(video_id: str, quality: str, timeout: int = 60) -> tuple[list[str], dict]:
35
+ """Ask the API for a download link; return candidate URLs (primary + reserved)."""
36
+ url = f"https://{RAPIDAPI_HOST}/download_video/{video_id}"
37
+ try:
38
+ r = requests.get(url, params={"quality": quality}, headers=_headers(), timeout=timeout)
39
+ except requests.RequestException as exc:
40
+ raise DownloadError(f"download API unreachable: {exc}") from exc
41
+ if r.status_code in (401, 403):
42
+ raise DownloadError(f"download API auth failed (HTTP {r.status_code}); "
43
+ "check RAPIDAPI_KEY / that you're subscribed.")
44
+ if r.status_code != 200:
45
+ raise DownloadError(f"download API HTTP {r.status_code}: {r.text[:160]}")
46
+ data = r.json()
47
+ urls = [u for u in (data.get("file"), data.get("reserved_file")) if u]
48
+ if not urls:
49
+ raise DownloadError(f"no download URL in API response: {str(data)[:200]}")
50
+ return urls, data
51
+
52
+
53
+ def download_video(video_id: str, dest: str, quality: str = DEFAULT_QUALITY,
54
+ poll_timeout: int = 330, chunk: int = 1 << 20,
55
+ progress=None) -> str:
56
+ """Download ``video_id`` to ``dest`` and return the path.
57
+
58
+ Polls the (async) provider link until ready (404 -> wait), then streams it to ``dest``.
59
+ Raises DownloadError if it never becomes ready within ``poll_timeout`` seconds.
60
+ """
61
+ urls, _ = _request_urls(video_id, quality)
62
+ deadline = time.time() + poll_timeout
63
+ last = "not ready"
64
+ while time.time() < deadline:
65
+ for url in urls:
66
+ try:
67
+ with requests.get(url, stream=True, timeout=90) as resp:
68
+ if resp.status_code == 404:
69
+ last = "404 (server still preparing)"
70
+ continue
71
+ resp.raise_for_status()
72
+ with open(dest, "wb") as fh:
73
+ for c in resp.iter_content(chunk):
74
+ if c:
75
+ fh.write(c)
76
+ if os.path.getsize(dest) > 0:
77
+ return dest
78
+ except requests.RequestException as exc:
79
+ last = f"{type(exc).__name__}: {exc}"
80
+ if progress:
81
+ progress(0.0, desc="Preparing video (server-side, up to ~5 min)…")
82
+ time.sleep(8)
83
+ raise DownloadError(f"video not ready after {poll_timeout}s ({last}).")