mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-10-28 10:48:49 -04:00
added release gate setting to seeker ui
This commit is contained in:
1 parent
469467249a
commit
abff028ba1
9 files changed
+81
-3
No files matched your search
@@ -66,4 +66,5 @@ export const NEW_FEATURES: NewFeature[] = [
|
||||
{ id: 'seeker-cf-gap-strategy' },
|
||||
{ id: 'seeker-unit-rest' },
|
||||
{ id: 'seeker-quiet-hours' },
|
||||
{ id: 'seeker-radarr-release-gate' },
|
||||
];
|
||||
@@ -87,6 +87,7 @@ export interface SearchEvent {
|
||||
export enum SeekerExclusionReason {
|
||||
NotReleased = 'NotReleased',
|
||||
InGracePeriod = 'InGracePeriod',
|
||||
NotAvailable = 'NotAvailable',
|
||||
NotMonitored = 'NotMonitored',
|
||||
SkipTag = 'SkipTag',
|
||||
MissingIncludeTag = 'MissingIncludeTag',
|
||||
|
||||
@@ -252,6 +252,7 @@ export class DocumentationService {
|
||||
'searchSpecials': 'instance-search-for-specials',
|
||||
'searchAnime': 'instance-search-for-anime',
|
||||
'sonarrSearchMode': 'instance-search-mode',
|
||||
'radarrReleaseGate': 'instance-release-gate',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ export function funnelStages(funnel: InstanceFunnel, instanceType: string): { na
|
||||
export const EXCLUSION_REASON_LABELS: Record<SeekerExclusionReason, string> = {
|
||||
[SeekerExclusionReason.NotReleased]: 'Not released',
|
||||
[SeekerExclusionReason.InGracePeriod]: 'In grace period',
|
||||
[SeekerExclusionReason.NotAvailable]: 'Not available in Radarr',
|
||||
[SeekerExclusionReason.NotMonitored]: 'Not monitored',
|
||||
[SeekerExclusionReason.SkipTag]: 'Skip tag',
|
||||
[SeekerExclusionReason.MissingIncludeTag]: 'No include tag',
|
||||
|
||||
@@ -130,6 +130,13 @@
|
||||
helpKey="seeker:searchAnime"
|
||||
featureId="seeker-search-anime" />
|
||||
}
|
||||
@if (instance.instanceType === 'Radarr') {
|
||||
<app-select label="Release Gate" [options]="radarrReleaseGateOptions" [value]="instance.radarrReleaseGate"
|
||||
(valueChange)="updateInstanceRadarrReleaseGate($index, $event)"
|
||||
hint="Release dates searches a movie once Radarr marks it released and its digital, physical or cinema date is past the grace period. Follow Radarr availability uses each movie's minimum availability and Radarr's availability delay; the grace period does not apply."
|
||||
helpKey="seeker:radarrReleaseGate"
|
||||
featureId="seeker-radarr-release-gate" />
|
||||
}
|
||||
<app-chip-input
|
||||
label="Include Tags"
|
||||
placeholder="Add tag..."
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ConfirmService } from '@core/services/confirm.service';
|
||||
import { ToastService } from '@core/services/toast.service';
|
||||
import { ApiError } from '@core/interceptors/error.interceptor';
|
||||
import { SeekerConfig, SeekerInstanceConfig, UpdateSeekerConfig } from '@shared/models/seeker-config.model';
|
||||
import { SelectionStrategy, SonarrSearchMode } from '@shared/models/enums';
|
||||
import { RadarrReleaseGate, SelectionStrategy, SonarrSearchMode } from '@shared/models/enums';
|
||||
import { SeekerComponent } from './seeker.component';
|
||||
|
||||
const SONARR: SeekerInstanceConfig = {
|
||||
@@ -26,6 +26,7 @@ const SONARR: SeekerInstanceConfig = {
|
||||
useCutoff: false,
|
||||
useCustomFormatScore: false,
|
||||
sonarrSearchMode: SonarrSearchMode.Auto,
|
||||
radarrReleaseGate: RadarrReleaseGate.ReleaseDate,
|
||||
};
|
||||
|
||||
const RADARR: SeekerInstanceConfig = {
|
||||
@@ -47,6 +48,7 @@ const RADARR: SeekerInstanceConfig = {
|
||||
useCutoff: true,
|
||||
useCustomFormatScore: true,
|
||||
sonarrSearchMode: SonarrSearchMode.Auto,
|
||||
radarrReleaseGate: RadarrReleaseGate.ReleaseDate,
|
||||
};
|
||||
|
||||
const CONFIG: SeekerConfig = {
|
||||
@@ -142,6 +144,13 @@ describe('SeekerComponent', () => {
|
||||
) as HTMLElement | undefined;
|
||||
}
|
||||
|
||||
function releaseGateSelect(fixture: ComponentFixture<SeekerComponent>, row: number): HTMLElement | undefined {
|
||||
const details = fixture.nativeElement.querySelectorAll('.instance-row__details')[row] as HTMLElement;
|
||||
return Array.from(details.querySelectorAll('app-select')).find(
|
||||
(el) => el.textContent!.includes('Release Gate'),
|
||||
) as HTMLElement | undefined;
|
||||
}
|
||||
|
||||
function text(fixture: ComponentFixture<SeekerComponent>): string {
|
||||
return fixture.nativeElement.textContent as string;
|
||||
}
|
||||
@@ -514,6 +523,42 @@ describe('SeekerComponent', () => {
|
||||
expect(sent.instances[1].sonarrSearchMode).toBe(SonarrSearchMode.Auto);
|
||||
});
|
||||
|
||||
it('shows the release gate select for Radarr only', () => {
|
||||
const { fixture } = setup();
|
||||
|
||||
expect(releaseGateSelect(fixture, 0)).toBeUndefined();
|
||||
|
||||
const radarrToggle = fixture.nativeElement.querySelectorAll('.instance-row__header [role="switch"]')[1] as HTMLButtonElement;
|
||||
radarrToggle.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(releaseGateSelect(fixture, 1)).toBeDefined();
|
||||
});
|
||||
|
||||
it('sends the picked release gate for the Radarr row only', () => {
|
||||
const { fixture, updateConfig } = setup();
|
||||
|
||||
const radarrToggle = fixture.nativeElement.querySelectorAll('.instance-row__header [role="switch"]')[1] as HTMLButtonElement;
|
||||
radarrToggle.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const select = releaseGateSelect(fixture, 1)!;
|
||||
(select.querySelector('.select-trigger') as HTMLButtonElement).click();
|
||||
fixture.detectChanges();
|
||||
const option = Array.from(select.querySelectorAll('.select-option')).find(
|
||||
(el) => el.textContent!.trim() === 'Follow Radarr availability',
|
||||
) as HTMLButtonElement;
|
||||
option.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
saveButton(fixture).click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const sent = updateConfig.mock.calls[0][0] as UpdateSeekerConfig;
|
||||
expect(sent.instances[0].radarrReleaseGate).toBe(RadarrReleaseGate.ReleaseDate);
|
||||
expect(sent.instances[1].radarrReleaseGate).toBe(RadarrReleaseGate.ArrAvailability);
|
||||
});
|
||||
|
||||
it('patches the instance limits typed into the numeric inputs', () => {
|
||||
const { fixture, component } = setup();
|
||||
|
||||
@@ -763,6 +808,7 @@ describe('SeekerComponent', () => {
|
||||
useCutoff: false,
|
||||
useCustomFormatScore: false,
|
||||
sonarrSearchMode: SonarrSearchMode.Auto,
|
||||
radarrReleaseGate: RadarrReleaseGate.ReleaseDate,
|
||||
},
|
||||
{
|
||||
arrInstanceId: RADARR.arrInstanceId,
|
||||
@@ -779,6 +825,7 @@ describe('SeekerComponent', () => {
|
||||
useCutoff: true,
|
||||
useCustomFormatScore: true,
|
||||
sonarrSearchMode: SonarrSearchMode.Auto,
|
||||
radarrReleaseGate: RadarrReleaseGate.ReleaseDate,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ import { HasPendingChanges } from '@core/guards/pending-changes.guard';
|
||||
import { ApiError } from '@core/interceptors/error.interceptor';
|
||||
import { createSettingsResource } from '@shared/utils/settings-resource.util';
|
||||
import { createDirtyTracker, SAVED_FLASH_MS } from '@shared/utils/dirty-tracker.util';
|
||||
import { SelectionStrategy, SonarrSearchMode } from '@shared/models/enums';
|
||||
import { RadarrReleaseGate, SelectionStrategy, SonarrSearchMode } from '@shared/models/enums';
|
||||
import { instanceTypeSeverity } from '@shared/utils/instance-display.util';
|
||||
|
||||
const INTERVAL_OPTIONS: SelectOption[] = [
|
||||
@@ -53,6 +53,11 @@ const SONARR_SEARCH_MODE_OPTIONS: SelectOption[] = [
|
||||
{ label: 'Episode', value: SonarrSearchMode.Episode },
|
||||
];
|
||||
|
||||
const RADARR_RELEASE_GATE_OPTIONS: SelectOption[] = [
|
||||
{ label: 'Release dates', value: RadarrReleaseGate.ReleaseDate },
|
||||
{ label: 'Follow Radarr availability', value: RadarrReleaseGate.ArrAvailability },
|
||||
];
|
||||
|
||||
const STRATEGY_DESCRIPTIONS: Record<SelectionStrategy, string> = {
|
||||
[SelectionStrategy.BalancedWeighted]: 'Prioritizes items that are both newly added and haven\'t been searched recently. Good default for most libraries.',
|
||||
[SelectionStrategy.OldestSearchFirst]: 'Picks the items Sonarr or Radarr searched longest ago, never-searched items first. Manual searches and other tools count too.',
|
||||
@@ -94,6 +99,7 @@ interface InstanceState {
|
||||
useCutoff: boolean;
|
||||
useCustomFormatScore: boolean;
|
||||
sonarrSearchMode: SonarrSearchMode;
|
||||
radarrReleaseGate: RadarrReleaseGate;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -122,6 +128,7 @@ export class SeekerComponent implements HasPendingChanges {
|
||||
readonly intervalOptions = INTERVAL_OPTIONS;
|
||||
readonly strategyOptions = STRATEGY_OPTIONS;
|
||||
readonly sonarrSearchModeOptions = SONARR_SEARCH_MODE_OPTIONS;
|
||||
readonly radarrReleaseGateOptions = RADARR_RELEASE_GATE_OPTIONS;
|
||||
readonly loader = this.settings.loader;
|
||||
readonly loadError = this.settings.loadError;
|
||||
readonly saving = signal(false);
|
||||
@@ -220,6 +227,7 @@ export class SeekerComponent implements HasPendingChanges {
|
||||
useCutoff: i.useCutoff,
|
||||
useCustomFormatScore: i.useCustomFormatScore,
|
||||
sonarrSearchMode: i.sonarrSearchMode,
|
||||
radarrReleaseGate: i.radarrReleaseGate,
|
||||
})));
|
||||
this.dirtyTracker.markSaved();
|
||||
});
|
||||
@@ -278,6 +286,10 @@ export class SeekerComponent implements HasPendingChanges {
|
||||
this.patchInstance(index, { sonarrSearchMode: mode as SonarrSearchMode });
|
||||
}
|
||||
|
||||
updateInstanceRadarrReleaseGate(index: number, gate: unknown): void {
|
||||
this.patchInstance(index, { radarrReleaseGate: gate as RadarrReleaseGate });
|
||||
}
|
||||
|
||||
getInstanceIcon(instanceType: string): string {
|
||||
return `icons/ext/${instanceType.toLowerCase()}-light.svg`;
|
||||
}
|
||||
@@ -311,6 +323,7 @@ export class SeekerComponent implements HasPendingChanges {
|
||||
useCutoff: i.useCutoff,
|
||||
useCustomFormatScore: i.useCustomFormatScore,
|
||||
sonarrSearchMode: i.sonarrSearchMode,
|
||||
radarrReleaseGate: i.radarrReleaseGate,
|
||||
})),
|
||||
};
|
||||
|
||||
|
||||
@@ -128,6 +128,11 @@ export enum SonarrSearchMode {
|
||||
Episode = 'Episode',
|
||||
}
|
||||
|
||||
export enum RadarrReleaseGate {
|
||||
ReleaseDate = 'ReleaseDate',
|
||||
ArrAvailability = 'ArrAvailability',
|
||||
}
|
||||
|
||||
export enum SearchCommandStatus {
|
||||
Pending = 'Pending',
|
||||
Started = 'Started',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SelectionStrategy, SonarrSearchMode } from './enums';
|
||||
import { RadarrReleaseGate, SelectionStrategy, SonarrSearchMode } from './enums';
|
||||
|
||||
export interface SeekerConfig {
|
||||
searchEnabled: boolean;
|
||||
@@ -32,6 +32,7 @@ export interface SeekerInstanceConfig {
|
||||
useCutoff: boolean;
|
||||
useCustomFormatScore: boolean;
|
||||
sonarrSearchMode: SonarrSearchMode;
|
||||
radarrReleaseGate: RadarrReleaseGate;
|
||||
}
|
||||
|
||||
export interface UpdateSeekerConfig {
|
||||
@@ -62,4 +63,5 @@ export interface UpdateSeekerInstanceConfig {
|
||||
useCutoff: boolean;
|
||||
useCustomFormatScore: boolean;
|
||||
sonarrSearchMode: SonarrSearchMode;
|
||||
radarrReleaseGate: RadarrReleaseGate;
|
||||
}
|
||||
Reference in new issue
Block a user