Skip to content

feat: ARR webhook cleanup and managed-only mode with Maintenance tools - #392

Open
buzzromain wants to merge 2 commits into
sirrobot01:betafrom
buzzromain:combined-managed-only-arr-cleanup
Open

buzzromain wants to merge 2 commits into
sirrobot01:betafrom
buzzromain:combined-managed-only-arr-cleanup

Conversation

@buzzromain

Copy link
Copy Markdown

Summary

Two features that work well together — automatic ARR webhook cleanup and managed-only mode with a Maintenance tab — submitted as one PR since the second's cleanup criterion depends on the tracking the first builds.

Automatic debrid cleanup driven by ARR webhooks. Decypharr registers its own webhook notification with each configured ARR instance at startup, and tracks which files it manages for each ARR (arr_refs) — updated on Download/Rename/FileDelete/SeriesDelete/MovieDelete events. Import history is bootstrapped for media imported before the webhook was active, or during downtime.

allow_delete (opt-in, default off) ties this into cleanup: when a tracked file is deleted in Sonarr/Radarr, the tracked-file reference is always dropped; the underlying debrid torrent is only removed if allow_delete is enabled for that ARR, and only once no other tracked file still references it. A SeriesDelete/MovieDelete only ever cleans up files tracked for the ARR it came from, even if library roots happen to overlap across two configured ARRs.

The webhook endpoint can't sit behind session auth — Sonarr/Radarr can't do interactive login — so it's authenticated against the ARR's own configured API token, embedded in the registered URL instead.

Managed-only mode with Maintenance cleanup tools. managed_only makes Decypharr the single source of truth for a debrid account: external torrents (added directly on the provider, outside Sonarr/Radarr) are ignored instead of synced in. If a managed torrent disappears from the provider (manual deletion, provider cleanup, expired slot), Decypharr automatically re-adds it from the stored magnet — up to 3 retries, 5-minute cooldown between attempts.

The Settings > Maintenance tab adds two cleanup tools:

  • Local Cleanup removes entries no ARR currently references (via arr_refs) from Decypharr's own database — reversible, the provider is untouched. This covers both torrents that were never imported through an ARR, and ones that were but whose media was later deleted or upgraded there; a category on the entry does not exempt it.
  • Provider Cleanup removes torrents from the debrid provider that Decypharr doesn't track at all — irreversible. Scan first to review what would be removed before purging.

Changes

  • pkg/storage/arr_files.goarr_refs store: managed-path index, per-ARR scoping, ReferencedInfoHashes
  • pkg/server/webhook_arr.go — webhook handler, token-based auth
  • pkg/manager/arr_cleanup.go — webhook registration, import-history bootstrap, delete/rename handling
  • pkg/manager/manager.goGetUnmanagedEntries/PurgeUnmanagedEntries (reads arr_refs), provider-side equivalents
  • pkg/manager/torrent.go — managed-only sync filtering, auto-reinsertion (mutex-guarded against concurrent providers)
  • pkg/server/api_maintenance.go — scan/purge endpoints
  • internal/configallow_delete, managed_only fields
  • Docs: docs/src/content/docs/guides/arrs.mdx, managed-only.md
  • Tests: pkg/server/webhook_arr_test.go, pkg/storage/arr_files_test.go, pkg/manager/reinsert_attempt_test.go, pkg/manager/unmanaged_test.go

Configuration example

{
  "managed_only": true,
  "arrs": [
    {
      "name": "Sonarr",
      "host": "http://sonarr:8989",
      "token": "API_TOKEN",
      "allow_delete": true
    }
  ]
}

Decypharr registers its webhook in Sonarr/Radarr automatically at startup — no manual webhook configuration needed.

Decypharr registers its own webhook notification with each configured
ARR instance at startup, and tracks which files it manages for each
ARR (arr_refs) — updated on Download/Rename/FileDelete/SeriesDelete/
MovieDelete events. Import history is bootstrapped for media imported
before the webhook was active, or during downtime, and synced
incrementally afterward.

`allow_delete` (opt-in, default off) ties this into cleanup: when a
tracked file is deleted in Sonarr/Radarr, the tracked-file reference is
always dropped; the underlying debrid torrent is only removed if
allow_delete is enabled for that ARR, and only once no other tracked
file still references it. A SeriesDelete/MovieDelete only ever cleans
up files tracked for the ARR it came from, even if library roots
happen to overlap across two configured ARRs.

The webhook endpoint can't sit behind session auth — Sonarr/Radarr
can't do interactive login — so it's authenticated against the ARR's
own configured API token, embedded in the registered URL instead.

Configuration example:

{
  "arrs": [
    {
      "name": "Sonarr",
      "host": "http://sonarr:8989",
      "token": "API_TOKEN",
      "allow_delete": true
    }
  ]
}
managed_only makes Decypharr the single source of truth for a debrid
account: external torrents (added directly on the provider, outside
Sonarr/Radarr) are ignored instead of synced in. If a managed torrent
disappears from the provider (manual deletion, provider cleanup,
expired slot), Decypharr automatically re-adds it from the stored
magnet — up to 3 retries, 5-minute cooldown between attempts, guarded
against concurrent reinsertion from multiple providers.

The Maintenance tab (Settings > Maintenance) adds two cleanup tools:

- Local Cleanup removes entries no ARR currently references (via
  arr_refs) from Decypharr's own database — reversible, the provider
  is untouched. This covers both torrents that were never imported
  through an ARR, and ones that were but whose media was later deleted
  or upgraded there; a category on the entry does not exempt it.
- Provider Cleanup removes torrents from the debrid provider that
  Decypharr doesn't track at all — irreversible. Scan first to review
  what would be removed before purging.

There's currently no way to exempt an individual entry from Local
Cleanup — review the scan list carefully before purging.

Configuration example:

{
  "managed_only": true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant