-
Notifications
You must be signed in to change notification settings - Fork 0
Advanced Topics
The system caches API responses per-torrent to minimize calls:
# First access to trackers.url: API call made and cached
if torrent["trackers.url"] contains "example.com":
...
# Second access to trackers.url: Uses cached data (no API call)
if torrent["trackers.url"] contains "private":
...Best practices:
-
Access fields in order of expense:
# Check cheap fields first all: - field: info.category # Free operator: "==" value: "Movies" - field: files.name # Expensive, but only if category matches operator: matches value: '.*\.mkv$'
-
Reuse field references:
# BAD: Multiple string operations any: - field: info.name operator: contains value: "1080p" - field: info.name operator: contains value: "2160p" # GOOD: Single regex any: - field: info.name operator: matches value: '(?i).*(1080p|2160p).*'
Rules execute top-to-bottom. Order matters for performance:
# GOOD: Specific rules first, then general
- name: "High-priority: Private trackers"
stop_on_match: true
conditions:
all:
- field: trackers.url
operator: contains
value: "privatehd.to"
- name: "General cleanup"
conditions:
# This only runs if private tracker rule didn't matchNever hardcode credentials in config files:
# BAD
qbittorrent:
username: admin
password: MyPassword123
# GOOD
qbittorrent:
username: ${QB_USERNAME}
password: ${QB_PASSWORD}Set environment variables:
export QB_USERNAME="admin"
export QB_PASSWORD="secure_password"Protect config files containing credentials:
# Restrict access to config files
chmod 600 config/config.yml
chown qbittorrent:qbittorrent config/config.ymlIf running webhook listeners:
- Use authentication (implement token validation)
-
Bind to localhost if qBittorrent is local:
python triggers/on_added.py --host 127.0.0.1 --port 8081
- Use reverse proxy with HTTPS for remote access
- Implement rate limiting to prevent abuse
Scenario: Auto-categorize torrents added by Sonarr/Radarr
-
Run webhook listener:
python triggers/on_added.py --port 8081
-
Configure qBittorrent webhook (if supported by your setup)
-
Create categorization rules:
- name: "Categorize Sonarr downloads" conditions: trigger: on_added all: - field: info.category operator: "==" value: "tv-sonarr" actions: - type: add_tag params: tag: "sonarr"
Scenario: Run custom script after specific condition
While the system doesn't directly support script execution, you can:
- Tag torrents with automation rules
-
Monitor tags with external script:
#!/bin/bash # Watch for "process-me" tag TAGGED=$(curl -s http://localhost:8080/api/v2/torrents/info | \ jq -r '.[] | select(.tags | contains("process-me")) | .hash') for hash in $TAGGED; do # Run your custom logic ./process-torrent.sh "$hash" done
Scenario: Send notifications when rules match
Implement custom action type (requires code modification):
# lib/actions.py
def send_notification(torrent, params):
import requests
message = params.get("message", "Rule matched")
requests.post("https://ntfy.sh/qbittorrent",
data=message.encode('utf-8'))Then use in rules:
actions:
- type: send_notification
params:
message: "Torrent {info.name} completed!"Edit lib/engine.py to add new comparison operators:
def evaluate_condition(torrent, condition):
# ... existing code ...
elif operator == "starts_with":
return str(field_value).startswith(str(value))
elif operator == "ends_with":
return str(field_value).endswith(str(value))Then use in rules:
- field: info.name
operator: starts_with
value: "Ubuntu"Edit lib/actions.py:
def set_priority(api, torrent_hash, params):
"""Set torrent priority"""
priority = params.get("priority", "normal")
# Implement priority logic
api.post(f"/api/v2/torrents/setPriority",
data={"hashes": torrent_hash, "priority": priority})Register in action mapping:
ACTION_MAP = {
# ... existing actions ...
"set_priority": set_priority,
}Use in rules:
actions:
- type: set_priority
params:
priority: "high"Create new trigger file triggers/on_error.py:
#!/usr/bin/env python3
from lib.config import load_config
from lib.api import QBittorrentAPI
from lib.engine import RulesEngine
def main():
config = load_config()
api = QBittorrentAPI(config["qbittorrent"])
api.login()
# Fetch only errored torrents
torrents = api.get("/api/v2/torrents/info",
params={"filter": "errored"})
# Load and execute rules with trigger: on_error
engine = RulesEngine(config["rules"], trigger_type="on_error")
engine.execute(api, torrents)
if __name__ == "__main__":
main()Use in rules:
- name: "Handle errored torrents"
conditions:
trigger: on_error
all:
- field: info.state
operator: "=="
value: "error"
actions:
- type: recheckRun multiple instances with different rule subsets:
Instance 1 - Categorization (runs frequently):
python triggers/scheduled.py --rules config/rules-categorization.ymlInstance 2 - Cleanup (runs daily):
python triggers/scheduled.py --rules config/rules-cleanup.ymlInstance 3 - Webhooks (always running):
python triggers/on_added.py --port 8081For very large torrent libraries (10,000+), implement batch processing:
# Process torrents in chunks of 100
BATCH_SIZE = 100
for i in range(0, len(all_torrents), BATCH_SIZE):
batch = all_torrents[i:i+BATCH_SIZE]
engine.execute(api, batch)
time.sleep(1) # Rate limitingqbt-rules v0.3.0 | Python-based rules engine for qBittorrent automation
GitHub • PyPI • Issues • Discussions • Contributing • License
Requirements: Python 3.8+ • qBittorrent 5.0+
Made with ❤️ by the qbt-rules community | Licensed under MIT
v0.3.0 | GitHub
- ✅ Unified CLI (
qbt-rules) - ✅ Custom triggers
- ✅ Trigger-agnostic mode
- ✅ Size operators (
50GB)