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.
Metadata
Release files for qbt-rules 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| qbt_rules-0.3.2.tar.gz | 29.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| qbt_rules-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 57.0 kB
Release files / qbt_rules-0.3.2.tar.gz
| Download URL | qbt_rules-0.3.2.tar.gz |
|---|---|
| Size | 29.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bc44a194ca59c729f6527ffeca9fb60589c64e42a6cd32b6125d02b9b3f5e285
|
|
BLAKE2b-256 checksum How to use checksums |
7b4c73361028f90b8ab259620dace9d495240e735b99e8505125356f62f7c3a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 13, 2025.
Transparency logRelease files / qbt_rules-0.3.2-py3-none-any.whl
| Download URL | qbt_rules-0.3.2-py3-none-any.whl |
|---|---|
| Size | 27.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
27c757b41402a481f0997c373d7c27fe4e42cc682f9c3a479e468d48d33be08a
|
|
BLAKE2b-256 checksum How to use checksums |
114a02b01a793efce78f459e1d4cc2d979bb8048c9b341084a5a43645fc0832f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 13, 2025.
Transparency logSecurity for qbt-rules 0.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for qbt_rules-0.3.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Dec 13, 2025.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.