Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cypress/config/settings.cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"mediaServerType": 1,
"partialRequestsEnabled": true,
"enableSpecialEpisodes": false,
"removeUnmonitoredSeasonsEnabled": false,
"removeUnmonitoredSeriesSeasonsEnabled": false,
"locale": "en"
},
"plex": {
Expand Down
6 changes: 6 additions & 0 deletions seerr-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ components:
enableSpecialEpisodes:
type: boolean
example: false
removeUnmonitoredSeasonsEnabled:
type: boolean
example: false
removeUnmonitoredSeriesSeasonsEnabled:
type: boolean
example: false
NetworkSettings:
type: object
properties:
Expand Down
2 changes: 2 additions & 0 deletions server/interfaces/api/settingsInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export interface PublicSettingsResponse {
emailEnabled: boolean;
newPlexLogin: boolean;
youtubeUrl: string;
removeUnmonitoredSeasonsEnabled: boolean;
removeUnmonitoredSeriesSeasonsEnabled: boolean;
}

export interface CacheItem {
Expand Down
17 changes: 15 additions & 2 deletions 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;
unmonitored?: boolean;
}

class BaseScanner<T> {
Expand Down Expand Up @@ -281,6 +282,10 @@ class BaseScanner<T> {
? MediaStatus.AVAILABLE
: season.episodes > 0
? MediaStatus.PARTIALLY_AVAILABLE
: !season.is4kOverride &&
season.unmonitored &&
existingSeason.status !== MediaStatus.DELETED
? MediaStatus.UNKNOWN
: !season.is4kOverride &&
season.processing &&
existingSeason.status !== MediaStatus.DELETED
Expand All @@ -296,6 +301,10 @@ class BaseScanner<T> {
? MediaStatus.AVAILABLE
: this.enable4kShow && season.episodes4k > 0
? MediaStatus.PARTIALLY_AVAILABLE
: season.is4kOverride &&
season.unmonitored &&
existingSeason.status !== MediaStatus.DELETED
? MediaStatus.UNKNOWN
: season.is4kOverride &&
season.processing &&
existingSeason.status4k !== MediaStatus.DELETED
Expand All @@ -310,7 +319,9 @@ class BaseScanner<T> {
? MediaStatus.AVAILABLE
: season.episodes > 0
? MediaStatus.PARTIALLY_AVAILABLE
: !season.is4kOverride && season.processing
: !season.is4kOverride &&
season.processing &&
!season.unmonitored
? MediaStatus.PROCESSING
: MediaStatus.UNKNOWN,
status4k:
Expand All @@ -320,7 +331,9 @@ class BaseScanner<T> {
? MediaStatus.AVAILABLE
: this.enable4kShow && season.episodes4k > 0
? MediaStatus.PARTIALLY_AVAILABLE
: season.is4kOverride && season.processing
: season.is4kOverride &&
season.processing &&
!season.unmonitored
? MediaStatus.PROCESSING
: MediaStatus.UNKNOWN,
})
Expand Down
7 changes: 7 additions & 0 deletions server/lib/scanners/sonarr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ class SonarrScanner

for (const season of filteredSeasons) {
const totalAvailableEpisodes = season.statistics?.episodeFileCount ?? 0;
const unmonitoredSeason =
settings.main.removeUnmonitoredSeasonsEnabled &&
season.monitored === false;
const unmonitoredSeries =
settings.main.removeUnmonitoredSeriesSeasonsEnabled &&
sonarrSeries.monitored === false;

processableSeasons.push({
seasonNumber: season.seasonNumber,
Expand All @@ -120,6 +126,7 @@ class SonarrScanner
totalEpisodes: season.statistics?.totalEpisodeCount ?? 0,
processing: season.monitored && totalAvailableEpisodes === 0,
is4kOverride: server4k,
unmonitored: unmonitoredSeason || unmonitoredSeries,
});
}

Expand Down
10 changes: 10 additions & 0 deletions server/lib/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ export interface MainSettings {
enableSpecialEpisodes: boolean;
locale: string;
youtubeUrl: string;
removeUnmonitoredSeasonsEnabled: boolean;
removeUnmonitoredSeriesSeasonsEnabled: boolean;
}

export interface ProxySettings {
Expand Down Expand Up @@ -203,6 +205,8 @@ interface FullPublicSettings extends PublicSettings {
userEmailRequired: boolean;
newPlexLogin: boolean;
youtubeUrl: string;
removeUnmonitoredSeasonsEnabled: boolean;
removeUnmonitoredSeriesSeasonsEnabled: boolean;
}

export interface NotificationAgentConfig {
Expand Down Expand Up @@ -403,6 +407,8 @@ class Settings {
enableSpecialEpisodes: false,
locale: 'en',
youtubeUrl: '',
removeUnmonitoredSeasonsEnabled: false,
removeUnmonitoredSeriesSeasonsEnabled: false,
},
plex: {
name: '',
Expand Down Expand Up @@ -697,6 +703,10 @@ class Settings {
this.data.notifications.agents.email.options.userEmailRequired,
newPlexLogin: this.data.main.newPlexLogin,
youtubeUrl: this.data.main.youtubeUrl,
removeUnmonitoredSeasonsEnabled:
this.data.main.removeUnmonitoredSeasonsEnabled,
removeUnmonitoredSeriesSeasonsEnabled:
this.data.main.removeUnmonitoredSeriesSeasonsEnabled,
};
}

Expand Down
77 changes: 77 additions & 0 deletions src/components/Settings/SettingsMain/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ const messages = defineMessages('components.Settings.SettingsMain', {
'Base URL for YouTube videos if a self-hosted YouTube instance is used.',
validationUrl: 'You must provide a valid URL',
validationUrlTrailingSlash: 'URL must not end in a trailing slash',
removeUnmonitoredSeasonsEnabled: 'Remove Unmonitored Seasons',
removeUnmonitoredSeasonsExplanation:
'Remove seasons from Seerr if they are not available and are unmonitored in Sonarr',
removeUnmonitoredSeriesSeasonsEnabled:
'Remove Unavailable Seasons for Unmonitored Series',
removeUnmonitoredSeriesSeasonsExplanation:
'Remove seasons from Seerr if they are not available and parent series is unmonitored in Sonarr',
});

const SettingsMain = () => {
Expand Down Expand Up @@ -175,6 +182,10 @@ const SettingsMain = () => {
enableSpecialEpisodes: data?.enableSpecialEpisodes,
cacheImages: data?.cacheImages,
youtubeUrl: data?.youtubeUrl,
removeUnmonitoredSeasonsEnabled:
data?.removeUnmonitoredSeasonsEnabled,
removeUnmonitoredSeriesSeasonsEnabled:
data?.removeUnmonitoredSeriesSeasonsEnabled,
}}
enableReinitialize
validationSchema={MainSettingsSchema}
Expand All @@ -195,6 +206,10 @@ const SettingsMain = () => {
enableSpecialEpisodes: values.enableSpecialEpisodes,
cacheImages: values.cacheImages,
youtubeUrl: values.youtubeUrl,
removeUnmonitoredSeasonsEnabled:
values.removeUnmonitoredSeasonsEnabled,
removeUnmonitoredSeriesSeasonsEnabled:
values.removeUnmonitoredSeriesSeasonsEnabled,
});
mutate('/api/v1/settings/public');
mutate('/api/v1/status');
Expand Down Expand Up @@ -535,6 +550,68 @@ const SettingsMain = () => {
/>
</div>
</div>
<div className="form-row">
<label
htmlFor="removeUnmonitoredSeasonsEnabled"
className="checkbox-label"
>
<span className="mr-2">
{intl.formatMessage(
messages.removeUnmonitoredSeasonsEnabled
)}
</span>
<SettingsBadge badgeType="experimental" />
<span className="label-tip">
{intl.formatMessage(
messages.removeUnmonitoredSeasonsExplanation
)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="removeUnmonitoredSeasonsEnabled"
name="removeUnmonitoredSeasonsEnabled"
onChange={() => {
setFieldValue(
'removeUnmonitoredSeasonsEnabled',
!values.removeUnmonitoredSeasonsEnabled
);
}}
/>
</div>
</div>
<div className="form-row">
<label
htmlFor="removeUnmonitoredSeriesSeasonsEnabled"
className="checkbox-label"
>
<span className="mr-2">
{intl.formatMessage(
messages.removeUnmonitoredSeriesSeasonsEnabled
)}
</span>
<SettingsBadge badgeType="experimental" />
<span className="label-tip">
{intl.formatMessage(
messages.removeUnmonitoredSeriesSeasonsExplanation
)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="removeUnmonitoredSeriesSeasonsEnabled"
name="removeUnmonitoredSeriesSeasonsEnabled"
onChange={() => {
setFieldValue(
'removeUnmonitoredSeriesSeasonsEnabled',
!values.removeUnmonitoredSeriesSeasonsEnabled
);
}}
/>
</div>
</div>
<div className="form-row">
<label htmlFor="youtubeUrl" className="text-label">
{intl.formatMessage(messages.youtubeUrl)}
Expand Down
2 changes: 2 additions & 0 deletions src/context/SettingsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const defaultSettings = {
emailEnabled: false,
newPlexLogin: true,
youtubeUrl: '',
removeUnmonitoredSeasonsEnabled: false,
removeUnmonitoredSeriesSeasonsEnabled: false,
};

export const SettingsContext = React.createContext<SettingsContextProps>({
Expand Down
8 changes: 6 additions & 2 deletions src/i18n/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@
"components.Discover.StudioSlider.studios": "Studios",
"components.Discover.TvGenreList.seriesgenres": "Series Genres",
"components.Discover.TvGenreSlider.tvgenres": "Series Genres",
"components.DiscoverTvUpcoming.upcomingtv": "Upcoming Series",
"components.Discover.createnewslider": "Create New Slider",
"components.Discover.customizediscover": "Customize Discover",
"components.Discover.discover": "Discover",
Expand Down Expand Up @@ -139,6 +138,7 @@
"components.Discover.upcomingtv": "Upcoming Series",
"components.Discover.updatefailed": "Something went wrong updating the discover customization settings.",
"components.Discover.updatesuccess": "Updated discover customization settings.",
"components.DiscoverTvUpcoming.upcomingtv": "Upcoming Series",
"components.DownloadBlock.estimatedtime": "Estimated {time}",
"components.DownloadBlock.formattedTitle": "{title}: Season {seasonNumber} Episode {episodeNumber}",
"components.IssueDetails.IssueComment.areyousuredelete": "Are you sure you want to delete this comment?",
Expand Down Expand Up @@ -981,6 +981,10 @@
"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.removeUnmonitoredSeasonsEnabled": "Remove Unmonitored Seasons",
"components.Settings.SettingsMain.removeUnmonitoredSeasonsExplanation": "Remove seasons from Seerr if they are not available and are unmonitored in Sonarr",
"components.Settings.SettingsMain.removeUnmonitoredSeriesSeasonsEnabled": "Remove Unavailable Seasons for Unmonitored Series",
"components.Settings.SettingsMain.removeUnmonitoredSeriesSeasonsExplanation": "Remove seasons from Seerr if they are not available and parent series is unmonitored in Sonarr",
"components.Settings.SettingsMain.streamingRegion": "Streaming Region",
"components.Settings.SettingsMain.streamingRegionTip": "Show streaming sites by regional availability",
"components.Settings.SettingsMain.toastApiKeyFailure": "Something went wrong while generating a new API key.",
Expand Down Expand Up @@ -1256,7 +1260,7 @@
"components.Setup.librarieserror": "Validation failed. Please toggle the libraries again to continue.",
"components.Setup.servertype": "Choose Server Type",
"components.Setup.setup": "Setup",
"components.Setup.signin": "Sign In",
"components.Setup.signin": "Sign in to your account",
"components.Setup.signinMessage": "Get started by signing in",
"components.Setup.signinWithEmby": "Enter your Emby details",
"components.Setup.signinWithJellyfin": "Enter your Jellyfin details",
Expand Down
2 changes: 2 additions & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ CoreApp.getInitialProps = async (initialProps) => {
emailEnabled: false,
newPlexLogin: true,
youtubeUrl: '',
removeUnmonitoredSeasonsEnabled: false,
removeUnmonitoredSeriesSeasonsEnabled: false,
};

if (ctx.res) {
Expand Down