Post-Processing
Post-Processing
Section titled “Post-Processing”Sublarr can run a shell command automatically after every successful subtitle download. This lets you notify Plex, rename files, or trigger any automation without requiring a plugin.
Disabled by default. Post-processing must be explicitly enabled in Settings → Automation before the command is executed.
Enable Post-Processing
Section titled “Enable Post-Processing”- Go to Settings → Automation
- Toggle Post-Processing on
- Enter your command in the Post-Download Command field
- Click Save
Available Variables
Section titled “Available Variables”Variables are substituted into the command string before execution.
| Variable | Example value | Description |
|---|---|---|
{subtitle_path} | /media/anime/Naruto.srt | Absolute path to the saved subtitle file |
{path} | /media/anime/Naruto.srt | Alias for {subtitle_path} |
{language} | de | ISO 639-1 language code |
{provider} | jimaku | Provider name that supplied the subtitle |
{score} | 93 | Integer match score (0–100) |
{media_type} | series | series, movie, or empty string |
{video_path} | (empty) | Reserved — always empty in current release |
Examples
Section titled “Examples”Notify Plex after download:
curl -s "http://plex:32400/library/sections/1/refresh?X-Plex-Token=TOKEN" \ -o /dev/nullWrite a log line:
/usr/local/bin/log-subtitle.sh {subtitle_path} {language} {provider}Discord webhook on download:
curl -s -X POST https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN \ -H "Content-Type: application/json" \ -d '{"content":"Subtitle downloaded: {subtitle_path} ({language}) via {provider}"}'Note: The command is tokenised with
shlex.split— quote paths that may contain spaces, or pass them through a wrapper script.
Behavior & Limits
Section titled “Behavior & Limits”- Timeout: 60 seconds. Commands exceeding this are killed; Sublarr logs a warning and continues.
- Non-blocking errors: A failing command (non-zero exit, crash, or timeout) is logged as a warning. It never blocks or retries the download pipeline.
- No shell expansion: The command runs with
shell=False. Shell features like&&,|,$VAR, or glob patterns are not available. Use a wrapper script for complex logic. - Execution context: The command runs as the same user Sublarr runs as (container:
sublarruser, default UID 1000). Ensure the command and any target paths are accessible to that user.
Troubleshooting
Section titled “Troubleshooting”Command does not execute
- Confirm Post-Processing is toggled on in Settings → Automation.
- Check that the
Post-Download Commandfield is not empty.
“invalid shell syntax” in logs
- Sublarr uses
shlex.splitto tokenise the command. Unmatched quotes or unsupported shell syntax causes this error. Test your command withpython3 -c "import shlex; print(shlex.split('YOUR COMMAND'))".
Timeout warning in logs
- Your command exceeds 60 seconds. Move long-running work to a background job and have the post-processing command only trigger it.
Curated-Ops Pipeline
Section titled “Curated-Ops Pipeline”The curated-ops pipeline runs alongside the legacy single-command mechanism above. It fires on three triggers and ships with eight built-in ops. Configure under Settings → Post-Processing.
Triggers
Section titled “Triggers”| Trigger | Fires when |
|---|---|
after_download | A subtitle has been downloaded + repaired + saved |
after_translate | A subtitle translation pass has finished writing output |
after_sync | ffsubsync / alass finished aligning a subtitle to the video |
Each trigger has its own ordered list of op_ids. Ops run sequentially on a dedicated 2-worker thread pool so request handlers are never blocked.
Built-In Ops (8)
Section titled “Built-In Ops (8)”Text ops — fix the file on disk:
strip_html— remove<i>,<b>,<font>,<br>tagsremove_bom— strip UTF-8 BOM from file startconvert_encoding— re-encode to UTF-8 (auto-detects source via chardet)
HTTP ops — notify other services:
webhook— POST to a URL with{subtitle_path}/{video_path}/{lang}/{score}substitution, SSRF-protected viavalidate_service_url(blocksfile://, metadata IPs, link-local)discord_notify— send a Discord webhook message
Media server refresh:
plex_refresh— trigger a Plex library scan viaX-Plex-Tokenemby_refresh— trigger an Emby scanjellyfin_refresh— trigger a Jellyfin scan
Shell Escape Hatch (opt-in)
Section titled “Shell Escape Hatch (opt-in)”Enable Allow shell scripts under Settings → Automation → Post-Processing → Shell escape hatch to allow a custom shell-script op. The pipeline uses shlex.quote for
every substituted value, subprocess.run(shell=False, args=shlex.split(…)),
a 30-second timeout, and a PATH-only restricted env. stdout + stderr are
captured to the post_processing_runs audit table. This path exists for
operators who explicitly want it; the curated ops above cover the 90% case
without the security surface.
Audit Trail
Section titled “Audit Trail”Every pipeline run writes a row to post_processing_runs:
trigger— which trigger firedops_executed— JSON list with per-op{op_id, ok, duration_ms, message}duration_ms— total pipeline timeoutcome—ok/partial_failure/failurecreated_at— timestamp
Query recent runs via GET /api/v1/post-processing/runs?limit=50 or view
them in the Settings → Post-Processing tab’s run history.