qbittorrent common lisp cli application similar to pyrocore
  • Common Lisp 99.2%
  • Shell 0.4%
  • Nix 0.4%
Find a file
htayj 29717b1dc3 build: add executable build wrapper
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-28 23:31:35 -04:00
.serena initial commit 2026-03-22 00:58:17 -04:00
src cli: add tracker reporting command 2026-08-28 23:31:35 -04:00
.envrc build: add Nix development environment 2026-08-28 23:31:35 -04:00
.gitignore initial commit 2026-03-22 00:58:17 -04:00
build.lisp initial commit 2026-03-22 00:58:17 -04:00
build.sh build: add executable build wrapper 2026-08-28 23:31:35 -04:00
flake.lock build: add Nix development environment 2026-08-28 23:31:35 -04:00
flake.nix build: add Nix development environment 2026-08-28 23:31:35 -04:00
qbcl initial commit 2026-03-22 00:58:17 -04:00
qbcl.asd cli: add tracker reporting command 2026-08-28 23:31:35 -04:00
README.md docs: expand command reference 2026-08-28 23:31:35 -04:00
run.sh initial commit 2026-03-22 00:58:17 -04:00

qbcl

A Common Lisp terminal application for controlling qBittorrent, inspired by pyrocore's tools for rTorrent.

Overview

qbcl brings pyrocore's powerful command-line torrent management philosophy to qBittorrent, communicating via qBittorrent's WebAPI (REST/JSON) instead of rTorrent's XMLRPC/SCGI protocol.

Tools

qbcl command pyrocore equivalent Description
qbcl (default) rtcontrol Filter, display, and act on torrents
qbcl lstor lstor Inspect .torrent files

Key Features

  • Filter expressions — pyrocore-style filtering: name=*linux*, ratio=+1.0, state=downloading
  • Output formats — configurable field lists and named presets
  • Actions — start, stop, delete, recheck, tag, categorize matching torrents
  • JSON output — machine-readable output for scripting
  • Multiple connections — named connection profiles in config

Quick Start

# Initialize config (creates ~/.config/qbcl/config.lisp)
qbcl config init

# Edit the config with your qBittorrent URL and credentials, then:

# List all torrents
qbcl

# Filter by name (glob)
qbcl '*ubuntu*'

# Seeds with good ratio
qbcl ratio=+1.0

# Large downloads, sorted by size descending
qbcl size=+1g -s -size

# Stop well-seeded torrents
qbcl ratio=+2.0 --stop

# Show all available fields
qbcl --help-fields

# Inspect a .torrent file
qbcl lstor some-file.torrent

Usage

qbcl [OPTIONS] [FILTER...] [ACTION]
qbcl lstor [OPTIONS] <file.torrent>...
qbcl config <subcommand>
qbcl help
qbcl version

Options

Flag Short Description
--help -h Show help text
--help-fields List all torrent fields with descriptions
--version Show version
--output-format FORMAT -o Output preset name or comma-separated field list
--sort-fields FIELDS -s Sort by fields; prefix with - for descending
--reverse-sort -r Reverse the sort order
--column-headers -c Print column headers above output
--json Output results as JSON
--select N-M -/ Select a subset of results (e.g. 1-10)
--dry-run -n Show what would happen without making changes
--verbose -v Verbose logging
--debug Debug mode
--quiet -q Suppress informational output
--interactive -i Prompt before applying each action
--yes Answer yes to all prompts
--url URL qBittorrent WebAPI URL
--username USER Username for authentication
--password PASS Password for authentication
@NAME Use named connection profile from config

Actions

Flag Description
--start Resume matching torrents
--stop Pause matching torrents
--delete Remove torrents from client (keep data)
--cull Remove torrents and delete data
--hash-check / -H Force hash recheck
--reannounce Reannounce to trackers
--tag TAGS Add or remove tags (+tag to add, -tag to remove)
--category CAT Set category

Output Format

Pass a preset name or a comma-separated field list to -o:

qbcl -o detail                   # named preset
qbcl -o name,size,ratio,state    # custom field list
qbcl -o name,size,ratio -c       # with column headers
Preset Fields
default name, size, progress, ratio, dlspeed, upspeed, state
detail + category, tags, tracker, save-path, added-on
short name, progress, state
files name, size, progress, save-path
action hash, name, state
transfer name, size, downloaded, uploaded, ratio

Run qbcl --help-fields for all available field names.

Config Subcommands

qbcl config init    # Write default config to ~/.config/qbcl/config.lisp
qbcl config show    # Print current config
qbcl config path    # Print path to config file

Examples

Listing and filtering

# List all torrents (default format)
qbcl

# Name contains "linux" (glob, case-insensitive)
qbcl '*linux*'

# Exact state match
qbcl state=downloading
qbcl state=uploading
qbcl state=pausedUP

# Numeric comparisons
qbcl ratio=+1.0          # ratio > 1.0
qbcl size=+1g            # size > 1 GB
qbcl size=-500m          # size < 500 MB
qbcl eta=+1h             # ETA more than 1 hour away
qbcl dlspeed=+1m         # downloading faster than 1 MB/s

# Regex match
qbcl 'name=/^linux/'     # name starts with "linux"

# Negation
qbcl 'state=!error'      # everything except errored

# Filter by category or tag
qbcl category=movies
qbcl tags=keep

# AND (implicit — space-separated filters)
qbcl state=downloading dlspeed=+500k

# OR (explicit)
qbcl 'ratio=+2.0 OR category=archive'

Output and sorting

# Custom column list
qbcl -o name,size,ratio,state

# Named preset with column headers
qbcl -o detail -c

# Sort by name
qbcl -s name

# Sort by size, largest first
qbcl -s -size

# Sort by ratio descending, show top 10
qbcl -s -ratio -/ 1-10

# JSON output for scripting
qbcl state=downloading --json

# Quiet (no headers/info, just data rows)
qbcl -q '*ubuntu*'

Actions

# Stop all torrents with ratio above 2.0 (prompts for confirmation)
qbcl ratio=+2.0 -i --stop

# Start all paused torrents
qbcl state=pausedDL --start

# Remove errored torrents (dry run first)
qbcl state=error -n --delete
qbcl state=error --delete

# Remove torrents AND their data
qbcl category=trash --cull

# Recheck all torrents in a category
qbcl category=movies --hash-check

# Reannounce all active torrents
qbcl state=uploading --reannounce

# Add a tag
qbcl ratio=+3.0 --tag +seeded

# Remove a tag
qbcl tags=old --tag -old

# Set category on matching torrents
qbcl 'name=*4K*' --category hd

Multiple connections

# Use a named connection profile from config
qbcl @seedbox state=downloading

# Pass connection details inline
qbcl --url http://192.168.1.10:8080 --username admin --password secret

Inspecting .torrent files

qbcl lstor file.torrent
qbcl lstor *.torrent

Configuration

Config lives at ~/.config/qbcl/config.lisp:

((:URL . "http://localhost:8080")
 (:USERNAME . "admin")
 (:PASSWORD . "your-password")
 (:OUTPUT-FORMAT . "default")
 (:SORT-FIELDS . ("name"))
 (:CONNECTIONS
  (:DEFAULT . ((:URL . "http://localhost:8080")
               (:USERNAME . "admin")
               (:PASSWORD . "your-password")))))

Filter Syntax

field=value          Exact or glob match (* ? [a-z])     name=*ubuntu*
field=/regex/        Regex match                         name=/^linux/
field=+N             Greater than N                      ratio=+1.0   size=+1g
field=-N             Less than N                         size=-500m   eta=-1h
field=!value         Negate                              state=!error
term1 term2          AND (space-separated)               state=downloading dlspeed=+1m
term1 OR term2       OR                                  ratio=+2.0 OR category=archive
  • If no field name is given, name is assumed: qbcl '*ubuntu*' is the same as qbcl name=*ubuntu*
  • Size suffixes: b (bytes), k (KiB), m (MiB), g (GiB), t (TiB)
  • Time suffixes: s (secs), m (mins), h (hours), d (days), w (weeks) — time fields compare against an age offset from now; +7d means "older than 7 days", -7d means "within the last 7 days"

Field Reference

These are all fields you can use in filter expressions and with -o/-s.

Identity

Field Type Operators Example
name string glob, /regex/, ! name=*ubuntu*
hash string glob, /regex/, ! hash=abc123

Size & Data

Field Type Operators Example
size size +, -, =, ! size=+1g size=-500m
total-size size +, -, =, ! total-size=+2g
downloaded size +, -, =, ! downloaded=+100m
uploaded size +, -, =, ! uploaded=+1g
amount-left size +, -, =, ! amount-left=+500m
completed size +, -, =, ! completed=+1g

Size suffixes: b (bytes), k (KiB), m (MiB), g (GiB), t (TiB).

Progress & Ratio

Field Type Operators Example
progress numeric +, -, =, ! progress=+0.9 (>90%) progress=1.0 (complete)
ratio numeric +, -, =, ! ratio=+1.0 ratio=-0.5

Progress is 0.0–1.0. Ratio has no upper bound.

Transfer Speed

Field Type Operators Example
dlspeed size +, -, =, ! dlspeed=+1m (>1 MB/s)
upspeed size +, -, =, ! upspeed=+500k
dl-limit size +, -, =, ! dl-limit=-1 (unlimited)
up-limit size +, -, =, ! up-limit=+0

Time

Field Type Operators Example
added-on time +, - added-on=-7d (added in last 7 days)
completion-on time +, - completion-on=-30d
last-activity time +, - last-activity=+1d (no activity >1 day ago)
seen-complete time +, - seen-complete=+7d
eta numeric +, -, =, ! eta=+3600 (>1 hour remaining)

Time fields compare against an age offset in seconds from now, using the same suffixes as duration: s, m, h, d, w. +Nd means "older than N days"; -Nd means "within the last N days".

State & Peers

Field Type Operators Example
state string glob, /regex/, ! state=downloading state=!error
priority numeric +, -, =, ! priority=0 (do not download)
num-seeds numeric +, -, =, ! num-seeds=+5
num-leechs numeric +, -, =, ! num-leechs=+0
num-complete numeric +, -, =, ! num-complete=+10
num-incomplete numeric +, -, =, ! num-incomplete=+0

Known state values (from the qBittorrent WebAPI):

Value Meaning
downloading Actively downloading
uploading Actively seeding
stalledDL Downloading, but no peers
stalledUP Seeding, but no peers
pausedDL Paused before completing
pausedUP Paused after completing
queuedDL Queued for download
queuedUP Queued for seeding
checkingDL Hash-checking during download
checkingUP Hash-checking after download
checkingResumeData Checking resume data on startup
moving Being moved to a new location
error Stopped with an error
missingFiles Data files are missing
unknown Unknown state

Glob patterns work on state: state=paused* matches both pausedDL and pausedUP.

Classification

Field Type Operators Example
category tags exact, ! category=movies category=! (no category)
tags tags exact, ! tags=keep tags=!seeded
tracker string glob, /regex/, ! tracker=*example.com*

Tag matching checks whether the given value is present as a member of the comma-separated tag list. category works the same way (it's treated as a single-value tag field).

Paths

Field Type Operators Example
save-path string glob, /regex/, ! save-path=/mnt/media*
content-path string glob, /regex/, ! content-path=*/downloads/*
magnet-uri string glob, /regex/, ! magnet-uri=*xt=urn:btih:abc*

Boolean Flags

Field Type Values Example
auto-tmm bool yes/no, true/false, 1/0 auto-tmm=yes
force-start bool yes/no, true/false, 1/0 force-start=yes
seq-dl bool yes/no, true/false, 1/0 seq-dl=yes
f-l-piece-prio bool yes/no, true/false, 1/0 f-l-piece-prio=yes
super-seeding bool yes/no, true/false, 1/0 super-seeding=no

Boolean filters do not use +/-; pass a truthy or falsy string instead.

Dependencies

Architecture

qbcl/
├── qbcl.asd              # ASDF system definition
└── src/
    ├── packages.lisp      # Package definitions
    ├── conditions.lisp    # Error hierarchy (≈ pyrocore.error)
    ├── main.lisp          # Entry point & command dispatch
    ├── api/
    │   ├── client.lisp    # HTTP client (≈ pyrocore.util.xmlrpc)
    │   └── endpoints.lisp # WebAPI wrappers
    ├── config/
    │   └── settings.lisp  # Configuration (≈ pyrocore.config)
    ├── torrent/
    │   ├── fields.lisp    # Field definitions (≈ engine.FieldDefinition)
    │   ├── engine.lisp    # Engine interface (≈ torrent.rtorrent)
    │   └── filter.lisp    # Filter bridge (≈ torrent.filter)
    ├── util/
    │   ├── formatting.lisp # Display formatting (≈ util.fmt)
    │   └── matching.lisp  # Filter parser/engine (≈ util.matching)
    └── scripts/
        ├── base.lisp      # CLI framework (≈ scripts.base)
        ├── qbcontrol.lisp # Main control tool (≈ scripts.rtcontrol)
        └── lstor.lisp     # Torrent inspector (≈ scripts.lstor)

License

GPL-2.0-or-later (same as pyrocore)