Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 5 additions & 6 deletions apps/server/src/modules/collections/collection-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ describe('CollectionHandler', () => {
expect(seerrApi.removeSeasonRequest).toHaveBeenCalledTimes(1);
});

it('should call removeSeasonRequest for episodes', async () => {
it('does not mutate Seerr requests for episodes (no per-episode request granularity)', async () => {
const collection = createCollection({
arrAction: ServarrAction.DELETE,
forceSeerr: true,
Expand All @@ -263,11 +263,10 @@ describe('CollectionHandler', () => {
collectionHandler.handleMedia(collection, collectionMedia),
).resolves.toBe(true);

expect(seerrApi.removeSeasonRequest).toHaveBeenCalledWith(
collectionMedia.tmdbId,
collectionMedia.mediaData.parentIndex,
);
expect(seerrApi.removeSeasonRequest).toHaveBeenCalledTimes(1);
// Removing one episode must not delete the whole season's request (which
// covers the still-present episodes); rely on Seerr's availability sync.
expect(seerrApi.removeSeasonRequest).not.toHaveBeenCalled();
expect(seerrApi.removeMediaByTmdbId).not.toHaveBeenCalled();
});

it('should not mutate Seerr requests for DELETE_SHOW_IF_EMPTY season actions', async () => {
Expand Down
22 changes: 9 additions & 13 deletions apps/server/src/modules/collections/collection-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,16 @@ export class CollectionHandler {
break;
}
case 'episode': {
const mediaDataEpisode = await mediaServer.getMetadata(
media.mediaServerId,
// Seerr tracks requests per season, not per episode, so there is
// no per-episode request to remove — deleting the season request
// would drop the request for every other (still-present) episode
// in that season. Skip the force-removal and let Seerr's
// availability sync reconcile, as it does when Force Seerr is off.
// The UI hides the toggle for episode rules; this also guards
// existing collections that still have it set.
this.logger.debug(
`[Seerr] Skipping request removal for episode-level collection '${collection.title}' (TMDB ID '${tmdbId}'): Seerr has no per-episode request granularity. Relying on availability sync.`,
);

if (mediaDataEpisode?.parentIndex !== undefined) {
await this.seerrApi.removeSeasonRequest(
tmdbId,
mediaDataEpisode.parentIndex,
);

this.logger.log(
`[Seerr] Removed request of season ${mediaDataEpisode.parentIndex} from show with TMDB ID '${tmdbId}'. Because episode ${mediaDataEpisode.index} was removed.`,
);
}
break;
}
default:
Expand Down
43 changes: 29 additions & 14 deletions apps/server/src/modules/rules/rules.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,19 @@ export class RulesService {
}
}

// Resolve the collection's media type: a movie library is always 'movie';
// a TV library uses the rule group's selected dataType (show/season/episode),
// defaulting to 'show'.
private resolveCollectionType(
libType: MediaItemType,
params: RulesDto,
): MediaItemType {
if (libType === 'movie') {
return 'movie';
}
return params.dataType !== undefined ? params.dataType : 'show';
}

async setRules(params: RulesDto) {
try {
let state: ReturnStatus = this.createReturnStatus(true, 'Success');
Expand Down Expand Up @@ -344,21 +357,22 @@ export class RulesService {
const lib = (await mediaServer.getLibraries()).find(
(el) => el.id === params.libraryId,
);
const collectionType = this.resolveCollectionType(lib.type, params);
const collection = (
await this.collectionService.createCollection({
libraryId: params.libraryId,
type:
lib.type === 'movie'
? 'movie'
: params.dataType !== undefined
? params.dataType
: 'show',
type: collectionType,
title: params.name,
description: params.description,
arrAction: params.arrAction ? params.arrAction : 0,
isActive: params.isActive,
listExclusions: params.listExclusions ? params.listExclusions : false,
forceSeerr: params.forceSeerr ? params.forceSeerr : false,
// Force Seerr is unsupported for episode rules (Seerr has no
// per-episode request granularity), so never persist it enabled. The
// UI hides the toggle; this also clears the flag on re-save for rules
// created before it was hidden.
forceSeerr:
collectionType !== 'episode' && params.forceSeerr ? true : false,
tautulliWatchedPercentOverride:
params.tautulliWatchedPercentOverride ?? null,
radarrSettingsId: params.radarrSettingsId ?? null,
Expand Down Expand Up @@ -518,20 +532,21 @@ export class RulesService {
(el) => el.id === params.libraryId,
);

const collectionType = this.resolveCollectionType(lib.type, params);
const collectionData = {
libraryId: params.libraryId,
type:
lib.type === 'movie'
? 'movie'
: params.dataType !== undefined
? params.dataType
: 'show',
type: collectionType,
title: params.name,
description: params.description,
arrAction: params.arrAction ? params.arrAction : 0,
isActive: params.isActive,
listExclusions: params.listExclusions ? params.listExclusions : false,
forceSeerr: params.forceSeerr ? params.forceSeerr : false,
// Force Seerr is unsupported for episode rules (Seerr has no
// per-episode request granularity), so never persist it enabled. The
// UI hides the toggle; this also clears the flag on re-save for rules
// created before it was hidden.
forceSeerr:
collectionType !== 'episode' && params.forceSeerr ? true : false,
tautulliWatchedPercentOverride:
params.tautulliWatchedPercentOverride ?? null,
radarrSettingsId: params.radarrSettingsId ?? null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1350,7 +1350,7 @@ const AddModal = (props: AddModal) => {
</div>
)}

{seerrEnabled && (
{seerrEnabled && selectedType !== 'episode' && (
<div className="flex flex-row items-center justify-between py-4">
<label htmlFor="force_seerr" className="text-label">
Force delete Seerr request
Expand Down