mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 20:15:10 +00:00
--daily flag plus Service.DAILY/Title.daily, -w selection by ISO air date or colon date range, and a pre-1970 air-date guard.
822 lines
35 KiB
Markdown
822 lines
35 KiB
Markdown
# Downloading
|
||
|
||
`unshackle dl` is the main download command: it fetches a title's manifest from a
|
||
streaming service, selects exactly the tracks you asked for, acquires and applies
|
||
decryption keys, and muxes everything into a finished Matroska file. The flags below
|
||
cover quality and codec selection, language and subtitle handling, episode ranges, and
|
||
the download flow itself.
|
||
|
||
!!! note "Where the full flag list lives"
|
||
This page covers the flags you will use most and explains how they interact. It is
|
||
not an exhaustive enumeration. For the complete, always-current list of every option
|
||
and its exact wording, run `unshackle dl --help`. Every flag shown here can also be
|
||
given a default in your configuration file. See
|
||
[Configuration file](../getting-started/configuration-file.md).
|
||
|
||
## Command shape
|
||
|
||
A download is always three layers: the root CLI, the `dl` command (which is where all
|
||
the flags below are parsed), and a **service tag** subcommand that carries the title
|
||
argument.
|
||
|
||
```shell
|
||
unshackle dl [OPTIONS] SERVICE [SERVICE ARGUMENTS]
|
||
```
|
||
|
||
- `unshackle`: the root command.
|
||
- `dl`: the download command. **All of the options on this page are parsed here**, so
|
||
they go *before* the service tag.
|
||
- `SERVICE`: a service tag such as `EXAMPLE1`, `EXAMPLE2`, `EXAMPLE3`. Service tags are
|
||
case-insensitive and honour each service's aliases (for example `example+` resolves to
|
||
its real tag).
|
||
- `SERVICE ARGUMENTS`: usually a title ID or URL. This positional argument belongs to
|
||
the **service**, not to `dl`, so it comes *after* the tag.
|
||
|
||
```shell title="A minimal download"
|
||
unshackle dl EXAMPLE 81234567
|
||
```
|
||
|
||
```shell title="Flags belong to dl, the title belongs to the service"
|
||
unshackle dl -q 1080 -v H.265 -r HDR10 --lang en EXAMPLE 'https://www.example.com/...'
|
||
```
|
||
|
||
!!! tip "See what a service accepts"
|
||
Because the positional argument is defined by each service, run
|
||
`unshackle dl SERVICE --help` (e.g. `unshackle dl EXAMPLE --help`) to see that service's
|
||
own argument and any service-specific options.
|
||
|
||
## The download flow
|
||
|
||
When you run a download, `dl` moves through the pipeline below. Understanding the order
|
||
helps explain why, for example, `--list` stops early and why keys are fetched even with
|
||
`--skip-dl`.
|
||
|
||
1. **Setup**: load the DRM CDM, key vaults, proxy providers, cookies, and credentials.
|
||
2. **Authenticate**: sign in to the service with your profile's cookies/credentials.
|
||
3. **Fetch titles**: retrieve the movie, episode list, or album (cached unless
|
||
`--no-cache`/`--reset-cache`).
|
||
4. **Filter titles**: apply `--wanted`, `--latest-episode`, or `--select-titles`.
|
||
5. **Get tracks**: parse the manifest into video, audio, subtitle, and chapter tracks.
|
||
6. **Select tracks**: narrow each type down using your quality, codec, range, language,
|
||
and bitrate flags.
|
||
7. **Download**: pull segments for the selected tracks (concurrently per `--downloads`).
|
||
8. **License & decrypt**: acquire content keys (vault → CDM), decrypt each track.
|
||
9. **Post-process**: extract closed captions, convert subtitles, repack, mux with
|
||
`mkvmerge`, and move the finished file to your downloads directory.
|
||
|
||
`--list` prints the tracks a title exposes and stops before selection. `--list-titles`
|
||
prints every title the service returned and stops before `--wanted`/`--latest-episode`
|
||
filtering. `--skip-dl` runs the license step but skips the actual segment download.
|
||
|
||
## Choosing quality
|
||
|
||
### Resolution
|
||
|
||
`-q` / `--quality` takes one or more target resolutions (heights) as a comma-separated
|
||
list. Without it, unshackle picks the **best available** resolution.
|
||
|
||
```shell title="Single resolution"
|
||
unshackle dl -q 1080 EXAMPLE 81234567
|
||
```
|
||
|
||
```shell title="Multiple resolutions in one run"
|
||
unshackle dl -q 2160,1080,720 EXAMPLE 81234567
|
||
```
|
||
|
||
!!! note "16:9 canvas matching"
|
||
Resolution matching is by track **height** first. If no track has an exact matching
|
||
height, unshackle falls back to a 16:9-canvas match, so `-q 1080` will also match an
|
||
anamorphic `1920×804` track (computed as `int(width × 9 / 16)`).
|
||
|
||
### Best-available and worst
|
||
|
||
By default a missing requested resolution is an error. Two flags change that:
|
||
|
||
| Flag | Behaviour |
|
||
| --- | --- |
|
||
| `--best-available` | If the requested resolution(s) aren't present, continue with the best that *is* available instead of failing. Also softens missing video/audio/subtitle languages and hybrid fallbacks. |
|
||
| `--worst` | Within the specified quality, pick the **lowest** bitrate rendition. **Requires `-q/--quality`.** |
|
||
|
||
```shell title="Never fail on a missing resolution"
|
||
unshackle dl -q 2160 --best-available EXAMPLE 0ABC123
|
||
```
|
||
|
||
## Video codec and color range
|
||
|
||
### Codec
|
||
|
||
`-v` / `--vcodec` selects one or more video codecs; the default is any codec. It accepts
|
||
either enum **names** or their **values**, comma-separated.
|
||
|
||
| Name | Value |
|
||
| --- | --- |
|
||
| `AVC` | `H.264` |
|
||
| `HEVC` | `H.265` |
|
||
| `VC1` | `VC-1` |
|
||
| `VP8` | `VP8` |
|
||
| `VP9` | `VP9` |
|
||
| `AV1` | `AV1` |
|
||
|
||
```shell title="Either spelling works"
|
||
unshackle dl -v HEVC EXAMPLE 81234567
|
||
unshackle dl -v H.265 EXAMPLE 81234567
|
||
unshackle dl -v hevc,avc EXAMPLE 81234567
|
||
```
|
||
|
||
### Color range
|
||
|
||
`-r` / `--range` selects one or more color ranges; the default is `SDR`.
|
||
|
||
| Range | Meaning |
|
||
| --- | --- |
|
||
| `SDR` | Standard dynamic range (default) |
|
||
| `HLG` | Hybrid Log-Gamma |
|
||
| `HDR10` | HDR10 |
|
||
| `HDR10P` | HDR10+ |
|
||
| `DV` | Dolby Vision |
|
||
| `HYBRID` | Fetch both an HDR10/HDR10+ base and a DV track, then merge them |
|
||
|
||
```shell title="Grab HDR10 and Dolby Vision in one run"
|
||
unshackle dl -q 2160 -r HDR10,DV EXAMPLE '...'
|
||
```
|
||
|
||
!!! warning "HYBRID requires dovi_tool"
|
||
`-r HYBRID` produces a single hybrid stream by injecting the Dolby Vision RPU
|
||
metadata onto an HDR10/HDR10+ base layer using **dovi_tool**.
|
||
It requires the `dovi_tool` binary, resolved from unshackle's `binaries/` folder or
|
||
your `PATH`. The normal case is a DV track plus an HDR10 or HDR10+ base. With HDR10+
|
||
and no DV, unshackle converts the HDR10+ metadata to DV instead, which also needs
|
||
`hdr10plus_tool`. With no DV and no HDR10+, the title fails.
|
||
When HDR10+ is present it is preferred over HDR10 as the base layer.
|
||
|
||
To keep an HDR10+ deliverable *alongside* the hybrid, request both ranges:
|
||
`-r HYBRID,HDR10P`. `-r HYBRID` on its own muxes only the merged hybrid.
|
||
|
||
## Bitrate selection
|
||
|
||
By default unshackle keeps the highest-bitrate rendition for each selected
|
||
resolution/codec/range/language combination. You can constrain this.
|
||
|
||
| Flag | Purpose |
|
||
| --- | --- |
|
||
| `-vb` / `--vbitrate` | Exact video bitrate in kbps. |
|
||
| `-ab` / `--abitrate` | Exact audio bitrate in kbps. |
|
||
| `-vb-range` / `--vbitrate-range` | Video bitrate range in kbps, e.g. `6000-7000`; picks the highest within it. |
|
||
| `-ab-range` / `--abitrate-range` | Audio bitrate range in kbps, e.g. `128-256`. |
|
||
|
||
!!! warning "Exact vs range are mutually exclusive"
|
||
`--vbitrate` cannot be combined with `--vbitrate-range`, and likewise for the audio
|
||
pair. Pick one form.
|
||
|
||
### Real bitrate probing
|
||
|
||
Manifest-declared bitrates are sometimes rounded or wrong. Services often advertise a
|
||
**peak** or **nominal** bandwidth that is far from the stream's real average. This matters
|
||
because a track's bitrate drives the track listing, the sort order, *and* the
|
||
`--vbitrate`/`--vbitrate-range` selection above. A bogus declared value therefore makes
|
||
unshackle pick the wrong track. These flags probe the actual media size to compute a true
|
||
bitrate for the top renditions, overriding the manifest value:
|
||
|
||
- `-rvb` / `--real-video-bitrate`: probe real video bitrates (per codec/range).
|
||
- `-rab` / `--real-audio-bitrate`: probe real audio bitrates (per codec/channels/
|
||
language). Slower than the video variant because there are more renditions to probe.
|
||
|
||
!!! note "Reading the probed numbers"
|
||
A single-file track (DASH `SegmentBase`/`BaseURL`) is measured **exactly**. A
|
||
multi-segment track (most HLS) is a **sampled estimate**, normally within a few percent
|
||
of the true value. For MPEG-TS HLS the probed figure also reads a few percent *above*
|
||
the demuxed elementary stream, because the segment bytes include container overhead.
|
||
That is the real *delivered* size, not an over-report or a bug.
|
||
|
||
!!! tip "Why not every rendition is probed"
|
||
Probing does not touch every rendition. Only the five highest declared-bitrate
|
||
renditions of each quality tier are probed in parallel (video grouped by codec and
|
||
range, audio by codec, channels, language, and descriptive flag), and unshackle
|
||
extends a group downward while a lower unprobed rendition could still outrank a
|
||
probed one. This keeps probing fast even when a service exposes dozens of renditions.
|
||
Tracks whose duration can't be determined fall back to `ffprobe`, and a probe failure
|
||
is non-fatal: the manifest value is simply left in place.
|
||
|
||
## Audio codec, channels, and Atmos
|
||
|
||
### Codec
|
||
|
||
`-a` / `--acodec` selects one or more audio codecs (comma-separated); the default is any.
|
||
|
||
| Name | Value | Codec |
|
||
| --- | --- | --- |
|
||
| `AAC` | `AAC` | Advanced Audio Coding |
|
||
| `AC3` | `DD` | Dolby Digital |
|
||
| `EC3` | `DD+` | Dolby Digital Plus |
|
||
| `AC4` | `AC-4` | Dolby AC-4 |
|
||
| `OPUS` | `OPUS` | Opus |
|
||
| `OGG` | `VORB` | Vorbis |
|
||
| `DTS` | `DTS` | DTS |
|
||
| `ALAC` | `ALAC` | Apple Lossless |
|
||
| `FLAC` | `FLAC` | FLAC |
|
||
|
||
Names, values, and a few aliases are accepted: `eac3` and `ddp` both resolve to `EC3`,
|
||
and `vorbis` resolves to `OGG`.
|
||
|
||
```shell title="Prefer Dolby Digital Plus, fall back to AAC"
|
||
unshackle dl -a EC3,AAC EXAMPLE 81234567
|
||
```
|
||
|
||
### Channels and Atmos
|
||
|
||
- `-c` / `--channels`: desired channel layout, e.g. `5.1` or `2.0`. Matching is by
|
||
ceiling, so `5.1` implicitly matches a `6.0`-reported layout.
|
||
- `-naa` / `--noatmos`: exclude Dolby Atmos audio tracks from selection.
|
||
|
||
```shell title="5.1 audio, no Atmos"
|
||
unshackle dl -c 5.1 --noatmos EXAMPLE 0ABC123
|
||
```
|
||
|
||
## Languages
|
||
|
||
### Video and audio language
|
||
|
||
`-l` / `--lang` sets the wanted language(s) for **both video and audio**. The default is
|
||
`orig`, the title's original language.
|
||
|
||
```shell title="Original language plus English"
|
||
unshackle dl -l orig,en EXAMPLE 81234567
|
||
```
|
||
|
||
The special token `orig` is resolved to the title's actual original language everywhere
|
||
it is used. You can override each stream type independently:
|
||
|
||
- `-vl` / `--v-lang`: language for **video only** (overrides `-l` for video). Useful
|
||
when the burned-in video language differs from the audio you want.
|
||
- `-al` / `--a-lang`: language for **audio only** (overrides `-l` for audio).
|
||
|
||
```shell title="English audio over the original video"
|
||
unshackle dl -al en EXAMPLE 81234567
|
||
```
|
||
|
||
### Excluding languages
|
||
|
||
Put a `-` in front of a language to remove it from the selection. This works on `-l`,
|
||
`-vl`, `-al`, `-sl`, and `-fsl`, in the same way as it works on `-w` / `--wanted`.
|
||
|
||
```shell title="Every subtitle language except Spanish"
|
||
unshackle dl -sl all,-es EXAMPLE 81234567
|
||
```
|
||
|
||
The exclusions are subtracted from the languages the flag would otherwise select, and the
|
||
order of the values does not matter. If you give only exclusions, the flag keeps its own
|
||
default: `-sl -es` is the same as `-sl all,-es`, and `-l -es` is the same as `-l orig,-es`.
|
||
|
||
```shell title="Only exclusions"
|
||
# every subtitle language except Spanish and French
|
||
unshackle dl -sl -es,-fr EXAMPLE 81234567
|
||
|
||
# the original audio language, unless it is Spanish
|
||
unshackle dl -l -es EXAMPLE 81234567
|
||
```
|
||
|
||
`-vl` and `-al` have no default of their own: they cascade to `-l`. An override that names
|
||
a language replaces `-l` for that stream type, exclusions included. An override that gives
|
||
only exclusions adds them to the ones from `-l`.
|
||
|
||
```shell title="Cascade"
|
||
# video keeps the Japanese track; only the audio drops Spanish
|
||
unshackle dl -l en,-es -vl ja EXAMPLE 81234567
|
||
```
|
||
|
||
!!! note "Rules for exclusion tokens"
|
||
- `orig` is resolved per title, so `-sl -orig` drops the subtitles in the title's
|
||
original language.
|
||
- `--exact-lang` applies to exclusions with the same matching rules as selection:
|
||
`-es` removes exactly the tracks that `es` would select. Without it, `-es` also
|
||
removes `es-419` and `es-ES`.
|
||
- Tracks with no language tag are never excluded.
|
||
- `-all` is rejected. Name the languages you do not want instead.
|
||
- The comma form is the documented style. `-sl -es` also works, but a shell can read
|
||
a lone `-es` as another option.
|
||
|
||
### Sort order
|
||
|
||
`-l` and `-sl` **select** languages: naming some removes the rest. To keep every language
|
||
but decide which ones come first, set a priority list in your configuration file:
|
||
|
||
```yaml title="unshackle.yaml"
|
||
audio:
|
||
language_priority: [orig, en]
|
||
subtitle:
|
||
group_by: language
|
||
language_priority: [en, es, fr]
|
||
```
|
||
|
||
Subtitles then start with English, Spanish, and French. The languages you leave out follow
|
||
alphabetically, after the title's original language. Nothing is removed. Audio works the
|
||
same way, but the languages you leave out keep their bitrate and codec order instead of an
|
||
alphabetical one.
|
||
|
||
See [`subtitle.language_priority`](../reference/configuration/download.md#subtitle) for the details.
|
||
|
||
### Exact vs fuzzy matching
|
||
|
||
By default language matching is fuzzy: `-l en` also accepts `en-US`, `en-GB`, etc. (up
|
||
to a small distance). Pass `--exact-lang` to require an exact match: with it, `-l es-419`
|
||
matches only `es-419`, not `es-ES`.
|
||
|
||
!!! tip "The `all` and `best` tokens"
|
||
For audio, the tokens `all` and `best` bypass the usual one-track-per-language pick
|
||
and instead select the best track for **each** language present. For subtitles, `all`
|
||
keeps every subtitle language.
|
||
|
||
## Subtitles
|
||
|
||
### Selecting subtitle languages
|
||
|
||
`-sl` / `--s-lang` sets the wanted subtitle language(s). The default is `all`, meaning
|
||
every available subtitle language is downloaded.
|
||
|
||
```shell title="Only English and Spanish subtitles"
|
||
unshackle dl -sl en,es EXAMPLE 81234567
|
||
```
|
||
|
||
To keep the default and drop one language, use the `-` prefix described in
|
||
[Excluding languages](#excluding-languages). `-sl all,-es` keeps every subtitle language
|
||
except Spanish, forced Spanish subtitles included.
|
||
|
||
### Requiring subtitles
|
||
|
||
`--require-subs` takes a list of languages that **must** exist. If they all exist, *all*
|
||
subtitles are downloaded; if any is missing, the title fails. This is useful for gating a
|
||
download on the presence of a specific subtitle track.
|
||
|
||
!!! warning "Cannot combine with `--s-lang`"
|
||
`--require-subs` and `--s-lang` are mutually exclusive. Use one or the other.
|
||
|
||
### Forced subtitles and output format
|
||
|
||
- `-fs` / `--forced-subs`: include forced subtitle tracks (signs/foreign dialogue).
|
||
Without this flag, forced subs are dropped from selection.
|
||
- `-fsl` / `--forced-s-lang`: keep forced subtitles only in these languages (implies
|
||
`-fs`). Works independently of `--s-lang`, so `-sl all -fsl en` grabs every full
|
||
subtitle but only the English forced track. It accepts exclusions too: `-fsl all,-es`
|
||
keeps every forced subtitle except the Spanish one, and `-fsl -es` means the same.
|
||
- `--sub-format`: set the output subtitle format, converting only when necessary.
|
||
Accepts codec names/values and common aliases (`srt`, `vtt`, `ass`, `ssa`, `ttml`,
|
||
etc.), or the literal `original` to keep the source format.
|
||
|
||
| Value | Format |
|
||
| --- | --- |
|
||
| `SRT` / `srt` | SubRip |
|
||
| `VTT` / `vtt` | WebVTT |
|
||
| `ASS` / `ass` | Advanced SubStation Alpha |
|
||
| `SSA` / `ssa` | SubStation Alpha |
|
||
| `TTML` / `ttml` | Timed Text Markup |
|
||
| `original` | Keep the source format, no conversion |
|
||
|
||
```shell title="Convert subtitles to SRT"
|
||
unshackle dl --sub-format srt EXAMPLE 81234567
|
||
```
|
||
|
||
!!! note "SDH stripping happens by default"
|
||
When a subtitle track is marked SDH (for the deaf/hard-of-hearing) and there is no
|
||
plain same-language subtitle, unshackle produces a stripped, non-SDH variant
|
||
automatically. This behaviour is governed by the `subtitle.strip_sdh` config option
|
||
(default on).
|
||
|
||
## Selecting episodes
|
||
|
||
For series, several flags control which episodes are downloaded. By default, **all**
|
||
episodes are downloaded.
|
||
|
||
### Wanted ranges
|
||
|
||
`-w` / `--wanted` accepts flexible season/episode ranges, comma-separated. Prefix a token
|
||
with `-` to exclude it.
|
||
|
||
=== "Whole seasons"
|
||
|
||
```shell
|
||
unshackle dl -w S01 EXAMPLE 81234567
|
||
unshackle dl -w S01-S03 EXAMPLE 81234567
|
||
unshackle dl -w S01,S03,S05 EXAMPLE 81234567
|
||
```
|
||
|
||
=== "Specific episodes"
|
||
|
||
```shell
|
||
unshackle dl -w S01E01 EXAMPLE 81234567
|
||
unshackle dl -w S01E01-S01E05 EXAMPLE 81234567
|
||
unshackle dl -w S01E01-S02E03 EXAMPLE 81234567
|
||
```
|
||
|
||
=== "Exclusions"
|
||
|
||
```shell
|
||
# Seasons 1 through 5, but not season 3
|
||
unshackle dl -w S01-S05,-S03 EXAMPLE 81234567
|
||
```
|
||
|
||
=== "Split episodes"
|
||
|
||
```shell
|
||
# Part 2 of episode 1 only
|
||
unshackle dl -w S01E01.2 EXAMPLE 81234567
|
||
|
||
# Parts 1 through 3 of episode 1
|
||
unshackle dl -w S01E01.1-S01E01.3 EXAMPLE 81234567
|
||
|
||
# All of season 1 except episode 1 part 2
|
||
unshackle dl -w S01,-S01E01.2 EXAMPLE 81234567
|
||
```
|
||
|
||
=== "Air dates"
|
||
|
||
```shell
|
||
# One day
|
||
unshackle dl -w 2026-08-11 EXAMPLE 81234567
|
||
|
||
# A range of days, colon separated
|
||
unshackle dl -w 2026-08-01:2026-08-31 EXAMPLE 81234567
|
||
|
||
# August, but not the 15th
|
||
unshackle dl -w 2026-08-01:2026-08-31,-2026-08-15 EXAMPLE 81234567
|
||
```
|
||
|
||
### Daily and date-based content
|
||
|
||
Talk shows, news and sports have no official episode numbering, so unshackle names them
|
||
by air date. An episode that carries an air date is written as `Show.YYYY.MM.DD` instead of
|
||
`SxxExx`, the `{date}` token holds the ISO date, and the season folder becomes the year.
|
||
|
||
A service that only carries this kind of content sets `DAILY = True` on its class, and a
|
||
service can set `air_date` on each episode itself. Add `--daily` to mark any other title
|
||
as date-based:
|
||
|
||
```shell
|
||
unshackle dl --daily --tvdb 73871 --enrich EXAMPLE 81234567
|
||
```
|
||
|
||
With `--enrich` and a TVDB ID, `--daily` fills in the air date of every episode that has
|
||
none. An air date the service already set is kept. Dates before 1970 and dates in the
|
||
future are skipped, because TVDB carries placeholder schedule dates for episodes that
|
||
have not aired. Without `--enrich` unshackle has no source to fill from, and says so.
|
||
|
||
A dated episode answers to its air date in `-w`, as well as to its `SxxExx` key. A date
|
||
token is a plain ISO date (`2026-08-11`). A date range uses a colon (`2026-08-01:2026-08-31`),
|
||
because the dashes in a date are part of the date. A range cannot span more than 1000 days.
|
||
Date tokens and `SxxExx` tokens can be mixed in one `-w`.
|
||
|
||
### Split episodes
|
||
|
||
A few services split one episode into several separately playable videos. Where a service
|
||
reports that, add `.N` after the episode to pick one part.
|
||
|
||
| Token | Selects |
|
||
| --- | --- |
|
||
| `S01E01` | All parts of episode 1. |
|
||
| `S01E01.2` | Part 2 of episode 1, and nothing else. |
|
||
| `S01E01.1-S01E01.3` | Parts 1 to 3 of episode 1. |
|
||
| `S01,-S01E01.2` | All of season 1 except episode 1 part 2. Parts 1 and 3 are kept. |
|
||
|
||
A season token such as `S01` covers every episode and every part, so you only need `.N`
|
||
when you want a part on its own.
|
||
|
||
!!! warning "A part range stays inside one episode"
|
||
`S01E01.1-S01E01.3` is valid. `S01E01.1-S01E02.3` is rejected, because the parser
|
||
cannot know how many parts episode 1 has and so cannot work out where the range ends.
|
||
To span episodes, list the parts you want as separate tokens:
|
||
`-w S01E01.2,S01E02.1`.
|
||
|
||
!!! note "A part of an unsplit episode selects nothing"
|
||
`-w S01E02.2` on an episode that was never split is deliberately empty: it tells you
|
||
the episode has no parts, rather than quietly handing you the whole episode. Run
|
||
`--list-titles` to see which episodes carry parts.
|
||
|
||
### Other selection flags
|
||
|
||
| Flag | Behaviour |
|
||
| --- | --- |
|
||
| `--select-titles` | Interactively pick episodes of a series, or films when a title has more than one. **Cannot combine with `-w`.** |
|
||
| `--latest-episode` | Download only the single most recent episode. |
|
||
| `--list-titles` | List every title the service returned, then stop. `-w`/`--latest-episode` are not applied to this listing. |
|
||
|
||
```shell title="Grab just the newest episode"
|
||
unshackle dl --latest-episode EXAMPLE 81234567
|
||
```
|
||
|
||
## Including and excluding track types
|
||
|
||
You can restrict a download to certain track categories, either positively (only these)
|
||
or negatively (everything but these).
|
||
|
||
**Only these types** (`*-only` flags):
|
||
|
||
- `-V` / `--video-only`
|
||
- `-A` / `--audio-only`
|
||
- `-S` / `--subs-only`
|
||
- `-C` / `--chapters-only`
|
||
|
||
**Skip these types** (`no-*` flags):
|
||
|
||
- `-nv` / `--no-video`
|
||
- `-na` / `--no-audio`
|
||
- `-ns` / `--no-subs`
|
||
- `-nc` / `--no-chapters`
|
||
|
||
Additional track-type flags:
|
||
|
||
- `-ad` / `--audio-description`: include descriptive (audio-description) tracks, which
|
||
are dropped by default.
|
||
- `--skip-subtitle-errors`: if a subtitle fails to download, skip it and continue rather
|
||
than aborting the whole title. Video and audio failures remain fatal.
|
||
|
||
```shell title="Subtitles only"
|
||
unshackle dl -S -sl en EXAMPLE 81234567
|
||
```
|
||
|
||
```shell title="Everything except chapters"
|
||
unshackle dl -nc EXAMPLE 81234567
|
||
```
|
||
|
||
!!! note
|
||
You can combine an `*-only` flag with `no-*` flags to fine-tune: `*-only` chooses the
|
||
starting set of categories, then `no-*` subtracts from it. Attachments (e.g. fonts)
|
||
are always kept.
|
||
|
||
## Listing and dry runs
|
||
|
||
Before committing to a long download, inspect what unshackle *would* do:
|
||
|
||
| Flag | Effect |
|
||
| --- | --- |
|
||
| `--list` | List the tracks the service exposes for each title, then stop. No selection, no download. |
|
||
| `--list-titles` | List every title the service returned, then stop. `-w`/`--latest-episode` are not applied to this listing. |
|
||
| `--skip-dl` | Skip downloading but still acquire the decryption keys. |
|
||
|
||
```shell title="See the track selection without downloading"
|
||
unshackle dl -q 1080 -v H.265 -r HDR10 --list EXAMPLE 81234567
|
||
```
|
||
|
||
## Output and muxing
|
||
|
||
### Output location and folders
|
||
|
||
- `-o` / `--output`: override the output directory for this run (otherwise the
|
||
configured downloads directory is used).
|
||
- `--no-folder`: do not create a per-show folder for TV downloads.
|
||
- `--no-source`: remove the service source tag from the filename and path.
|
||
|
||
```shell title="Send this download somewhere specific"
|
||
unshackle dl -o ~/Videos/incoming EXAMPLE 81234567
|
||
```
|
||
|
||
### Muxing behaviour
|
||
|
||
By default, tracks are muxed into a single Matroska (`.mkv`) file with `mkvmerge`. These
|
||
flags change how the output is assembled:
|
||
|
||
| Flag | Behaviour | Default source |
|
||
| --- | --- | --- |
|
||
| `--no-mux` | Do not mux; keep the individual track files. | - |
|
||
| `--split-audio` | Write a separate output file per audio codec instead of merging all audio. | config `muxing.merge_audio` (on) |
|
||
| `--merge-video` | Mux video tracks that share a height, range, and codec into one file, so only language varies inside a file. | config `muxing.merge_video` (off) |
|
||
|
||
### Naming tags
|
||
|
||
- `--tag`: set the release group tag (overrides the configured tag).
|
||
- `--repack`: add a `REPACK` tag to the output filename.
|
||
|
||
```shell title="Custom group tag and a REPACK label"
|
||
unshackle dl --tag MYGRP --repack EXAMPLE 81234567
|
||
```
|
||
|
||
## Proxies
|
||
|
||
`--proxy` accepts a full proxy URI, a 2-letter country code (resolved through your
|
||
configured proxy providers), or a `provider:region` form.
|
||
|
||
```shell title="Proxy forms"
|
||
unshackle dl --proxy us EXAMPLE 81234567
|
||
unshackle dl --proxy nordvpn:ca EXAMPLE 81234567
|
||
unshackle dl --proxy 'http://user:pass@host:8080' EXAMPLE 81234567
|
||
```
|
||
|
||
Two related flags:
|
||
|
||
- `--no-proxy`: force-disable all proxy use for this run.
|
||
- `--no-proxy-download`: bypass the proxy for **all downloads**. The manifest,
|
||
license, and authentication requests still go through the proxy. This is useful when the
|
||
proxy is only needed to satisfy geo-checks, not to move the bulk of the data.
|
||
|
||
## Performance and caching
|
||
|
||
| Flag | Purpose |
|
||
| --- | --- |
|
||
| `--workers N` | Threads used per track for segment downloads. Default depends on the downloader. |
|
||
| `--downloads N` | Number of tracks downloaded concurrently. Default `1`. |
|
||
| `--slow [MIN-MAX]` | Add a delay between titles to look more like a real device. `--slow` alone means 60-120s; `--slow 20-40` sets a custom range. Minimum 20s. |
|
||
| `--no-cache` | Bypass the title cache for this download. |
|
||
| `--reset-cache` | Clear the title cache before fetching. |
|
||
|
||
```shell title="Two tracks at a time, eight threads each"
|
||
unshackle dl --downloads 2 --workers 8 EXAMPLE 81234567
|
||
```
|
||
|
||
```shell title="Space out a season download"
|
||
unshackle dl -w S01 --slow 30-60 EXAMPLE 81234567
|
||
```
|
||
|
||
## Keys, vaults, and export
|
||
|
||
By default, unshackle checks your **key vaults** first and only asks a **CDM** to license
|
||
a key when the vault misses. You can force one side or the other:
|
||
|
||
- `--cdm-only`: only use the CDM (skip vaults).
|
||
- `--vaults-only`: only use key vaults (never license via the CDM); a missing key fails.
|
||
|
||
```shell title="Fetch keys only, no download"
|
||
unshackle dl --skip-dl EXAMPLE 81234567
|
||
```
|
||
|
||
`--export` writes a JSON file, into the configured exports directory, containing track
|
||
info and the acquired content keys for each title. This is the format consumed by
|
||
`unshackle import` to reconstruct a download later.
|
||
|
||
```shell title="Export track info and keys"
|
||
unshackle dl --skip-dl --export EXAMPLE 81234567
|
||
```
|
||
|
||
!!! note "Region is recorded only with a proxy"
|
||
When `--proxy` is used, the export records the region so an import can reproduce the
|
||
correct geofence. Without a proxy, no region is stored.
|
||
|
||
!!! warning "Some DASH and Smooth exports need a title language"
|
||
An import re-fetches the DASH or ISM manifest and parses it again. Most manifests label
|
||
their own streams, and those import fine. When a manifest labels nothing, the parse falls
|
||
back to the title's original language, which comes from `Title.language` on the exporting
|
||
service. If a service never sets it, importing that export fails with a message naming
|
||
the service. Neither end guesses a language for you, so the fix belongs in the service.
|
||
|
||
## Metadata and tagging
|
||
|
||
unshackle looks up metadata automatically, but you can override the identifiers used for
|
||
tagging and naming. An ID you give is authoritative: unshackle looks it up directly instead of
|
||
searching by title, and the ID stays in the tags. An ID only settles *which* title this is.
|
||
To also take that source's title, year and original language, add `--enrich`. Give at most one
|
||
of `--tmdb`, `--imdb` and `--tvdb`; unshackle works the rest out from it. To stop the automatic
|
||
lookups but keep the IDs you give, set
|
||
[`disable_metadata`](../reference/configuration/misc.md#external-api-keys) in your config.
|
||
|
||
| Flag | Example | Purpose |
|
||
| --- | --- | --- |
|
||
| `--tmdb` | `--tmdb 27205` | Use this TMDB ID instead of an automatic search. Needs `tmdb_api_key`. |
|
||
| `--imdb` | `--imdb tt1375666` | Use this IMDb ID instead of an automatic search. Needs no key. |
|
||
| `--tvdb` | `--tvdb 73871` | Use this TVDB ID instead of looking the series up. Needs `tvdb_api_key`. |
|
||
| `--anilist` | `--anilist 21` | Use this AniList ID instead of an automatic search. `mal:12345` is also accepted. Needs no key. |
|
||
| `--enrich` | - | Overwrite the show title, year and original language with the external source's. **Requires** one of `--tmdb`, `--imdb`, `--tvdb`, or `--anilist`. |
|
||
| `--tvdb-order` | `--tvdb-order dvd` | Renumber episodes to a TVDB season order. Needs `tvdb_api_key`. |
|
||
|
||
```shell title="Force the right IMDb match, and keep the service's own naming"
|
||
unshackle dl --imdb tt1375666 EXAMPLE 81234567
|
||
```
|
||
|
||
```shell title="Force the match and take IMDb's title, year and language too"
|
||
unshackle dl --imdb tt1375666 --enrich EXAMPLE 81234567
|
||
```
|
||
|
||
### What each metadata provider supplies
|
||
|
||
Providers differ in what they answer with, so the fields `--enrich` can replace depend on
|
||
which ID you gave. Every provider except `imdb` only runs when its key is configured:
|
||
|
||
| Provider | Config key | Title and year | Original language | External IDs it returns |
|
||
| --- | --- | --- | --- | --- |
|
||
| TMDB | `tmdb_api_key` | yes | yes, alpha-2 such as `ko` | IMDb, TMDB, TVDB |
|
||
| TVDB | `tvdb_api_key` | yes | yes, alpha-3 such as `kor` | TMDB, TVDB, sometimes IMDb |
|
||
| OMDb | `omdb_api_key` | yes | yes, as an English name such as `Korean` | IMDb |
|
||
| IMDb | *(no key needed)* | yes | yes, alpha-2 such as `ko` | IMDb |
|
||
| SIMKL | `simkl_client_id` | yes | no, it publishes a country and no language | IMDb, TMDB, TVDB |
|
||
| AniList | *(no key needed)* | yes | yes, worked out from the country of origin, such as `ja` | AniList |
|
||
|
||
Whatever the tag looks like, unshackle normalises it before use, so `ko`, `kor` and `Korean`
|
||
all end up as the same language.
|
||
|
||
!!! note "AniList only answers for anime"
|
||
It is last in the default provider order and returns nothing for a title that is not
|
||
anime. The only cost is one search that misses. Put `anilist` earlier in
|
||
[`metadata_providers`](../reference/configuration/misc.md#external-api-keys) if you mostly
|
||
download anime. Which of the three AniList title variants is used is set by
|
||
[`anilist_title_language`](../reference/configuration/misc.md#external-api-keys).
|
||
|
||
A service can also mark its titles as anime. For those titles unshackle tries AniList
|
||
first. When AniList has no match, it falls through to the normal order.
|
||
|
||
Each ID goes to the providers that read that kind of ID, in
|
||
[`metadata_providers`](../reference/configuration/misc.md#external-api-keys) order:
|
||
|
||
- `--tmdb` reads TMDB.
|
||
- `--imdb` reads IMDb, then falls back to OMDb. IMDb needs no key, so this path works out of
|
||
the box, and an OMDb key only matters when IMDb has no answer.
|
||
- `--tvdb` reads TVDB. It needs `tvdb_api_key`.
|
||
- `--anilist` reads AniList, which needs no key. It supplies the title, the year and the
|
||
original language, and writes an AniList tag. It knows no TMDB, IMDB or TVDB ID, so pair it
|
||
with `--tmdb`, `--imdb` or `--tvdb` when you want those tags as well.
|
||
|
||
Give **one** of `--tmdb`, `--imdb` and `--tvdb`. They cannot be combined, because one ID does
|
||
the job on its own: unshackle resolves the other two from it and writes all three to the tags.
|
||
Passing two is an error rather than a silent choice between them. `--anilist` is the exception
|
||
and still pairs with one of the three, which is how you tag an anime title with a western ID.
|
||
|
||
The ID you give must have a provider that can actually resolve it, so unshackle checks that
|
||
before it downloads anything. `--tmdb` without `tmdb_api_key`, or an ID whose providers your
|
||
`metadata_providers` list leaves out, fails immediately with a message naming what to set.
|
||
`--imdb` needs no key, since the `imdb` provider is keyless and in the default order.
|
||
|
||
With `--enrich`, the title, year and original language are all replaced with the external
|
||
source's values, whether or not the service already filled them in. A field the source does
|
||
not answer with is left alone, and unshackle logs which fields those were, so a provider with
|
||
a thin record cannot blank out what the service told you.
|
||
|
||
`--enrich` also fills in the absolute episode number of each episode from TVDB's absolute
|
||
order, for any series that has one and where the service did not supply it. Anime is the
|
||
usual beneficiary, but no part of this is limited to anime. This only adds the
|
||
[`{absolute}`](../reference/configuration/output.md#output_template) naming variable. The
|
||
season and episode numbers are never changed.
|
||
|
||
With `--daily`, `--enrich` also fills in the air date of each episode from TVDB. See
|
||
[Daily and date-based content](#daily-and-date-based-content).
|
||
|
||
!!! warning "`--enrich` replaces the original language, which affects more than the filename"
|
||
Track selection reads the original language, so replacing it changes which audio is
|
||
treated as the original. That is the point when a service mislabels it, but it means a
|
||
wrong ID can pick the wrong audio track and not only write a wrong name. Without
|
||
`--enrich` the service's own value is kept and track selection is untouched.
|
||
|
||
### Episode ordering
|
||
|
||
A service does not always number a series the way TVDB's aired order does. Some services list
|
||
Futurama in TVDB's `alternate` (Streaming) order, for example, while TVDB's `official` order
|
||
holds back four season-one episodes to the start of season two. `--tvdb-order` works out which
|
||
order the service used, then renumbers the episodes into the order you asked for:
|
||
|
||
```shell title="Download Futurama in DVD order"
|
||
unshackle dl --tvdb-order dvd EXAMPLE 81234567
|
||
```
|
||
|
||
Available orders are `official` (aired), `dvd`, `absolute`, `alternate`, and `regional`. Set
|
||
`tvdb_order` in your config to apply one by default.
|
||
|
||
!!! warning "Orders that do not cover the whole series"
|
||
An order can leave out episodes the service carries. TVDB's `dvd` order does not list
|
||
Futurama's four movies. Those episodes keep their original numbering. If that would give
|
||
two episodes the same season/episode slot, and so the same filename, unshackle logs an
|
||
error and leaves the numbering untouched. Pick an order that covers the whole series.
|
||
|
||
## Configuration defaults
|
||
|
||
Every flag on this page can be set as a default in your configuration file so you don't
|
||
have to type it each time, and defaults can be scoped per service. See
|
||
[Configuration file](../getting-started/configuration-file.md) for the `dl:` block and
|
||
per-service overrides.
|
||
|
||
!!! tip "Explicit flags always win"
|
||
A value you pass on the command line (or via an environment variable) always takes
|
||
precedence over both the global `dl:` defaults and any per-service `dl:` overrides.
|
||
Config only fills in options you didn't set explicitly.
|
||
|
||
## Quick reference
|
||
|
||
A condensed lookup of the most-used options. Run `unshackle dl --help` for the complete,
|
||
authoritative list.
|
||
|
||
| Flag | Short | Meaning |
|
||
| --- | --- | --- |
|
||
| `--quality` | `-q` | Target resolution(s), e.g. `1080,720`. |
|
||
| `--vcodec` | `-v` | Video codec(s). |
|
||
| `--acodec` | `-a` | Audio codec(s). |
|
||
| `--range` | `-r` | Color range(s); default `SDR`. |
|
||
| `--channels` | `-c` | Audio channel layout. |
|
||
| `--noatmos` | `-naa` | Exclude Atmos audio. |
|
||
| `--lang` | `-l` | Video + audio language(s); default `orig`. `-` excludes, e.g. `all,-es`. |
|
||
| `--a-lang` | `-al` | Audio-only language override. `-` excludes. |
|
||
| `--v-lang` | `-vl` | Video-only language override. `-` excludes. |
|
||
| `--s-lang` | `-sl` | Subtitle language(s); default `all`. `-` excludes, e.g. `all,-es`. |
|
||
| `--forced-subs` | `-fs` | Include forced subtitles. |
|
||
| `--forced-s-lang` | `-fsl` | Forced subtitle language(s); implies `-fs`. `-` excludes. |
|
||
| `--sub-format` | | Output subtitle format. |
|
||
| `--wanted` | `-w` | Episode/season range, or an air date. |
|
||
| `--daily` | | Fill missing air dates from TVDB during `--enrich`. |
|
||
| `--select-titles` | | Interactively pick episodes or films. |
|
||
| `--latest-episode` | | Only the newest episode. |
|
||
| `--video-only` / `--audio-only` / `--subs-only` | `-V` / `-A` / `-S` | Restrict track types. |
|
||
| `--no-video` / `--no-audio` / `--no-subs` / `--no-chapters` | `-nv` / `-na` / `-ns` / `-nc` | Skip track types. |
|
||
| `--worst` | | Lowest bitrate within `-q`. |
|
||
| `--best-available` | | Degrade gracefully instead of failing. |
|
||
| `--output` | `-o` | Output directory for this run. |
|
||
| `--split-audio` / `--merge-video` / `--no-mux` | | Muxing behaviour. |
|
||
| `--proxy` / `--no-proxy` / `--no-proxy-download` | | Proxy control. |
|
||
| `--workers` / `--downloads` / `--slow` | | Concurrency and pacing. |
|
||
| `--list` / `--list-titles` / `--skip-dl` | | Dry runs. |
|
||
| `--cdm-only` / `--vaults-only` | | Key source control. |
|
||
| `--export` | | Export track info and keys to JSON. |
|
||
| `--tmdb` / `--imdb` / `--tvdb` / `--anilist` / `--enrich` | | Metadata overrides. |
|
||
| `--tvdb-order` | | Renumber episodes to a TVDB season order. |
|