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.
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 |
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"
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"]
Documentation
Complete documentation is available on the GitHub Wiki:
- Getting Started - Installation and first rule
- Configuration Reference - config.yml and rules.yml
- Rules Architecture - How rules are evaluated
- Triggers - Manual, scheduled, webhooks
- Conditions - Operators and logical groups
- Available Fields - Complete field reference
- Actions - All available actions
- Examples - Real-world use cases
- FAQ - Common questions
- Troubleshooting - Debug and fix issues
- Advanced Topics - Performance, security, extending
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc44a194ca59c729f6527ffeca9fb60589c64e42a6cd32b6125d02b9b3f5e285
|
|
| MD5 |
2340f69c07a1fa0a2c3f23e3baa2bb0b
|
|
| BLAKE2b-256 |
7b4c73361028f90b8ab259620dace9d495240e735b99e8505125356f62f7c3a5
|
Provenance
The following attestation bundles were made for qbt_rules-0.3.2.tar.gz:
Publisher:
release.yml on andronics/qbt-rules
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qbt_rules-0.3.2.tar.gz -
Subject digest:
bc44a194ca59c729f6527ffeca9fb60589c64e42a6cd32b6125d02b9b3f5e285 - Sigstore transparency entry: 763546954
- Sigstore integration time:
-
Permalink:
andronics/qbt-rules@ea912943a275a81c1afad2a1ad1139ca6a3aac28 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/andronics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ea912943a275a81c1afad2a1ad1139ca6a3aac28 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27c757b41402a481f0997c373d7c27fe4e42cc682f9c3a479e468d48d33be08a
|
|
| MD5 |
1123995dc1ad91cdbc830010008f3294
|
|
| BLAKE2b-256 |
114a02b01a793efce78f459e1d4cc2d979bb8048c9b341084a5a43645fc0832f
|
Provenance
The following attestation bundles were made for qbt_rules-0.3.2-py3-none-any.whl:
Publisher:
release.yml on andronics/qbt-rules
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qbt_rules-0.3.2-py3-none-any.whl -
Subject digest:
27c757b41402a481f0997c373d7c27fe4e42cc682f9c3a479e468d48d33be08a - Sigstore transparency entry: 763546955
- Sigstore integration time:
-
Permalink:
andronics/qbt-rules@ea912943a275a81c1afad2a1ad1139ca6a3aac28 -
Branch / Tag:
refs/tags/v0.3.2 - Owner: https://github.com/andronics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ea912943a275a81c1afad2a1ad1139ca6a3aac28 -
Trigger Event:
push
-
Statement type: