Skip to main content

qbittorrent-orphaned banner

PyPI version License: GPL-3.0 Python 3.8+ requests Coverage


qbittorrent-orphaned is a lightweight utility that identifies orphaned files -- files that exist on disk but are not tracked by any torrent in your qBittorrent instance. It connects to the qBittorrent Web API v2, walks the directories you configure, cross-references every file against every torrent, and reports what does not belong.

What Are Orphaned Files?

When you remove a torrent from qBittorrent but keep the data on disk, or when external tools (transcoders, renaming scripts, etc.) create files that were never part of a torrent, those files become orphans. They consume storage without being seeded or managed. This tool finds them so you can decide what to keep and what to reclaim.

Features

  • Single-file, pure Python -- no build step, no complex dependencies, just requests.
  • Web API v2 -- authenticates and queries qBittorrent over HTTP; works locally or across a network.
  • Case-insensitive matching -- handles mixed-case filenames on Windows and Linux alike.
  • Category-aware grouping -- results are organized by qBittorrent category, with uncategorized torrents collected under __UNCATEGORIZED__.
  • Human-readable sizes -- every orphan is printed alongside its size in KiB, MiB, GiB, etc.
  • Configurable metadata ignore list -- common metadata files (.nfo, .jpg, .png, .srt, .sub, .idx, .txt, .bin, .svg) are skipped by default. You can extend this list.
  • Exclude patterns -- filter out known non-torrent files (e.g., transcoded 720p copies) by substring match.
  • macOS-safe -- automatically skips ._ resource fork files.

Quick Start

Install from PyPI

pip install qbittorrent-orphaned

Standalone

QBIT_HOST=http://localhost:8080 \
QBIT_USER=admin \
QBIT_PASS=yourpassword \
CATEGORY_FOLDERS="Films=/mnt/media/films;Shows=/mnt/media/shows" \
qbittorrent-orphaned

You can also run the script directly without installing:

pip install requests

QBIT_HOST=http://localhost:8080 \
QBIT_USER=admin \
QBIT_PASS=yourpassword \
CATEGORY_FOLDERS="Films=/mnt/media/films;Shows=/mnt/media/shows" \
python orphan_detector.py

Docker

There is no pre-built image yet, but you can run it easily with a one-liner:

docker run --rm \
  -e QBIT_HOST=http://qbittorrent:8080 \
  -e QBIT_USER=admin \
  -e QBIT_PASS=yourpassword \
  -e CATEGORY_FOLDERS="Films=/media/films;Shows=/media/shows" \
  -v /mnt/media:/media:ro \
  --network=host \
  python:3-alpine sh -c "pip install --quiet requests && python /app/orphan_detector.py"

Mount the script into the container if you prefer a cleaner approach:

docker run --rm \
  -v "$(pwd)/orphan_detector.py:/app/orphan_detector.py:ro" \
  -v /mnt/media:/media:ro \
  -e QBIT_HOST=http://qbittorrent:8080 \
  -e QBIT_USER=admin \
  -e QBIT_PASS=yourpassword \
  -e CATEGORY_FOLDERS="Films=/media/films;Shows=/media/shows" \
  python:3-alpine sh -c "pip install --quiet requests && python /app/orphan_detector.py"

Tip: If qBittorrent runs in its own container, make sure both containers share a Docker network (or use --network=host) so the hostname resolves.

Configuration

All configuration is done through environment variables.

Variable Default Description
QBIT_HOST http://qbittorrent:8080 qBittorrent Web UI URL
QBIT_USER admin Username for Web UI authentication
QBIT_PASS password Password for Web UI authentication
CATEGORY_FOLDERS Films=W:\Films;Shows=X:\Series Semicolon-separated Category=Path pairs. Categories must match those configured in qBittorrent.
EXCLUDE_PATTERNS (empty) Comma-separated substrings. Any file whose relative path contains one of these patterns (case-insensitive) is skipped. A comma inside a pattern is escaped as \, — see below.
IGNORE_SUFFIXES (empty) Comma-separated file extensions to ignore in addition to the built-in list. Leading dots are optional (e.g., ass,ssa or .ass,.ssa).

Category Folders Format

CATEGORY_NAME=ABSOLUTE_PATH;CATEGORY_NAME2=ABSOLUTE_PATH2

Each category name must match exactly what is configured in qBittorrent. Torrents with no category are grouped under the key __UNCATEGORIZED__.

Category names and paths may contain spaces and commas — write them literally, and quote only the variable as a whole:

CATEGORY_FOLDERS="Books, Comics & Manga=F:\Downloads\Books, Comics & Manga;TV & Movies=F:\Downloads\TV & Movies"

One category folder may sit inside another — mapping __UNCATEGORIZED__ to qBittorrent's default save path, which is the parent of the per-category folders, is a common setup. Files under a nested folder are scanned as part of the category that owns that folder, and the overlap is noted on stderr, so it stays out of a redirected report.

Patterns Containing Commas

EXCLUDE_PATTERNS is comma-separated, so a pattern that itself contains a comma is escaped with a backslash:

EXCLUDE_PATTERNS="Books\, Comics & Manga,sample"

Quote the whole value. Unquoted, the shell splits on the spaces and runs Comics and Manga,sample as commands, and Python receives just Books, — which is the very mistake this escape exists to prevent.

Note the difference from CATEGORY_FOLDERS above: that variable is semicolon-separated, so commas inside a category name or path need no escape. EXCLUDE_PATTERNS is comma-separated, so they do.

That is two patterns — Books, Comics & Manga and sample. Without the escape it would be three, and the stray Books would silently skip every path containing "books" — Audiobooks/Dune.m4b, for instance — while the stray Comics & Manga would skip Comics & Manga Weekly.mkv. Values with no \, in them are unaffected.

Example Output

===== Films =====
/mnt/media/films/Some.Movie.2023/Some.Movie.2023.mkv    (4,215 MiB)
/mnt/media/films/Old.Film.1999/Old.Film.1999.avi        (702 MiB)

===== Shows =====
/mnt/media/shows/Series.Name.S01/Episode.05.mkv          (1,102 MiB)

When no orphans are found the output is simply:

No orphaned files found.

How It Works

  1. Authenticate -- the script logs in to qBittorrent via /api/v2/auth/login and obtains a session cookie.
  2. Fetch torrents -- it retrieves the full torrent list from /api/v2/torrents/info, then for each torrent calls /api/v2/torrents/files to get every file path the torrent manages.
  3. Index by category -- all torrent file paths are normalized (forward slashes, lowercase) and grouped into a lookup set per category.
  4. Walk the filesystem -- for each configured category folder, the script recursively enumerates files, skipping ignored suffixes, macOS resource forks, and exclude-pattern matches.
  5. Cross-reference -- every disk file is checked against the corresponding category set. Files not present in any torrent are reported as orphans with their absolute path and human-readable size.

License

GPL-3.0

Metadata

Release files for qbittorrent-orphaned 1.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qbittorrent-orphaned 1.1.2
File Size Uploaded
qbittorrent_orphaned-1.1.2.tar.gz 27.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qbittorrent-orphaned 1.1.2
File Interpreter ABI Platform
qbittorrent_orphaned-1.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 49.2 kB

Release files / qbittorrent_orphaned-1.1.2.tar.gz

Download URL qbittorrent_orphaned-1.1.2.tar.gz
Size 27.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f387c3b92c603797f6012567402b568c5f2abcc2cf2bde01792200fc6fc123f8
BLAKE2b-256 checksum
How to use checksums
91a7f536d3d390343e493f9dd0e8224ab23874680f681b062d16ea4066e9fd7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / qbittorrent_orphaned-1.1.2-py3-none-any.whl

Download URL qbittorrent_orphaned-1.1.2-py3-none-any.whl
Size 21.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1eb58654650c6bc83230cb9b43fb52dca3c17f383669aa1adc3ad12c4791b305
BLAKE2b-256 checksum
How to use checksums
8e25226877771ee323109fb510a5951929491e58109e66ec902b65fa699a6d7e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

1.1.3

2 release files

This release

1.1.2 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page