diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json index 8ba919357..3dc7a97fd 100644 --- a/cypress/config/settings.cypress.json +++ b/cypress/config/settings.cypress.json @@ -21,7 +21,7 @@ "trustProxy": false, "mediaServerType": 1, "partialRequestsEnabled": true, - "removeUnmonitoredFromRequestsEnabled": false, + "removeUnmonitoredEnabled": false, "locale": "en" }, "plex": { diff --git a/overseerr-api.yml b/overseerr-api.yml index 970a9528f..b7fc0ff9a 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -174,7 +174,7 @@ components: partialRequestsEnabled: type: boolean example: false - removeUnmonitoredFromRequestsEnabled: + removeUnmonitoredEnabled: type: boolean example: false localLogin: diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index 878e4c6ac..362a9e3c3 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -36,7 +36,7 @@ export interface PublicSettingsResponse { originalLanguage: string; mediaServerType: number; partialRequestsEnabled: boolean; - removeUnmonitoredFromRequestsEnabled: boolean; + removeUnmonitoredEnabled: boolean; cacheImages: boolean; vapidPublic: string; enablePushRegistration: boolean; diff --git a/server/lib/scanners/baseScanner.ts b/server/lib/scanners/baseScanner.ts index 305457058..4d1a0f88f 100644 --- a/server/lib/scanners/baseScanner.ts +++ b/server/lib/scanners/baseScanner.ts @@ -1,8 +1,11 @@ +import type { SonarrSeason } from '@server/api/servarr/sonarr'; import TheMovieDb from '@server/api/themoviedb'; import { MediaStatus, MediaType } from '@server/constants/media'; import { getRepository } from '@server/datasource'; import Media from '@server/entity/Media'; +import { MediaRequest } from '@server/entity/MediaRequest'; import Season from '@server/entity/Season'; +import SeasonRequest from '@server/entity/SeasonRequest'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import AsyncLock from '@server/utils/asyncLock'; @@ -212,7 +215,7 @@ class BaseScanner { /** * processShow takes a TMDB ID and an array of ProcessableSeasons, which - * should include the total episodes a sesaon has + the total available + * should include the total episodes a season has + the total available * episodes that each season currently has. Unlike processMovie, this method * does not take an `is4k` option. We handle both the 4k _and_ non 4k status * in one method. @@ -279,13 +282,14 @@ class BaseScanner { // force it to stay available (to avoid competing scanners) existingSeason.status = (season.totalEpisodes === season.episodes && season.episodes > 0) || - existingSeason.status === MediaStatus.AVAILABLE + (existingSeason.status === MediaStatus.AVAILABLE && + season.episodes > 0) ? MediaStatus.AVAILABLE : season.episodes > 0 ? MediaStatus.PARTIALLY_AVAILABLE : !season.is4kOverride && season.processing ? MediaStatus.PROCESSING - : settings.main.removeUnmonitoredFromRequestsEnabled && + : settings.main.removeUnmonitoredEnabled && !season.monitored && season.episodes == 0 ? MediaStatus.UNKNOWN @@ -296,13 +300,14 @@ class BaseScanner { (this.enable4kShow && season.episodes4k === season.totalEpisodes && season.episodes4k > 0) || - existingSeason.status4k === MediaStatus.AVAILABLE + (existingSeason.status4k === MediaStatus.AVAILABLE && + season.episodes > 0) ? MediaStatus.AVAILABLE : this.enable4kShow && season.episodes4k > 0 ? MediaStatus.PARTIALLY_AVAILABLE : season.is4kOverride && season.processing ? MediaStatus.PROCESSING - : settings.main.removeUnmonitoredFromRequestsEnabled && + : settings.main.removeUnmonitoredEnabled && !season.monitored && season.episodes4k == 0 ? MediaStatus.UNKNOWN @@ -316,7 +321,9 @@ class BaseScanner { ? MediaStatus.AVAILABLE : season.episodes > 0 ? MediaStatus.PARTIALLY_AVAILABLE - : !season.is4kOverride && season.processing + : !season.is4kOverride && + season.processing && + season.monitored ? MediaStatus.PROCESSING : MediaStatus.UNKNOWN, status4k: @@ -326,7 +333,7 @@ class BaseScanner { ? MediaStatus.AVAILABLE : this.enable4kShow && season.episodes4k > 0 ? MediaStatus.PARTIALLY_AVAILABLE - : season.is4kOverride && season.processing + : season.is4kOverride && season.processing && season.monitored ? MediaStatus.PROCESSING : MediaStatus.UNKNOWN, }) @@ -643,6 +650,41 @@ class BaseScanner { } }); } + + protected async processUnmonitoredSeason( + tmdbId: number, + season: SonarrSeason + ): Promise { + // 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 tmdbId ${tmdbId} as it is unmonitored` + ); + await seasonRequestRepository.remove(requestedSeason); + } + }); + }); + } + } } export default BaseScanner; diff --git a/server/lib/scanners/radarr/index.ts b/server/lib/scanners/radarr/index.ts index 69bc5b1d9..465204590 100644 --- a/server/lib/scanners/radarr/index.ts +++ b/server/lib/scanners/radarr/index.ts @@ -81,7 +81,7 @@ class RadarrScanner private async processRadarrMovie(radarrMovie: RadarrMovie): Promise { const settings = getSettings(); if ( - settings.main.removeUnmonitoredFromRequestsEnabled && + settings.main.removeUnmonitoredEnabled && !radarrMovie.monitored && !radarrMovie.hasFile ) { diff --git a/server/lib/scanners/sonarr/index.ts b/server/lib/scanners/sonarr/index.ts index b79df98e8..8556469e0 100644 --- a/server/lib/scanners/sonarr/index.ts +++ b/server/lib/scanners/sonarr/index.ts @@ -1,11 +1,8 @@ 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, @@ -117,39 +114,11 @@ class SonarrScanner const totalAvailableEpisodes = season.statistics?.episodeFileCount ?? 0; if ( - settings.main.removeUnmonitoredFromRequestsEnabled && + settings.main.removeUnmonitoredEnabled && 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); - } - }); - }); - } + this.processUnmonitoredSeason(tmdbId, season); } processableSeasons.push({ diff --git a/server/lib/settings/index.ts b/server/lib/settings/index.ts index 218863626..38d3e5173 100644 --- a/server/lib/settings/index.ts +++ b/server/lib/settings/index.ts @@ -129,7 +129,7 @@ export interface MainSettings { trustProxy: boolean; mediaServerType: number; partialRequestsEnabled: boolean; - removeUnmonitoredFromRequestsEnabled: boolean; + removeUnmonitoredEnabled: boolean; locale: string; proxy: ProxySettings; } @@ -152,7 +152,7 @@ interface FullPublicSettings extends PublicSettings { jellyfinForgotPasswordUrl?: string; jellyfinServerName?: string; partialRequestsEnabled: boolean; - removeUnmonitoredFromRequestsEnabled: boolean; + removeUnmonitoredEnabled: boolean; cacheImages: boolean; vapidPublic: string; enablePushRegistration: boolean; @@ -338,7 +338,7 @@ class Settings { trustProxy: false, mediaServerType: MediaServerType.NOT_CONFIGURED, partialRequestsEnabled: true, - removeUnmonitoredFromRequestsEnabled: false, + removeUnmonitoredEnabled: false, locale: 'en', proxy: { enabled: false, @@ -577,8 +577,7 @@ class Settings { originalLanguage: this.data.main.originalLanguage, mediaServerType: this.main.mediaServerType, partialRequestsEnabled: this.data.main.partialRequestsEnabled, - removeUnmonitoredFromRequestsEnabled: - this.data.main.removeUnmonitoredFromRequestsEnabled, + removeUnmonitoredEnabled: this.data.main.removeUnmonitoredEnabled, cacheImages: this.data.main.cacheImages, vapidPublic: this.vapidPublic, enablePushRegistration: this.data.notifications.agents.webpush.enabled, diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx index 2ed48a36d..b5a8431fe 100644 --- a/src/components/Settings/SettingsMain/index.tsx +++ b/src/components/Settings/SettingsMain/index.tsx @@ -54,8 +54,9 @@ 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', + removeUnmonitoredEnabled: 'Remove Unmonitored Media', + removeUnmonitoredExplanation: + 'Remove Movies/Seasons from Jellyseerr that are not available and have been un-monitored since', locale: 'Display Language', proxyEnabled: 'HTTP(S) Proxy', proxyHostname: 'Proxy Hostname', @@ -154,8 +155,7 @@ const SettingsMain = () => { region: data?.region, originalLanguage: data?.originalLanguage, partialRequestsEnabled: data?.partialRequestsEnabled, - removeUnmonitoredFromRequestsEnabled: - data?.removeUnmonitoredFromRequestsEnabled, + removeUnmonitoredEnabled: data?.removeUnmonitoredEnabled, trustProxy: data?.trustProxy, cacheImages: data?.cacheImages, proxyEnabled: data?.proxy?.enabled, @@ -185,8 +185,7 @@ const SettingsMain = () => { region: values.region, originalLanguage: values.originalLanguage, partialRequestsEnabled: values.partialRequestsEnabled, - removeUnmonitoredFromRequestsEnabled: - values.removeUnmonitoredFromRequestsEnabled, + removeUnmonitoredEnabled: values.removeUnmonitoredEnabled, trustProxy: values.trustProxy, cacheImages: values.cacheImages, proxy: { @@ -480,24 +479,28 @@ const SettingsMain = () => {
{ setFieldValue( - 'removeUnmonitoredFromRequestsEnabled', - !values.removeUnmonitoredFromRequestsEnabled + 'removeUnmonitoredEnabled', + !values.removeUnmonitoredEnabled ); }} /> diff --git a/src/context/SettingsContext.tsx b/src/context/SettingsContext.tsx index 98262be63..ea00a5b4c 100644 --- a/src/context/SettingsContext.tsx +++ b/src/context/SettingsContext.tsx @@ -20,7 +20,7 @@ const defaultSettings = { originalLanguage: '', mediaServerType: MediaServerType.NOT_CONFIGURED, partialRequestsEnabled: true, - removeUnmonitoredFromRequestsEnabled: false, + removeUnmonitoredEnabled: false, cacheImages: false, vapidPublic: '', enablePushRegistration: false, diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 972f98c82..1642872b3 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -885,7 +885,6 @@ "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.", diff --git a/src/i18n/locale/fr.json b/src/i18n/locale/fr.json index e9d3e7161..5fa1a0f56 100644 --- a/src/i18n/locale/fr.json +++ b/src/i18n/locale/fr.json @@ -1240,7 +1240,6 @@ "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", diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 44d65b721..ae2925a21 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -196,7 +196,7 @@ CoreApp.getInitialProps = async (initialProps) => { originalLanguage: '', mediaServerType: MediaServerType.NOT_CONFIGURED, partialRequestsEnabled: true, - removeUnmonitoredFromRequestsEnabled: false, + removeUnmonitoredEnabled: false, cacheImages: false, vapidPublic: '', enablePushRegistration: false,