Spaces:
Running
Running
Poker commited on
Commit ·
33dda2e
1
Parent(s): f973023
fix: add Chrome TLS impersonation + tv_embedded client + curl-cffi for YouTube bypass
Browse files- app.py +14 -12
- requirements.txt +1 -0
app.py
CHANGED
|
@@ -247,24 +247,25 @@ def clip_youtube_video():
|
|
| 247 |
print(f"Downloading video from {url}...")
|
| 248 |
download_cmd = [
|
| 249 |
ytdlp_bin,
|
| 250 |
-
# Quality: max 720p
|
| 251 |
"-f", "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height<=720]+bestaudio/best[height<=720]/best",
|
| 252 |
"--merge-output-format", "mp4",
|
| 253 |
-
#
|
| 254 |
-
#
|
| 255 |
-
"--
|
|
|
|
|
|
|
|
|
|
| 256 |
# Geo-bypass
|
| 257 |
"--geo-bypass",
|
| 258 |
-
# SSL resilience
|
| 259 |
"--no-check-certificates",
|
| 260 |
"--socket-timeout", "60",
|
| 261 |
-
# Headers to look like a real browser
|
| 262 |
-
"--add-header", "Accept-Language:en-US,en;q=0.9",
|
| 263 |
# Retry logic
|
| 264 |
"--retries", "15",
|
| 265 |
"--fragment-retries", "15",
|
| 266 |
"--retry-sleep", "exp=2:60",
|
| 267 |
-
"--sleep-requests", "
|
| 268 |
# Output
|
| 269 |
"-o", raw_download_path,
|
| 270 |
url
|
|
@@ -272,17 +273,18 @@ def clip_youtube_video():
|
|
| 272 |
|
| 273 |
result = subprocess.run(download_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=480)
|
| 274 |
stderr_text = result.stderr.decode('utf-8', errors='ignore')
|
| 275 |
-
stdout_text = result.stdout.decode('utf-8', errors='ignore')
|
| 276 |
|
| 277 |
if result.returncode != 0 or not os.path.exists(raw_download_path) or os.path.getsize(raw_download_path) == 0:
|
| 278 |
if 'SSL' in stderr_text or 'EOF' in stderr_text or 'network' in stderr_text.lower():
|
| 279 |
-
err = 'YouTube is blocking server downloads.
|
| 280 |
-
elif 'Private
|
| 281 |
err = 'This video is private or members-only. Only public videos can be downloaded.'
|
| 282 |
elif 'removed' in stderr_text or 'unavailable' in stderr_text or 'not available' in stderr_text:
|
| 283 |
err = 'This video is unavailable or has been removed from YouTube.'
|
| 284 |
elif 'Sign in' in stderr_text or 'bot' in stderr_text.lower():
|
| 285 |
-
err = 'YouTube
|
|
|
|
|
|
|
| 286 |
else:
|
| 287 |
err = stderr_text[-600:] if stderr_text else 'Unknown download error'
|
| 288 |
return jsonify({'success': False, 'error': f'Download failed: {err}'}), 500
|
|
|
|
| 247 |
print(f"Downloading video from {url}...")
|
| 248 |
download_cmd = [
|
| 249 |
ytdlp_bin,
|
| 250 |
+
# Quality: max 720p
|
| 251 |
"-f", "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/bestvideo[height<=720]+bestaudio/best[height<=720]/best",
|
| 252 |
"--merge-output-format", "mp4",
|
| 253 |
+
# TLS fingerprint spoofing — impersonate a real Chrome browser at network level
|
| 254 |
+
# This is much more effective than just setting User-Agent headers
|
| 255 |
+
"--impersonate", "chrome",
|
| 256 |
+
# Use tv_embedded player client — bypasses datacenter IP blocking
|
| 257 |
+
# tv_embedded is used for embedded YouTube players and has looser restrictions
|
| 258 |
+
"--extractor-args", "youtube:player_client=tv_embedded,ios,android",
|
| 259 |
# Geo-bypass
|
| 260 |
"--geo-bypass",
|
| 261 |
+
# SSL resilience
|
| 262 |
"--no-check-certificates",
|
| 263 |
"--socket-timeout", "60",
|
|
|
|
|
|
|
| 264 |
# Retry logic
|
| 265 |
"--retries", "15",
|
| 266 |
"--fragment-retries", "15",
|
| 267 |
"--retry-sleep", "exp=2:60",
|
| 268 |
+
"--sleep-requests", "2",
|
| 269 |
# Output
|
| 270 |
"-o", raw_download_path,
|
| 271 |
url
|
|
|
|
| 273 |
|
| 274 |
result = subprocess.run(download_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=480)
|
| 275 |
stderr_text = result.stderr.decode('utf-8', errors='ignore')
|
|
|
|
| 276 |
|
| 277 |
if result.returncode != 0 or not os.path.exists(raw_download_path) or os.path.getsize(raw_download_path) == 0:
|
| 278 |
if 'SSL' in stderr_text or 'EOF' in stderr_text or 'network' in stderr_text.lower():
|
| 279 |
+
err = 'YouTube is blocking server downloads. This is a YouTube restriction on server/datacenter IPs. Try: 1) A less popular/unlisted video, 2) A short clip under 3 min.'
|
| 280 |
+
elif 'Private' in stderr_text or 'members-only' in stderr_text:
|
| 281 |
err = 'This video is private or members-only. Only public videos can be downloaded.'
|
| 282 |
elif 'removed' in stderr_text or 'unavailable' in stderr_text or 'not available' in stderr_text:
|
| 283 |
err = 'This video is unavailable or has been removed from YouTube.'
|
| 284 |
elif 'Sign in' in stderr_text or 'bot' in stderr_text.lower():
|
| 285 |
+
err = 'YouTube detected bot activity. Try a different less-popular public video.'
|
| 286 |
+
elif 'impersonate' in stderr_text.lower() or 'curl' in stderr_text.lower():
|
| 287 |
+
err = 'Browser impersonation not available. Retrying without it — please try again.'
|
| 288 |
else:
|
| 289 |
err = stderr_text[-600:] if stderr_text else 'Unknown download error'
|
| 290 |
return jsonify({'success': False, 'error': f'Download failed: {err}'}), 500
|
requirements.txt
CHANGED
|
@@ -7,3 +7,4 @@ numpy
|
|
| 7 |
Pillow>=10.0.0
|
| 8 |
requests
|
| 9 |
gunicorn
|
|
|
|
|
|
| 7 |
Pillow>=10.0.0
|
| 8 |
requests
|
| 9 |
gunicorn
|
| 10 |
+
curl-cffi>=0.7.0
|