diff --git a/code/frontend/src/app/core/feature-badges/feature-registry.ts b/code/frontend/src/app/core/feature-badges/feature-registry.ts index c8015252..463408a1 100644 --- a/code/frontend/src/app/core/feature-badges/feature-registry.ts +++ b/code/frontend/src/app/core/feature-badges/feature-registry.ts @@ -54,4 +54,5 @@ export const NEW_FEATURES: NewFeature[] = [ { id: 'theme-follow-system' }, { id: 'errored-downloads' }, { id: 'seeker-include-tags' }, + { id: 'seeker-recent-grab-window' }, ]; diff --git a/code/frontend/src/app/core/services/documentation.service.ts b/code/frontend/src/app/core/services/documentation.service.ts index 53d3445d..e6b66253 100644 --- a/code/frontend/src/app/core/services/documentation.service.ts +++ b/code/frontend/src/app/core/services/documentation.service.ts @@ -243,6 +243,7 @@ export class DocumentationService { 'skipTags': 'instance-skip-tags', 'activeDownloadLimit': 'instance-active-download-limit', 'ignoreStruckDownloads': 'instance-ignore-struck-downloads', + 'recentGrabWindowHours': 'instance-recent-grab-window', 'minCycleTimeDays': 'instance-min-cycle-time-days', }, }; diff --git a/code/frontend/src/app/features/settings/seeker/seeker.component.html b/code/frontend/src/app/features/settings/seeker/seeker.component.html index 0075a5c9..979123b9 100644 --- a/code/frontend/src/app/features/settings/seeker/seeker.component.html +++ b/code/frontend/src/app/features/settings/seeker/seeker.component.html @@ -134,6 +134,17 @@ (checkedChange)="patchInstance($index, { ignoreStruckDownloads: $event })" helpKey="seeker:ignoreStruckDownloads" featureId="seeker-ignore-struck-downloads" /> + { const { fixture, component } = setup(); setNumberField(fixture, 'Active Download Limit', '9'); + setNumberField(fixture, 'Recent Grab Window', '48'); setNumberField(fixture, 'Min Cycle Time', '21'); expect(component.instances()[0].activeDownloadLimit).toBe(9); + expect(component.instances()[0].recentGrabWindowHours).toBe(48); expect(component.instances()[0].minCycleTimeDays).toBe(21); }); @@ -387,9 +391,11 @@ describe('SeekerComponent', () => { const { fixture, component } = setup(); setNumberField(fixture, 'Active Download Limit', ''); + setNumberField(fixture, 'Recent Grab Window', ''); setNumberField(fixture, 'Min Cycle Time', ''); expect(component.instances()[0].activeDownloadLimit).toBe(3); + expect(component.instances()[0].recentGrabWindowHours).toBe(24); expect(component.instances()[0].minCycleTimeDays).toBe(7); }); @@ -487,7 +493,7 @@ describe('SeekerComponent', () => { component.seekerForm.searchInterval().value.set(10); component.seekerForm.selectionStrategy().value.set(SelectionStrategy.NewestFirst); component.seekerForm.postReleaseGraceHours().value.set(24); - component.patchInstance(0, { skipTags: ['gamma'], includeTags: ['anime'], ignoreStruckDownloads: true }); + component.patchInstance(0, { skipTags: ['gamma'], includeTags: ['anime'], ignoreStruckDownloads: true, recentGrabWindowHours: 48 }); fixture.detectChanges(); expect(component.dirty()).toBe(true); @@ -510,6 +516,7 @@ describe('SeekerComponent', () => { includeTags: ['anime'], activeDownloadLimit: 3, ignoreStruckDownloads: true, + recentGrabWindowHours: 48, minCycleTimeDays: 7, monitoredOnly: true, useCutoff: false, @@ -522,6 +529,7 @@ describe('SeekerComponent', () => { includeTags: [], activeDownloadLimit: 5, ignoreStruckDownloads: true, + recentGrabWindowHours: 48, minCycleTimeDays: 14, monitoredOnly: false, useCutoff: true, diff --git a/code/frontend/src/app/features/settings/seeker/seeker.component.ts b/code/frontend/src/app/features/settings/seeker/seeker.component.ts index 8ef9e233..7c97b2b8 100644 --- a/code/frontend/src/app/features/settings/seeker/seeker.component.ts +++ b/code/frontend/src/app/features/settings/seeker/seeker.component.ts @@ -75,6 +75,7 @@ interface InstanceState { arrInstanceEnabled: boolean; activeDownloadLimit: number; ignoreStruckDownloads: boolean; + recentGrabWindowHours: number; minCycleTimeDays: number; monitoredOnly: boolean; useCutoff: boolean; @@ -173,6 +174,7 @@ export class SeekerComponent implements HasPendingChanges { arrInstanceEnabled: i.arrInstanceEnabled, activeDownloadLimit: i.activeDownloadLimit, ignoreStruckDownloads: i.ignoreStruckDownloads, + recentGrabWindowHours: i.recentGrabWindowHours, minCycleTimeDays: i.minCycleTimeDays, monitoredOnly: i.monitoredOnly, useCutoff: i.useCutoff, @@ -219,6 +221,10 @@ export class SeekerComponent implements HasPendingChanges { this.patchInstance(index, { minCycleTimeDays: days ?? 7 }); } + updateInstanceRecentGrabWindowHours(index: number, hours: number | null): void { + this.patchInstance(index, { recentGrabWindowHours: hours ?? 24 }); + } + getInstanceIcon(instanceType: string): string { return `icons/ext/${instanceType.toLowerCase()}-light.svg`; } @@ -241,6 +247,7 @@ export class SeekerComponent implements HasPendingChanges { includeTags: i.includeTags, activeDownloadLimit: i.activeDownloadLimit, ignoreStruckDownloads: i.ignoreStruckDownloads, + recentGrabWindowHours: i.recentGrabWindowHours, minCycleTimeDays: i.minCycleTimeDays, monitoredOnly: i.monitoredOnly, useCutoff: i.useCutoff, diff --git a/code/frontend/src/app/shared/models/seeker-config.model.ts b/code/frontend/src/app/shared/models/seeker-config.model.ts index fc74eebf..7969258b 100644 --- a/code/frontend/src/app/shared/models/seeker-config.model.ts +++ b/code/frontend/src/app/shared/models/seeker-config.model.ts @@ -21,6 +21,7 @@ export interface SeekerInstanceConfig { arrInstanceEnabled: boolean; activeDownloadLimit: number; ignoreStruckDownloads: boolean; + recentGrabWindowHours: number; minCycleTimeDays: number; monitoredOnly: boolean; useCutoff: boolean; @@ -44,6 +45,7 @@ export interface UpdateSeekerInstanceConfig { includeTags: string[]; activeDownloadLimit: number; ignoreStruckDownloads: boolean; + recentGrabWindowHours: number; minCycleTimeDays: number; monitoredOnly: boolean; useCutoff: boolean; diff --git a/docs/docs/configuration/seeker/index.mdx b/docs/docs/configuration/seeker/index.mdx index daf83e4a..cb002c4b 100644 --- a/docs/docs/configuration/seeker/index.mdx +++ b/docs/docs/configuration/seeker/index.mdx @@ -249,6 +249,20 @@ Only downloads that have received strikes in the most recent completed `Queue Cl + + +Seeker reads the arr's grab history for this many hours and skips every item grabbed in that window, even when the download already left the queue (imported, failed and removed, grabbed by RSS or another tool). + +For Sonarr, a grabbed episode skips its whole season for the window. + +Set to `0` to turn this off. + +Default is `24`, maximum is `168`. + + +