mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 20:15:10 +00:00
docs(post-scripts): use post-script as the one term for hooks
Add the post-script row to the ste-writing terminology contract and replace hook/postscript/post-download script across docs and docstrings.
This commit is contained in:
@@ -425,9 +425,9 @@ Make a download job. It requires `service` and `title_id`. Every other field is
|
|||||||
!!! danger "Rejected parameters"
|
!!! danger "Rejected parameters"
|
||||||
`postscript`, `post_script` and `post_scripts` are never accepted. A body containing any
|
`postscript`, `post_script` and `post_scripts` are never accepted. A body containing any
|
||||||
of them gets `400 INVALID_PARAMETERS`, because a command string that arrives from
|
of them gets `400 INVALID_PARAMETERS`, because a command string that arrives from
|
||||||
an HTTP caller would be remote code execution. You configure post-download hooks only in
|
an HTTP caller would be remote code execution. You configure post-scripts only in
|
||||||
`unshackle.yaml`, where they still operate for API jobs. See
|
`unshackle.yaml`, where they still operate for API jobs. See
|
||||||
[Post-download scripts](../../reference/configuration/post-scripts.md).
|
[Post-scripts](../../reference/configuration/post-scripts.md).
|
||||||
|
|
||||||
!!! warning "Gated parameters (developer)"
|
!!! warning "Gated parameters (developer)"
|
||||||
The server accepts a few keys, but permits them only when the server config opts in. Otherwise the server rejects the request with `403 FORBIDDEN`:
|
The server accepts a few keys, but permits them only when the server config opts in. Otherwise the server rejects the request with `403 FORBIDDEN`:
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ The top-level keys group loosely into these areas:
|
|||||||
| Naming & tagging | `tag`, `output_template`, `chapter_fallback_name` |
|
| Naming & tagging | `tag`, `output_template`, `chapter_fallback_name` |
|
||||||
| External API keys | `tmdb_api_key`, `tvdb_api_key`, `simkl_client_id`, `ipinfo_api_key`, `metadata_providers` |
|
| External API keys | `tmdb_api_key`, `tvdb_api_key`, `simkl_client_id`, `ipinfo_api_key`, `metadata_providers` |
|
||||||
| Behaviour & logging | `update_checks`, `redact_paths`, `debug`, `unicode_filenames` |
|
| Behaviour & logging | `update_checks`, `redact_paths`, `debug`, `unicode_filenames` |
|
||||||
| Post-download scripts | `post_scripts` |
|
| Post-scripts | `post_scripts` |
|
||||||
| Paths | `directories`, `filenames` |
|
| Paths | `directories`, `filenames` |
|
||||||
|
|
||||||
For the full list with types and defaults, see the [Configuration Reference](../reference/configuration/index.md).
|
For the full list with types and defaults, see the [Configuration Reference](../reference/configuration/index.md).
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ Keep only certain track types, or skip certain track types.
|
|||||||
| `--no-mux` | Do not mux; keep individual track files. |
|
| `--no-mux` | Do not mux; keep individual track files. |
|
||||||
| `--tag` | Group tag override. |
|
| `--tag` | Group tag override. |
|
||||||
| `--repack` | Add a `REPACK` tag to the filename. |
|
| `--repack` | Add a `REPACK` tag to the filename. |
|
||||||
| `--postscript` | Run a command after each output file, with `{variable}` placeholders substituted. Repeatable. Replaces the `post_scripts` config for this run. See [Post-download scripts](../reference/configuration/post-scripts.md). |
|
| `--postscript` | Run a command after each output file, with `{variable}` placeholders substituted. Repeatable. Replaces the `post_scripts` config for this run. See [Post-scripts](../reference/configuration/post-scripts.md). |
|
||||||
|
|
||||||
### Metadata & tagging
|
### Metadata & tagging
|
||||||
|
|
||||||
|
|||||||
@@ -686,16 +686,16 @@ By default, `mkvmerge` muxes the tracks of a movie or an episode into a single M
|
|||||||
|
|
||||||
`--postscript "<command>"` runs your own command once per output file, with unshackle's
|
`--postscript "<command>"` runs your own command once per output file, with unshackle's
|
||||||
metadata substituted into `{variable}` placeholders. It is repeatable, and it replaces the
|
metadata substituted into `{variable}` placeholders. It is repeatable, and it replaces the
|
||||||
`post_scripts` config for that run. No `success` hook operates under `--no-mux`, because
|
`post_scripts` config for that run. No `success` post-script operates under `--no-mux`, because
|
||||||
that run writes no muxed output. A `failure` hook still operates if the download fails.
|
that run writes no muxed output. A `failure` post-script still operates if the download fails.
|
||||||
|
|
||||||
```shell title="Hand each finished file to an uploader"
|
```shell title="Hand each finished file to an uploader"
|
||||||
unshackle dl --postscript "python /opt/upload.py {filepath} --service={service}" EXAMPLE 81234567
|
unshackle dl --postscript "python /opt/upload.py {filepath} --service={service}" EXAMPLE 81234567
|
||||||
```
|
```
|
||||||
|
|
||||||
For hooks that continue across runs, for the `season` and `run` modes, for `failure` hooks
|
For post-scripts that continue across runs, for the `season` and `run` modes, for `failure`
|
||||||
and for the full variable list, see
|
post-scripts and for the full variable list, see
|
||||||
[Post-download scripts](../reference/configuration/post-scripts.md).
|
[Post-scripts](../reference/configuration/post-scripts.md).
|
||||||
|
|
||||||
### Naming tags
|
### Naming tags
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ This page has moved. The configuration reference is now split by area under
|
|||||||
| DRM & CDM | [configuration/drm.md](configuration/drm.md) |
|
| DRM & CDM | [configuration/drm.md](configuration/drm.md) |
|
||||||
| Network & proxy | [configuration/network.md](configuration/network.md) |
|
| Network & proxy | [configuration/network.md](configuration/network.md) |
|
||||||
| Key vaults | [configuration/vaults.md](configuration/vaults.md) |
|
| Key vaults | [configuration/vaults.md](configuration/vaults.md) |
|
||||||
| Post-download scripts | [configuration/post-scripts.md](configuration/post-scripts.md) |
|
| Post-scripts | [configuration/post-scripts.md](configuration/post-scripts.md) |
|
||||||
| Other settings | [configuration/misc.md](configuration/misc.md) |
|
| Other settings | [configuration/misc.md](configuration/misc.md) |
|
||||||
| Complete annotated example | [configuration/example.md](configuration/example.md) |
|
| Complete annotated example | [configuration/example.md](configuration/example.md) |
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Where the file lives, how unshackle discovers it, and how to edit it:
|
|||||||
| [DRM & CDM](drm.md) | `cdm`, `remote_cdm`, `decryption` |
|
| [DRM & CDM](drm.md) | `cdm`, `remote_cdm`, `decryption` |
|
||||||
| [Network & proxy](network.md) | `network`, `headers`, `proxy_providers` |
|
| [Network & proxy](network.md) | `network`, `headers`, `proxy_providers` |
|
||||||
| [Key vaults](vaults.md) | `key_vaults`, `vault_timeout` |
|
| [Key vaults](vaults.md) | `key_vaults`, `vault_timeout` |
|
||||||
| [Post-download scripts](post-scripts.md) | `post_scripts` |
|
| [Post-scripts](post-scripts.md) | `post_scripts` |
|
||||||
| [External API keys](misc.md#external-api-keys) | `omdb_api_key`, `tmdb_api_key`, `tvdb_api_key`, `tvdb_pin`, `tvdb_order`, `simkl_client_id`, `metadata_providers`, `anilist_title_language`, `disable_metadata`, `decrypt_labs_api_key`, `ipinfo_api_key` |
|
| [External API keys](misc.md#external-api-keys) | `omdb_api_key`, `tmdb_api_key`, `tvdb_api_key`, `tvdb_pin`, `tvdb_order`, `simkl_client_id`, `metadata_providers`, `anilist_title_language`, `disable_metadata`, `decrypt_labs_api_key`, `ipinfo_api_key` |
|
||||||
| [Caching & updates](misc.md#caching-updates) | `title_cache_enabled`, `title_cache_time`, `title_cache_max_retention`, `update_checks`, `update_check_interval` |
|
| [Caching & updates](misc.md#caching-updates) | `title_cache_enabled`, `title_cache_time`, `title_cache_max_retention`, `update_checks`, `update_check_interval` |
|
||||||
| [Appearance](misc.md#appearance) | `theme` |
|
| [Appearance](misc.md#appearance) | `theme` |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Post-download scripts { #post-scripts }
|
# Post-scripts { #post-scripts }
|
||||||
|
|
||||||
Operate your own script when a download finishes, with unshackle's metadata passed in as
|
Operate your own script when a download finishes, with unshackle's metadata passed in as
|
||||||
`{variable}` placeholders. Use it to hand a finished file to an uploader, send a
|
`{variable}` placeholders. Use it to hand a finished file to an uploader, send a
|
||||||
@@ -87,7 +87,7 @@ post_scripts:
|
|||||||
```
|
```
|
||||||
|
|
||||||
With `wait: true`, unshackle waits until the script exits, then writes the exit code to the
|
With `wait: true`, unshackle waits until the script exits, then writes the exit code to the
|
||||||
debug log and carries on. Because the `file` hook runs between titles, unshackle runs one
|
debug log and carries on. Because the `file` post-script runs between titles, unshackle runs one
|
||||||
script at a time over a season pack instead of starting them all at once. unshackle only
|
script at a time over a season pack instead of starting them all at once. unshackle only
|
||||||
logs the exit code: a failing script does not fail the download or change unshackle's own
|
logs the exit code: a failing script does not fail the download or change unshackle's own
|
||||||
exit code. `--postscript` entries never wait.
|
exit code. `--postscript` entries never wait.
|
||||||
@@ -132,7 +132,7 @@ Every variable from your `output_template` is available here as well, including
|
|||||||
file, because SDR is the absence of an HDR tag in a filename rather than a tag that
|
file, because SDR is the absence of an HDR tag in a filename rather than a tag that
|
||||||
reads `SDR`. `{edition}`, `{atmos}`, `{multi}`, `{dual}` and `{hfr}` behave the same
|
reads `SDR`. `{edition}`, `{atmos}`, `{multi}`, `{dual}` and `{hfr}` behave the same
|
||||||
way. If your script needs the word `SDR`, derive it from an empty `{hdr}` yourself.
|
way. If your script needs the word `SDR`, derive it from an empty `{hdr}` yourself.
|
||||||
- The hook is a `failure` hook. A failed download has no output file to read metadata
|
- The post-script is a `failure` one. A failed download has no output file to read metadata
|
||||||
from, so everything that comes from the naming context is empty. See
|
from, so everything that comes from the naming context is empty. See
|
||||||
[Events](#events).
|
[Events](#events).
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ Every variable from your `output_template` is available here as well, including
|
|||||||
|
|
||||||
### Music variables
|
### Music variables
|
||||||
|
|
||||||
A music download builds its variables from the music naming context, so a hook gets the
|
A music download builds its variables from the music naming context, so a post-script gets the
|
||||||
release fields instead of the season and episode ones. `{season}`, `{episode}` and
|
release fields instead of the season and episode ones. `{season}`, `{episode}` and
|
||||||
`{episode_name}` are always empty for music.
|
`{episode_name}` are always empty for music.
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ release fields instead of the season and episode ones. `{season}`, `{episode}` a
|
|||||||
| `{release_type}` | `album`, `single`, `ep`, and so on. A service that reports nothing gives `album` |
|
| `{release_type}` | `album`, `single`, `ep`, and so on. A service that reports nothing gives `album` |
|
||||||
| `{explicit}` | The word `Explicit` when the track is flagged, otherwise empty |
|
| `{explicit}` | The word `Explicit` when the track is flagged, otherwise empty |
|
||||||
|
|
||||||
In a `file` hook `{title}` is the track name. See [Modes](#modes) for what an album hook
|
In a `file` post-script `{title}` is the track name. See [Modes](#modes) for what an album post-script
|
||||||
puts in `{title}`.
|
puts in `{title}`.
|
||||||
|
|
||||||
`{year}` is empty when the service gives the release no year. `{ext}` is the container the
|
`{year}` is empty when the service gives the release no year. `{ext}` is the container the
|
||||||
@@ -170,11 +170,11 @@ track arrived in, such as `.flac`, because a music download is not muxed.
|
|||||||
### Tagging IDs { #tagging-ids }
|
### Tagging IDs { #tagging-ids }
|
||||||
|
|
||||||
`{tmdb}`, `{imdb}` and `{tvdb}` carry the IDs unshackle tags the file with. unshackle reads
|
`{tmdb}`, `{imdb}` and `{tvdb}` carry the IDs unshackle tags the file with. unshackle reads
|
||||||
them at the moment it sends the hook, and not at the start of the run. The title search inside
|
them at the moment it dispatches the post-script, and not at the start of the run. The title search
|
||||||
the download loop finds an episode's TMDB ID, so a hook fires with it already filled in.
|
inside the download loop finds an episode's TMDB ID, so a post-script fires with it already filled in.
|
||||||
|
|
||||||
They hold an ID you passed with `--tmdb`, `--imdb` or `--tvdb`, plus a TMDB ID the episode
|
They hold an ID you passed with `--tmdb`, `--imdb` or `--tvdb`, plus a TMDB ID the episode
|
||||||
search resolved. No such search runs for a movie, so a movie hook gets them empty unless you
|
search resolved. No such search runs for a movie, so a movie post-script gets them empty unless you
|
||||||
passed one.
|
passed one.
|
||||||
|
|
||||||
### Sidecar files
|
### Sidecar files
|
||||||
@@ -206,19 +206,19 @@ subs = [p for p in sys.argv[i].removeprefix("--subs=").split("\n") if p]
|
|||||||
| `run` | Once per folder written, at the end of the run | Empty | Empty |
|
| `run` | Once per folder written, at the end of the run | Empty | Empty |
|
||||||
|
|
||||||
!!! warning "`season` means everything this run asked for"
|
!!! warning "`season` means everything this run asked for"
|
||||||
A `season` hook fires when the last title **this run queued** for that season finishes.
|
A `season` post-script fires when the last title **this run queued** for that season
|
||||||
With `-w S01-S02` the S01 hook runs while S02 is still downloading. It says nothing
|
finishes. With `-w S01-S02` the S01 post-script runs while S02 is still downloading. It says
|
||||||
about whether the season is complete on disk. Downloading a single episode fires the
|
nothing about whether the season is complete on disk. Downloading a single episode fires
|
||||||
hook for that season as soon as the episode lands.
|
the post-script for that season as soon as the episode lands.
|
||||||
|
|
||||||
A title that fails stops the whole run. unshackle dispatches the `failure` hook for
|
A title that fails stops the whole run. unshackle dispatches the `failure` post-script for
|
||||||
that title and then returns. It downloads no further title, and no `season` or `run`
|
that title and then returns. It downloads no further title, and no `season` or `run`
|
||||||
hook fires after that point. This applies to every title type. If one episode of S01
|
post-script fires after that point. This applies to every title type. If one episode of S01
|
||||||
fails, the S01 hook does not fire at all.
|
fails, the S01 post-script does not fire at all.
|
||||||
|
|
||||||
unshackle attaches the hook to the folder, not to the season, so one season landing in several
|
unshackle attaches the post-script to the folder, not to the season, so one season landing in
|
||||||
folders (`-q 1080,720` with a quality-dependent folder template) fires it once per folder,
|
several folders (`-q 1080,720` with a quality-dependent folder template) fires it once per
|
||||||
each with its own `{folder}`. A movie is a group of its own: a `season` hook fires for a
|
folder, each with its own `{folder}`. A movie is a group of its own: a `season` post-script fires for a
|
||||||
movie's folder as soon as the movie finishes.
|
movie's folder as soon as the movie finishes.
|
||||||
|
|
||||||
In `run` mode every variable except `{folder}` is empty, because one run can cover several
|
In `run` mode every variable except `{folder}` is empty, because one run can cover several
|
||||||
@@ -235,27 +235,27 @@ Music uses the same three modes, with the album standing in for the season.
|
|||||||
| `season` | Once per album folder, after every queued track of that album has landed |
|
| `season` | Once per album folder, after every queued track of that album has landed |
|
||||||
| `run` | Once per folder written, at the end of the run |
|
| `run` | Once per folder written, at the end of the run |
|
||||||
|
|
||||||
In a `file` hook `{title}` and `{title_raw}` are the track name. In an album hook they are
|
In a `file` post-script `{title}` and `{title_raw}` are the track name. In an album post-script they
|
||||||
the album name, and the per-track variables `{track_number}`, `{disc}` and `{isrc}` are
|
are the album name, and the per-track variables `{track_number}`, `{disc}` and `{isrc}` are
|
||||||
empty, in the same way that `{episode}` is empty in a season hook.
|
empty, in the same way that `{episode}` is empty in a season post-script.
|
||||||
|
|
||||||
An album hook obeys the same rule as a season hook. It fires when the last track **this run
|
An album post-script obeys the same rule as a season post-script. It fires when the last track
|
||||||
queued** for that album lands. This does not mean that the album is complete on disk. A
|
**this run queued** for that album lands. This does not mean that the album is complete on disk.
|
||||||
one-track download fires the album hook as soon as that track lands. If a track fails, the
|
A one-track download fires the album post-script as soon as that track lands. If a track fails,
|
||||||
run stops there, so the album hook never fires, in the same way as an episode and its
|
the run stops there, so the album post-script never fires, in the same way as an episode and its
|
||||||
season.
|
season.
|
||||||
|
|
||||||
## Events { #events }
|
## Events { #events }
|
||||||
|
|
||||||
`success` runs after the file has moved to its final path. `failure` runs when the download
|
`success` runs after the file has moved to its final path. `failure` runs when the download
|
||||||
fails, with `{filepath}` empty and `{error}` set. Each track of an album is its own title.
|
fails, with `{filepath}` empty and `{error}` set. Each track of an album is its own title.
|
||||||
A music `failure` hook therefore describes the track that failed, not the release.
|
A music `failure` post-script therefore describes the track that failed, not the release.
|
||||||
|
|
||||||
!!! warning "`failure` only fires in `file` mode"
|
!!! warning "`failure` only fires in `file` mode"
|
||||||
A `failure` entry with `mode: season` or `mode: run` never runs. Leave `mode` at its
|
A `failure` entry with `mode: season` or `mode: run` never runs. Leave `mode` at its
|
||||||
default on a failure entry.
|
default on a failure entry.
|
||||||
|
|
||||||
A `failure` hook has no output file to read metadata from, so it carries only what the
|
A `failure` post-script has no output file to read metadata from, so it carries only what the
|
||||||
title object already knew: `{title}`, `{title_raw}`, `{title_id}`, `{year}`, `{season}`,
|
title object already knew: `{title}`, `{title_raw}`, `{title_id}`, `{year}`, `{season}`,
|
||||||
`{episode}`, `{episode_name}`, `{service}`, the tagging IDs and `{error}`. Everything that
|
`{episode}`, `{episode_name}`, `{service}`, the tagging IDs and `{error}`. Everything that
|
||||||
comes from the naming context, such as `{quality}`, `{hdr}`, `{artist}` and `{album}`, is
|
comes from the naming context, such as `{quality}`, `{hdr}`, `{artist}` and `{album}`, is
|
||||||
@@ -266,7 +266,7 @@ sees your script's exit code only when that entry sets `wait: true`, and even th
|
|||||||
writes the code to the debug log. Nothing else reports a failing script. Your script owns
|
writes the code to the debug log. Nothing else reports a failing script. Your script owns
|
||||||
its own error handling and logging.
|
its own error handling and logging.
|
||||||
|
|
||||||
A `success` hook always names a file this run wrote. There is no skip-if-exists path in
|
A `success` post-script always names a file this run wrote. There is no skip-if-exists path in
|
||||||
`dl` today, so nothing fires for a title that was not downloaded.
|
`dl` today, so nothing fires for a title that was not downloaded.
|
||||||
|
|
||||||
## `--postscript`
|
## `--postscript`
|
||||||
@@ -300,7 +300,7 @@ unshackle logs the command it operated twice: at `DEBUG` level on the console, a
|
|||||||
when that log is on.
|
when that log is on.
|
||||||
|
|
||||||
unshackle masks both the same way, and honours [`redact_paths`](misc.md), which is on by default.
|
unshackle masks both the same way, and honours [`redact_paths`](misc.md), which is on by default.
|
||||||
Debug logs get shared in issue reports, and a hook command line is mostly paths. URL
|
Debug logs get shared in issue reports, and a post-script command line is mostly paths. URL
|
||||||
credentials and `token=` / `api_key=` query parameters are also masked.
|
credentials and `token=` / `api_key=` query parameters are also masked.
|
||||||
|
|
||||||
!!! warning "Do not hardcode secrets in the command"
|
!!! warning "Do not hardcode secrets in the command"
|
||||||
@@ -310,8 +310,8 @@ credentials and `token=` / `api_key=` query parameters are also masked.
|
|||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
- No `success` hook operates under `--no-mux` or `--skip-dl`, because neither writes a muxed
|
- No `success` post-script operates under `--no-mux` or `--skip-dl`, because neither writes a
|
||||||
output. A `failure` hook still runs under `--no-mux` if the download itself fails.
|
muxed output. A `failure` post-script still runs under `--no-mux` if the download itself fails.
|
||||||
- `failure` hooks only operate in `file` mode.
|
- `failure` post-scripts only operate in `file` mode.
|
||||||
- There is no webhook sender. Call `curl` from a script instead.
|
- There is no webhook sender. Call `curl` from a script instead.
|
||||||
- Script output is not captured. Redirect it inside your own script if you want a log.
|
- Script output is not captured. Redirect it inside your own script if you want a log.
|
||||||
|
|||||||
+1
-1
@@ -128,7 +128,7 @@ nav:
|
|||||||
- DRM & CDM: reference/configuration/drm.md
|
- DRM & CDM: reference/configuration/drm.md
|
||||||
- Network & Proxy: reference/configuration/network.md
|
- Network & Proxy: reference/configuration/network.md
|
||||||
- Key Vaults: reference/configuration/vaults.md
|
- Key Vaults: reference/configuration/vaults.md
|
||||||
- Post-Download Scripts: reference/configuration/post-scripts.md
|
- Post-Scripts: reference/configuration/post-scripts.md
|
||||||
- Other Settings: reference/configuration/misc.md
|
- Other Settings: reference/configuration/misc.md
|
||||||
- Annotated Example: reference/configuration/example.md
|
- Annotated Example: reference/configuration/example.md
|
||||||
- Developer:
|
- Developer:
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"""Music post-script hooks, which no live service was available to test against.
|
"""Music post-scripts, which no live service was available to test against.
|
||||||
|
|
||||||
A Song runs through the same title loop as an Episode, so its hooks come from the same
|
A Song runs through the same title loop as an Episode, so its post-scripts come from the same
|
||||||
two functions: ``build_context`` for a file and ``season_context`` for the release folder.
|
two functions: ``build_context`` for a file and ``season_context`` for the release folder.
|
||||||
These check what those two feed a script: that a Song builds a usable context, that an
|
These check what those two feed a script: that a Song builds a usable context, that an
|
||||||
album groups the way a season does, and that the album hook describes the album rather
|
album groups the way a season does, and that the album post-script describes the album rather
|
||||||
than whichever track landed last.
|
than whichever track landed last.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ from unshackle.core.utils.post_scripts import build_context, dispatch, season_co
|
|||||||
|
|
||||||
|
|
||||||
class FakeSong(SimpleNamespace):
|
class FakeSong(SimpleNamespace):
|
||||||
"""A Song as the hook sees it: name/artist/album, no season or episode."""
|
"""A Song as the post-script sees it: name/artist/album, no season or episode."""
|
||||||
|
|
||||||
def build_template_context(self, media_info, show_service=True):
|
def build_template_context(self, media_info, show_service=True):
|
||||||
return {
|
return {
|
||||||
@@ -45,7 +45,7 @@ def song(**kwargs):
|
|||||||
|
|
||||||
|
|
||||||
class FakeEpisode(SimpleNamespace):
|
class FakeEpisode(SimpleNamespace):
|
||||||
"""An Episode as the hook sees it: a show title, a season, and a number."""
|
"""An Episode as the post-script sees it: a show title, a season, and a number."""
|
||||||
|
|
||||||
def build_template_context(self, media_info, show_service=True):
|
def build_template_context(self, media_info, show_service=True):
|
||||||
return {"title": self.title, "season": f"{self.season:02}", "audio": "EAC3", "video": "H.265"}
|
return {"title": self.title, "season": f"{self.season:02}", "audio": "EAC3", "video": "H.265"}
|
||||||
@@ -109,7 +109,7 @@ def test_album_grouping_matches_the_season_grouping_rule():
|
|||||||
|
|
||||||
@pytest.mark.parametrize("mode", ["season", "run"])
|
@pytest.mark.parametrize("mode", ["season", "run"])
|
||||||
def test_album_and_run_contexts_blank_the_file_variables(mode):
|
def test_album_and_run_contexts_blank_the_file_variables(mode):
|
||||||
"""Mirrors the blanking dl.py applies before dispatching an album or run hook."""
|
"""Mirrors the blanking dl.py applies before dispatching an album or run post-script."""
|
||||||
base = build_context(
|
base = build_context(
|
||||||
song(),
|
song(),
|
||||||
media_info=object(),
|
media_info=object(),
|
||||||
@@ -133,7 +133,7 @@ def test_album_and_run_contexts_blank_the_file_variables(mode):
|
|||||||
assert context["album"] == "Example Album, Live & Remastered"
|
assert context["album"] == "Example Album, Live & Remastered"
|
||||||
|
|
||||||
|
|
||||||
def test_a_music_hook_command_builds_the_argv_a_script_would_get(monkeypatch, tmp_path):
|
def test_a_music_post_script_command_builds_the_argv_a_script_would_get(monkeypatch, tmp_path):
|
||||||
"""End to end through the real dispatcher: config entry in, argv out."""
|
"""End to end through the real dispatcher: config entry in, argv out."""
|
||||||
from unshackle.core.config import config
|
from unshackle.core.config import config
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ def test_a_music_hook_command_builds_the_argv_a_script_would_get(monkeypatch, tm
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
assert marker.exists(), "the hook process never ran"
|
assert marker.exists(), "the post-script process never ran"
|
||||||
argv = marker.read_text().splitlines()
|
argv = marker.read_text().splitlines()
|
||||||
assert "--album=Example Album, Live & Remastered" in argv
|
assert "--album=Example Album, Live & Remastered" in argv
|
||||||
assert "--artist=Example Artist" in argv
|
assert "--artist=Example Artist" in argv
|
||||||
@@ -172,7 +172,7 @@ def test_a_music_hook_command_builds_the_argv_a_script_would_get(monkeypatch, tm
|
|||||||
|
|
||||||
|
|
||||||
def test_season_context_blanks_the_file_variables_and_sets_the_folder():
|
def test_season_context_blanks_the_file_variables_and_sets_the_folder():
|
||||||
"""An album hook describes the release folder, so no track's file may leak into it."""
|
"""An album post-script describes the release folder, so no track's file may leak into it."""
|
||||||
folder = Path("/dl/Example Artist - Example Album, Live & Remastered (1983)")
|
folder = Path("/dl/Example Artist - Example Album, Live & Remastered (1983)")
|
||||||
context = build_context(
|
context = build_context(
|
||||||
song(),
|
song(),
|
||||||
@@ -194,7 +194,7 @@ def test_season_context_blanks_the_file_variables_and_sets_the_folder():
|
|||||||
def test_season_context_identifies_the_album_not_the_last_track():
|
def test_season_context_identifies_the_album_not_the_last_track():
|
||||||
"""A Song's ``{title}`` is the song, so blanking only the file fields is not enough.
|
"""A Song's ``{title}`` is the song, so blanking only the file fields is not enough.
|
||||||
|
|
||||||
Guards a regression where an album hook was handed whichever track landed last:
|
Guards a regression where an album post-script was handed whichever track landed last:
|
||||||
``{title}`` was a song name and ``{track_number}``/``{isrc}`` were that track's.
|
``{title}`` was a song name and ``{track_number}``/``{isrc}`` were that track's.
|
||||||
"""
|
"""
|
||||||
folder = Path("/dl/Example Artist - Example Album (1983)")
|
folder = Path("/dl/Example Artist - Example Album (1983)")
|
||||||
@@ -219,7 +219,7 @@ def test_season_context_identifies_the_album_not_the_last_track():
|
|||||||
|
|
||||||
|
|
||||||
def test_season_context_leaves_a_non_music_context_untouched():
|
def test_season_context_leaves_a_non_music_context_untouched():
|
||||||
"""The music branch is gated on ``{album}``; a season hook must not lose its own keys."""
|
"""The music branch is gated on ``{album}``; a season post-script must not lose its own keys."""
|
||||||
folder = Path("/dl/Some Show/Season 1")
|
folder = Path("/dl/Some Show/Season 1")
|
||||||
context = build_context(
|
context = build_context(
|
||||||
episode(),
|
episode(),
|
||||||
|
|||||||
@@ -597,7 +597,7 @@ async def download(request: web.Request) -> web.Response:
|
|||||||
description: >-
|
description: >-
|
||||||
Download a video title based on the specified parameters.
|
Download a video title based on the specified parameters.
|
||||||
'postscript', 'post_script' and 'post_scripts' are never accepted and return 400.
|
'postscript', 'post_script' and 'post_scripts' are never accepted and return 400.
|
||||||
You configure post-download hooks only in unshackle.yaml.
|
You configure post-scripts only in unshackle.yaml.
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ def build_context(
|
|||||||
|
|
||||||
|
|
||||||
def season_context(context: dict[str, str], folder: Path) -> dict[str, str]:
|
def season_context(context: dict[str, str], folder: Path) -> dict[str, str]:
|
||||||
"""Blank the file-level variables so a season/album hook describes the folder."""
|
"""Blank the file-level variables so a season/album post-script describes the folder."""
|
||||||
out = dict(context)
|
out = dict(context)
|
||||||
for key in ("filepath", "filename", "ext", "sidecars", "episode", "episode_name"):
|
for key in ("filepath", "filename", "ext", "sidecars", "episode", "episode_name"):
|
||||||
out[key] = ""
|
out[key] = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user