diff --git a/cypress/config/settings.cypress.json b/cypress/config/settings.cypress.json index 7a4bbef5d6..cd045a468d 100644 --- a/cypress/config/settings.cypress.json +++ b/cypress/config/settings.cypress.json @@ -20,6 +20,7 @@ "originalLanguage": "", "trustProxy": false, "partialRequestsEnabled": true, + "hideSpecials": false, "locale": "en" }, "plex": { diff --git a/overseerr-api.yml b/overseerr-api.yml index c48b6575f7..13aa55a9e6 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -126,6 +126,9 @@ components: partialRequestsEnabled: type: boolean example: false + hideSpecials: + type: boolean + example: false localLogin: type: boolean example: true diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index 0cd2f171ae..84b39de923 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -32,6 +32,7 @@ export interface PublicSettingsResponse { originalLanguage: string; partialRequestsEnabled: boolean; cacheImages: boolean; + hideSpecials: boolean; vapidPublic: string; enablePushRegistration: boolean; locale: string; diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 63daf17f36..afa1f3039b 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -104,6 +104,7 @@ export interface MainSettings { trustProxy: boolean; partialRequestsEnabled: boolean; locale: string; + hideSpecials: boolean; } interface PublicSettings { @@ -126,6 +127,7 @@ interface FullPublicSettings extends PublicSettings { locale: string; emailEnabled: boolean; newPlexLogin: boolean; + hideSpecials: boolean; } export interface NotificationAgentConfig { @@ -301,6 +303,7 @@ class Settings { trustProxy: false, partialRequestsEnabled: true, locale: 'en', + hideSpecials: false, }, plex: { name: '', @@ -507,6 +510,7 @@ class Settings { originalLanguage: this.data.main.originalLanguage, partialRequestsEnabled: this.data.main.partialRequestsEnabled, cacheImages: this.data.main.cacheImages, + hideSpecials: this.data.main.hideSpecials, vapidPublic: this.vapidPublic, enablePushRegistration: this.data.notifications.agents.webpush.enabled, locale: this.data.main.locale, diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index 4487cf8138..ec2457b508 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -97,7 +97,9 @@ const ManageSlideOver = ({ await axios.post(`/api/v1/media/${data.mediaInfo?.id}/available`, { is4k, ...(mediaType === 'tv' && { - seasons: data.seasons.filter((season) => season.seasonNumber !== 0), + seasons: data.seasons.filter((season) => + settings.currentSettings.hideSpecials ? season.seasonNumber !== 0 : true + ), }), }); revalidate(); diff --git a/src/components/RequestModal/TvRequestModal.tsx b/src/components/RequestModal/TvRequestModal.tsx index 3b4273e9e7..33f369c7be 100644 --- a/src/components/RequestModal/TvRequestModal.tsx +++ b/src/components/RequestModal/TvRequestModal.tsx @@ -201,7 +201,7 @@ const TvRequestModal = ({ ? selectedSeasons : getAllSeasons().filter( (season) => - !getAllRequestedSeasons().includes(season) && season !== 0 + !getAllRequestedSeasons().includes(season) && (!settings.currentSettings.hideSpecials || season !== 0) ), ...overrideParams, }); @@ -235,7 +235,7 @@ const TvRequestModal = ({ const getAllSeasons = (): number[] => { return (data?.seasons ?? []) - .filter((season) => season.episodeCount !== 0) + .filter((season) => season.episodeCount !== 0 && (!settings.currentSettings.hideSpecials || season.seasonNumber !== 0)) .map((season) => season.seasonNumber); }; @@ -568,11 +568,15 @@ const TvRequestModal = ({
{data?.seasons - .filter((season) => - !settings.currentSettings.partialRequestsEnabled - ? season.episodeCount !== 0 && season.seasonNumber !== 0 - : season.episodeCount !== 0 - ) + .filter((season) => { + if (settings.currentSettings.hideSpecials && season.seasonNumber === 0) { + return false; + } + if (!settings.currentSettings.partialRequestsEnabled) { + return season.episodeCount !== 0 && season.seasonNumber !== 0; + } + return season.episodeCount !== 0; + }) .map((season) => { const seasonRequest = getSeasonRequest( season.seasonNumber diff --git a/src/components/Settings/SettingsMain/index.tsx b/src/components/Settings/SettingsMain/index.tsx index 62f26d49a2..52f7ab5807 100644 --- a/src/components/Settings/SettingsMain/index.tsx +++ b/src/components/Settings/SettingsMain/index.tsx @@ -54,6 +54,8 @@ const messages = defineMessages({ validationApplicationUrl: 'You must provide a valid URL', validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash', partialRequestsEnabled: 'Allow Partial Series Requests', + hideSpecials: 'Hide Specials Season', + hideSpecialsTip: 'Hide specials season and prevent requesting specials', locale: 'Display Language', }); @@ -132,6 +134,7 @@ const SettingsMain = () => { region: data?.region, originalLanguage: data?.originalLanguage, partialRequestsEnabled: data?.partialRequestsEnabled, + hideSpecials: data?.hideSpecials, trustProxy: data?.trustProxy, cacheImages: data?.cacheImages, }} @@ -148,6 +151,7 @@ const SettingsMain = () => { region: values.region, originalLanguage: values.originalLanguage, partialRequestsEnabled: values.partialRequestsEnabled, + hideSpecials: values.hideSpecials, trustProxy: values.trustProxy, cacheImages: values.cacheImages, }); @@ -401,6 +405,31 @@ const SettingsMain = () => { onChange={() => { setFieldValue('hideAvailable', !values.hideAvailable); }} + /> + +