Torrent Post-Processing: Running Scripts on Completion

In a torrent workflow, post-processing refers to the automated series of actions performed on files immediately after a download finishes. This guide explains the core concepts of torrent post-processing, typical automation use cases, and the exact steps required to configure popular torrent clients to trigger external scripts upon download completion.

What Is Post-Processing in a Torrent Workflow?

Post-processing bridges the gap between completing a raw download and preparing the file for its final purpose. Once a torrent client finishes downloading and verifying data chunks, it enters the post-processing phase.

Common post-processing tasks include: * Extracting archives: Automatically uncompressing .rar or .zip archives included in the download payload. * File organization and renaming: Renaming files according to specific naming conventions and moving them to target directories. * Media server integration: Triggering library scans in media managers like Plex, Jellyfin, or Emby. * Format conversion: Transcoding video or audio files into preferred codecs or containers. * Notifications: Sending alerts to platforms like Discord, Telegram, or email with download statistics. * Seed management: Moving data via hardlinks or symlinks so the client can continue seeding without interfering with the target destination.

How Torrent Clients Trigger Scripts

Torrent clients monitor the download progress. When a torrent reaches 100% (or meets specific criteria like category completion), the client executes a system command pointing to a script—such as a Bash script, Python script, PowerShell script, or batch file.

During execution, the client passes metadata to the script using command-line arguments or environment variables. This metadata typically includes: * Torrent name * Save path / target directory * Torrent hash / ID * Category or tag * Number of files

1. qBittorrent

qBittorrent provides native support for passing command-line parameters directly to an external executable.

  1. Open Options (Tools > Options or Preferences).
  2. Navigate to the Downloads tab.
  3. Scroll down to the Run external program section.
  4. Check Run external program on torrent finished.
  5. Input the path to your interpreter/script followed by parameters.

Example command for Linux/macOS:

/usr/bin/python3 /scripts/post_process.py "%N" "%F" "%D" "%I"

Example command for Windows:

powershell.exe -ExecutionPolicy Bypass -File "C:\Scripts\post_process.ps1" -TorrentName "%N" -ContentPath "%F"

Key qBittorrent Variables: * %N: Torrent name * %F: Content path (same as root path for multi-file torrents) * %D: Save path * %I: Info hash * %L: Category

2. Transmission

Transmission triggers scripts using its configuration file or daemon settings, passing metadata via predefined environment variables.

  1. Stop the Transmission service.

  2. Open settings.json.

  3. Configure the following keys:

    "script-torrent-done-enabled": true,
    "script-torrent-done-filename": "/path/to/script.sh"
  4. Save the file and restart Transmission.

Key Transmission Environment Variables: * TR_TORRENT_NAME: Name of the torrent * TR_TORRENT_DIR: Path to the download directory * TR_TORRENT_HASH: Torrent info hash * TR_TIME_LOCALTIME: Time the download completed

3. Deluge

Deluge uses the official Execute plugin to handle post-processing triggers.

  1. Open Preferences and select Plugins.
  2. Enable the Execute plugin.
  3. Select Execute from the left-hand sidebar menu.
  4. Click Add, select the event Torrent Complete, and provide the absolute path to your script.
  5. Apply the changes.

Deluge automatically passes arguments to the script: torrent_id (Arg 1), torrent_name (Arg 2), and save_path (Arg 3).

Writing a Basic Post-Processing Script

Below is an example of a Python post-processing script designed to handle inputs, log the completion, and extract compressed archives if present:

#!/usr/bin/env python3
import sys
import os
import zipfile
import logging

# Configure logging
logging.basicConfig(filename='/tmp/torrent_post_process.log', level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def main():
    if len(sys.argv) < 3:
        logging.error("Insufficient arguments passed.")
        sys.exit(1)

    torrent_name = sys.argv[1]
    content_path = sys.argv[2]

    logging.info(f"Processing finished download: {torrent_name}")
    logging.info(f"Target path: {content_path}")

    # Check if target is a directory or single file
    if os.path.isdir(content_path):
        for root, _, files in os.walk(content_path):
            for file in files:
                if file.endswith('.zip'):
                    zip_path = os.path.join(root, file)
                    logging.info(f"Extracting: {zip_path}")
                    with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                        zip_ref.extractall(root)

    logging.info(f"Post-processing complete for {torrent_name}")

if __name__ == "__main__":
    main()

Best Practices