Startup and download hooks¶
Hooks are your own scripts, run at three fixed points: when the container starts, once the server is up, and after each download. Put them in a directory on the host and mount it at /app/hooks, read-only:
The extended Compose example has the same line, commented out. Nothing in Settings or the API can add a hook or point at another directory; only whoever controls the mount can.
Layout¶
hooks/
├── init.d/ # each time the container starts
├── started.d/ # each time the server starts listening
└── download-finished.d/ # once for each new file
Leave out the stages you do not use.
| Stage | Runs | As | If a script fails |
|---|---|---|---|
init.d |
Each time the container starts, before the app | Root by default, or the container's user: |
The container stops starting |
started.d |
Each time the server starts listening, including after an update or a restart from Settings | The app's user, 1000:1000 by default | It is logged, and the next script runs |
download-finished.d |
Once for each new file, after it is in the library | The app's user | It is logged, and the next script runs |
In every stage:
- Only executable files run. A script without
chmod +xis skipped, with a line in the log saying so. Hidden files and directories are ignored. - Scripts run one at a time, in name order, so prefix them with numbers:
10-install-tools,20-.... Download hooks also run one download at a time, in the order files finish, so a playlist does not start hundreds of scripts at once. - Output goes to the container log, prefixed with the stage and script name, such as
[hook started.d/10-announce]. - Scripts run in
/appwith the container's environment, plus the variables below. The image is Ubuntu withbash,curl, andpython3.
A download or started hook that never exits holds up the scripts after it. Start anything long-running in the background and let the hook return.
Variables¶
| Variable | Stages | Value |
|---|---|---|
YTDL_EVENT |
All | init, started, or download-finished |
YTDL_PORT |
started.d |
Port the server listens on |
YTDL_URL |
started.d |
The configured URL of the app |
YTDL_FILE_PATH |
download-finished.d |
Absolute path of the new file |
YTDL_FILE_UID |
download-finished.d |
The file's ID in the library |
YTDL_FILE_TITLE |
download-finished.d |
Its title |
YTDL_FILE_URL |
download-finished.d |
The page it was downloaded from |
YTDL_FILE_TYPE |
download-finished.d |
audio or video |
YTDL_USER_UID |
download-finished.d |
The owner in multi-user mode, otherwise empty |
YTDL_SUBSCRIPTION_ID |
download-finished.d |
The subscription that downloaded it, otherwise empty |
YTDL_DOWNLOAD_UID |
download-finished.d |
The download it came from; a playlist's files share one |
Values are passed as environment variables, never pasted into a command, so a title with quotes or $ in it is only ever data. Quote the variables in your scripts all the same: "$YTDL_FILE_PATH".
Examples¶
Install a tool before the app starts¶
hooks/init.d/10-install-jq runs as root, so it can install packages. It runs on every start, so it checks first:
#!/bin/bash
set -e
command -v jq >/dev/null && exit 0
apt-get update
apt-get install -y --no-install-recommends jq
rm -rf /var/lib/apt/lists/*
A failing init script stops the container from starting, since what follows may depend on it. If a step is optional, end it with || true.
Say the server is up¶
hooks/started.d/10-announce posts to an ntfy topic on your own server:
#!/bin/sh
curl -fsS -m 10 -d "ytdl-material is up on port $YTDL_PORT" https://ntfy.example.com/ytdl || true
Copy new audio to a music library¶
hooks/download-finished.d/10-copy-audio, with the music folder mounted at /music:
Tell a media server to rescan¶
hooks/download-finished.d/20-refresh-jellyfin, with JELLYFIN_API_KEY set in the container's environment:
#!/bin/sh
curl -fsS -m 10 -X POST -H "Authorization: MediaBrowser Token=\"$JELLYFIN_API_KEY\"" \
http://jellyfin:8096/Library/Refresh
Security¶
A hook runs with the app's access to your media, configuration, and credentials, and an init hook runs as root. Anyone who can write to the hooks directory can run code in the container. Mount it read-only, keep it outside the media folders, and give its host directory the same care as the Compose file itself.
To check that the server itself is up, rather than running something when it is, use the health check.