Skip to content

Pre and Post-Processing ​

Pre- and post-processing in FFmate allow you to extend transcoding tasks by running custom scripts before the ffmpeg command starts and after it successfully completes. This powerful feature enables you to automate a wide range of activities, from input validation and file preparation to notifications, archiving, and integration with other systems.

You can define pre and post-processing steps either directly within a task creation request or as part of a Preset. If defined in both, the task-specific definition will take precedence.

Configuration Parameters ​

For both pre-processing and post-processing, you can configure the following:

  • scriptPath [optional] – The command or script FFmate should run before the main ffmpeg command. It supports wildcards to pass dynamic values like filenames, UUIDs, or dates as arguments to your script.

Example: python3 /opt/ffmate_scripts/prepare_audio.py --input ${INPUT_FILE} --normalize-level -3dBFS

Note:

FFmate will attempt to run the scriptPath as a system command. Make sure the script is executable and the path is correct. It will run with the same environment and permissions as the FFmate process.

How Exit Codes Work ​

When a script finishes running, it returns an exit code — a number that tells ffmate whether it succeeded or failed.

  • An exit code of 0 means the script completed successfully.
  • A non-zero exit code means the script encountered an error.
    • For pre-processing, if the script fails, the ffmpeg command will not run, and the task will be marked as failed.
    • For post-processing, the ffmpeg command will already have completed successfully, but the task will still be marked as failed due to the post-processing error.
  • sidecarPath [optional] – Specifies the path where FFmate should write a JSON "sidecar" file containing detailed information about the current task. This path supports wildcards. Your script can then read this file to get full context and make decisions accordingly.

    • What’s in the sidecard file?
      The sidecar JSON contains a snapshot of the task at the time the script runs:

      • For pre-processing, this includes input/output paths (raw or partially resolved), task metadata, UUID, name, priority, and more.

    Example: ​

    json
        {
      "uuid": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "name": "My Epic Movie.mov",
      "command": {
        "raw": "-i ${INPUT_FILE} -c:v libx264 -preset fast ${OUTPUT_FILE}",
        "resolved": ""
      },
      "inputFile": {
        "raw": "/watch/My Epic Movie.mov",
        "resolved": ""
      },
      "outputFile": {
        "raw": "/output/${INPUT_FILE_BASENAME}.mp4",
        "resolved": ""
      },
      "metadata": {
        "show": "My Awesome Show",
        "season": 2,
        "episode": 5
      },
      "status": "PRE_PROCESSING",
      "progress": 0,
      "remaining": 0,
      "error": "",
      "priority": 5,
      "source": "watchfolder",
      "preProcessing": {
        "scriptPath": {
          "raw": "/scripts/preprocess.sh",
          "resolved": "/scripts/preprocess.sh"
        },
        "sidecarPath": {
          "raw": "/temp/${UUID}.json",
          "resolved": "/temp/a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d.json"
        },
        "importSidecar": true,
        "startedAt": 1735689600000
      },
      "postProcessing": null,
      "startedAt": 0,
      "finishedAt": 0,
      "createdAt": 1735689590000,
      "updatedAt": 1735689600000
    }
    • For post-processing, it includes all of the above plus the final resolved output path from ffmpeg. The task status at this point will typically be RUNNING, just before it's marked DONE_SUCCESSFUL if post-processing completes without errors.

Importing a Task’s Sidecar ​

When the property importSidecar is set to true, FFmate will re-import the sidecar JSON after your pre-processing script finishes.

The flow is simple:

  1. When the task starts, FFmate writes the task’s sidecar JSON to the path you configured in preProcessing.sidecarPath, then runs your pre-processing script.
  2. Inside your script, you can read and modify the sidecar JSON programmatically — but only by changing existing properties.
  3. When the script exits, FFmate re-imports the updated sidecar and continues processing the task with those updates.

This lets you add custom logic to influence how FFmate processes a task. For example:

  • Run tools like ffprobe or MediaInfo and inject the results into the metadata object.
  • Adjust the task’s command or preset dynamically based on resolution, bitrate, or aspect ratio.
  • Change the task’s priority if the file comes from a specific location or matches certain conditions.

⚠️ Important:
Only modify existing properties in the sidecar.
Do not add or remove keys or change the JSON structure; doing so will cause the FFmate task to fail.

Workflow ​

This section outlines how FFmate runs a task, showing where pre- and post-processing scripts fit, how wildcards are resolved, where the sidecar import happens, and how errors are handled.

  1. Task Queued — A new task is created (directly or via a watchfolder).
  2. Pre-Processing (if defined)
    • FFmate resolves wildcards in sidecarPath (if defined) and writes the task sidecar JSON.
    • FFmate resolves wildcards in scriptPath.
    • FFmate executes the pre-processing script.
    • If the script fails (non-zero exit code), the task status is set to DONE_ERROR and processing stops. The script error is logged.
    • If importSidecar is true: FFmate re-imports the updated sidecar JSON after the script finishes. If re-import fails, the task is set to DONE_ERROR.
  3. FFmpeg Processing
    • If pre-processing was successful (or not defined), FFmate resolves wildcards for the main command, input file, and output file.
    • FFmate executes the FFmpeg command.
    • If FFmpeg fails, the task status is set to DONE_ERROR and processing stops. Post-processing will not run.
  4. Post-Processing (if defined)
    • Assuming FFmpeg completed successfully, FFmate resolves wildcards in sidecarPath (if defined) and writes the task sidecar JSON (now including final output paths).
    • FFmate resolves wildcards in scriptPath.
    • FFmate executes the post-processing script.
    • If the script fails (non-zero exit code), the task status is set to DONE_ERROR. The script error is logged.
  5. Task Completion
    • If post-processing was successful (or not defined), the task status is set to DONE_SUCCESSFUL.

Examples ​

Post-Processing – Upload to Cloud Storage and Notify ​

Once transcoding completes successfully, upload the output file to an S3 bucket and send a Slack notification to keep your team informed.

Example:

This example shows how post-processing can be configured to run a custom script after a successful ffmpeg transcode, while also generating a sidecar JSON file containing task details.

json
{
  "postProcessing": {
    "scriptPath": "/opt/ffmate_scripts/upload_and_notify.sh",
    "sidecarPath": "${OUTPUT_FILE_DIR}/${OUTPUT_FILE_BASENAME}.post_task_info.json"
  }
  // ... other preset/task details
}
  • upload_and_notify.sh (Conceptual):

    bash
    #!/bin/bash
    set -e # Exit immediately if a command exits with a non-zero status.
    
    SIDECAR_FILE=""
    
    # Basic argument parsing (robust scripts would use getopts)
    if [ "$1" == "--sidecar" ] && [ -n "$2" ]; then
        SIDECAR_FILE="$2"
    else
        # If ffmate passes sidecar path as the first arg directly
        SIDECAR_FILE="$1" 
    fi
    
    
    if [ -z "$SIDECAR_FILE" ] || [ ! -f "$SIDECAR_FILE" ]; then
        echo "Error: Sidecar file path not provided or file not found." >&2
        exit 1
    fi
    
    # Read data from sidecar using 'jq' (JSON processor)
    OUTPUT_FILE=$(jq -r '.outputFile.resolved' "$SIDECAR_FILE")
    TASK_NAME=$(jq -r '.name // "Untitled Task"' "$SIDECAR_FILE")
    TASK_UUID=$(jq -r '.uuid' "$SIDECAR_FILE")
    
    if [ -z "$OUTPUT_FILE" ]; then
        echo "Error: Could not extract output file from sidecar." >&2
        exit 1
    fi
    
    S3_BUCKET="s3://my-ffmate-outputs"
    SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
    
    echo "Uploading ${OUTPUT_FILE} to ${S3_BUCKET}..."
    aws s3 cp "${OUTPUT_FILE}" "${S3_BUCKET}/"
    if [ $? -ne 0 ]; then
        echo "Error: S3 upload failed for ${OUTPUT_FILE}." >&2
        exit 2
    fi
    echo "Upload successful."
    
    # Send Slack notification
    MESSAGE_TEXT="Task Complete: '${TASK_NAME}' (UUID: ${TASK_UUID}). Output: ${S3_BUCKET}/$(basename "${OUTPUT_FILE}")"
    PAYLOAD="{\"text\": \"${MESSAGE_TEXT}\"}"
    
    curl -X POST -H 'Content-type: application/json' --data "${PAYLOAD}" "${SLACK_WEBHOOK_URL}"
    if [ $? -ne 0 ]; then
        echo "Warning: Slack notification failed, but file was uploaded." >&2
        # Decide if this should be a hard fail (exit 3) or just a warning
    fi
    
    echo "Post-processing complete for ${TASK_UUID}."
    exit 0 # Success