Skip to content

CRtheHILLS/yt-dlp-rescue

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

4 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

yt-dlp-rescue

yt-dlp-rescue

YouTube broke your downloads. We fixed it. Permanently.


Version License: MIT yt-dlp YouTube Bot Bypass Cloud Ready Node.js Python Docker PRs Welcome Stars


  _   _          _ _
 | | | |        | | |
 | |_| |_ _____ | | |_ ____     ____ ___  ___  _____ _ _  ___
 |_   _) ___ | | | |  _ \   / ___) _ \/ __|/ ___ | | |/ _ \
   | |_| ____|| | | | |_) ) | |  |  __/\__ \ (___| |_|  __/
    \__)_____) \_)_)  __/  |_|   \___||___/\____)____/\___|
                    |_|     v2.2 - Battle Tested

YouTube broke downloads in two ways:

Problem 1 πŸ“‰ SABR streaming (2024~) β€” forces 360p regardless of quality setting

Problem 2 🚫 Bot detection (2025~) β€” blocks datacenter/cloud IPs entirely

This repo fixes both. Battle-tested in production serving real users.


⚑ Quick Fix Β β€’Β  πŸ€– Bot Bypass Β β€’Β  ☁️ Cloud Deploy Β β€’Β  πŸ“‹ Commands Β β€’Β  πŸ’Ύ Config Β β€’Β  πŸ› οΈ Troubleshoot



🎬 Before vs After

❌ Before (Broken)

$ yt-dlp -f "bestvideo+bestaudio" URL

[youtube] Extracting URL...
[download] 100% of 9.2MiB

# Requested 1080p -> Got 360p
# Requested 720p  -> Got 360p
# Requested 4K    -> Got 360p

Every quality = same 9MB file (360p) πŸ’€

βœ… After (Fixed)

$ yt-dlp --extractor-args \
  "youtube:player_client=web,android_vr,tv_downgraded" \
  -f "bestvideo*+bestaudio/best" \
  -S "res:1080" URL

[download] 100% of 34.1MiB  <- Real 1080p!

Each quality = correct file size πŸŽ‰


πŸ“Š Proof It Works

Tested with the same video, same URL, different quality settings:

  Quality    File Size    Visualization
  -------    ---------    ----------------------------------------
  360p          9 MB      ##
  480p         15 MB      ####
  720p         21 MB      ######
  1080p        34 MB      #########
  4K          244 MB      ##################################

  Before fix: ALL qualities -> 9 MB (360p)  [BROKEN]
  After fix:  Each quality  -> correct size [FIXED]



⚑ Quick Fix

Copy-paste this. It just works.

yt-dlp \
  --extractor-args "youtube:player_client=tv,web_embedded;player_skip=webpage" \
  -f "bestvideo*+bestaudio/best" \
  -S "res:1080" \
  --force-ipv4 \
  --merge-output-format mp4 \
  "https://youtube.com/watch?v=VIDEO_ID"
Flag What it does
player_client=tv,web_embedded Most reliable combo β€” bypasses SABR, gets full format list (144p~4K)
player_skip=webpage Skips webpage request β€” fewer HTTP calls = less rate limiting
--force-ipv4 Prevents IPv6 routing issues on cloud servers
-S "res:1080" Picks best format matching your target resolution
-f "bestvideo*+bestaudio/best" The * adds progressive fallback for reliability

πŸ’‘ That's literally it. The rest of this README covers bot detection, cloud deployment, and advanced usage.




πŸ”¬ Why It Happens

πŸ“– Click to expand the full technical explanation

The Root Cause: YouTube's SABR Migration

Starting in late 2024, YouTube forced SABR (Server ABR) on the web client:

  +---------------------------------------------+
  |         YouTube's SABR Change               |
  +---------------------------------------------+
  |                                             |
  |  Before (2024):                             |
  |    web client -> 20+ DASH formats           |
  |                  (144p, 240p, ... 4K)       |
  |                                             |
  |  After (2025+):                             |
  |    web client -> 1 progressive format       |
  |                  (360p only!)               |
  |                                             |
  +---------------------------------------------+

Why format IDs (-f 137+140) break

  +------------------+          +------------------+
  |   --dump-json    |          |  Actual Download  |
  |   says: 137      |  ==X==> |  137 not found!   |
  |   available      |          |  (SABR replaced)  |
  +------------------+          +------------------+

  - IDs CHANGE between requests
  - YouTube A/B tests swap formats
  - SABR may list but block downloads

Solution: Use -S (sort) instead β€” yt-dlp picks the best match at download time.

Which clients work? (Updated March 2026)

Client Status Quality Range Token Needed Notes
web (default) ❌ SABR only 360p PO Token Broken for quality selection
android_vr βœ… Full DASH 144p ~ 4K None May intermittently return 360p only
tv_downgraded βœ… Full DASH 144p ~ 4K None Most reliable fallback
web_creator βœ… Full DASH 144p ~ 4K None YouTube Studio client
mweb βœ… Full DASH 144p ~ 4K None Mobile web client
web_embedded βœ… Full DASH 144p ~ 4K None Embedded player client

By combining multiple clients, yt-dlp gets the full format catalog from whichever responds.




πŸ€– Bot Detection Bypass

🚨 New in 2025-2026: YouTube aggressively blocks datacenter IPs and suspicious requests with "Sign in to confirm you're not a bot" errors.


πŸ”„ Strategy 1: Client Rotation (Free, No Setup)

If one client gets blocked, rotate to another:

# Primary strategy
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" URL

# Blocked? Try these alternatives in order:
yt-dlp --extractor-args "youtube:player_client=tv_downgraded,web,android_vr" URL
yt-dlp --extractor-args "youtube:player_client=android_vr,tv_downgraded,web" URL
yt-dlp --extractor-args "youtube:player_client=web_creator,tv_downgraded,android_vr" URL
yt-dlp --extractor-args "youtube:player_client=mweb,tv_downgraded,web" URL

πŸ” Strategy 2: PO Token Auto-Generation (THE KEY FIX)

🎯 This is the #1 fix that actually solved our production issues. Without proper PO Token setup, datacenter servers get blocked even with client rotation.

PO Tokens (Proof-of-Origin) prove to YouTube that you're not a bot. There are two modes:

Mode A: Plugin Only (for personal use)

# Install the PO Token provider plugin
pip install bgutil-ytdlp-pot-provider

# Use with yt-dlp (Node.js required)
yt-dlp --js-runtimes node \
  --remote-components ejs:github \
  --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  URL

Mode B: Dedicated PO Token Server (for cloud/production - RECOMMENDED)

# 1. Clone and build the PO Token server
git clone https://github.com/Brainicism/bgutil-ytdlp-pot-provider.git
cd bgutil-ytdlp-pot-provider/server
npm ci && npx tsc

# 2. Start the server (runs on port 4416)
node build/main.js &

# 3. CRITICAL: Set this env var so yt-dlp connects to the server
export YT_DLP_POT_PROVIDER_URL="http://127.0.0.1:4416"

# 4. Now yt-dlp automatically uses the PO Token server
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" URL

🚨 Common mistake: Running the PO Token server WITHOUT setting YT_DLP_POT_PROVIDER_URL. The server runs fine but yt-dlp doesn't know it exists! This single env var was the fix that solved our production bot-blocking issues.

🧹 Strategy 3: Clear Cache Regularly

YouTube's challenge solver responses go stale:

# Clear cache when downloads start failing
yt-dlp --rm-cache-dir

# Then retry
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" URL

πŸͺ Strategy 4: Browser Cookies (Last Resort)

# Auto-extract from browser (easiest)
yt-dlp --cookies-from-browser chrome URL
yt-dlp --cookies-from-browser firefox URL

# Or export manually with a browser extension
yt-dlp --cookies cookies.txt URL

⚠️ Cookies expire and need periodic re-export. The strategies above are preferred for automation.




☁️ Cloud/Server Deployment

Running yt-dlp on Railway, Heroku, AWS, GCP, or any cloud server? YouTube blocks datacenter IPs at the network level. Here's how to fix it permanently.

⚠️ The Problem

  Your Server (Datacenter IP)
         |
         v
  +-------------------+
  |     YouTube       |
  |                   |
  |  IP Reputation DB |-----> BLOCKED
  |  "Datacenter IP   |
  |   detected"       |
  +-------------------+

βœ… The Solution: Cloudflare WARP Proxy

Route yt-dlp through Cloudflare's network. YouTube doesn't block Cloudflare IPs.

Using wireproxy (userspace WireGuard, no root/NET_ADMIN needed):

# 1. Install wgcf (WARP credential generator)
curl -fsSL -o /usr/local/bin/wgcf \
  "https://github.com/ViRb3/wgcf/releases/download/v2.2.22/wgcf_2.2.22_linux_amd64"
chmod +x /usr/local/bin/wgcf

# 2. Register free WARP account + generate WireGuard config
wgcf register --accept-tos
wgcf generate

# 3. Download wireproxy
# https://github.com/pufferffish/wireproxy/releases

# 4. Add SOCKS5 section to config
cat >> wgcf-profile.conf << 'EOF'

[Socks5]
BindAddress = 127.0.0.1:1080
EOF

# 5. Start proxy + use with yt-dlp
wireproxy -c wgcf-profile.conf &
yt-dlp --proxy socks5://127.0.0.1:1080 URL

Using Docker (one-liner):

# Pre-built WARP proxy images
docker run -d -p 1080:1080 ghcr.io/kingcc/warproxy:latest
# or
docker run -d -p 9091:9091 ghcr.io/mon-ius/docker-warp-socks:v5

# Then use with yt-dlp
yt-dlp --proxy socks5://127.0.0.1:1080 URL

πŸ—οΈ Production Architecture

This is the exact architecture running in production, serving real users worldwide.

  +-------------------------------------------------------+
  |                 Docker Container                       |
  |                                                        |
  |   +------------------+    +--------------------+       |
  |   | bgutil PO Token  |--->| HTTP :4416         |       |
  |   | Server (Node.js) |    | Auto-generates     |       |
  |   |                  |    | YouTube auth tokens |       |
  |   +------------------+    +--------------------+       |
  |         |                          |                   |
  |         |  YT_DLP_POT_PROVIDER_URL |                   |
  |         |  = http://127.0.0.1:4416 |                   |
  |         v                          v                   |
  |   +------------------+    +--------------------+       |
  |   | yt-dlp           |--->| 5 client strategies|       |
  |   | + PO Token plugin|    | + free proxy pool  |       |
  |   +------------------+    +--------------------+       |
  |         |                                              |
  |   +------------------+    +--------------------+       |
  |   | Flask / Gunicorn |--->| HTTP :8080         |       |
  |   |                  |    | Web Interface       |       |
  |   +------------------+    +--------------------+       |
  |                                                        |
  +-------------------------------------------------------+

πŸ’‘ The critical connection: YT_DLP_POT_PROVIDER_URL env var links yt-dlp to the PO Token server. Without it, the server runs but yt-dlp ignores it.

6-Layer Defense System:

Layer Defense Description
1 YouTube oEmbed API Gets video metadata (title, thumbnail, author) without yt-dlp β€” never blocked, unlimited
2 PO Token Server bgutil generates auth tokens + YT_DLP_POT_PROVIDER_URL connects to yt-dlp
3 Client Rotation 4 player_client strategies (tv,web_embedded first), auto-fallback on failure
4 Request Serialization Semaphore prevents parallel YouTube API calls + 10-min video info cache
5 Free Proxy Pool Auto-fetches proxies from ProxyScrape as last resort
6 --force-ipv4 + player_skip=webpage Reduces HTTP calls to YouTube, prevents IPv6 routing issues

πŸ“‘ Pro Tip: Use oEmbed for Video Info (Web App Developers)

🚨 If you're building a web app, don't use yt-dlp for video metadata. YouTube rate-limits datacenter IPs after just 1-2 requests. Use the free, unlimited oEmbed API instead:

import urllib.request, json

def get_video_info(youtube_url):
    """Get title, author, thumbnail β€” never blocked by YouTube."""
    oembed = f"https://www.youtube.com/oembed?url={youtube_url}&format=json"
    with urllib.request.urlopen(oembed, timeout=10) as r:
        return json.loads(r.read())

# Returns: {"title": "...", "author_name": "...", "thumbnail_url": "...", ...}
# Use yt-dlp ONLY for the actual download, not for metadata.

This single change eliminated 90% of our "temporarily blocking requests" errors in production.




πŸ“‹ All Commands

πŸŽ₯ Video Download

# Just change the number after "res:" !

# 4K (best available)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -f "bestvideo*+bestaudio/best" --merge-output-format mp4 "URL"

# 1080p (Full HD)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -f "bestvideo*+bestaudio/best" -S "res:1080" --merge-output-format mp4 "URL"

# 720p (HD)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -f "bestvideo*+bestaudio/best" -S "res:720" --merge-output-format mp4 "URL"

# 480p (SD)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -f "bestvideo*+bestaudio/best" -S "res:480" --merge-output-format mp4 "URL"

# 360p (Low)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -f "bestvideo*+bestaudio/best" -S "res:360" --merge-output-format mp4 "URL"

🎡 Audio Only

# MP3 (192kbps - standard)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -x --audio-format mp3 --audio-quality 192 "URL"

# MP3 (320kbps - highest quality)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -x --audio-format mp3 --audio-quality 320 "URL"

# FLAC (lossless)
yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  -x --audio-format flac "URL"

πŸ” List Available Formats

yt-dlp --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" -F "URL"

πŸ›‘οΈ Full Command with All Protections

yt-dlp \
  --extractor-args "youtube:player_client=web,android_vr,tv_downgraded" \
  --js-runtimes node \
  --remote-components ejs:github \
  --rm-cache-dir \
  -f "bestvideo*+bestaudio/best" \
  -S "res:1080" \
  --merge-output-format mp4 \
  --retries 3 \
  --fragment-retries 3 \
  "URL"



πŸ’Ύ Permanent Config

Tired of typing long commands? Save this once, use forever.

Create ~/.config/yt-dlp/config:

# yt-dlp-rescue config v2.0 (March 2026)
# https://github.com/bravomylife-lab/yt-dlp-rescue

# Fix YouTube SABR quality selection
--extractor-args youtube:player_client=web,android_vr,tv_downgraded

# Best video + audio with progressive fallback
-f bestvideo*+bestaudio/best

# Output format
--merge-output-format mp4

# Bot detection bypass: PO Token auto-generation
# Requires: pip install bgutil-ytdlp-pot-provider
# Requires: Node.js installed
# --js-runtimes node
# --remote-components ejs:github

# Reliability settings
--retries 3
--fragment-retries 3
--socket-timeout 30

Now just run:

yt-dlp -S "res:1080" "URL"   # That's it!



πŸ› οΈ Troubleshooting

😀 Still getting 360p
# 1. Update yt-dlp to latest
pip install -U yt-dlp

# 2. Clear stale cache
yt-dlp --rm-cache-dir

# 3. Try different client order
yt-dlp --extractor-args "youtube:player_client=tv_downgraded,web_creator,mweb" \
  -f "bestvideo*+bestaudio/best" -S "res:1080" "URL"

# 4. Check what formats are actually available
yt-dlp --extractor-args "youtube:player_client=tv_downgraded" -F "URL"
πŸ€– "Sign in to confirm you're not a bot"

This means YouTube flagged your IP. Try in order:

# 1. Clear cache + different client
yt-dlp --rm-cache-dir
yt-dlp --extractor-args "youtube:player_client=tv_downgraded,web,android_vr" "URL"

# 2. Enable PO Token generation (install once)
pip install bgutil-ytdlp-pot-provider
yt-dlp --js-runtimes node --remote-components ejs:github "URL"

# 3. Use WARP proxy (if on cloud/datacenter)
yt-dlp --proxy socks5://127.0.0.1:1080 "URL"

# 4. Last resort: browser cookies
yt-dlp --cookies-from-browser chrome "URL"
🚫 "HTTP Error 403: Forbidden"
# Your IP is blocked. Rotate user agent + client:
yt-dlp --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
  --extractor-args "youtube:player_client=tv_downgraded,web_creator" "URL"

# If on cloud server, use WARP proxy (see Cloud Deployment section)
πŸ”§ ffmpeg not found
# macOS
brew install ffmpeg

# Ubuntu / Debian
sudo apt install ffmpeg

# Windows
winget install ffmpeg

# Verify
ffmpeg -version

Without ffmpeg, you're limited to progressive formats. Install ffmpeg for the full quality range.

πŸ’‘ What does the * in bestvideo* mean?
-f "bestvideo*+bestaudio/best"
             ^
             +-- This asterisk = safety net!

  bestvideo   ->  video-only streams
  bestvideo*  ->  video-only + progressive (video+audio) streams

If DASH merge fails, the * lets yt-dlp fall back to
a single-file format instead of failing completely.
πŸ’» Works locally, fails on my server

Your server has a datacenter IP which YouTube blocks at the network level. This cannot be fixed with cookies or PO Tokens alone.

➑️ See the Cloud/Server Deployment section for the WARP proxy solution.




πŸ“ƒ Quick Reference Card

+------------------------------------------------------------------+
|                    yt-dlp-rescue v2.2                             |
+------------------------------------------------------------------+
|                                                                  |
|  QUALITY FIX (SABR bypass):                                      |
|    --extractor-args                                              |
|      "youtube:player_client=tv,web_embedded;player_skip=webpage" |
|    -f "bestvideo*+bestaudio/best"                                |
|    -S "res:1080" --force-ipv4                                    |
|                                                                  |
|  BOT DETECTION FIX:                                              |
|    pip install bgutil-ytdlp-pot-provider                         |
|    export YT_DLP_POT_PROVIDER_URL="http://127.0.0.1:4416"       |
|                                                                  |
|  WEB APP METADATA (no rate limit):                               |
|    youtube.com/oembed?url=VIDEO_URL&format=json                  |
|                                                                  |
|  WHEN THINGS BREAK:                                              |
|    yt-dlp --rm-cache-dir                                         |
|    pip install -U "yt-dlp[default] @ https://github.com/         |
|      yt-dlp/yt-dlp-nightly-builds/releases/latest/...tar.gz"    |
|                                                                  |
+------------------------------------------------------------------+



πŸ“… Changelog

Version Date Changes
v2.2.0 2026-03-13 oEmbed API for metadata, player_skip=webpage + --force-ipv4, request serialization + caching, yt-dlp nightly support
v2.1.0 2026-03-13 PO Token server mode (YT_DLP_POT_PROVIDER_URL), free proxy auto-rotation, production-tested fix
v2.0.0 2026-03-13 Bot detection bypass, cloud deployment guide, WARP proxy, 5-layer defense, client rotation
v1.0.0 2026-03-01 Initial release: SABR quality fix, format sort, multi-client strategy



🀝 Contributing

YouTube changes things constantly. Found a better fix? Something stopped working?

Open an Issue Β /Β  Send a PR


⭐ If this rescued your downloads, leave a star

It helps others find this fix too.




Keywords: yt-dlp fix, youtube 360p fix, youtube sabr fix, yt-dlp quality fix, youtube download broken, yt-dlp bot detection, youtube sign in to confirm, yt-dlp cloud server, yt-dlp datacenter blocked, yt-dlp proxy, yt-dlp warp, youtube downloader fix 2025, youtube downloader fix 2026, yt-dlp player_client, yt-dlp format sort, yt-dlp rescue, youtube quality stuck 360p, youtube oembed api, yt-dlp temporarily blocking, youtube rate limit bypass, yt-dlp po token server, player_skip webpage, yt-dlp nightly, yt-dlp railway deploy, youtube bot bypass free


Made with determination by @bravomylife-lab

About

YouTube broke your downloads. We fixed it. Permanently. SABR quality fix + bot detection bypass + cloud deployment guide. Battle-tested in production.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors