Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
ad02edd
chore: Add GitHub issue templates for bugs and features
lolimmlost May 7, 2026
d4fc68b
chore: Add OS, test_run, frequency, and log-sanitization fields to bu…
lolimmlost May 7, 2026
54279fb
Merge branch 'dev' into chore/issue-templates
ManiMatter May 8, 2026
f1cc08c
chore: Fix version field description — no startup log line exists
lolimmlost May 10, 2026
f1ab69d
chore: Restructure bug template per @ManiMatter feedback on #357
lolimmlost May 10, 2026
0e88557
chore: Require commit hash for version field
lolimmlost May 11, 2026
c382850
Merge pull request #357 from lolimmlost/chore/issue-templates
ManiMatter May 23, 2026
2ede62f
feat(remove_metadata_missing): detect stuck metadata for non-qBittorr…
alexis-morain Jun 17, 2026
7d700cd
Fix detect_deletions gating and harden timeout handling
tomerh2001 Feb 28, 2026
2e39485
Apply black formatting to files touched by this PR
jrhager84 Jul 9, 2026
58510b1
Merge pull request #333 from tomerh2001/fix-detect-deletions-timeout-…
jrhager84 Jul 9, 2026
2df9b38
Adding timeout property to instances.
eadgbear Apr 19, 2026
3732748
Adding build permission
eadgbear Apr 19, 2026
82a5269
Merge pull request #346 from eadgbear/eadgbear/timeout_fix
jrhager84 Jul 10, 2026
852cea5
fix: handle qBit check_connected 500 errors gracefully
jamtur01 Jul 10, 2026
cf225b8
Degrade failing instances at startup instead of exiting the whole app
jrhager84 Jul 9, 2026
5050cc4
Merge pull request #366 from jrhager84/startup-degraded-mode
jrhager84 Jul 10, 2026
701f67f
Support qBittorrent 5.2 API-key authentication (closes #353)
jrhager84 Jul 9, 2026
6564212
Merge pull request #367 from jrhager84/qbit-api-key-353
jrhager84 Jul 13, 2026
a41f679
Merge branch 'dev' into feat/metadata-missing-client-agnostic
ManiMatter Jul 13, 2026
57a5c49
Merge pull request #364 from alexis-morain/feat/metadata-missing-clie…
ManiMatter Jul 13, 2026
58d4d7c
Surface actionable errors for malformed env YAML
jrhager84 Jul 12, 2026
745f47e
Merge pull request #372 from ManiMatter/fix/issue-363-env-yaml-errors
jrhager84 Jul 13, 2026
105584a
Skip stale queue items during unmonitored checks
jrhager84 Jul 12, 2026
4bfd163
Merge pull request #371 from ManiMatter/fix/issue-341-stale-queue
jrhager84 Jul 13, 2026
1a359c4
Fix metadata missing detection in full queue
d4rk22 Jul 16, 2026
e25b933
Merge pull request #373 from d4rk22/codex/fix-metadata-missing-full-q…
lolimmlost Jul 21, 2026
d8fe715
Update README with version hints and breaking changes
ManiMatter Jul 27, 2026
304f8c4
Clarify README version in bug report template
ManiMatter Jul 27, 2026
dc67e4e
Merge pull request #375 from ManiMatter/patch/bug-template
ManiMatter Jul 28, 2026
c80492c
Add Sportarr support
Sportarr Aug 4, 2026
79851e6
Detect Sportarr via its identity marker instead of appName
Sportarr Aug 4, 2026
57f901f
Add remove_from_queue tracker handling mode
sgtsquiggs Aug 28, 2026
a460f93
Fix bool/dict job configs losing built-in defaults
nathang21 Aug 28, 2026
72dd989
fix: re-arm removals for downloads that never left the queue
qoole Sep 4, 2026
c2e671e
Fix remove_orphans deleting active downloads
keez97 Sep 23, 2026
ed30b9a
Merge pull request #385 from keez97/fix/orphans-compare-by-id
lolimmlost Sep 24, 2026
7584ee8
Merge branch 'dev' into fix/bool-job-config-loses-defaults
lolimmlost Sep 26, 2026
e27f3ff
Merge pull request #382 from nathang21/fix/bool-job-config-loses-defa…
lolimmlost Sep 26, 2026
d2c90dd
Merge branch 'dev' into fix/rearm-removals-still-in-queue
lolimmlost Sep 26, 2026
253431f
Merge pull request #383 from qoole/fix/rearm-removals-still-in-queue
lolimmlost Sep 26, 2026
3f3b7dc
Merge branch 'dev' into sportarr-support
lolimmlost Sep 26, 2026
ac1b0c1
Merge pull request #377 from Sportarr/sportarr-support
lolimmlost Sep 26, 2026
d855964
Add Sportarr section to README
ManiMatter Sep 27, 2026
8d34c75
Merge branch 'dev' into feat/remove-from-queue-handling
lolimmlost Sep 29, 2026
c71571b
test: expect remove_from_client kwarg in re-arm removal test
lolimmlost Sep 29, 2026
08f02e5
Merge pull request #381 from sgtsquiggs/feat/remove-from-queue-handling
lolimmlost Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
name: 🐛 Bug Report
description: Report a reproducible problem with decluttarr
title: "[Bug] "
labels: ["Bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to file a bug. **Please fill in every required field** — incomplete reports take much longer to resolve and may be closed pending more info.

Before submitting, please confirm:
- You've read the ReadMe, especially the **Dependencies & Hints & FAQ** section.
Make sure you read the correct ReadMe version:

- [**LATEST** ReadMe](https://github.com/ManiMatter/decluttarr/blob/latest/README.md) if you use ```latest``` code
- [**DEV** ReadMe](https://github.com/ManiMatter/decluttarr/blob/dev/README.md) if you use ```dev``` code
- You've searched [open and closed issues](https://github.com/ManiMatter/decluttarr/issues?q=is%3Aissue) for the same problem
- This is a reproducible bug, not a configuration question (use [Discussions](https://github.com/ManiMatter/decluttarr/discussions) for those)

- type: input
id: version
attributes:
label: Decluttarr commit hash
description: |
The **short commit hash** of the build you're running. `latest` / `dev` tags move too quickly to be useful for reproduction, so please give us the specific commit.

- **Docker users:** grab it from the `short_commit_id` line in the `🛠️ Decluttarr - Settings 🛠️` startup banner (you'll be pasting the full banner in the logs field below)
- **Local Python users:** run `git rev-parse --short HEAD`
placeholder: "e.g. 51b6a4c"
validations:
required: true

- type: dropdown
id: deployment
attributes:
label: Deployment method
options:
- Docker Compose with config.yaml
- Docker Compose with environment variables
- docker run
- Local Python (main.py)
- Other (describe under "Steps to reproduce")
validations:
required: true

- type: dropdown
id: os
attributes:
label: Host OS / architecture
description: |
Where decluttarr is running. Path-related bugs (especially `detect_deletions`) and qBit cookie issues sometimes behave differently on Windows or ARM.
options:
- Linux x86_64 (amd64)
- Linux ARM (arm64 / armv7)
- macOS
- Windows
- Other (describe under "Steps to reproduce")
validations:
required: true

- type: input
id: arr_versions
attributes:
label: "*arr instance versions"
description: Versions of any Sonarr / Radarr / Lidarr / Readarr / Whisparr instances involved in the bug.
placeholder: "e.g. Sonarr 4.0.10, Radarr 5.18.4"
validations:
required: true

- type: input
id: download_client_version
attributes:
label: Download client + version
description: qBittorrent / SABnzbd / etc. and version. Write `n/a` if the bug doesn't involve a download client.
placeholder: "e.g. qBittorrent 5.2.0, or n/a"
validations:
required: true

- type: dropdown
id: test_run
attributes:
label: test_run state
description: |
Removal-related bugs behave very differently with `test_run: true` (no actual removals) vs `false`. Pick the value of `general.test_run` (or `TEST_RUN`) when the bug occurred.
options:
- "false (production — actual removals)"
- "true (test_run mode — no actual removals)"
- Reproduces with both
- N/A (bug doesn't involve removal logic)
validations:
required: true

- type: dropdown
id: frequency
attributes:
label: Reproduction frequency
options:
- Always (every cycle / every time)
- Sometimes (intermittent)
- Once / haven't tried again
- Unknown
validations:
required: true

- type: textarea
id: logs
attributes:
label: Decluttarr startup banner + debug logs
description: |
Please follow these three steps to produce a useful log dump:

1. **Disable jobs not related to your issue** in your config — keeps the logs focused and short
2. **Set `log_level: DEBUG`** (or `VERBOSE` if DEBUG is too noisy)
3. **Restart decluttarr and paste everything from the `🛠️ Decluttarr - Settings 🛠️` startup banner through the cycle that exhibits the bug**

The startup banner already contains `image_tag` / `short_commit_id` and the entire active config (general, jobs, instances, download clients) with API keys masked as `*****`.

**Still sanitize before pasting:** scrub any `?apikey=...` query parameters, `Authorization:` headers, session cookies, and any arr/qBit URLs you'd rather not share. Decluttarr already redacts `X-Api-Key` headers but not query-string keys.
render: shell
validations:
required: true

- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
placeholder: |
1. Start decluttarr with the config above
2. ...
3. ...
validations:
required: true

- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true

- type: textarea
id: actual
attributes:
label: Actual behavior
validations:
required: true

- type: textarea
id: extra
attributes:
label: Anything else?
description: Screenshots, related issues, what you've already tried, hardware/OS details if relevant, etc.
10 changes: 10 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Questions, usage help, config troubleshooting
url: https://github.com/ManiMatter/decluttarr/discussions
about: |
For general questions, configuration help, or "why is this happening" — please use Discussions instead of opening an issue. The issue tracker is reserved for reproducible bugs and feature requests.
- name: 📚 README & FAQ
url: https://github.com/ManiMatter/decluttarr#dependencies--hints--faq
about: |
Many common problems (qBit cookie issues, multi-instance YAML format, "no valid arr instances" errors, etc.) are covered in the README's Dependencies & Hints & FAQ section. Please check there first.
41 changes: 41 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: ✨ Feature Request
description: Suggest a new feature or enhancement
title: "[Feature] "
labels: ["Feature"]
body:
- type: markdown
attributes:
value: |
Thanks for proposing an improvement! Please make sure there isn't already an [open issue](https://github.com/ManiMatter/decluttarr/issues?q=is%3Aissue+is%3Aopen+label%3AFeature) or [discussion](https://github.com/ManiMatter/decluttarr/discussions) covering the same idea.

- type: textarea
id: problem
attributes:
label: What problem does this solve?
description: Describe the use case or pain point that the feature would address. What are you trying to accomplish that's currently awkward, manual, or impossible?
validations:
required: true

- type: textarea
id: solution
attributes:
label: Proposed solution
description: |
How would this feature work from the user's perspective?
- What does the config look like?
- What does decluttarr do at runtime?
- Are there new env vars / yaml keys / log messages?
validations:
required: true

- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Workarounds you've tried, other tools that solve this differently, or alternative implementations you've thought through.

- type: textarea
id: extra
attributes:
label: Anything else?
description: Mockups, references, related issues, or willingness to contribute a PR.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ jobs:
needs: unit-tests
runs-on: ubuntu-latest
if: github.event_name == 'push'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
with:
Expand Down
44 changes: 36 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
_Like this app? Thanks for giving it a_ ⭐️

# **Decluttarr**
**Decluttar V2 was released on Nov 1st, 2025 with _breaking config file changes_.**
_Couple of quick hints:_

**1. Are you looking at the right ReadMe?**
Check that the ReadMe version corresponds to the branch you are using (i.e. ```latest``` or ```dev```). Default view on this GitHub repo is ```dev``` branch, but you are most likely using ```latest``` (as per below [instructions](#getting-started)). Thus make sure you change the branch of the ReadMe if that‘s the case:
- [**LATEST** ReadMe](https://github.com/ManiMatter/decluttarr/blob/latest/README.md)
- [**DEV** ReadMe](https://github.com/ManiMatter/decluttarr/blob/dev/README.md)

**2. Decluttar V2 was released on Nov 1st, 2025 with _breaking config file changes_.**

Looking to **upgrade from V1 to V2**? Look [here](#upgrading-from-v1-to-v2)

Expand All @@ -20,6 +27,7 @@ Looking to **upgrade from V1 to V2**? Look [here](#upgrading-from-v1-to-v2)
- [LOG_LEVEL](#log_level)
- [TEST_RUN](#test_run)
- [TIMER](#timer)
- [REQUEST_TIMEOUT](#request_timeout)
- [SSL_VERIFICATION](#ssl_verification)
- [IGNORE_DOWNLOAD_CLIENTS](#ignore_download_clients)
- [PRIVATE_TRACKER_HANDLING / PUBLIC_TRACKER_HANDLING](#private_tracker_handling--public_tracker_handling)
Expand Down Expand Up @@ -49,14 +57,15 @@ Looking to **upgrade from V1 to V2**? Look [here](#upgrading-from-v1-to-v2)
- [READARR](#readarr)
- [LIDARR](#lidarr)
- [WHISPARR](#whisparr)
- [SPORTARR](#sportarr)
- [Downloaders](#download-clients)
- [QBITTORRENT](#qbittorrent)

## Overview

Decluttarr is a helper tool that works with the *arr-application suite, and automates the clean-up for their download queues, keeping them free of stalled / redundant downloads.

It supports [Radarr](https://github.com/Radarr/Radarr/), [Sonarr](https://github.com/Sonarr/Sonarr/), [Readarr](https://github.com/Readarr/Readarr/), [Lidarr](https://github.com/Lidarr/Lidarr/), and [Whisparr](https://github.com/Whisparr/Whisparr/).
It supports [Radarr](https://github.com/Radarr/Radarr/), [Sonarr](https://github.com/Sonarr/Sonarr/), [Sportarr](https://github.com/Sportarr/Sportarr/), [Readarr](https://github.com/Readarr/Readarr/), [Lidarr](https://github.com/Lidarr/Lidarr/), and [Whisparr](https://github.com/Whisparr/Whisparr/).

Feature overview:

Expand Down Expand Up @@ -193,6 +202,7 @@ services:
LOG_LEVEL: INFO
TEST_RUN: True
TIMER: 10
# REQUEST_TIMEOUT: 15
# IGNORED_DOWNLOAD_CLIENTS: >
# - emulerr
# SSL_VERIFICATION: true
Expand Down Expand Up @@ -267,6 +277,10 @@ services:
- base_url: "http://sonarr2:8989"
api_key: "$SONARR_API_KEY"

# SPORTARR: >
# - base_url: "http://sportarr:1867"
# api_key: "$SPORTARR_API_KEY"

# RADARR: >
# - base_url: "http://radarr:7878"
# api_key: "$RADARR_API_KEY"
Expand All @@ -286,8 +300,9 @@ services:
# --- Download Clients ---
QBITTORRENT: >
- base_url: "http://qbittorrent:8080"
# username: "$QBIT_USERNAME" # (optional -> if not provided, assuming not needed)
# password: "$QBIT_PASSWORD" # (optional -> if not provided, assuming not needed)
# api_key: "$QBIT_API_KEY" # (recommended -> requires qBittorrent 5.2.0+; takes precedence over username/password)
# username: "$QBIT_USERNAME" # (legacy -> for qBittorrent < 5.2; ignored if api_key is set)
# password: "$QBIT_PASSWORD" # (legacy -> for qBittorrent < 5.2; ignored if api_key is set)
name: "qBittorrent 1" # (optional -> if not provided, assuming "qBittorrent". Must correspond with what is specified in your *arr as download client name)
- base_url: "http://qbittorrent:8080"
name: "qBittorrent 2"
Expand Down Expand Up @@ -399,6 +414,13 @@ Configures the general behavior of the application (across all features)
- Unit: Minutes
- Is Mandatory: No (Defaults to 10)

#### REQUEST_TIMEOUT

- Timeout used for HTTP/API requests to *arr and download clients
- Type: Integer or Float
- Unit: Seconds
- Is Mandatory: No (Defaults to 15)

#### SSL_VERIFICATION

- Turns SSL certificate verification on or off for all API calls
Expand All @@ -422,8 +444,9 @@ Configures the general behavior of the application (across all features)
- "remove" means that torrents are removed (default behavior)
- "skip" means they are disregarded (which some users might find handy to protect their private trackers prematurely, i.e., before their seed targets are met)
- "obsolete_tag" means that rather than being removed, the torrents are tagged. This allows other applications (such as [qbit_manage](https://github.com/StuffAnThings/qbit_manage) to monitor them and remove them once seed targets are fulfilled)
- "remove_from_queue" behaves like "remove" on the *arr side (the queue entry is cleared, and blocklisted if the job blocklists), but leaves the torrent untouched in the download client. Use this when the torrent still carries a hit-and-run obligation you must honour: the *arr stops waiting on a download that will never import and is free to grab an alternative, while seeding continues and another application (such as [qbit_manage](https://github.com/StuffAnThings/qbit_manage)) retires the torrent on the tracker's terms. Unlike "obsolete_tag" this clears the queue entry immediately rather than waiting for the torrent to disappear
- Type: String
- Permissible Values: remove, skip, obsolete_tag
- Permissible Values: remove, remove_from_queue, skip, obsolete_tag
- Is Mandatory: No (Defaults to remove)


Expand Down Expand Up @@ -551,11 +574,13 @@ This is the interesting section. It defines which job you want decluttarr to run
- Steers whether downloads stuck obtaining metadata are removed from the queue
- Blocklisted: Yes
- Type: Boolean or Dict
- Permissible Values: True, False or max_strikes (int)
- Permissible Values: True, False or max_strikes (int), detect_via_missing_size (bool)
- Is Mandatory: No (Defaults to False)
- Note:
- With max_strikes you can define how many times this torrent can be caught before being removed
- Instead of configuring it here, you may also configure it as a default across all jobs or use the built-in defaults (see further above under "max_strikes")
- By default, this check relies on the "qBittorrent is downloading metadata" message that qBittorrent surfaces in the \*arr queue. Other download clients (e.g. Transmission, Deluge) do not surface such a message, so a torrent stuck fetching metadata stays undetected (see [#57](https://github.com/ManiMatter/decluttarr/issues/57)).
- Set `detect_via_missing_size: true` to additionally flag, regardless of download client, queued items whose size is not yet known (size 0), which is the client-agnostic signature of "no metadata yet". This is debounced by max_strikes. Defaults to False to keep existing (qBittorrent) behavior unchanged.

#### REMOVE_MISSING_FILES

Expand Down Expand Up @@ -679,6 +704,8 @@ Defines arr-instances on which download queue should be decluttered
- Equivalent of [Radarr](#radarr)
#### Whisparr
- Equivalent of [Radarr](#radarr)
#### Sportarr
- Equivalent of [Radarr](#radarr)


---
Expand All @@ -695,8 +722,9 @@ Supported download clients: **qBittorrent** and **SABnzbd**.
- Type: List of qbit instances
- Keys per instance
- base_url: URL under which the qbit can be reached (mandatory)
- username: Optional - only needed if your qbit requires authentication (which you may not need if you have configured qbit in a way that it disables it for local connections)
- password: Optional - see above
- api_key: Recommended - qBittorrent API key (requires qBittorrent 5.2.0 or newer; generate it under Web UI settings). Authenticates without storing credentials, and takes precedence over username/password if both are set.
- username: Legacy - only for qBittorrent < 5.2, or if your qbit requires authentication (which you may not need if qbit disables it for local connections). Ignored when api_key is set.
- password: Legacy - see above
- name: Optional. Needs to correspond with the name that you have set up in your Arr instance. Defaults to "qBittorrent"

#### SABNZBD
Expand Down
16 changes: 12 additions & 4 deletions config/config_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ general:
log_level: INFO
test_run: true
timer: 10
# request_timeout: 15 # Optional: timeout for all HTTP/API calls in seconds
# ignored_download_clients: ["emulerr"]
# ssl_verification: false # Optional: Defaults to true
# private_tracker_handling: "obsolete_tag" # remove, skip, obsolete_tag. Optional. Default: remove
# public_tracker_handling: "remove" # remove, skip, obsolete_tag. Optional. Default: remove
# request_timeout: 15 # Optional: Request timeout in seconds. Defaults to 15. Can be overridden per instance.
# private_tracker_handling: "obsolete_tag" # remove, remove_from_queue, skip, obsolete_tag. Optional. Default: remove
# public_tracker_handling: "remove" # remove, remove_from_queue, skip, obsolete_tag. Optional. Default: remove
# obsolete_tag: "Obsolete" # optional. Default: "Obsolete"
# protected_tag: "Keep" # optional. Default: "Keep"

Expand Down Expand Up @@ -52,6 +54,10 @@ instances:
sonarr:
- base_url: "http://sonarr:8989"
api_key: "xxxx"
# timeout: 30 # Optional: Overrides general timeout for this instance.
# sportarr:
# - base_url: "http://sportarr:1867"
# api_key: "xxxx"
radarr:
- base_url: "http://radarr:7878"
api_key: "xxxx"
Expand All @@ -68,9 +74,11 @@ instances:
download_clients:
qbittorrent:
- base_url: "http://qbittorrent:8080" # You can use decluttarr without qbit (not all features available, see readme).
# username: xxxx # (optional -> if not provided, assuming not needed)
# password: xxxx # (optional -> if not provided, assuming not needed)
# api_key: "qbt_xxxx" # (recommended -> requires qBittorrent 5.2.0+; generate under Web UI settings. Takes precedence over username/password.)
# username: xxxx # (legacy -> for qBittorrent < 5.2; ignored if api_key is set)
# password: xxxx # (legacy -> for qBittorrent < 5.2; ignored if api_key is set)
# name: "qBittorrent" # (optional -> if not provided, assuming "qBittorrent". Must correspond with what is specified in your *arr as download client name)
# timeout: 30 # (optional -> overrides general timeout for this instance)
# sabnzbd:
# - base_url: "http://sabnzbd:8080" # SABnzbd server URL
# api_key: "your_api_key_here" # (required -> SABnzbd API key)
Expand Down
Loading
Loading