Skip to content

Output & Naming

This page tells you where unshackle writes finished downloads and how it names them. You control naming with output templates (the filename) and folder templates (the directory the file lands in). Both take {variable} placeholders that unshackle fills in from the downloaded media. This page also covers muxing options, release-group tagging, Unicode filenames, and how to relocate unshackle's working directories.

If you have not configured unshackle.yaml yet, read Configuration File first. Everything below lives in that file.

How unshackle assembles an output path

When a download finishes, unshackle builds the final path from three pieces:

<output directory> / <folder template> / <filename template><extension>
  • Output directory: directories.downloads from your config, or whatever you pass to -o/--output on the command line for a single run.
  • Folder template: an optional per-title subfolder. unshackle always puts TV episodes and songs in a folder. Movies only get one if you define a movies folder template, or a single-string folder template that applies to all title kinds (see Folder templates).
  • Filename template: the output_template for the title kind (movie, series, or song).
  • Extension: chosen by the muxer for a movie or an episode: .mkv for video, .mka for audio-only, .mks for subtitle-only. unshackle does not mux a song. It remuxes a song into the container of its codec, such as .flac, .opus, or .m4a. Read Music output files.

A typical episode path

/home/user/Downloads/
  └─ Show Name S01 1080p EXAMPLE WEB-DL DDP5.1 H.264/
     └─ Show.Name.S01E01.Pilot.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv
The folder name comes from the series folder template and the file from the series output template.

Two command-line flags change this layout for a single run:

Flag Effect
-o, --output Use this directory instead of directories.downloads.
--no-folder Skip the per-title subfolder; write the file directly into the output directory.
--no-source Omit the service source tag from both the filename and folder (the {source} variable resolves to empty).

Output templates

Filename templates live under the output_template config key, keyed by title kind. Each value is a format string built from {variable} placeholders.

unshackle.yaml
output_template:
  movies: "{title}.{year}.{quality}.{source}.WEB-DL.{audio_full}.{video}-{tag}"
  series: "{title}.{season_episode}.{episode_name?}.{quality}.{source}.WEB-DL.{audio_full}.{video}-{tag}"
  songs: "{track_number}. {title}"

The recognized kinds are:

Kind Applies to Default when unset
movies Movies (required for movie downloads to be named, no built-in default)
series TV episodes (required for episode downloads to be named)
songs Music tracks "{track_number}. {title}"
albums Album folders only (see below) -

Note

movies and series have no hardcoded filename fallback. If you download those title types, define their templates. songs falls back to "{track_number}. {title}" when omitted.

Required vs. optional variables

  • {variable}: required. If it is missing from the download's context, formatting raises an error. Note that most variables are always present in context. A variable that does not apply becomes an empty string, so "present but empty" is different from "missing".
  • {variable?}: optional. A trailing ? marks the variable conditional. When it is empty, unshackle removes the placeholder and one adjacent separator (., -, or space), so the surrounding text collapses cleanly.

Use ? for anything that is not always there

Episode names, editions, tags, HDR labels and audio extras are not present on every title. Mark them optional so you never get doubled dots or dangling separators:

series: "{title}.{season_episode}.{episode_name?}.{quality}.{hdr?}.{source}-{tag?}"
For an episode with no on-screen name, {episode_name?} and its adjacent dot disappear.

Separator style (dots vs. spaces)

unshackle looks at the characters you place between variables to decide the filename's separator style. If spaces outnumber dots in your template, it sanitises with spaces. If not, it uses dots (the scene-style default). unshackle also applies this style to the auto-generated date separators of daily content, and to every segment of a nested folder template.

output_template:
  movies: "{title}.{year}.{quality}.{source}.WEB-DL.{audio_full}.{video}-{tag}"
The.Movie.2024.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv

output_template:
  movies: "{title} ({year}) {quality} {source} WEB-DL {audio_full} {video}"
The Movie (2024) 1080p EXAMPLE WEB-DL DDP5.1 H.264.mkv

Template variables

Every variable below is valid in both output and folder templates. unshackle takes the values from the downloaded media (through MediaInfo), from the title metadata, or from your config. A variable that does not apply to a title becomes an empty string.

Common / video

Variable Meaning Example
title Title name (movie/show/song name; $ is rendered as S) The Show
title_type Media kind of the title movie, series, music
year Release year 2024
source Service tag / class name (empty with --no-source) EXAMPLE
quality Resolution with scan suffix 1080p, 2160p, 576i
resolution Resolution number only 1080
video Video codec H.264, H.265
hdr Dynamic-range label HDR, HDR10P, DV, DV.HDR, DV.HDR10P, HLG
hfr High frame rate marker (frame rate > 30) HFR
edition Edition label from the track, if any Directors Cut
tag Release-group tag (from config.tag or --tag) TAG
repack REPACK when the --repack flag is used REPACK
lang_tag Result of your language_tags rules, if configured MULTi

How language_tags rules match

unshackle tries the rules in order, but language comparison is fuzzy: a rule condition of en matches en-US, en-GB, and any other en-* form. Within a single rule, all conditions must match (AND logic) for it to apply. The first rule that fully matches wins. If none match, unshackle removes {lang_tag?} from the filename cleanly.

Episode-specific

Variable Meaning Example
season Zero-padded season (S%02d) S01
episode Zero-padded episode (E%02d) E05
season_episode Combined S01E05
episode_name Episode title Pilot
part Part index of a split episode, empty otherwise 2
absolute Absolute episode number, zero-padded to 3 digits, empty when unknown 007
date ISO air date, empty when the episode has none 2024-06-01

Daily & sports content

When an episode has an air date, unshackle switches to date-based naming automatically. season and season_episode become the formatted air date, unshackle clears episode and year, and {date} holds the ISO date. The date's internal separator (dots or spaces) follows your series template style.

Split episodes

A few services split one episode into several separately playable videos. Where a service reports that, unshackle folds the part index into {episode} and {season_episode}, so the stock series template names it with no change to your config.

Show.Name.S01E01.Part.2.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv

The part token sits immediately after the episode token, before the quality tags, and the group tag stays last. Its separator follows your template's own style, the same way daily content does, so a spaced template gives S01E01 Part 2 instead.

The part is left out of the folder name, so every part of an episode lands in the same season folder:

The.Show.S01.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG/
  ├─ The.Show.S01E01.Part.1.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv
  ├─ The.Show.S01E01.Part.2.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv
  └─ The.Show.S01E02.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv

The same holds for a folder template of your own: {season} never picks up the part, and {season_episode} drops it inside a folder template. unshackle names an episode with no part like any other episode.

What media servers do with the part token

The form above follows scene practice for genuinely split episodes. Kodi stacks the parts back into one playable episode automatically. Jellyfin lists two versions of S01E01, still correct and playable, because its stacking pattern expects the part marker at the end of the name. Plex uses a closed-source scanner, so what it does with text after the part token is not known.

For custom templates there is also a standalone {part} variable. You rarely want it, see the caveat on that page.

Audio

Variable Meaning Example
audio Audio codec DDP, DD, AAC
audio_channels Channel layout 5.1, 2.0
audio_full Codec + channels combined DDP5.1
atmos Atmos when any track carries JOC Atmos
dual DUAL for two audio languages (see dual_multi_mode) DUAL
multi MULTi for more than two audio languages MULTi
dubbed DUBBED for a single audio language that is not the original (strict mode only) DUBBED

Music-specific

Variable Meaning
track_number Zero-padded track number
artist / album_artist Artist / album artist (falls back to artist)
album Album name
disc Disc number (only shown when greater than 1)
track_total / disc_total Totals
release_type album, single, ep, or another type the service supplies
genre Genre
explicit Explicit when flagged
isrc / upc / label ISRC, UPC and record label

Unknown variables are ignored, not errors

If you use a variable that is not in the list above, unshackle gives a warning at startup but continues. Double-check spelling. A typo'd {quailty} stays in the filename literally, or unshackle drops it. unshackle does not fill it in.

Folder templates

Folder templates sit under a special folder config key inside output_template. unshackle reads this config key separately from the filename kinds. You can give it a single string (for all title kinds) or a per-kind map.

unshackle.yaml
output_template:
  movies: "{title}.{year}.{quality}.{source}-{tag}"
  series: "{title}.{season_episode}.{quality}.{source}-{tag}"
  folder: "{title}.{year}"
unshackle.yaml
output_template:
  movies: "{title}.{year}.{quality}.{source}-{tag}"
  series: "{title}.{season_episode}.{quality}.{source}-{tag}"
  folder:
    movies: "{title} ({year})"
    series: "{title} ({year})/Season {season}"
    albums: "{artist} - {album} ({year})"

The per-kind folder config keys are movies, series, songs, and albums. Any other config key gives a startup warning. Folder templates permit path separators (/ or \). unshackle formats each segment independently, so you can assemble nested directory structures like Show/Season 01. unshackle reads the separator style from the whole template, so it spaces every segment alike.

Fallback behaviour:

  • A per-kind folder template wins if present. If there is none, unshackle uses the single folder string. If there is no folder string either, unshackle falls back to a built-in default.
  • Built-in defaults when no folder template is set. Movies get no folder at all unless a movies folder template (or a single-string folder template) exists. Series fall back to a folder derived from the series output template (stripping {episode}, {episode_name}, and collapsing {season_episode} down to {season}). Music albums fall back to {artist} - {album} ({year}). If the song has no year, the folder name is only {artist} - {album}.

Movies are flat by default

Episodes and songs are always foldered. unshackle writes movies directly into the output directory unless you define a movies folder template (or a single-string folder template, which folders every kind). Set one if you want each movie in its own directory.

Music output files

A song does not go through the muxer. Music services deliver audio in a fragmented MP4, which is not a music file. Its header states a length of zero, so a player stops after the first fragment. FLAC inside an MP4 is also not a FLAC stream. A rename cannot correct either fault, so unshackle remuxes each downloaded audio track into the container of its codec, then moves the file to its final path.

Codec Extension
FLAC .flac
Opus .opus
Vorbis .ogg
AAC, ALAC, and all other codecs .m4a

unshackle re-encodes FLAC in this step. The audio does not change, but the new file states its true length. A plain copy cannot, because it keeps the source header and the zero in it.

unshackle writes no .mkv file for music, and the muxing options below do not apply.

The songs output template names the file. The albums folder template names the folder around it.

/home/user/Downloads/
  └─ The Artist - The Album (2024)/
     ├─ 01. First Track.flac
     └─ 02. Second Track.flac

Music metadata

After the file moves, unshackle writes the metadata into the audio file. It uses Vorbis comments for FLAC and Ogg, ID3 frames for MP3, and MP4 atoms for M4A. The tags hold the track title, artist, album artist, track and disc numbers, date, genre, ISRC, UPC, copyright, and record label. When tag and tag_group_name are set, unshackle also writes the group name into a GROUP tag.

unshackle embeds cover art when the service gives an artwork URL. It downloads the image and puts it in the file.

unshackle writes lyrics when the service supplies them: a LYRICS Vorbis comment for FLAC, a USLT frame for MP3, and a ©lyr atom for M4A. Ogg uses the same LYRICS field as FLAC. unshackle does not write synchronized lyrics (SYLT).

A metadata fault does not stop the run. The file is already in place, so unshackle writes a warning and continues.

--no-mux leaves music files untagged

--no-mux moves each downloaded track file to the output path without the tagging step. A music file from such a run holds only the metadata that the service put in it.

Muxing options

You configure muxing (the combination of video, audio, subtitle, chapter and attachment tracks into a single Matroska file with mkvmerge) under the muxing config key. unshackle muxes only movies and episodes. Read Music output files for what a music download does instead.

unshackle.yaml
muxing:
  set_title: true
  merge_video: false
  merge_audio: true
  default_language:
    audio: en
    subtitle: en
Key Type Default Effect
set_title bool true Write the title name into the MKV container title with --title. Set to false to omit it.
merge_video bool false Group video tracks that share the same resolution, range, and codec into one file so only language varies inside it.
merge_audio bool true Merge audio tracks of the same kind so multiple languages sit in one file.
default_language map (unset) Preferred language per track type (video / audio / subtitle). A track in the preferred language is flagged as the default track.

When no track has the preferred language, unshackle applies sensible defaults. The video default falls back to the title language, then the original-language track, then the first track. The audio default is the original-language track. The subtitle default is a forced track in the first audio's language.

default_language only sets the default-track flag

default_language controls which track carries the MKV --default-track flag. Nothing else. It does not change track selection (that stays with -l/--a-lang and friends). It also does not touch which track carries the original flag: --original-flag still tags the true original-audio track. That is the point: you can make your player default to, say, Polish audio on an English-original title without altering the original marker. When the configured language is not present in the manifest, each track type falls back to its normal default rule described above.

merge_video collapses only the language dimension

merge_video groups video tracks by (resolution, range, codec) and merges them so that only language differs within a single file, with no re-encode and no concatenation. Different resolutions, ranges (SDR / HDR10 / HDR10+ / DV / HYBRID), and codecs (H.264 / H.265) always land in separate files. So -r HYBRID,DV,HDR10,SDR --merge-video gives one file per range, never a single fused file. English and French video of identical resolution, range, and codec gives one file that holds both video tracks. merge_audio works the same way for audio languages.

A few muxing behaviors are automatic and not configurable:

  • --no-date is always passed for privacy (no timestamps embedded).
  • Descriptive audio gets the visually-impaired flag. SDH subtitles get the hearing-impaired flag. Forced subtitles get the forced flag.
  • unshackle writes the chapters from the title's chapter list (see Chapters below).

The --no-mux flag skips muxing entirely and writes the individual track files, each with a track-type suffix, into the same output/folder structure.

Tags & group naming

The release-group tag is the -TAG portion at the end of scene-style names, which the {tag} variable gives. You can swap it per release with tag_rules: ordered rules whose conditions read the release-attribute variables only (quality, resolution, hdr, source, lang_tag, title_type, and so on), and the first rule that matches wins. tag_rules cannot read the title's own fields, such as title and season_episode. See the reference page for the exact list.

unshackle.yaml
tag: "MYGROUP"
tag_group_name: true
tag_imdb_tmdb: true
Key Type Default Effect
tag str "" The release-group tag used for {tag}.
tag_group_name bool true Write the group name (config.tag) into the MKV Group metadata tag.
tag_imdb_tmdb bool true Look up and embed IMDb / TMDB / TVDB external-ID tags in the MKV metadata (uses tmdb_api_key / tvdb_api_key / simkl_client_id when available).

unshackle writes the IDs as the Matroska IMDB, TMDB, and TVDB2 tags. TVDB2 values carry the entity prefix the Matroska tagging spec requires: series/73871 for a show, movies/113 for a film.

You can override the tag per run without editing config:

unshackle dl --tag OTHERGROUP EXAMPLE "..."

The --repack flag adds the REPACK marker (surfaced through the {repack} variable) to the filename for that run:

unshackle dl --repack EXAMPLE "..."

Empty tags collapse cleanly

If config.tag is empty and your template ends with -{tag?}, the trailing - and the placeholder are both removed. Use the optional form {tag?} if you sometimes download without a tag set.

Unicode filenames

By default unshackle transliterates non-ASCII characters into ASCII equivalents. Korean, Japanese, Chinese, and accented Latin are examples. This is safest for older tools, DDL, and P2P sharing. Set unicode_filenames to keep the native characters.

unshackle.yaml
unicode_filenames: true
Value Result
false (default) 기생충 → transliterated ASCII; accents stripped.
true Native characters preserved in filenames and folders.

Regardless of this setting, unshackle always removes or replaces a set of filesystem-unsafe and structural characters during sanitisation. For example, / and ; become &, and unshackle removes characters like \ * ! ? , ' " < > | $ # ~. This is why you must never put path-unsafe characters directly in a template.

unshackle warns at startup about a template that contains < > : " / \ | ? *. It examines a folder template per path segment, so you can use / there as a directory separator.

Directory & filename configuration

The directories config key relocates unshackle's working folders. You can move only the folders in the table below. unshackle protects the core package paths and silently ignores them if you try to override them.

unshackle.yaml
directories:
  downloads: "~/Media/unshackle"
  temp: "/mnt/fast/unshackle-temp"
  cache: "~/.cache/unshackle"
  cookies: "~/.config/unshackle/cookies"
  logs: "~/.config/unshackle/logs"
Name Purpose
downloads Default output directory for finished files.
temp Working directory for in-progress downloads, muxing, and intermediate files.
cache Title/HTTP cache and update-check state.
cookies Per-service cookie files.
logs Log files.
exports Export JSONs.
services, vaults, fonts, commands Search paths for services, key-vault backends, bundled fonts, and CLI commands.
wvds, prds, dcsl Widevine devices, PlayReady devices, and DCSL data.

Paths can hold a ~ for expansion. unshackle protects the names app_dirs, core_dir, namespace_dir, user_configs, and data, so you cannot change them.

Filename patterns

The filenames config key overrides the naming patterns for a handful of internal files. These are not your media output names. They are logs, temp files, and per-service configs.

unshackle.yaml
filenames:
  log: "unshackle_{name}_{time}.log"
  chapters: "Chapters_{title}_{random}.txt"
  subtitle: "Subtitle_{id}_{language}.srt"
Name Default Where
log unshackle_{name}_{time}.log directories.logs
debug_log unshackle_debug_{service}_{time}.jsonl directories.logs
config config.yaml per-service directory
root_config unshackle.yaml main config filename
chapters Chapters_{title}_{random}.txt directories.temp
subtitle Subtitle_{id}_{language}.srt directories.temp

Chapters naming

When a title has chapters, unshackle writes them to an OGM chapters file (filenames.chapters) and passes it to the muxer. Unnamed chapters use the chapter_fallback_name template:

unshackle.yaml
chapter_fallback_name: "Chapter {i:02}"

The fallback can hold two placeholders: {i} (chapter number, starting at 1) and {j} (which increments only for unnamed chapters). It permits standard format directives like {i:02}.

Removed & migrated options

scene_naming was removed

The old scene_naming option no longer exists. If it is present in your config, unshackle exits immediately with an error asking you to configure output_template instead. Migrate any scene-naming setup to the templates described on this page.

Worked examples

Each tab shows a template and what it produces for a few different downloads, so you can see how the optional (?) variables appear and disappear.

output_template:
  movies: "{title}.{year}.{edition?}.{quality}.{source}.WEB-DL.{audio_full}.{atmos?}.{video}.{hdr?}-{tag?}"
  folder:
    movies: "{title} ({year})"

A 4K Atmos HDR download fills every placeholder:

The Movie (2024)/
  └─ The.Movie.2024.2160p.EXAMPLE.WEB-DL.DDP5.1.Atmos.H.265.HDR-TAG.mkv

The same template on a plain 1080p SDR stereo download, where {edition?}, {atmos?} and {hdr?} collapse along with their dots:

The Movie (2024)/
  └─ The.Movie.2024.1080p.EXAMPLE.WEB-DL.AAC2.0.H.264-TAG.mkv

A director's cut fills {edition?}:

The Movie (2024)/
  └─ The.Movie.2024.Directors.Cut.2160p.EXAMPLE.WEB-DL.DDP5.1.Atmos.H.265.DV.HDR-TAG.mkv

output_template:
  series: "{title}.{season_episode}.{episode_name?}.{quality}.{source}.WEB-DL.{audio_full}.{video}-{tag}"
  folder:
    series: "{title} ({year})/Season {season}"
The Show (2023)/Season S02/
  └─ The.Show.S02E04.The.Reckoning.2160p.EXAMPLE.WEB-DL.DDP5.1.H.265-TAG.mkv

unshackle decides the separator style once for the whole folder template, and every segment then follows it. Spaces outnumber dots in {title} ({year})/Season {season}, so Season {season} keeps its space even though it holds only one variable.

When an episode has no on-screen name, {episode_name?} disappears cleanly:

The Show (2023)/Season S02/
  └─ The.Show.S02E05.2160p.EXAMPLE.WEB-DL.DDP5.1.H.265-TAG.mkv

Place {dual?}, {multi?} and {dubbed?} next to each other. At most one of them is ever set (see dual_multi_mode), and the empty ones collapse:

output_template:
  movies: "{title}.{year}.{quality}.{source}.WEB-DL.{dual?}.{multi?}.{dubbed?}.{audio_full}.{video}-{tag}"

Original English audio plus a French dub (-l en,fr):

The.Movie.2024.1080p.EXAMPLE.WEB-DL.DUAL.DDP5.1.H.264-TAG.mkv

Original plus several dubs (-l en,fr,de,es):

The.Movie.2024.1080p.EXAMPLE.WEB-DL.MULTi.DDP5.1.H.264-TAG.mkv

Only a dub, original audio left out (-l de on an English-original title):

The.Movie.2024.1080p.EXAMPLE.WEB-DL.DUBBED.DDP5.1.H.264-TAG.mkv

Two dialects of one language (en-US + en-GB) count as a single language, so none of the three variables is set:

The.Movie.2024.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv

For finer control (for example a SUBBED tag driven by subtitle languages), use {lang_tag?} with language_tags rules instead.

Daily content needs no special template. When an episode carries an air date, {season_episode} becomes the date automatically and {episode} and {year} clear:

output_template:
  series: "{title}.{season_episode}.{episode_name?}.{quality}.{source}.WEB-DL.{audio_full}.{video}-{tag}"

A normal episode and a dated one, same template:

The.Daily.Show.S28E101.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv
The.Daily.Show.2024.06.01.1080p.EXAMPLE.WEB-DL.DDP5.1.H.264-TAG.mkv

The date separator follows your template style: dots here, spaces if your series template uses spaces.

In folder names, {season} becomes the air year (Season 2024 style grouping), and unshackle skips a {year?} in the folder template when it would only repeat that year.

output_template:
  songs: "{track_number}. {title}"
  folder:
    albums: "{album_artist} - {album} ({year})"
The Artist - The Album (2024)/
  └─ 01.Opening.Track.mka

In "{track_number}. {title}" the number of dots and spaces is equal, and a tie goes to dot style. Add more spaces between variables (as below) if you want space-separated names.

A multi-disc album with explicit tracks, using more of the music variables. {disc} is empty on disc 1, so {disc?} and its separator only appear from disc 2 onward:

output_template:
  songs: "{disc?}-{track_number}. {title} {explicit?}"
  folder:
    albums: "{album_artist} - {album} ({year}) [{label?}]"
The Artist - The Album (2024) [The Label]/
  ├─ 01. Opening Track.mka
  └─ 02-03. Closing Track Explicit.mka

See also

  • Configuration File: the full unshackle.yaml structure.
  • Downloading: the dl command and per-run flags like -o, --no-folder, --tag, and --repack.