Skip to content

Instantly share code, notes, and snippets.

@devinschumacher
Last active October 30, 2025 15:12
Show Gist options
  • Select an option

  • Save devinschumacher/73bca8b4c4554e0a295be8d175b2ac77 to your computer and use it in GitHub Desktop.

Select an option

Save devinschumacher/73bca8b4c4554e0a295be8d175b2ac77 to your computer and use it in GitHub Desktop.
How to Download YouTube Videos for Free

๐Ÿ“ฅ How to Download YouTube Videos for Free with yt-dlp


๐Ÿ‘‰ Get the Youbtube Video Downloader


1. The Basic Command

For most public YouTube videos, this works fine:

yt-dlp -f "bv*+ba/best" \
  --merge-output-format mp4 \
  "https://www.youtube.com/watch?v=VIDEO_ID"
  • -f "bv*+ba/best" โ†’ pick best video + best audio.
  • --merge-output-format mp4 โ†’ final file is MP4 even if YouTube offers WebM.

2. Why Downloads Sometimes Fail (403 Errors)

YouTube serves every video in multiple delivery protocols:

  • DASH (default) โ†’ uses videoplayback?...range=... URLs.

    • Pros: more quality choices.
    • Cons: sometimes 403s if a range expires.
  • HLS (m3u8 playlists) โ†’ chunked .ts/.mp4 streams.

    • Pros: more resilient, rarely 403s.
    • Cons: fewer quality tiers in some cases.

๐Ÿ‘‰ Thatโ€™s why you sometimes see:

ERROR: unable to download video data: HTTP Error 403: Forbidden

The fix is to have fallback commands to switch protocols and adjust retry behavior.

3. Command Sequence You Should Try

๐Ÿ”น Step 1: Normal DASH Download (default)

yt-dlp -f "bv*+ba/best" -N 16 \
  --merge-output-format mp4 \
  "https://www.youtube.com/watch?v=VIDEO_ID"
  • -N 16 โ†’ download 16 fragments at once (faster).
  • โœ… Try this first โ€” usually works.

๐Ÿ”น Step 2: Force HLS if DASH 403s

yt-dlp -f "bv*[protocol=m3u8]+ba[protocol=m3u8]/best" \
  --hls-prefer-native -N 16 \
  --merge-output-format mp4 \
  "https://www.youtube.com/watch?v=VIDEO_ID"
  • Switches to .m3u8 streams.
  • โœ… Best fallback when DASH fails.

๐Ÿ”น Step 3: Add Stronger Retry Logic

yt-dlp -f "bv*[protocol=m3u8]+ba[protocol=m3u8]/best" \
  --hls-prefer-native -N 16 \
  --retries 20 --fragment-retries 100 \
  --merge-output-format mp4 \
  "https://www.youtube.com/watch?v=VIDEO_ID"
  • Retries aggressively if any chunk fails.
  • โœ… Good for long/high-bitrate videos.

๐Ÿ”น Step 4: Use aria2c for Maximum Resilience

First install aria2:

brew install aria2

Then run:

yt-dlp -f "bv*+ba/best" \
  --downloader aria2c \
  --downloader-args "aria2c:-x 16 -s 16 -k 1M --max-tries=0 --retry-wait=5" \
  --merge-output-format mp4 \
  "https://www.youtube.com/watch?v=VIDEO_ID"
  • -x 16 -s 16 โ†’ 16 parallel connections.
  • Retries indefinitely until the file completes.
  • โœ… Almost bulletproof, even on unstable networks.

4. Extra Tips

  • Clear cache if signatures go stale:

    yt-dlp --rm-cache-dir
  • Age-restricted or region-locked videos: Use cookies from your browser:

    yt-dlp --cookies-from-browser "chrome:Default" "URL"
  • Specific resolution:

    yt-dlp -f "bv*[height=1080]+ba/best" "URL"

โœ… Summary

  • yt-dlp is the tool to use for YouTube โ€” there isnโ€™t a better maintained alternative.

  • Sometimes downloads 403 mid-way because YouTubeโ€™s CDN rejects DASH requests.

  • Solution: keep a 4-step playbook:

    1. Normal DASH with concurrency.
    2. HLS fallback.
    3. HLS + retries.
    4. aria2c external downloader.

With this sequence, youโ€™ll succeed in almost every case.

Great question ๐Ÿ‘

Unfortunately, yt-dlp itself doesnโ€™t have a built-in โ€œtry this, and if it fails, try thatโ€ fallback sequence. It will either succeed or exit with an error. But you can string your four approaches together in one shell command using || (OR chaining).

That way, if the first command fails, the shell runs the next one automatically.


One-liner โ€œall in oneโ€ fallback command

yt-dlp -f "bv*+ba/best" -N 16 --merge-output-format mp4 "https://www.youtube.com/watch?v=VIDEO_ID" \
|| yt-dlp -f "bv*[protocol=m3u8]+ba[protocol=m3u8]/best" --hls-prefer-native -N 16 --merge-output-format mp4 "https://www.youtube.com/watch?v=VIDEO_ID" \
|| yt-dlp -f "bv*[protocol=m3u8]+ba[protocol=m3u8]/best" --hls-prefer-native -N 16 --retries 20 --fragment-retries 100 --merge-output-format mp4 "https://www.youtube.com/watch?v=VIDEO_ID" \
|| yt-dlp -f "bv*+ba/best" --downloader aria2c --downloader-args "aria2c:-x 16 -s 16 -k 1M --max-tries=0 --retry-wait=5" --merge-output-format mp4 "https://www.youtube.com/watch?v=VIDEO_ID"

๐Ÿ” How it works

  • cmd1 || cmd2 || cmd3 ... Shell runs cmd1.

    • If it succeeds โ†’ stop.
    • If it fails (non-zero exit code) โ†’ run the next one.

So this tries:

  1. Normal DASH with concurrency.
  2. HLS fallback.
  3. HLS with retries.
  4. aria2c external downloader.

Whichever works first will complete the download.

Yep โ€” you can chain them, but itโ€™s cleaner to try max-quality (MKV, AV1/VP9 OK) first, then fall back to MP4-only variants, with HLS + retries, and finally aria2c. Hereโ€™s a single paste-and-go you can reuse.

One-liner (Zsh/Bash)

VIDEO_URL="https://www.youtube.com/watch?v=S-mow-gu2XA"; \
yt-dlp -N 16 -f "bestvideo*+bestaudio/best" \
  --remux-video mkv "$VIDEO_URL" \
|| yt-dlp -N 16 -f "bv*+ba/best" \
  --http-chunk-size 10M --retries 20 --fragment-retries 100 --force-ipv4 \
  --remux-video mkv "$VIDEO_URL" \
|| yt-dlp -N 16 -f "bv*[protocol=m3u8]+ba[protocol=m3u8]/best" \
  --hls-prefer-native --retries 20 --fragment-retries 100 \
  --remux-video mkv "$VIDEO_URL" \
|| yt-dlp -N 16 -f "bv*[ext=mp4][vcodec*=avc1]+ba[ext=m4a]/best[ext=mp4]" \
  --merge-output-format mp4 "$VIDEO_URL" \
|| yt-dlp -f "bv*+ba/best" \
  --downloader aria2c \
  --downloader-args "aria2c:-x 16 -s 16 -k 1M --max-tries=0 --retry-wait=5" \
  --merge-output-format mp4 "$VIDEO_URL"

What this does (in order)

  1. Max quality (DASH) โ†’ grabs AV1/VP9 + best audio, remux to MKV (keeps 4K/HDR).
  2. DASH w/ tougher HTTP โ†’ smaller chunks, retries, IPv4; still MKV so high tiers are allowed.
  3. Force HLS โ†’ more resilient when DASH 403s; remux MKV (usually โ‰ค1080p).
  4. MP4-only โ†’ AVC + M4A for widest compatibility (often โ‰ค1080p).
  5. Aria2c fallback โ†’ segmented downloader with infinite retries; outputs MP4.

Tips

  • Change VIDEO_URL once; the chain reuses it.
  • Want a specific res for MKV path? e.g., 2160p: -f "bestvideo*[height=2160]+bestaudio/best".
  • Add an output template if you want a specific filename/path: -o "%(title)s.%(ext)s".
  • If a password/exclamation ever appears in args, quote it with single quotes in zsh.

Related

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment