diff --git a/overseerr-api.yml b/overseerr-api.yml index e070361be..7a7ea490a 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -368,6 +368,9 @@ components: externalHostname: type: string example: 'http://my.jellyfin.host' + jellyfinForgotPasswordUrl: + type: string + example: 'http://my.jellyfin.host/web/index.html#!/forgotpassword.html' adminUser: type: string example: 'admin' diff --git a/server/interfaces/api/settingsInterfaces.ts b/server/interfaces/api/settingsInterfaces.ts index b709a0c4b..7e714fdae 100644 --- a/server/interfaces/api/settingsInterfaces.ts +++ b/server/interfaces/api/settingsInterfaces.ts @@ -41,6 +41,7 @@ export interface PublicSettingsResponse { locale: string; emailEnabled: boolean; newPlexLogin: boolean; + jellyfinForgotPasswordUrl: string; } export interface CacheItem { diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 133d0e327..60e327dbb 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -42,6 +42,7 @@ export interface JellyfinSettings { externalHostname?: string; libraries: Library[]; serverId: string; + jellyfinForgotPasswordUrl: string; } export interface TautulliSettings { hostname?: string; @@ -124,6 +125,7 @@ interface FullPublicSettings extends PublicSettings { applicationUrl: string; hideAvailable: boolean; localLogin: boolean; + jellyfinForgotPasswordUrl: string; movie4kEnabled: boolean; series4kEnabled: boolean; region: string; @@ -331,6 +333,7 @@ class Settings { name: '', hostname: '', externalHostname: '', + jellyfinForgotPasswordUrl: '', libraries: [], serverId: '', }, @@ -534,6 +537,7 @@ class Settings { applicationUrl: this.data.main.applicationUrl, hideAvailable: this.data.main.hideAvailable, localLogin: this.data.main.localLogin, + jellyfinForgotPasswordUrl: this.data.jellyfin.jellyfinForgotPasswordUrl, movie4kEnabled: this.data.radarr.some( (radarr) => radarr.is4k && radarr.isDefault ), diff --git a/src/components/Login/JellyfinLogin.tsx b/src/components/Login/JellyfinLogin.tsx index 126fa4f76..8a6370c24 100644 --- a/src/components/Login/JellyfinLogin.tsx +++ b/src/components/Login/JellyfinLogin.tsx @@ -222,6 +222,7 @@ const JellyfinLogin: React.FC = ({ const baseUrl = settings.currentSettings.jellyfinExternalHost ? settings.currentSettings.jellyfinExternalHost : settings.currentSettings.jellyfinHost; + const jellyfinForgotPasswordUrl = settings.currentSettings.jellyfinForgotPasswordUrl; return (
= ({ diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx index 5d56431fb..2c7a9d772 100644 --- a/src/components/Settings/SettingsJellyfin.tsx +++ b/src/components/Settings/SettingsJellyfin.tsx @@ -30,9 +30,10 @@ const messages = defineMessages({ jellyfinSettingsSuccess: '{mediaServerName} settings saved successfully!', jellyfinSettings: '{mediaServerName} Settings', jellyfinSettingsDescription: - 'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL.', + 'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.', externalUrl: 'External URL', internalUrl: 'Internal URL', + jellyfinForgotPasswordUrl: 'Forgot Password URL', validationUrl: 'You must provide a valid URL', syncing: 'Syncing', syncJellyfin: 'Sync Libraries', @@ -94,6 +95,10 @@ const SettingsJellyfin: React.FC = ({ /^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm, intl.formatMessage(messages.validationUrl) ), + jellyfinForgotPasswordUrl: Yup.string().matches( + /^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm, + intl.formatMessage(messages.validationUrl) + ), }); const activeLibraries = @@ -171,20 +176,20 @@ const SettingsJellyfin: React.FC = ({

{publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? intl.formatMessage(messages.jellyfinlibraries, { - mediaServerName: 'Emby', - }) + mediaServerName: 'Emby', + }) : intl.formatMessage(messages.jellyfinlibraries, { - mediaServerName: 'Jellyfin', - })} + mediaServerName: 'Jellyfin', + })}

{publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? intl.formatMessage(messages.jellyfinlibrariesDescription, { - mediaServerName: 'Emby', - }) + mediaServerName: 'Emby', + }) : intl.formatMessage(messages.jellyfinlibrariesDescription, { - mediaServerName: 'Jellyfin', - })} + mediaServerName: 'Jellyfin', + })}

@@ -223,11 +228,11 @@ const SettingsJellyfin: React.FC = ({

{publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? intl.formatMessage(messages.manualscanDescriptionJellyfin, { - mediaServerName: 'Emby', - }) + mediaServerName: 'Emby', + }) : intl.formatMessage(messages.manualscanDescriptionJellyfin, { - mediaServerName: 'Jellyfin', - })} + mediaServerName: 'Jellyfin', + })}

@@ -271,11 +276,11 @@ const SettingsJellyfin: React.FC = ({ values={{ count: dataSync.currentLibrary ? dataSync.libraries.slice( - dataSync.libraries.findIndex( - (library) => - library.id === dataSync.currentLibrary?.id - ) + 1 - ).length + dataSync.libraries.findIndex( + (library) => + library.id === dataSync.currentLibrary?.id + ) + 1 + ).length : 0, }} /> @@ -333,26 +338,27 @@ const SettingsJellyfin: React.FC = ({

{publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? intl.formatMessage(messages.jellyfinSettings, { - mediaServerName: 'Emby', - }) + mediaServerName: 'Emby', + }) : intl.formatMessage(messages.jellyfinSettings, { - mediaServerName: 'Jellyfin', - })} + mediaServerName: 'Jellyfin', + })}

{publicRuntimeConfig.JELLYFIN_TYPE == 'emby' ? intl.formatMessage(messages.jellyfinSettingsDescription, { - mediaServerName: 'Emby', - }) + mediaServerName: 'Emby', + }) : intl.formatMessage(messages.jellyfinSettingsDescription, { - mediaServerName: 'Jellyfin', - })} + mediaServerName: 'Jellyfin', + })}

{ @@ -360,6 +366,7 @@ const SettingsJellyfin: React.FC = ({ await axios.post('/api/v1/settings/jellyfin', { hostname: values.jellyfinInternalUrl, externalHostname: values.jellyfinExternalUrl, + jellyfinForgotPasswordUrl: values.jellyfinForgotPasswordUrl, } as JellyfinSettings); addToast( @@ -437,6 +444,27 @@ const SettingsJellyfin: React.FC = ({ )} +
+ +
+
+ +
+ {errors.jellyfinForgotPasswordUrl && + touched.jellyfinForgotPasswordUrl && ( +
+ {errors.jellyfinForgotPasswordUrl} +
+ )} +
+
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 906892b04..07d4356cb 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -938,7 +938,7 @@ "components.Settings.internalUrl": "Internal URL", "components.Settings.is4k": "4K", "components.Settings.jellyfinSettings": "{mediaServerName} Settings", - "components.Settings.jellyfinSettingsDescription": "Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL.", + "components.Settings.jellyfinSettingsDescription": "Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.", "components.Settings.jellyfinSettingsFailure": "Something went wrong while saving {mediaServerName} settings.", "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} settings saved successfully!", "components.Settings.jellyfinlibraries": "{mediaServerName} Libraries", @@ -1305,4 +1305,4 @@ "pages.returnHome": "Return Home", "pages.serviceunavailable": "Service Unavailable", "pages.somethingwentwrong": "Something Went Wrong" -} +} \ No newline at end of file