Skip to content

Commit

Permalink
feat: remove unmonitored Shows & new setting (Fallenbagel#695)
Browse files Browse the repository at this point in the history
  • Loading branch information
bonswouar committed Nov 10, 2024
1 parent 7e1bb7c commit 3e24708
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 2 deletions.
1 change: 1 addition & 0 deletions cypress/config/settings.cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"trustProxy": false,
"mediaServerType": 1,
"partialRequestsEnabled": true,
"removeUnmonitoredFromRequestsEnabled": false,
"locale": "en"
},
"plex": {
Expand Down
3 changes: 3 additions & 0 deletions overseerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ components:
partialRequestsEnabled:
type: boolean
example: false
removeUnmonitoredFromRequestsEnabled:
type: boolean
example: false
localLogin:
type: boolean
example: true
Expand Down
1 change: 1 addition & 0 deletions server/interfaces/api/settingsInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface PublicSettingsResponse {
originalLanguage: string;
mediaServerType: number;
partialRequestsEnabled: boolean;
removeUnmonitoredFromRequestsEnabled: boolean;
cacheImages: boolean;
vapidPublic: string;
enablePushRegistration: boolean;
Expand Down
7 changes: 6 additions & 1 deletion server/lib/scanners/baseScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ProcessableSeason {
episodes4k: number;
is4kOverride?: boolean;
processing?: boolean;
monitored?: boolean;
}

class BaseScanner<T> {
Expand Down Expand Up @@ -234,6 +235,7 @@ class BaseScanner<T> {
}: ProcessOptions = {}
): Promise<void> {
const mediaRepository = getRepository(Media);
const settings = getSettings();

await this.asyncLock.dispatch(tmdbId, async () => {
const media = await this.getExisting(tmdbId, MediaType.TV);
Expand Down Expand Up @@ -283,6 +285,8 @@ class BaseScanner<T> {
? MediaStatus.PARTIALLY_AVAILABLE
: !season.is4kOverride && season.processing
? MediaStatus.PROCESSING
: settings.main.removeUnmonitoredFromRequestsEnabled && !season.monitored && season.episodes == 0
? MediaStatus.UNKNOWN
: existingSeason.status;

// Same thing here, except we only do updates if 4k is enabled
Expand All @@ -296,6 +300,8 @@ class BaseScanner<T> {
? MediaStatus.PARTIALLY_AVAILABLE
: season.is4kOverride && season.processing
? MediaStatus.PROCESSING
: settings.main.removeUnmonitoredFromRequestsEnabled && !season.monitored && season.episodes4k == 0
? MediaStatus.UNKNOWN
: existingSeason.status4k;
} else {
newSeasons.push(
Expand Down Expand Up @@ -623,7 +629,6 @@ class BaseScanner<T> {
const mediaRepository = getRepository(Media);
await this.asyncLock.dispatch(tmdbId, async () => {
const existing = await this.getExisting(tmdbId, MediaType.MOVIE);
// For some reason the status of missing movies isn't PENDING but PROCESSING
if (existing && existing.status === MediaStatus.PROCESSING) {
existing.status = MediaStatus.UNKNOWN;
await mediaRepository.save(existing);
Expand Down
3 changes: 2 additions & 1 deletion server/lib/scanners/radarr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class RadarrScanner
}

private async processRadarrMovie(radarrMovie: RadarrMovie): Promise<void> {
if (!radarrMovie.monitored && !radarrMovie.hasFile) {
const settings = getSettings();
if (settings.main.removeUnmonitoredFromRequestsEnabled && !radarrMovie.monitored && !radarrMovie.hasFile) {
this.processUnmonitoredMovie(radarrMovie.tmdbId);
return;
}
Expand Down
33 changes: 33 additions & 0 deletions server/lib/scanners/sonarr/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { SonarrSeries } from '@server/api/servarr/sonarr';
import SonarrAPI from '@server/api/servarr/sonarr';
import type { TmdbTvDetails } from '@server/api/themoviedb/interfaces';
import { MediaType } from '@server/constants/media';
import { getRepository } from '@server/datasource';
import Media from '@server/entity/Media';
import { MediaRequest } from '@server/entity/MediaRequest';
import SeasonRequest from '@server/entity/SeasonRequest';
import type {
ProcessableSeason,
RunnableScanner,
Expand Down Expand Up @@ -85,6 +88,7 @@ class SonarrScanner
private async processSonarrSeries(sonarrSeries: SonarrSeries) {
try {
const mediaRepository = getRepository(Media);
const settings = getSettings();
const server4k = this.enable4kShow && this.currentServer.is4k;
const processableSeasons: ProcessableSeason[] = [];
let tvShow: TmdbTvDetails;
Expand Down Expand Up @@ -112,13 +116,42 @@ class SonarrScanner
for (const season of filteredSeasons) {
const totalAvailableEpisodes = season.statistics?.episodeFileCount ?? 0;

if (settings.main.removeUnmonitoredFromRequestsEnabled && season.monitored === false && totalAvailableEpisodes === 0) {
// Remove unmonitored seasons from Requests
const requestRepository = getRepository(MediaRequest);
const seasonRequestRepository = getRepository(SeasonRequest);

const existingRequests = await requestRepository
.createQueryBuilder('request')
.innerJoinAndSelect('request.media', 'media')
.innerJoinAndSelect('request.seasons', 'seasons')
.where('media.tmdbId = :tmdbId', { tmdbId: tmdbId })
.andWhere('media.mediaType = :mediaType', {
mediaType: MediaType.TV
})
.andWhere('seasons.seasonNumber = :seasonNumber', { seasonNumber: season.seasonNumber })
.getMany();

if (existingRequests && existingRequests.length > 0) {
existingRequests.forEach((existingRequest) => {
existingRequest.seasons.forEach(async (requestedSeason) => {
if (requestedSeason.seasonNumber === season.seasonNumber) {
this.log(`Removing request for Season ${season.seasonNumber} of ${sonarrSeries.title} as it is unmonitored`);
await seasonRequestRepository.remove(requestedSeason);
}
});
})
}
}

processableSeasons.push({
seasonNumber: season.seasonNumber,
episodes: !server4k ? totalAvailableEpisodes : 0,
episodes4k: server4k ? totalAvailableEpisodes : 0,
totalEpisodes: season.statistics?.totalEpisodeCount ?? 0,
processing: season.monitored && totalAvailableEpisodes === 0,
is4kOverride: server4k,
monitored: season.monitored,
});
}

Expand Down
4 changes: 4 additions & 0 deletions server/lib/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export interface MainSettings {
trustProxy: boolean;
mediaServerType: number;
partialRequestsEnabled: boolean;
removeUnmonitoredFromRequestsEnabled: boolean;
locale: string;
proxy: ProxySettings;
}
Expand All @@ -151,6 +152,7 @@ interface FullPublicSettings extends PublicSettings {
jellyfinForgotPasswordUrl?: string;
jellyfinServerName?: string;
partialRequestsEnabled: boolean;
removeUnmonitoredFromRequestsEnabled: boolean;
cacheImages: boolean;
vapidPublic: string;
enablePushRegistration: boolean;
Expand Down Expand Up @@ -336,6 +338,7 @@ class Settings {
trustProxy: false,
mediaServerType: MediaServerType.NOT_CONFIGURED,
partialRequestsEnabled: true,
removeUnmonitoredFromRequestsEnabled: false,
locale: 'en',
proxy: {
enabled: false,
Expand Down Expand Up @@ -574,6 +577,7 @@ class Settings {
originalLanguage: this.data.main.originalLanguage,
mediaServerType: this.main.mediaServerType,
partialRequestsEnabled: this.data.main.partialRequestsEnabled,
removeUnmonitoredFromRequestsEnabled: this.data.main.removeUnmonitoredFromRequestsEnabled,
cacheImages: this.data.main.cacheImages,
vapidPublic: this.vapidPublic,
enablePushRegistration: this.data.notifications.agents.webpush.enabled,
Expand Down
26 changes: 26 additions & 0 deletions src/components/Settings/SettingsMain/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const messages = defineMessages('components.Settings.SettingsMain', {
validationApplicationUrl: 'You must provide a valid URL',
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
partialRequestsEnabled: 'Allow Partial Series Requests',
removeUnmonitoredFromRequestsEnabled: 'Remove Request for Movies/Seasons that have been un-monitored since',
locale: 'Display Language',
proxyEnabled: 'HTTP(S) Proxy',
proxyHostname: 'Proxy Hostname',
Expand Down Expand Up @@ -152,6 +153,7 @@ const SettingsMain = () => {
region: data?.region,
originalLanguage: data?.originalLanguage,
partialRequestsEnabled: data?.partialRequestsEnabled,
removeUnmonitoredFromRequestsEnabled: data?.removeUnmonitoredFromRequestsEnabled,
trustProxy: data?.trustProxy,
cacheImages: data?.cacheImages,
proxyEnabled: data?.proxy?.enabled,
Expand Down Expand Up @@ -181,6 +183,7 @@ const SettingsMain = () => {
region: values.region,
originalLanguage: values.originalLanguage,
partialRequestsEnabled: values.partialRequestsEnabled,
removeUnmonitoredFromRequestsEnabled: values.removeUnmonitoredFromRequestsEnabled,
trustProxy: values.trustProxy,
cacheImages: values.cacheImages,
proxy: {
Expand Down Expand Up @@ -472,6 +475,29 @@ const SettingsMain = () => {
/>
</div>
</div>
<div className="form-row">
<label
htmlFor="removeUnmonitoredFromRequestsEnabled"
className="checkbox-label"
>
<span className="mr-2">
{intl.formatMessage(messages.removeUnmonitoredFromRequestsEnabled)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="removeUnmonitoredFromRequestsEnabled"
name="removeUnmonitoredFromRequestsEnabled"
onChange={() => {
setFieldValue(
'removeUnmonitoredFromRequestsEnabled',
!values.removeUnmonitoredFromRequestsEnabled
);
}}
/>
</div>
</div>
<div className="form-row">
<label htmlFor="proxyEnabled" className="checkbox-label">
<span className="mr-2">
Expand Down
1 change: 1 addition & 0 deletions src/context/SettingsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const defaultSettings = {
originalLanguage: '',
mediaServerType: MediaServerType.NOT_CONFIGURED,
partialRequestsEnabled: true,
removeUnmonitoredFromRequestsEnabled: false,
cacheImages: false,
vapidPublic: '',
enablePushRegistration: false,
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,7 @@
"components.Settings.SettingsMain.originallanguage": "Discover Language",
"components.Settings.SettingsMain.originallanguageTip": "Filter content by original language",
"components.Settings.SettingsMain.partialRequestsEnabled": "Allow Partial Series Requests",
"components.Settings.SettingsMain.removeUnmonitoredFromRequestsEnabled": "Remove Request for Movies/Seasons that have been un-monitored since",
"components.Settings.SettingsMain.region": "Discover Region",
"components.Settings.SettingsMain.regionTip": "Filter content by regional availability",
"components.Settings.SettingsMain.toastApiKeyFailure": "Something went wrong while generating a new API key.",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locale/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,7 @@
"components.Settings.SettingsMain.trustProxy": "Activer la prise en charge proxy",
"components.Settings.SettingsMain.trustProxyTip": "Permettre Jellyseerr à enregistrer correctement les adresses IP des clients derrière un proxy",
"components.Settings.SettingsMain.partialRequestsEnabled": "Permettre les demandes partielles des séries",
"components.Settings.SettingsMain.removeUnmonitoredFromRequestsEnabled": "Supprimer les requests de Films/Séries qui ne sont plus suivis",
"components.Selector.nooptions": "Aucun résultat.",
"components.Layout.Sidebar.browsetv": "Séries",
"components.Selector.showmore": "En voir plus",
Expand Down
1 change: 1 addition & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ CoreApp.getInitialProps = async (initialProps) => {
originalLanguage: '',
mediaServerType: MediaServerType.NOT_CONFIGURED,
partialRequestsEnabled: true,
removeUnmonitoredFromRequestsEnabled: false,
cacheImages: false,
vapidPublic: '',
enablePushRegistration: false,
Expand Down

0 comments on commit 3e24708

Please sign in to comment.