yt-dlp Output Templates: Name and Sort Files Automatically
Quick answer
The -o template turns a folder of random filenames into a library. Fields go in
%(name)s, and slashes create directories. Preview it with
--print filename before committing to 400 videos:
yt-dlp -o "%(uploader)s/%(title)s [%(id)s].%(ext)s" "URL"
yt-dlp --simulate --print filename -o "%(uploader)s/%(title)s.%(ext)s" "PLAYLIST_URL"
Fields worth knowing
| Field | Example |
|---|---|
%(title)s | How to fix yt-dlp 403 errors |
%(id)s | dQw4w9WgXcQ |
%(ext)s | mp4 |
%(uploader)s | Some Channel |
%(channel)s | Some Channel |
%(upload_date)s | 20260801 |
%(duration)s | 212 |
%(resolution)s | 1920x1080 |
%(playlist)s | My Playlist |
%(playlist_index)s | 7 |
%(view_count)s | 1048576 |
%(artist)s, %(track)s, %(album)s | Music metadata, where the site provides it |
For any specific video, this prints every field that actually exists — more useful than a generic list, because the answer varies by site:
yt-dlp --dump-json "URL" | python -m json.tool | head -60
The three modifiers that matter
Padding numbers
%(playlist_index)s gives 1, 2, … 10, and a file manager sorts those as 1, 10, 11,
2. %(playlist_index)02d gives 01, 02, … 10, which sorts correctly:
yt-dlp -o "%(playlist)s/%(playlist_index)02d - %(title)s.%(ext)s" "PLAYLIST_URL"
Formatting dates
A > inside the field applies a strftime format:
yt-dlp -o "%(upload_date>%Y-%m-%d)s - %(title)s.%(ext)s" "URL"
Fallbacks
A comma lists alternatives, and the first present one wins. This is how you stop
NA appearing in filenames:
yt-dlp -o "%(artist,uploader,channel)s/%(album,playlist,title)s/%(track,title)s.%(ext)s" "URL"
Templates worth copying
| For | Template |
|---|---|
| Simple | %(title)s.%(ext)s |
| No collisions | %(title)s [%(id)s].%(ext)s |
| By channel | %(uploader)s/%(title)s [%(id)s].%(ext)s |
| Playlist, ordered | %(playlist)s/%(playlist_index)02d - %(title)s.%(ext)s |
| Chronological archive | %(uploader)s/%(upload_date>%Y)s/%(upload_date>%Y-%m-%d)s - %(title)s.%(ext)s |
| Music | %(artist,uploader)s/%(album,playlist)s/%(track_number,playlist_index)02d - %(track,title)s.%(ext)s |
| Plex / Jellyfin | %(uploader)s/Season %(upload_date>%Y)s/%(uploader)s - S%(upload_date>%Y)sE%(playlist_index)03d - %(title)s.%(ext)s |
Including [%(id)s] is worth the ugliness on any archive. Titles are not unique, get
edited, and collide; the id never does. It is also what lets you find the source of a file two
years later.
Separate paths for separate things
-P sets base directories, optionally per type. Keeping temporary files on fast
local disk while finished files go to a network share is the useful case:
yt-dlp -P "home:/media/archive" -P "temp:/var/tmp" -o "%(uploader)s/%(title)s.%(ext)s" "URL"
It also avoids a real failure: merging over SMB is slow and occasionally produces postprocessing errors that do not happen locally.
Filenames that survive contact with a filesystem
Video titles contain colons, quotes, slashes and emoji. Windows rejects several outright, and every filesystem has a path length limit a long title inside a deep folder will find:
yt-dlp --restrict-filenames --trim-filenames 120 -o "%(uploader)s/%(title)s.%(ext)s" "URL"
--restrict-filenames reduces names to ASCII with no spaces — worth having
permanently if files ever touch a network share, an external drive or an Android device.
Making it permanent
In a config file, and remember: no quotes there.
-o %(uploader)s/%(title)s [%(id)s].%(ext)s
-P ~/Videos/yt-dlp
--restrict-filenames
Testing before committing
Never point a new template at 400 videos untested. This prints the paths and downloads nothing:
yt-dlp --simulate --print filename -o "YOUR TEMPLATE" "PLAYLIST_URL" | head -20
NA anywhere in the output means a field that does not exist for those videos — add
a fallback. Everything landing in one folder when you expected several means a missing slash.
Frequently asked
Why does my filename say NA?
How do I number playlist items so they sort correctly?
Can I include the upload date?
Where do I find the full list of fields?
Related
- The yt-dlp Config File: Stop Retyping the Same Flags Every flag you type twice belongs in yt-dlp.conf. Where the file goes on each OS, the load order that decides...
- Download a Playlist or Whole Channel with yt-dlp Playlists are where yt-dlp goes from a command to a workflow: archives so reruns skip what you have, ranges so...
- Fix "Postprocessing: ffmpeg exited with code 1" in yt-dlp The download finished and the conversion failed — so the fix is never in yt-dlp's flags. How to read the ffmpe...