Skip to content

Advanced Custom lifecycle scripts

o51r15 edited this page Jun 18, 2026 · 1 revision

Note: This feature is in alpha and may introduce breaking changes or be removed without warning. Use at your own risk — no support will be provided for writing or maintaining custom scripts.

Lifecycle scripts let you run custom code when events occur in Pinchfork (downloads completing, media being deleted, etc.). This is useful for notifying external services, triggering transcoding, or any post-download automation.


Golden rules

Read these before writing scripts:

  • Do not use scripts to move or delete files — Pinchfork tracks file paths internally and moving files will break that tracking. Copying files (leaving the original intact) is relatively safe.
  • Scripts run synchronously — long-running scripts will block the operation that triggered them.
  • Script failures are silently ignored — Pinchfork continues regardless.
  • A script may run multiple times for the same event. Write your scripts idempotently.
  • Container paths differ from host paths — /config/extras/... inside the container may be /appdata/pinchfork/extras/... on your host. Account for this in your scripts.
  • Only handle events your script cares about — ignore others so new events don't break your scripts.

Setup

On app boot, a file is created at <config directory>/extras/user-scripts/lifecycle. This single script is called for every event and must contain logic to delegate based on event type.

The script can be written in bash or Python 3. jq is available for JSON parsing. The script receives two arguments: the event type, and a JSON representation of the relevant data.

Set LOG_LEVEL=debug to see your script's exit code and output in the container logs.


Events

Current events: app_init, media_pre_download, media_downloaded, media_deleted.

media_pre_download is special — returning a non-zero exit code prevents the download from starting. This lets you reject media that doesn't meet custom criteria.

Check this file for the current @event_types list — it's always more up to date than any documentation.


Example bash script

#!/bin/bash

EVENT_TYPE=$1
EVENT_DATA=$2

echo "Event: $EVENT_TYPE — ID: $(echo $EVENT_DATA | jq -r '.id')"

Example Python script

#!/bin/python3

import sys
import json

event_type = sys.argv[1]
event_data = json.loads(sys.argv[2])

print("Event: {} — ID: {}".format(event_type, event_data["id"]))

Testing your script

  1. Have at least one piece of downloaded media in Pinchfork.
  2. Temporarily set your lifecycle script to save the event payload: echo $EVENT_DATA >/tmp/example.json
  3. In Pinchfork, find a media item → Actions → Force Download. This triggers media_downloaded and saves the payload to /tmp/example.json.
  4. Exec into the container and inspect: cat /tmp/example.json
  5. Test your script directly: /config/extras/user-scripts/lifecycle media_downloaded "$(cat /tmp/example.json)"
  6. Replace the script with your actual logic once you've confirmed the payload structure.

The example JSON payload in the source code is not kept up to date. Always generate a fresh sample using the steps above.

Clone this wiki locally