diff --git a/server/api/jellyfin.ts b/server/api/jellyfin.ts index 962413ae6..a87441eea 100644 --- a/server/api/jellyfin.ts +++ b/server/api/jellyfin.ts @@ -352,7 +352,11 @@ class JellyfinAPI extends ExternalAPI { { label: 'Jellyfin API', error: e.response?.status } ); - throw new ApiError(e.response?.status, ApiErrorCode.InvalidAuthToken); + if (!e.response) { + throw new ApiError(502, ApiErrorCode.ConnectionError); + } + + throw new ApiError(e.response.status, ApiErrorCode.InvalidAuthToken); } } @@ -379,6 +383,10 @@ class JellyfinAPI extends ExternalAPI { } ); + if (!e.response) { + throw new ApiError(502, ApiErrorCode.ConnectionError); + } + return []; } } diff --git a/server/constants/error.ts b/server/constants/error.ts index daa02f1a1..54640b206 100644 --- a/server/constants/error.ts +++ b/server/constants/error.ts @@ -5,6 +5,7 @@ export enum ApiErrorCode { InvalidEmail = 'INVALID_EMAIL', NotAdmin = 'NOT_ADMIN', NoAdminUser = 'NO_ADMIN_USER', + ConnectionError = 'CONNECTION_ERROR', SyncErrorGroupedFolders = 'SYNC_ERROR_GROUPED_FOLDERS', SyncErrorNoLibraries = 'SYNC_ERROR_NO_LIBRARIES', Unauthorized = 'UNAUTHORIZED', diff --git a/server/routes/settings/index.ts b/server/routes/settings/index.ts index 7ca7e65cb..24c008783 100644 --- a/server/routes/settings/index.ts +++ b/server/routes/settings/index.ts @@ -1,3 +1,4 @@ +import type { JellyfinLibrary } from '@server/api/jellyfin'; import JellyfinAPI from '@server/api/jellyfin'; import PlexAPI from '@server/api/plexapi'; import PlexTvAPI from '@server/api/plextv'; @@ -400,22 +401,31 @@ settingsRoutes.post('/jellyfin/library/sync', async (_req, res, next) => { jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); - const libraries = await jellyfinClient.getLibraries(); + let libraries: JellyfinLibrary[]; - if (libraries.length === 0) { - // Check if no libraries are found due to the fallback to user views - // This only affects LDAP users - const account = await jellyfinClient.getUser(); + try { + libraries = await jellyfinClient.getLibraries(); - // Automatic Library grouping is not supported when user views are used to get library - if (account.Configuration.GroupedFolders?.length > 0) { - return next({ - status: 501, - message: ApiErrorCode.SyncErrorGroupedFolders, - }); + if (libraries.length === 0) { + // Check if no libraries are found due to the fallback to user views + // This only affects LDAP users + const account = await jellyfinClient.getUser(); + + // Automatic Library grouping is not supported when user views are used to get library + if (account.Configuration.GroupedFolders?.length > 0) { + return next({ + status: 501, + message: ApiErrorCode.SyncErrorGroupedFolders, + }); + } + + return next({ status: 404, message: ApiErrorCode.SyncErrorNoLibraries }); } - - return next({ status: 404, message: ApiErrorCode.SyncErrorNoLibraries }); + } catch (e) { + return next({ + status: e.statusCode ?? 500, + message: e.errorCode ?? ApiErrorCode.Unknown, + }); } const newLibraries: Library[] = libraries.map((library) => { diff --git a/src/components/Settings/SettingsJellyfin.tsx b/src/components/Settings/SettingsJellyfin.tsx index e9ff3bed4..a0b9ac363 100644 --- a/src/components/Settings/SettingsJellyfin.tsx +++ b/src/components/Settings/SettingsJellyfin.tsx @@ -47,6 +47,8 @@ const messages = defineMessages('components.Settings', { 'Custom authentication with Automatic Library Grouping not supported', jellyfinSyncFailedGenericError: 'Something went wrong while syncing libraries', + jellyfinSyncFailedConnectionError: + 'Unable to reach the {mediaServerName} server. Check that it is running and reachable from Seerr.', toggleLibraryFailure: 'Failed to update library.', invalidurlerror: 'Unable to connect to {mediaServerName} server.', syncing: 'Syncing', @@ -100,7 +102,7 @@ const SettingsJellyfin: React.FC = ({ const { data: dataSync, mutate: revalidateSync } = useSWR( '/api/v1/settings/jellyfin/sync', { - refreshInterval: (latestData) => (latestData?.running ? 1000 : 0), + refreshInterval: (latestData) => (latestData?.running ? 1000 : 10000), } ); const intl = useIntl(); @@ -175,6 +177,19 @@ const SettingsJellyfin: React.FC = ({ appearance: 'warning', } ); + } else if (e?.response?.data?.message === 'CONNECTION_ERROR') { + addToast( + intl.formatMessage(messages.jellyfinSyncFailedConnectionError, { + mediaServerName: + settings.currentSettings.mediaServerType === MediaServerType.EMBY + ? 'Emby' + : 'Jellyfin', + }), + { + autoDismiss: true, + appearance: 'error', + } + ); } else if (e?.response?.data?.message === 'SYNC_ERROR_NO_LIBRARIES') { addToast( intl.formatMessage(messages.jellyfinSyncFailedNoLibrariesFound), diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index ea0b089bd..17b24621b 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -129,7 +129,7 @@ const SettingsPlex = ({ isSetupSettings }: SettingsPlexProps) => { const { data: dataSync, mutate: revalidateSync } = useSWR( '/api/v1/settings/plex/sync', { - refreshInterval: (latestData) => (latestData?.running ? 1000 : 0), + refreshInterval: (latestData) => (latestData?.running ? 1000 : 10000), } ); const intl = useIntl(); diff --git a/src/components/Setup/index.tsx b/src/components/Setup/index.tsx index af52b1f00..dbf2f65fd 100644 --- a/src/components/Setup/index.tsx +++ b/src/components/Setup/index.tsx @@ -2,6 +2,7 @@ import EmbyLogo from '@app/assets/services/emby.svg'; import JellyfinLogo from '@app/assets/services/jellyfin.svg'; import PlexLogo from '@app/assets/services/plex.svg'; import AppDataWarning from '@app/components/AppDataWarning'; +import Alert from '@app/components/Common/Alert'; import Button from '@app/components/Common/Button'; import ImageFader from '@app/components/Common/ImageFader'; import PageTitle from '@app/components/Common/PageTitle'; @@ -37,6 +38,8 @@ const messages = defineMessages('components.Setup', { signin: 'Sign In', configuremediaserver: 'Configure Media Server', configureservices: 'Configure Services', + librarieserror: + 'Unable to load libraries from your media server. Check that it is reachable and still configured correctly.', }); const Setup = () => { @@ -72,9 +75,10 @@ const Setup = () => { [MediaServerType.NOT_CONFIGURED]: null, }; - const { data: mediaServerSettings } = useSWR<{ libraries: Library[] }>( - currentStep === 3 ? mediaServerSettingsEndpoint[mediaServerType] : null - ); + const { data: mediaServerSettings, error: mediaServerSettingsError } = + useSWR<{ libraries: Library[] }>( + currentStep === 3 ? mediaServerSettingsEndpoint[mediaServerType] : null + ); const mediaServerSettingsComplete = !!mediaServerSettings?.libraries?.some( (library) => library.enabled @@ -233,6 +237,12 @@ const Setup = () => { )} {currentStep === 3 && (
+ {!!mediaServerSettingsError && ( + + )} {mediaServerType === MediaServerType.PLEX ? ( ) : ( diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 7da975e49..93b5ab5e7 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -1214,6 +1214,7 @@ "components.Settings.jellyfinSettingsFailure": "Something went wrong while saving {mediaServerName} settings.", "components.Settings.jellyfinSettingsSuccess": "{mediaServerName} settings saved successfully!", "components.Settings.jellyfinSyncFailedAutomaticGroupedFolders": "Custom authentication with Automatic Library Grouping not supported", + "components.Settings.jellyfinSyncFailedConnectionError": "Unable to reach the {mediaServerName} server. Check that it is running and reachable from Seerr.", "components.Settings.jellyfinSyncFailedGenericError": "Something went wrong while syncing libraries", "components.Settings.jellyfinSyncFailedNoLibrariesFound": "No libraries were found", "components.Settings.jellyfinlibraries": "{mediaServerName} Libraries", @@ -1329,6 +1330,7 @@ "components.Setup.continue": "Continue", "components.Setup.finish": "Finish Setup", "components.Setup.finishing": "Finishing…", + "components.Setup.librarieserror": "Unable to load libraries from your media server. Check that it is reachable and still configured correctly.", "components.Setup.servertype": "Choose Server Type", "components.Setup.setup": "Setup", "components.Setup.signin": "Sign In",