feat: clean up empty Sonarr shows after season actions#2458
Conversation
Add two new ServarrAction variants for season-level rules that automatically clean up the parent show when it becomes empty and has ended status in Sonarr: - DELETE_SHOW_IF_EMPTY: deletes the season, then deletes the show if no episode files remain and the show has ended - UNMONITOR_SHOW_IF_EMPTY: unmonitors the season, then unmonitors the show if no monitored seasons remain and the show has ended
…, #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
|
This is now included in the You can check the latest commits here: https://github.com/Maintainerr/Maintainerr/commits/jellyfin-dev
Thank you very much! 🚀 |
|
Ideally this PR would also allow for cleaning up shows that haven't ended if there are no outstanding requests. Seerr recently added a new setting that makes shows added to sonarr not default to monitoring all new seasons automatically, therefore a request for one season will never put the show in a state to download any more episodes/seasons. In this case, when the season is deleted due to a rule's action, if there is no outstanding request (because outstanding requests create a monitored season) the show itself should be erased too. |
|
I went back through issue #892, and I think your point is valid but slightly outside the original scope. The issue there was specifically about ended shows that become empty after Maintainerr deletes a season, and the goal was to handle that at action time because those shows may no longer be discoverable through normal Plex-seeded rules afterward. Your Seerr example is a real adjacent case: with Monitor New Seasons set to None, a continuing show can also be left behind in Sonarr as an empty shell once the last requested season is removed. I prototyped a narrow extension for the season delete variant so that, when Force Seerr is enabled and Seerr shows no remaining season requests after the deleted season is excluded, Maintainerr also deletes that continuing show. I kept the ended-show behavior unchanged and made the Seerr path fail safe when request state is unknown. So I’d treat this as follow-up scope on top of the original 892 fix, rather than the PR missing the original issue entirely. |
|
@enoch85 nice! I agree that it makes more sense as a follow up. What does Force Seerr do? I don't think I've seen that option since I last explored around my Maintainerr instance when I first configured it a few months back. |
Well it's added now anyway 🙂 Here's the info: https://docs.maintainerr.info/latest/Rules/?h=force+see#general
|
|
@enoch85 Why must requests be deleted directly vs relying on availability sync for seerr? is the idea that if a request persists until the next availability sync, maintainerr may think it's an "unfulfilled" request and thus the show overall should stay present even after deleting the season? Couldn't we just check if any requests remain without forcing the deletion of requests for the currently processed season? Basically just ignore available requests for the season we are currently deleting, and only count other available or partially available requests for other seasons for the show. Then we don't have to futz with CSRF protection on Seerr etc. |
| this.logger.debug(err); | ||
| return undefined; | ||
| } | ||
| } |
There was a problem hiding this comment.
Here's some alternate pseudocode to get at what I mean:
public async hasRemainingSeasonRequests(
tmdbid: string | number,
removedSeasonNumber: number,
): Promise<boolean | undefined> {
try {
const media = await this.getShow(tmdbid);
if (!media?.mediaInfo) {
return undefined;
}
const requests = media.mediaInfo.requests ?? [];
return requests
.filter((request) => request.status !== MediaRequestStatus.DELETED)
.flatMap((request) => request.seasons)
.some((season) => season.seasonNumber !== removedSeasonNumber);
} catch (err) {
this.logger.warn(
'Seerr communication failed. Is the application running?',
);
this.logger.debug(err);
return undefined;
}
}Your original code means:
"Is there any request that contains at least one season other than removedSeasonNumber?"
but this rewrite proposal means:
"Is there any whole request left that does not contain removedSeasonNumber at all?"
Put another way, what we really want is:
"Among non-deleted requests for this show, does any request still contain any season whose number is not removedSeasonNumber?"
This would mean that we don't need to have Maintainerr mess with deleting Seerr requests itself at all, and we maintain the "audit trail" of cleaned up, deleted requests in the Seerr UI.
There was a problem hiding this comment.
Thanks for the review. Reverted the old commit and refactored a bit further. Please check it it's aligning wit your intentions.
Regarding the revert, and latest commit:Why The previous implementation could remove Seerr request data as part of the cleanup flow, which didn’t match the reviewer’s intent and risked losing useful request history. It also kept the action enum duplicated between server and UI code. What it changes This update makes Why it’s better The behavior is safer and more accurate because it preserves Seerr’s audit trail while still preventing orphaned empty shows in Sonarr. It’s also cleaner to maintain, since the shared enum removes magic numbers and keeps server and UI behavior aligned from one source of truth. Regarding the Force Seerr optionDELETE_SHOW_IF_EMPTY is explicitly excluded from the forceSeerr mutation gate in collection-handler.ts:85-92, so this behavior does not require the “Force Seerr” checkbox to be enabled just to run. The nuance is in the Sonarr handler: for continuing shows, it still calls Seerr to check whether any season requests remain in sonarr-action-handler.ts:326-335. So the behavior is: Ended empty show: not dependent on forceSeerr; it can be deleted without that flag. |
PR #2458 - feat: clean up empty Sonarr shows after season actions - Move ServarrAction enum to @maintainerr/contracts for shared use - Add SeerrApiService.hasRemainingSeasonRequests() for continuing show cleanup - Update deleteShowIfEmpty to handle continuing shows via Seerr state check - Exclude DELETE_SHOW_IF_EMPTY from Seerr request mutation in collection handler - Use ServarrAction enum constants in UI AddModal instead of magic numbers - Update force Seerr helper text to describe season-specific behavior - Add SeerrApiModule to ActionsModule for DI - Add comprehensive tests for all new Seerr-aware cleanup scenarios
There was a problem hiding this comment.
This looks good to me when it comes to non-ended shows, but I had a question about ended shows: why do we care if they are ended at all?
Imagine I requested season 1 of breaking bad, an ended show. Someone else requested season 2, but for whatever reason (indexer rate limits, etc) that request hasn't been fulfilled yet. My maintainerr rule eventually gets triggered to delete season 1. Why should it matter if the show is ended? Shouldn't deleting the show only happen regardless of if it's "ended" if two things are both true:
- Show is empty of seasons in sonarr.
- No unfulfilled seasons besides the current one being acted on exist in any seerr requests.
I suspect we don't need to take into account ended status at all.
|
Any chance I can help further towards getting this merged @enoch85? Would really smooth out a rough edge I experience a lot with Maintainerr leaving a lot of empty shows today. |
I want this as much as you. But I don't control the main repo, only jellyfin-dev. So use 'jellyfin-dev' until it gets merged into main. I run it myself in production. Cc @ydkmlt84 as we discussed already. |
|
@andrew-kennedy Have you tested this change on |
andrew-kennedy
left a comment
There was a problem hiding this comment.
Currently deleteShowIfEmpty is too aggressive in deleting shows. Left a comment explaining the issue and how to fix.
| if (series.status === 'ended') { | ||
| await sonarrApiClient.deleteShow(series.id, true, listExclusions); | ||
| this.logger.log( | ||
| `[Sonarr] Show '${series.title}' is ended with no files remaining — deleted from Sonarr`, | ||
| ); | ||
| return; | ||
| } |
There was a problem hiding this comment.
This will lead to ended shows getting deleted if they have another in-flight request for another season.
Here's the scenario:
- Someone requests season 1 of an ended show
- Season 1 Downloads
- Someone requests season 2 of the same ended show
- Due to some unknown issue, Season 2 does not yet dowload
- The rule runs and unmonitors and deletes season 1 in sonarr as all criteria are met
- This function runs as the show's files are "empty", and it deletes the show
In this case, there was still an outstanding unfulfilled request for season 2 of the show, and this function deletes the show before it can ever be filled. We still need to check Seerr or the monitored status of other seasons in sonarr to ensure we don't delete the show when pending requests still exist. Preferably seerr, as that's the true source of truth.
There was a problem hiding this comment.
Great catch!
Feel free to dig in to this PR. Help is welcome! 🚀
|
Just an update here... I plan to continue to work on this once 3.4.0 are released. Reason being that release is already quite huge in terms of UI + Metadata, and I don't want to push too many changes at once. So, I have not forgotten, just focused on UI this release. |
|
Superseded by #2618. Rebuilt from current development so the valid fixes and review intent could be carried forward cleanly. |
Summary
Closes #892
Test plan