Skip to content

Commit 5e586d6

Browse files
committed
feat(settings): make the feature optionnal
Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
1 parent b00fc99 commit 5e586d6

File tree

9 files changed

+97
-41
lines changed

9 files changed

+97
-41
lines changed

cypress/config/settings.cypress.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"mediaServerType": 1,
2525
"partialRequestsEnabled": true,
2626
"enableSpecialEpisodes": false,
27+
"enableEpisodeAvailability": false,
2728
"locale": "en"
2829
},
2930
"plex": {
@@ -201,4 +202,4 @@
201202
"forceMaxTtl": -1
202203
}
203204
}
204-
}
205+
}

jellyseerr-api.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ components:
254254
enableSpecialEpisodes:
255255
type: boolean
256256
example: false
257+
enableEpisodeAvailability:
258+
type: boolean
259+
example: false
257260
NetworkSettings:
258261
type: object
259262
properties:

server/interfaces/api/settingsInterfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export interface PublicSettingsResponse {
4141
mediaServerType: number;
4242
partialRequestsEnabled: boolean;
4343
enableSpecialEpisodes: boolean;
44+
enableEpisodeAvailability: boolean;
4445
cacheImages: boolean;
4546
vapidPublic: string;
4647
enablePushRegistration: boolean;

server/lib/availabilitySync.ts

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import MediaRequest from '@server/entity/MediaRequest';
1414
import type Season from '@server/entity/Season';
1515
import { User } from '@server/entity/User';
1616
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
17-
import { getSettings } from '@server/lib/settings';
17+
import { getSettings, MetadataProviderType } from '@server/lib/settings';
1818
import logger from '@server/logger';
1919
import { getHostname } from '@server/utils/getHostname';
2020

@@ -807,6 +807,12 @@ class AvailabilitySync {
807807
let seasonExists = false;
808808
const episodeRepository = getRepository(Episode);
809809

810+
const settings = getSettings();
811+
const shouldTrackEpisodes =
812+
settings.main.enableEpisodeAvailability &&
813+
(settings.metadataSettings.tv === MetadataProviderType.TVDB ||
814+
settings.metadataSettings.anime === MetadataProviderType.TVDB);
815+
810816
// Check each sonarr instance to see if the media still exists
811817
// If found, we will assume the media exists and prevent removal
812818
// We can use the cache we built when we fetched the series with mediaExistsInSonarr
@@ -855,48 +861,50 @@ class AvailabilitySync {
855861
return seasonExists;
856862
}
857863

858-
const episodes = await sonarrApi.getEpisodesBySeriesId(serviceId);
864+
if (shouldTrackEpisodes) {
865+
const episodes = await sonarrApi.getEpisodesBySeriesId(serviceId);
859866

860-
for (const ep of episodes) {
861-
if (ep.seasonNumber === season.seasonNumber) {
862-
const existingEpisode = await episodeRepository.findOne({
863-
where: {
864-
episodeNumber: ep.episodeNumber,
865-
season: { id: season.id },
866-
},
867-
relations: ['season'],
868-
});
867+
for (const ep of episodes) {
868+
if (ep.seasonNumber === season.seasonNumber) {
869+
const existingEpisode = await episodeRepository.findOne({
870+
where: {
871+
episodeNumber: ep.episodeNumber,
872+
season: { id: season.id },
873+
},
874+
relations: ['season'],
875+
});
869876

870-
if (existingEpisode) {
871-
existingEpisode[is4k ? 'status4k' : 'status'] = ep.hasFile
872-
? MediaStatus.AVAILABLE
873-
: MediaStatus.UNKNOWN;
874-
await episodeRepository.save(existingEpisode);
875-
} else {
876-
const newEpisode = new Episode();
877-
newEpisode.episodeNumber = ep.episodeNumber;
878-
newEpisode.status = is4k
879-
? MediaStatus.UNKNOWN
880-
: ep.hasFile
881-
? MediaStatus.AVAILABLE
882-
: MediaStatus.UNKNOWN;
883-
newEpisode.status4k = is4k
884-
? ep.hasFile
877+
if (existingEpisode) {
878+
existingEpisode[is4k ? 'status4k' : 'status'] = ep.hasFile
885879
? MediaStatus.AVAILABLE
886-
: MediaStatus.UNKNOWN
887-
: MediaStatus.UNKNOWN;
888-
newEpisode.season = Promise.resolve(season);
889-
890-
try {
891-
await episodeRepository.save(newEpisode);
892-
} catch (saveError) {
893-
logger.error('Failed to save new episode', {
894-
label: 'Availability Sync',
895-
errorMessage: saveError.message,
896-
tvId: media.tmdbId,
897-
seasonNumber: season.seasonNumber,
898-
episodeNumber: ep.episodeNumber,
899-
});
880+
: MediaStatus.UNKNOWN;
881+
await episodeRepository.save(existingEpisode);
882+
} else {
883+
const newEpisode = new Episode();
884+
newEpisode.episodeNumber = ep.episodeNumber;
885+
newEpisode.status = is4k
886+
? MediaStatus.UNKNOWN
887+
: ep.hasFile
888+
? MediaStatus.AVAILABLE
889+
: MediaStatus.UNKNOWN;
890+
newEpisode.status4k = is4k
891+
? ep.hasFile
892+
? MediaStatus.AVAILABLE
893+
: MediaStatus.UNKNOWN
894+
: MediaStatus.UNKNOWN;
895+
newEpisode.season = Promise.resolve(season);
896+
897+
try {
898+
await episodeRepository.save(newEpisode);
899+
} catch (saveError) {
900+
logger.error('Failed to save new episode', {
901+
label: 'Availability Sync',
902+
errorMessage: saveError.message,
903+
tvId: media.tmdbId,
904+
seasonNumber: season.seasonNumber,
905+
episodeNumber: ep.episodeNumber,
906+
});
907+
}
900908
}
901909
}
902910
}

server/lib/settings/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ export interface MainSettings {
144144
mediaServerType: number;
145145
partialRequestsEnabled: boolean;
146146
enableSpecialEpisodes: boolean;
147+
enableEpisodeAvailability: boolean;
147148
locale: string;
148149
youtubeUrl: string;
149150
}
@@ -195,6 +196,7 @@ interface FullPublicSettings extends PublicSettings {
195196
jellyfinServerName?: string;
196197
partialRequestsEnabled: boolean;
197198
enableSpecialEpisodes: boolean;
199+
enableEpisodeAvailability: boolean;
198200
cacheImages: boolean;
199201
vapidPublic: string;
200202
enablePushRegistration: boolean;
@@ -398,6 +400,7 @@ class Settings {
398400
mediaServerType: MediaServerType.NOT_CONFIGURED,
399401
partialRequestsEnabled: true,
400402
enableSpecialEpisodes: false,
403+
enableEpisodeAvailability: false,
401404
locale: 'en',
402405
youtubeUrl: '',
403406
},
@@ -674,6 +677,7 @@ class Settings {
674677
mediaServerType: this.main.mediaServerType,
675678
partialRequestsEnabled: this.data.main.partialRequestsEnabled,
676679
enableSpecialEpisodes: this.data.main.enableSpecialEpisodes,
680+
enableEpisodeAvailability: this.data.main.enableEpisodeAvailability,
677681
cacheImages: this.data.main.cacheImages,
678682
vapidPublic: this.vapidPublic,
679683
enablePushRegistration: this.data.notifications.agents.webpush.enabled,

src/components/Settings/SettingsMain/index.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ const messages = defineMessages('components.Settings.SettingsMain', {
6464
validationApplicationUrlTrailingSlash: 'URL must not end in a trailing slash',
6565
partialRequestsEnabled: 'Allow Partial Series Requests',
6666
enableSpecialEpisodes: 'Allow Special Episodes Requests',
67+
enableEpisodeAvailability: 'Enable Episode Availability Tracking',
68+
enableEpisodeAvailabilityTip:
69+
'Track individual episode availability status (requires TVDB as metadata provider for TV shows or anime)',
6770
locale: 'Display Language',
6871
youtubeUrl: 'YouTube URL',
6972
youtubeUrlTip:
@@ -82,6 +85,7 @@ const SettingsMain = () => {
8285
error,
8386
mutate: revalidate,
8487
} = useSWR<MainSettings>('/api/v1/settings/main');
88+
8589
const { data: userData } = useSWR<UserSettingsGeneralResponse>(
8690
currentUser ? `/api/v1/user/${currentUser.id}/settings/main` : null
8791
);
@@ -173,6 +177,7 @@ const SettingsMain = () => {
173177
blacklistedTagsLimit: data?.blacklistedTagsLimit || 50,
174178
partialRequestsEnabled: data?.partialRequestsEnabled,
175179
enableSpecialEpisodes: data?.enableSpecialEpisodes,
180+
enableEpisodeAvailability: data?.enableEpisodeAvailability,
176181
cacheImages: data?.cacheImages,
177182
youtubeUrl: data?.youtubeUrl,
178183
}}
@@ -193,6 +198,7 @@ const SettingsMain = () => {
193198
blacklistedTagsLimit: values.blacklistedTagsLimit,
194199
partialRequestsEnabled: values.partialRequestsEnabled,
195200
enableSpecialEpisodes: values.enableSpecialEpisodes,
201+
enableEpisodeAvailability: values.enableEpisodeAvailability,
196202
cacheImages: values.cacheImages,
197203
youtubeUrl: values.youtubeUrl,
198204
});
@@ -535,6 +541,34 @@ const SettingsMain = () => {
535541
/>
536542
</div>
537543
</div>
544+
<div className="form-row">
545+
<label
546+
htmlFor="enableEpisodeAvailability"
547+
className="checkbox-label"
548+
>
549+
<span className="mr-2">
550+
{intl.formatMessage(messages.enableEpisodeAvailability)}
551+
</span>
552+
<span className="label-tip">
553+
{intl.formatMessage(
554+
messages.enableEpisodeAvailabilityTip
555+
)}
556+
</span>
557+
</label>
558+
<div className="form-input-area">
559+
<Field
560+
type="checkbox"
561+
id="enableEpisodeAvailability"
562+
name="enableEpisodeAvailability"
563+
onChange={() => {
564+
setFieldValue(
565+
'enableEpisodeAvailability',
566+
!values.enableEpisodeAvailability
567+
);
568+
}}
569+
/>
570+
</div>
571+
</div>
538572
<div className="form-row">
539573
<label htmlFor="youtubeUrl" className="text-label">
540574
{intl.formatMessage(messages.youtubeUrl)}

src/context/SettingsContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const defaultSettings = {
2424
mediaServerType: MediaServerType.NOT_CONFIGURED,
2525
partialRequestsEnabled: true,
2626
enableSpecialEpisodes: false,
27+
enableEpisodeAvailability: false,
2728
cacheImages: false,
2829
vapidPublic: '',
2930
enablePushRegistration: false,

src/i18n/locale/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,8 @@
962962
"components.Settings.SettingsMain.cacheImagesTip": "Cache externally sourced images (requires a significant amount of disk space)",
963963
"components.Settings.SettingsMain.discoverRegion": "Discover Region",
964964
"components.Settings.SettingsMain.discoverRegionTip": "Filter content by regional availability",
965+
"components.Settings.SettingsMain.enableEpisodeAvailability": "Enable Episode Availability Tracking",
966+
"components.Settings.SettingsMain.enableEpisodeAvailabilityTip": "Track individual episode availability status (requires TVDB as metadata provider for TV shows or anime)",
965967
"components.Settings.SettingsMain.enableSpecialEpisodes": "Allow Special Episodes Requests",
966968
"components.Settings.SettingsMain.general": "General",
967969
"components.Settings.SettingsMain.generalsettings": "General Settings",
@@ -1158,6 +1160,7 @@
11581160
"components.Settings.menuServices": "Services",
11591161
"components.Settings.menuUsers": "Users",
11601162
"components.Settings.metadataProviderSelection": "Metadata Provider Selection",
1163+
"components.Settings.metadataProviderSettings": "Metadata Providers",
11611164
"components.Settings.metadataSettings": "Settings for metadata provider",
11621165
"components.Settings.metadataSettingsSaved": "Metadata provider settings saved",
11631166
"components.Settings.no": "No",

src/pages/_app.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ CoreApp.getInitialProps = async (initialProps) => {
242242
mediaServerType: MediaServerType.NOT_CONFIGURED,
243243
partialRequestsEnabled: true,
244244
enableSpecialEpisodes: false,
245+
enableEpisodeAvailability: false,
245246
cacheImages: false,
246247
vapidPublic: '',
247248
enablePushRegistration: false,

0 commit comments

Comments
 (0)