Skip to main content

A powerful Python-based rules engine for qBittorrent automation

Project description

qbt-rules

A powerful Python-based rules engine for automating torrent management in qBittorrent using declarative YAML configuration.

GitHub Release License: Unlicense


What is qbt-rules?

qbt-rules is a Python-based rules engine that automates torrent management through the qBittorrent Web API v5.0+. Define YAML-based rules to automatically categorize, tag, pause, resume, delete, and manage torrents based on flexible conditions.

Key Features:

  • Declarative YAML Rules - No coding required, just write rules
  • Multiple Trigger Types - Manual, scheduled (cron), webhooks (on_added, on_completed)
  • Powerful Conditions - Complex logic with AND/OR/NOT groups, 17+ operators
  • Rich Field Access - Dot notation access to all torrent metadata (info., trackers., files.*, etc.)
  • Idempotent Actions - Safe to run repeatedly, actions skip if already applied
  • Dry-Run Mode - Test rules without making changes
  • Comprehensive Logging - Trace mode for debugging

Quick Start

Installation

Option 1: Install from PyPI (recommended)

# Install the package
pip install qbt-rules

# Create config directory
mkdir -p ~/.config/qbt-rules

# Download example configs
curl -o ~/.config/qbt-rules/config.yml \
  https://raw.githubusercontent.com/andronics/qbt-rules/main/config/config.example.yml
curl -o ~/.config/qbt-rules/rules.yml \
  https://raw.githubusercontent.com/andronics/qbt-rules/main/config/rules.example.yml

# Edit config with your qBittorrent credentials
nano ~/.config/qbt-rules/config.yml

Option 2: Install from source

# Clone the repository
git clone https://github.com/andronics/qbt-rules.git
cd qbt-rules

# Install in editable mode with development dependencies
pip install -e ".[dev]"

# Copy example configs
cp config/config.example.yml config/config.yml
cp config/rules.example.yml config/rules.yml

# Edit config with your qBittorrent credentials
nano config/config.yml

Your First Rule

Create a simple rule in config/rules.yml:

rules:
  - name: "Auto-categorize HD movies"
    enabled: true
    stop_on_match: true
    conditions:
      trigger: on_added
      all:
        - field: info.name
          operator: matches
          value: '(?i).*(1080p|2160p|4k).*'
    actions:
      - type: set_category
        params:
          category: "Movies-HD"
      - type: add_tag
        params:
          tag: "movies"

Run It

Using console script (after pip install):

# Test with dry-run (shows what would happen)
qbt-rules --dry-run

# Run rules WITHOUT a trigger filter (default behavior)
qbt-rules

# Enable trace logging for debugging
qbt-rules --trace

# Run rules WITH a specific trigger
qbt-rules --trigger scheduled

# Process specific torrent with trigger
qbt-rules --trigger on_completed --hash abc123...

# Run ALL rules (ignore trigger filters)
qbt-rules --trigger none

Using Python module:

python -m qbt_rules.cli --dry-run

How It Works

┌─────────────────────────────────────────────────────────────┐
│                    TRIGGER ACTIVATION                       │
│  (Manual, Scheduled, on_added, on_completed)               │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│              FETCH TORRENTS FROM qBittorrent               │
│         (Optional: Apply filters by category/state)         │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│                   FOR EACH TORRENT:                        │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Evaluate Rules in File Order                        │  │
│  │  ┌────────────────────────────────────────────────┐  │  │
│  │  │  Check Conditions (all/any/none groups)        │  │  │
│  │  │  ├─ Access fields (info.*, trackers.*, etc.)   │  │  │
│  │  │  ├─ Apply operators (==, >, contains, etc.)    │  │  │
│  │  │  └─ Combine with AND/OR/NOT logic             │  │  │
│  │  └────────────┬───────────────────────────────────┘  │  │
│  │               │                                       │  │
│  │               ├─ Match? ──► Execute Actions          │  │
│  │               │             (stop, categorize, etc.)  │  │
│  │               │                                       │  │
│  │               └─ No Match? ──► Next Rule             │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

Rules execute top-to-bottom. Use stop_on_match: true to prevent later rules from running after a match.


Core Concepts

Triggers

Triggers are rule filters that control when rules execute. Rules specify a trigger condition, and you use the --trigger flag to run matching rules.

How Trigger Filtering Works:

# No --trigger flag: Runs ONLY rules WITHOUT a trigger condition
qbt-rules

# --trigger <name>: Runs ONLY rules WITH that trigger name
qbt-rules --trigger scheduled
qbt-rules --trigger on_added --hash abc123

# --trigger none: Runs ALL rules (ignores trigger filters)
qbt-rules --trigger none

There are no built-in triggers - all trigger names are arbitrary. You define them in your rules and specify them at runtime.

Common Trigger Naming Conventions:

Trigger Name Typical Use Case Example Schedule
scheduled Periodic maintenance Cron/systemd hourly/daily
on_added qBittorrent webhook When torrent added
on_completed qBittorrent webhook When download completes
hourly Frequent checks Every hour
nightly Daily cleanup 3 AM daily
weekly Weekly tasks Sunday midnight

Example Rules:

# This rule runs when: qbt-rules --trigger scheduled
- name: "Cleanup old torrents"
  conditions:
    trigger: scheduled  # Only runs with --trigger scheduled
    all:
      - field: info.completion_on
        operator: older_than
        value: "30 days"
  actions:
    - type: delete_torrent

# This rule runs when: qbt-rules (no --trigger flag)
- name: "Always check large downloads"
  conditions:
    # No trigger specified - runs by default
    all:
      - field: info.size
        operator: larger_than
        value: "50GB"
  actions:
    - type: add_tag
      params:
        tag: "large"

📖 Detailed Trigger Documentation

Conditions

Combine conditions with logical groups:

  • all - AND logic (all conditions must match)
  • any - OR logic (at least one condition must match)
  • none - NOT logic (no conditions can match)

Available Operators:

==, !=, >, <, >=, <=, contains, not_contains, matches, in, not_in, older_than, newer_than, smaller_than, larger_than

📖 Detailed Condition Documentation

Available Fields

Access torrent data using dot notation across 8 API categories:

Prefix Description Example Fields
info.* Main torrent info info.name, info.size, info.ratio, info.state
trackers.* Tracker data (collection) trackers.url, trackers.status, trackers.msg
files.* File list (collection) files.name, files.size, files.progress
properties.* Extended properties properties.save_path, properties.comment
transfer.* Global transfer stats transfer.dl_speed_global, transfer.up_speed_global
app.* Application info app.version, app.api_version
peers.* Peer data (collection) peers.ip, peers.client, peers.progress
webseeds.* Web seed data (collection) webseeds.url

📖 Complete Field Reference

Actions

Execute one or more actions when conditions match:

Action Description
stop / start / force_start Control torrent state
recheck / reannounce Maintenance operations
delete_torrent Remove torrent (optionally with files)
set_category Set torrent category
add_tag / remove_tag / set_tags Manage tags
set_upload_limit / set_download_limit Speed limiting

📖 Detailed Action Documentation


Example Rules

Auto-Categorize by Content Type

- name: "Categorize TV shows"
  enabled: true
  stop_on_match: true
  conditions:
    trigger: on_added
    all:
      - field: info.name
        operator: matches
        value: '(?i).*[Ss]\d{2}[Ee]\d{2}.*'
  actions:
    - type: set_category
      params:
        category: "TV-Shows"

Cleanup Old Completed Torrents

- name: "Delete old seeded torrents"
  enabled: true
  conditions:
    trigger: scheduled
    all:
      - field: info.state
        operator: in
        value: ["uploading", "pausedUP", "stalledUP"]
      - field: info.completion_on
        operator: older_than
        value: "30 days"  # Human-readable duration
      - field: info.ratio
        operator: ">="
        value: 2.0
  actions:
    - type: delete_torrent
      params:
        delete_files: false

Force Seed Low-Seeded Content

- name: "Force start underseded torrents"
  enabled: true
  conditions:
    trigger: scheduled
    all:
      - field: info.num_complete
        operator: "<="
        value: 2
      - field: info.state
        operator: in
        value: ["pausedUP", "stalledUP"]
  actions:
    - type: force_start

Pause Large Downloads

- name: "Pause very large downloads"
  enabled: true
  conditions:
    trigger: on_added
    all:
      - field: info.size
        operator: larger_than
        value: "50 GB"
      - field: info.state
        operator: in
        value: ["downloading", "metaDL"]
  actions:
    - type: stop
    - type: add_tag
      params:
        tag: "large-download"

📖 More Examples


Deployment

Scheduled Execution with Cron

# Run every hour
0 * * * * qbt-rules --trigger scheduled

# Run daily at 3 AM
0 3 * * * qbt-rules --trigger scheduled

# Custom trigger at midnight
0 0 * * * qbt-rules --trigger nightly_cleanup

Systemd Timer

Option 1: Single Timer (Simple)

Create /etc/systemd/system/qbt-rules.service:

[Unit]
Description=qbt-rules - qBittorrent Automation
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/qbt-rules --trigger scheduled
User=qbittorrent
Environment="PATH=/usr/local/bin:/usr/bin:/bin"

Create /etc/systemd/system/qbt-rules.timer:

[Unit]
Description=Run qbt-rules hourly

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

Enable:

sudo systemctl enable qbt-rules.timer
sudo systemctl start qbt-rules.timer

Option 2: Template Service (Multiple Triggers)

For running different triggers on different schedules, use systemd templates.

Create /etc/systemd/system/qbt-rules@.service:

[Unit]
Description=qbt-rules - %i trigger
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/qbt-rules --trigger %i
User=qbittorrent
Environment="PATH=/usr/local/bin:/usr/bin:/bin"

Create multiple timer files for different triggers:

/etc/systemd/system/qbt-rules@hourly.timer:

[Unit]
Description=Run qbt-rules hourly trigger

[Timer]
OnCalendar=hourly
Persistent=true

[Install]
WantedBy=timers.target

/etc/systemd/system/qbt-rules@nightly.timer:

[Unit]
Description=Run qbt-rules nightly trigger

[Timer]
OnCalendar=daily
OnCalendar=03:00
Persistent=true

[Install]
WantedBy=timers.target

/etc/systemd/system/qbt-rules@weekly.timer:

[Unit]
Description=Run qbt-rules weekly trigger

[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target

Enable the timers you need:

# Enable hourly trigger
sudo systemctl enable qbt-rules@hourly.timer
sudo systemctl start qbt-rules@hourly.timer

# Enable nightly trigger
sudo systemctl enable qbt-rules@nightly.timer
sudo systemctl start qbt-rules@nightly.timer

# Enable weekly trigger
sudo systemctl enable qbt-rules@weekly.timer
sudo systemctl start qbt-rules@weekly.timer

# Check timer status
sudo systemctl list-timers qbt-rules@*

Docker

FROM python:3.11-slim

WORKDIR /app

# Install the package
RUN pip install qbt-rules

# Copy configuration files
COPY config/ /app/config/

# Run scheduled trigger every hour
CMD ["sh", "-c", "while true; do qbt-rules --trigger scheduled; sleep 3600; done"]

📖 Deployment Guide


Documentation

Complete documentation is available on the GitHub Wiki:


Requirements

  • Python 3.8+
  • qBittorrent v5.0+ with Web UI enabled
  • Dependencies: requests, pyyaml

Contributing

Contributions are welcome! Please see the Contributing Guide on the wiki.


Changelog

See CHANGELOG.md for version history and release notes.

Latest Release: v0.3.0 - 2024-12-13


License

This project is released into the public domain under the Unlicense.

You are free to use, modify, and distribute this software for any purpose without attribution.


Happy automating! 🚀

For detailed documentation, visit the GitHub Wiki.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qbt_rules-0.3.2.tar.gz (29.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qbt_rules-0.3.2-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

File details

Details for the file qbt_rules-0.3.2.tar.gz.

File metadata

  • Download URL: qbt_rules-0.3.2.tar.gz
  • Upload date:
  • Size: 29.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qbt_rules-0.3.2.tar.gz
Algorithm Hash digest
SHA256 bc44a194ca59c729f6527ffeca9fb60589c64e42a6cd32b6125d02b9b3f5e285
MD5 2340f69c07a1fa0a2c3f23e3baa2bb0b
BLAKE2b-256 7b4c73361028f90b8ab259620dace9d495240e735b99e8505125356f62f7c3a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for qbt_rules-0.3.2.tar.gz:

Publisher: release.yml on andronics/qbt-rules

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qbt_rules-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: qbt_rules-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 27.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qbt_rules-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 27c757b41402a481f0997c373d7c27fe4e42cc682f9c3a479e468d48d33be08a
MD5 1123995dc1ad91cdbc830010008f3294
BLAKE2b-256 114a02b01a793efce78f459e1d4cc2d979bb8048c9b341084a5a43645fc0832f

See more details on using hashes here.

Provenance

The following attestation bundles were made for qbt_rules-0.3.2-py3-none-any.whl:

Publisher: release.yml on andronics/qbt-rules

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page