5N3BLOG ← 5N3

yt-dlp: the downloader that understands modern streaming

Why a browser and a download manager choke on streams that yt-dlp handles without complaint: fragmented HLS explained, the flags that matter, parallel fragments, cookies, and how it slots into a self-hosted media stack.

Project
Standalone
Difficulty
Beginner
Reading time
7 min
Published
Last updated
Never revised

Point a normal download manager at a modern streaming page and it either grabs nothing or grabs a few kilobytes of playlist file. The software isn't bad. It is looking for something that stopped being there years ago.

yt-dlp is a command-line downloader that knows what replaced it. It's free, open source, a fork of the older youtube-dl, and it supports well over a thousand sites. But the reason it works where other tools do not comes down to one thing worth understanding before any of the flags make sense.

The file you are looking for does not exist#

The old model was simple. A page had a video, the video was a file, and the file had a URL. Find it, download it, done. That's the model download managers were built for.

Streaming sites moved away from it, for reasons that have nothing to do with stopping downloads. A two-hour film as one 4 GB file is a bad fit for the real internet: you cannot start playing until enough has buffered, you cannot switch quality when the connection drops, and a failure part way through means starting again.

So the file gets chopped up. Instead of:

Movie.mp4

the server holds:

fragment0001.ts
fragment0002.ts
fragment0003.ts
...
fragment1025.ts

plus a small text playlist listing them in order. That is HLS (HTTP Live Streaming); DASH is the same idea with different plumbing. The player fetches fragments a few seconds ahead of what you are watching, and switches to a lower-quality set if your connection sags.

A download manager pointed at that page finds the playlist, downloads a text file of a few kilobytes, and reports success. It did exactly what it was asked.

yt-dlp reads the playlist, fetches all 1,025 fragments, and stitches them back into one file. That's the whole trick, and everything below is detail.

Installing it#

Windows:

winget install yt-dlp.yt-dlp

Linux:

sudo apt install yt-dlp
Note

The distro package is often well behind. yt-dlp releases frequently because sites change what they serve, and an old build fails on exactly the sites you want. If apt's version misbehaves, get the current one, though not with sudo pip install, which on Ubuntu 23.04 and later refuses to touch a system-managed Python. Use pipx install yt-dlp, or download the standalone binary from the project's releases page.

Then update it, because you will need to again later:

yt-dlp -U

Install FFmpeg too#

This is the step most guides bury, and it's the one that causes the most confusion.

By default yt-dlp downloads the best video stream and the best audio stream separately, because that is how sites store them, and then merges them. The merge is done by FFmpeg. Extracting audio to MP3 needs FFmpeg. Fixing up HLS fragments into a clean MP4 needs FFmpeg.

Without it, yt-dlp quietly falls back to a lower-quality combined stream, or leaves you two files, and it's not obvious that's what happened.

winget install Gyan.FFmpeg
sudo apt install ffmpeg

The commands that cover most of it#

The simplest form does the right thing:

yt-dlp URL

Before committing to a large download, look at what is actually on offer:

yt-dlp -F URL

You get a table of format IDs, resolutions and bitrates, something like hls-289 through hls-3756, where the number is roughly the bitrate in kbps. Higher means bigger and better.

Pick one explicitly:

yt-dlp -f hls-3756 URL

Choose where it lands, and what it is called:

yt-dlp -P D:\Media -o "%(title)s.%(ext)s" URL

The output template turns 9460844792410.mp4 into something a media server can actually match against a catalogue. Useful fields: %(title)s, %(uploader)s, %(upload_date)s, %(id)s, %(playlist_index)s.

Resume after a dropped connection:

yt-dlp -c URL

And when something fails in a way the message does not explain:

yt-dlp -v URL

Parallel fragments: the flag worth knowing#

This is where the fragmented format turns from a problem into an advantage.

By default fragments come down one after another, and each one pays a fresh round trip before any data moves. On a 1,025-fragment film that latency is most of your download time, and your connection sits idle for a good part of it.

yt-dlp -N 8 URL

-N (--concurrent-fragments) fetches several at once. On HLS content the difference is often dramatic, because you were never bandwidth-limited to begin with.

ConnectionReasonable -N
20 Mbps4
50 Mbps8
100 Mbps8-16
300 Mbps16-24
1 Gbps24-32

Treat these as starting points, not settings. Past a certain number you stop gaining, because the limit moves to the server. Many will throttle or refuse too many concurrent connections from one address, and a few will treat it as abuse. If throughput stops improving, that's your answer; going higher only makes you a worse guest.

Reading the progress line#

[download] 47.1% of 4.48GiB at 5.60MiB/s ETA 06:46 (frag 483/1025)

Left to right: how far through, the estimated final size, current speed, time remaining, and which fragment of how many. That last field is the useful one. If the percentage looks stuck but the fragment counter is climbing, nothing is wrong. The size estimate is just being revised as it goes.

Audio, subtitles, playlists#

Extract audio (needs FFmpeg):

yt-dlp -x --audio-format mp3 URL

Subtitles, either the site's own or machine-generated where offered:

yt-dlp --write-subs URL
yt-dlp --write-auto-subs URL
yt-dlp --write-subs --sub-langs all URL

A playlist, whole or in part:

yt-dlp --playlist-items 1-3 PLAYLIST_URL

And metadata a media server can use:

yt-dlp --write-thumbnail --write-description URL

Cookies, and why to be careful with them#

Some sites only serve video to a logged-in session. yt-dlp can borrow yours:

yt-dlp --cookies-from-browser firefox URL

Two things to know before you use it.

It can fail while the browser is open, because the cookie store is locked or encrypted with a key the running browser holds. Closing the browser first resolves most of it.

Cookies are credentials. A cookie file exported from your browser is a logged-in session for whatever sites it covers. Functionally it is a password that skips two-factor. If you export one to a cookies.txt, treat it exactly like a password: do not commit it, do not paste it into a chat or an issue report, and delete it when the job is done. This is the part of yt-dlp usage most likely to hurt someone, and it has nothing to do with the downloading.

Where it fits in a self-hosted stack#

The command line is the engine; most people end up wrapping it.

Browser
  |
  v
Paste URL into a web front-end (MeTube, Pinchflat, Tube Archivist)
  |
  v
yt-dlp
  |
  v
Media folder
  |
  v
Jellyfin picks it up on the next scan

It runs comfortably in Docker, on a NAS, on a Raspberry Pi, or on the same box as everything else. Because it is a single command with predictable output, it automates well: a cron job, a watched folder, a script that names files to match your library's conventions. That is the argument for a CLI tool over a graphical one: you can put it inside something else.

This is also where the naming templates earn their keep. A media server matches %(title)s.%(ext)s against its catalogue on the next scan; it does nothing useful with 9460844792410.mp4.

What it will not do#

DRM. Anything served through Widevine, PlayReady or FairPlay, which covers most paid streaming services, is not downloadable with yt-dlp, and the project does not implement circumvention. That is a line the project has drawn on purpose.

Beat the server. Your connection is frequently not the bottleneck. If a host caps you at 5 MiB/s, no flag changes that.

Stay working forever without updates. Sites change; extractors break. When something that worked last month stops, run yt-dlp -U before anything else. It's the fix more often than not.

One thing worth saying plainly#

yt-dlp is a tool, and what it is used for is the user's business rather than the tool's. But since this is a technical blog and not a disclaimer generator, the honest version is short:

Downloading something you have the right to download is fine: your own uploads, conference talks and lectures, Creative Commons material, public-domain film, a copy of something you already own for offline use where that is permitted where you live. Downloading a film you have no licence for is copyright infringement whether the tool is yt-dlp, a browser extension, or anything else, and the fact that the file was reachable is not permission.

That distinction is worth keeping straight, because the interesting parts of this tool (the fragment handling, the automation, the archive use cases) have nothing to do with the uninteresting part.

Cheat sheet#

yt-dlp URL                                     # best available
yt-dlp -F URL                                  # list formats
yt-dlp -f FORMAT_ID URL                        # pick one
yt-dlp -N 16 URL                               # parallel fragments
yt-dlp -P D:\Media URL                         # where to save
yt-dlp -o "%(title)s.%(ext)s" URL              # how to name it
yt-dlp -c URL                                  # resume
yt-dlp -x --audio-format mp3 URL               # audio only
yt-dlp --write-subs --sub-langs all URL        # subtitles
yt-dlp --cookies-from-browser firefox URL      # use a login session
yt-dlp --simulate URL                          # dry run
yt-dlp -v URL                                  # verbose, for debugging
yt-dlp -U                                      # update - try this first