feat(titles): add optional Episode.part, selected with -w s1e1.2

Opt-in and additive: an episode without a part behaves exactly as before.
This commit is contained in:
imSp4rky
2026-08-04 17:38:30 -06:00
parent 10bfaf6a73
commit 041ba5293b
17 changed files with 946 additions and 44 deletions
+1 -1
View File
@@ -112,7 +112,7 @@ The special language tokens `orig`, `all`, and `best` are honoured everywhere a
| Flag | Description |
|---|---|
| `-w`, `--wanted` | Wanted episodes, e.g. `S01-S05,S07`, `S01E01-S02E03`. Supports exclusions with a leading `-` (e.g. `-S03`). |
| `-w`, `--wanted` | Wanted episodes, e.g. `S01-S05,S07`, `S01E01-S02E03`. Supports exclusions with a leading `-` (e.g. `-S03`). For a [split episode](downloading.md#split-episodes), `.N` picks one part (`S01E01.2`), a range must stay inside the episode (`S01E01.1-S01E01.3`), and `S01E01` on its own takes every part. |
| `--select-titles` | Interactively select what to download: 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 titles only; do not download. |
+39
View File
@@ -368,6 +368,45 @@ with `-` to exclude it.
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
```
### 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 |
+27
View File
@@ -126,11 +126,38 @@ Every variable below is valid in both output and folder templates. Values are de
| `episode` | Zero-padded episode (`E%02d`) | `E05` |
| `season_episode` | Combined | `S01E05` |
| `episode_name` | Episode title | `Pilot` |
| `part` | Part index of a [split episode](#split-episodes), empty otherwise | `2` |
| `date` | ISO air date for daily/dated content | `2024-06-01` |
!!! note "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, `episode` and `year` are cleared, and `{date}` holds the ISO date. The date's internal separator (dots or spaces) follows your `series` template style.
### Split episodes {#split-episodes}
A few services split one episode into several separately playable videos. Where a service reports that, the part index is folded into `{episode}` and `{season_episode}`, so the stock `series` template names it with no change to your config.
```text
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 dated 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:
```text
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. An episode with no part is named like any other episode.
!!! note "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's scanner is closed-source, so its handling of text after the part token is unverified.
For custom templates there is also a standalone [`{part}`](../reference/configuration/output.md#output_template) variable. You rarely want it, see the caveat on that page.
### Audio
| Variable | Meaning | Example |