Skip to content

feat(rules): add ARR disk target path selection for disk space rules#2461

Merged
enoch85 merged 10 commits into
Maintainerr:mainfrom
tumeden:diskSpace-diskTarget
Mar 24, 2026
Merged

feat(rules): add ARR disk target path selection for disk space rules#2461
enoch85 merged 10 commits into
Maintainerr:mainfrom
tumeden:diskSpace-diskTarget

Conversation

@tumeden

@tumeden tumeden commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds path targeting for Radarr/Sonarr disk space rules.

  • add a Disk Target dropdown for Radarr/Sonarr disk space rules
  • the dropdown appears only when the first value is a Radarr/Sonarr disk space field
  • support Aggregate (default) or a specific Arr-reported path
  • evaluate disk space against the selected path when configured
  • keep existing rules unchanged (Aggregate mode by default)

Why

Users running containerized Arr setups need to evaluate free space on media mounts (for example /movies, /shows) instead of only using a generic aggregate value.

Feature request:
https://features.maintainerr.info/posts/151/disk-space-on-other-mounts-not-just-root-containerized-radarr-sonarr-deployments

Notes

  • Backward compatible: existing rules continue to work without changes.
  • Path options come from Arr disk space API results.
image

I did basic testing for these changes. I don’t currently run Sonarr/Radarr in Docker, so I couldn’t directly verify the Docker-specific mount-path scenario but this change adds more granular configuration when it comes to disk space rules.

- add Disk Target dropdown for Radarr/Sonarr disk space rules
- support Aggregate (default) or a specific Arr-reported path
- use selected path during disk space evaluation
- keep existing rules unchanged in Aggregate mode
@tumeden tumeden requested a review from ydkmlt84 as a code owner March 5, 2026 23:24
@azurecrash

Copy link
Copy Markdown

I was the one that requested this - happy to test it if that's helpful!

As a total sidetone, I suspect disk related data could also come from Tracearr in the future as it seems to have a robust view of storage use (now and predicted). Thanks again!

@enoch85

enoch85 commented Mar 6, 2026

Copy link
Copy Markdown
Collaborator

I was the one that requested this - happy to test it if that's helpful!

As a total sidetone, I suspect disk related data could also come from Tracearr in the future as it seems to have a robust view of storage use (now and predicted). Thanks again!

Yes, I agree on this. It should be server agnostic, DRY, and at the same time consistent with the rest of the code and easy to understand and maintain.

Didn't deep dive into the code, but I suppose the drop-down is conditional and only displayed for the space rule?

enoch85 added a commit that referenced this pull request Mar 9, 2026
…, #2442, #2406, #2386, #2370

PR #2466 - fix: honor Jellyfin played threshold
- Respect configured played percentage threshold for Jellyfin watch status

PR #2461 - feat(rules): add ARR disk target path selection for disk space rules
- Allow selecting specific disk target paths for Radarr/Sonarr disk space rules

PR #2458 - feat: clean up empty ended shows in Sonarr after season actions
- Automatically remove ended shows from Sonarr when all seasons are processed

PR #2453 - fix: improve Plex viewCount reliability and add isWatched boolean
- Use native Plex viewCount field with watch history fallback
- Add new isWatched boolean rule property

PR #2452 - build(deps): bump actions/download-artifact from 7 to 8

PR #2451 - build(deps): bump actions/upload-artifact from 6 to 7

PR #2442 - fix(server): reject null/undefined in numeric rule comparisons
- Add getComparisonResult wrapper that fails closed on null/undefined operands
- Strict type checking for BIGGER/SMALLER comparisons

PR #2406 - Metadata provider abstraction layer with TVDB support
- Add MetadataService as central metadata resolution layer
- TVDB support as alternative metadata provider
- Dynamic provider preference with fallback
- Replace TmdbIdService with unified MetadataService

PR #2386 - feat: missing_episode rules
- Add missing episode count as a rule property for Sonarr

PR #2370 - build(deps-dev): bump the eslint group with 2 updates

enoch85 commented Mar 9, 2026

Copy link
Copy Markdown
Collaborator

This is now included in the jellyfin-dev docker container - among some other fixes. To upgrade, change tag in docker from latest (or whatever you have) to jellyfin-dev. Then do:

docker compose pull jellyfin-dev && docker compose up -d

You can check the latest commits here: https://github.com/Maintainerr/Maintainerr/commits/jellyfin-dev

  1. Consider the jellyfin-dev branch to be early development, and make a backup before switching to that branch!
  2. Please test that your issue is fixed
  3. Report back here, and tag me and @ydkmlt84

Thank you very much! 🚀

@azurecrash

Copy link
Copy Markdown

Just pulled this down to test.

The fix does work for Radarr, but not Sonarr, and I can now show the exact asymmetry between the two which may be helpful for you all to be aware of, though I don't know if anything further can happen on this side.

Environment:

  • Radarr 6.1.1.10317 (linuxserver.io, develop tag, Docker)
  • Sonarr 4.0.16.2946 (linuxserver.io, develop tag, Docker)
  • Both running on the same host, both with NAS-backed media paths mounted via bind mount
  • Both have their respective media paths configured as root folders in Media Management

Radarr (working):

System Status → Disk Space correctly shows /movies at 6.9 TiB free / 26.2 TiB total — the NAS filesystem, not the container root.

Sonarr (broken):

System Status → Disk Space only lists /, /config, and /downloads, all resolving to the container root (232.2 GiB). /tv does not appear, despite being correctly mounted and showing 6.9 TiB free in Sonarr's own Root Folders settings under Media Management.

Root cause hypothesis:
This appears to be a difference in how the two apps expose paths via their APIs. Radarr surfaces /movies through the Disk Space endpoint; Sonarr does not surface /tv there — it only appears via the Root Folders endpoint. I am unclear whether this is a function of how the container images have been put together or Radarr and Sonarr differences at the application level. Maybe someone else can weigh in here?

@enoch85

enoch85 commented Mar 14, 2026

Copy link
Copy Markdown
Collaborator

@tumeden Do you have any plans to work further on this?

enoch85 commented Mar 15, 2026

Copy link
Copy Markdown
Collaborator

Changes pushed: Fix Sonarr disk target & DRY refactor

Root cause (Sonarr not working)

Sonarr's /api/v3/diskspace endpoint only returns DriveType.Fixed mounts. In Docker setups with NFS/CIFS bind-mounted media paths (e.g. /tv), these mounts are classified as DriveType.Network and never appear in Sonarr's disk space response. Radarr includes DriveType.Network mounts, which is why it works.

Fix: A new getDiskspaceWithRootFolders() method in the base ServarrApi class merges /rootfolder entries into /diskspace results, covering paths that Sonarr's disk space endpoint misses. Both endpoints are already cached (1h TTL via getRolling), so no extra network overhead.

Why remaining-space vs total-space are handled differently

The /rootfolder API returns freeSpace but not a reliable totalSpace. So:

  • diskspace_remaining_gb → uses merged data (getDiskspaceWithRootFolders) — root folder freeSpace is accurate
  • diskspace_total_gb → uses raw /diskspace data only — returns null (with warning log) if the targeted path only exists via root folders

A hasAccurateTotalSpace flag on each entry tracks the data source so the evaluator and UI can make informed decisions.

UI: preserved saved path

Previously, opening a rule for editing while the ARR was temporarily unreachable would silently clear the configured disk path (downgrading a targeted rule to aggregate mode). Now the saved path is preserved in the dropdown with a "(saved selection)" label.

For total-space rules, root-folder-only paths are hidden from the dropdown since they can't be evaluated accurately.

DRY improvements

Before After
normalizeDiskPath duplicated 3× (server API, server utils, UI) Single export in @maintainerr/contracts
DiskSpaceResource interface duplicated (server + UI) ArrDiskspaceResource in @maintainerr/contracts, re-exported
Magic strings 'diskspace_remaining_gb' / 'diskspace_total_gb' in 8+ locations DISKSPACE_REMAINING_PROPERTY / DISKSPACE_TOTAL_PROPERTY constants in contracts
~30 lines of identical diskspace evaluation logic in radarr-getter + sonarr-getter evaluateArrDiskspaceGiB() shared helper with ArrDiskspaceClient interface
getPropFromTuple recreated on every React render Moved outside component as pure function
createDiskspaceResource / createRuleDto test helpers duplicated per spec file Shared fixtures in test/utils/data.ts

Files changed (14)

Contracts:

  • packages/contracts/src/rules/arrDiskspaceResource.ts — new shared interface + normalizeDiskPath + hasAccurateTotalSpace
  • packages/contracts/src/rules/constants.tsDISKSPACE_REMAINING_PROPERTY, DISKSPACE_TOTAL_PROPERTY

Server:

  • servarr-api.service.tsgetDiskspaceWithRootFolders(), root folder merge logic
  • servarr.interface.tsDiskSpaceResource → re-export from contracts, RootFolder.totalSpace made optional
  • servarr-api.controller.ts — endpoints now use merged data + explicit return types
  • diskspace.utils.ts — new shared evaluateArrDiskspaceGiB(), filterDiskspaceByTargetPath(), computeDiskspaceGiB()
  • radarr-getter.service.ts / sonarr-getter.service.ts — simplified to 6-line evaluateArrDiskspaceGiB calls
  • radarr-getter.service.spec.ts / sonarr-getter.service.spec.ts — new diskspace tests
  • test/utils/data.ts — shared createArrDiskspaceResource, createRuleDto fixtures

UI:

  • rules.ts — re-export ArrDiskspaceResource from contracts instead of local definition
  • RuleInput/index.tsx — preserved path option, hasAccurateTotalSpace filtering, constants, removed normalizeDiskPath local copy

All 504 tests pass. Format and type-check clean.

Sonarr's /diskspace API excludes NFS/CIFS network mounts (DriveType.Fixed
only), so Docker bind-mounted media paths like /tv never appear. Radarr
includes DriveType.Network, so it works already.

Fix: merge /rootfolder data into diskspace results in the base ServarrApi
class (getDiskspaceWithRootFolders) for remaining-space rules and the UI
path picker. Total-space rules use raw /diskspace to avoid inaccurate
values from root-folder fallback entries.

Additional changes:
- Move ArrDiskspaceResource interface + normalizeDiskPath to
  @maintainerr/contracts, eliminating triple duplication
- Add DISKSPACE_REMAINING_PROPERTY / DISKSPACE_TOTAL_PROPERTY constants
  to contracts, replace magic strings in UI and server
- Extract evaluateArrDiskspaceGiB() to eliminate ~30-line copy-paste
  between radarr-getter and sonarr-getter
- Move getPropFromTuple outside React component (avoid re-creation)
- Preserve saved disk path in UI dropdown when ARR is unreachable
  instead of silently clearing it
- Add hasAccurateTotalSpace flag to track root-folder fallback entries
- Mark RootFolder.totalSpace as optional (not in Sonarr/Radarr API)
- Add diskspace tests for both getter services
- Move test fixtures to shared test/utils/data.ts
@enoch85 enoch85 force-pushed the diskSpace-diskTarget branch from 28101ca to 697f445 Compare March 15, 2026 17:01
enoch85 added a commit that referenced this pull request Mar 15, 2026
PR #2461 - feat(rules): add ARR disk target path selection for disk space rules
- Add Disk Target dropdown for Radarr/Sonarr disk space rules
- Support Aggregate (default) or specific Arr-reported path
- Fix Sonarr disk target (NFS/CIFS mounts not in /diskspace API)
- Merge /rootfolder data into diskspace results for remaining-space rules
- Move ArrDiskspaceResource + normalizeDiskPath to @maintainerr/contracts
- Extract evaluateArrDiskspaceGiB() to eliminate copy-paste between getters
- Add diskspace tests for both getter services
@enoch85

enoch85 commented Mar 15, 2026

Copy link
Copy Markdown
Collaborator

@azurecrash Hey! Thanks for testing. Please pull the latest changes (also included in the jellyfin-dev branch and test if it works. 👍

@azurecrash

Copy link
Copy Markdown

Works! Thanks very much, this is awesome!

@enoch85

enoch85 commented Mar 16, 2026

Copy link
Copy Markdown
Collaborator

@azurecrash Thanks for confirming!

@enoch85 enoch85 self-requested a review as a code owner March 24, 2026 20:05
@enoch85 enoch85 enabled auto-merge (squash) March 24, 2026 20:21
@enoch85 enoch85 merged commit e3431fb into Maintainerr:main Mar 24, 2026
9 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants