diff --git a/overseerr-api.yml b/overseerr-api.yml index 637af162..1ded2696 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -171,6 +171,9 @@ components: readOnly: true items: $ref: '#/components/schemas/PlexLibrary' + webAppUrl: + type: string + example: 'https://app.plex.tv/desktop' required: - name - machineId diff --git a/server/entity/Media.ts b/server/entity/Media.ts index 3d821651..9666ac28 100644 --- a/server/entity/Media.ts +++ b/server/entity/Media.ts @@ -147,12 +147,22 @@ class Media { @AfterLoad() public setPlexUrls(): void { - const machineId = getSettings().plex.machineId; + const { machineId, webAppUrl } = getSettings().plex; + if (this.ratingKey) { - this.plexUrl = `https://app.plex.tv/desktop#!/server/${machineId}/details?key=%2Flibrary%2Fmetadata%2F${this.ratingKey}`; + this.plexUrl = `${ + webAppUrl ? webAppUrl : 'https://app.plex.tv/desktop' + }#!/server/${machineId}/details?key=%2Flibrary%2Fmetadata%2F${ + this.ratingKey + }`; } + if (this.ratingKey4k) { - this.plexUrl4k = `https://app.plex.tv/desktop#!/server/${machineId}/details?key=%2Flibrary%2Fmetadata%2F${this.ratingKey4k}`; + this.plexUrl4k = `${ + webAppUrl ? webAppUrl : 'https://app.plex.tv/desktop' + }#!/server/${machineId}/details?key=%2Flibrary%2Fmetadata%2F${ + this.ratingKey4k + }`; } } diff --git a/server/lib/settings.ts b/server/lib/settings.ts index a9e459b4..edc4026f 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -30,6 +30,7 @@ export interface PlexSettings { port: number; useSsl?: boolean; libraries: Library[]; + webAppUrl?: string; } export interface DVRSettings { diff --git a/src/components/Settings/Notifications/NotificationsEmail.tsx b/src/components/Settings/Notifications/NotificationsEmail.tsx index a7684bf0..d7e45491 100644 --- a/src/components/Settings/Notifications/NotificationsEmail.tsx +++ b/src/components/Settings/Notifications/NotificationsEmail.tsx @@ -92,15 +92,13 @@ const NotificationsEmail: React.FC = () => { /^(([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i, intl.formatMessage(messages.validationSmtpHostRequired) ), - smtpPort: Yup.number() - .typeError(intl.formatMessage(messages.validationSmtpPortRequired)) - .when('enabled', { - is: true, - then: Yup.number().required( - intl.formatMessage(messages.validationSmtpPortRequired) - ), - otherwise: Yup.number().nullable(), - }), + smtpPort: Yup.number().when('enabled', { + is: true, + then: Yup.number() + .nullable() + .required(intl.formatMessage(messages.validationSmtpPortRequired)), + otherwise: Yup.number().nullable(), + }), pgpPrivateKey: Yup.string() .when('pgpPassword', { is: (value: unknown) => !!value, diff --git a/src/components/Settings/RadarrModal/index.tsx b/src/components/Settings/RadarrModal/index.tsx index 62c04bb2..e25b8c53 100644 --- a/src/components/Settings/RadarrModal/index.tsx +++ b/src/components/Settings/RadarrModal/index.tsx @@ -41,7 +41,7 @@ const messages = defineMessages({ servername: 'Server Name', hostname: 'Hostname or IP Address', port: 'Port', - ssl: 'Enable SSL', + ssl: 'Use SSL', apiKey: 'API Key', baseUrl: 'URL Base', syncEnabled: 'Enable Scan', @@ -116,7 +116,7 @@ const RadarrModal: React.FC = ({ intl.formatMessage(messages.validationHostnameRequired) ), port: Yup.number() - .typeError(intl.formatMessage(messages.validationPortRequired)) + .nullable() .required(intl.formatMessage(messages.validationPortRequired)), apiKey: Yup.string().required( intl.formatMessage(messages.validationApiKeyRequired) @@ -135,33 +135,18 @@ const RadarrModal: React.FC = ({ .test( 'no-trailing-slash', intl.formatMessage(messages.validationApplicationUrlTrailingSlash), - (value) => { - if (value?.substr(value.length - 1) === '/') { - return false; - } - return true; - } + (value) => !value || !value.endsWith('/') ), baseUrl: Yup.string() .test( 'leading-slash', intl.formatMessage(messages.validationBaseUrlLeadingSlash), - (value) => { - if (value && value?.substr(0, 1) !== '/') { - return false; - } - return true; - } + (value) => !value || value.startsWith('/') ) .test( 'no-trailing-slash', intl.formatMessage(messages.validationBaseUrlTrailingSlash), - (value) => { - if (value?.substr(value.length - 1) === '/') { - return false; - } - return true; - } + (value) => !value || !value.endsWith('/') ), }); diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx index b584247e..0130c23f 100644 --- a/src/components/Settings/SettingsPlex.tsx +++ b/src/components/Settings/SettingsPlex.tsx @@ -22,8 +22,6 @@ const messages = defineMessages({ plexsettings: 'Plex Settings', plexsettingsDescription: 'Configure the settings for your Plex server. Overseerr scans your Plex libraries to determine content availability.', - servername: 'Server Name', - servernameTip: 'Automatically retrieved from Plex after saving', serverpreset: 'Server', serverLocal: 'local', serverRemote: 'remote', @@ -41,7 +39,7 @@ const messages = defineMessages({ 'To set up Plex, you can either enter the details manually or select a server retrieved from plex.tv. Press the button to the right of the dropdown to fetch the list of available servers.', hostname: 'Hostname or IP Address', port: 'Port', - enablessl: 'Enable SSL', + enablessl: 'Use SSL', plexlibraries: 'Plex Libraries', plexlibrariesDescription: 'The libraries Overseerr scans for titles. Set up and save your Plex connection settings, then click the button below if no libraries are listed.', @@ -57,6 +55,10 @@ const messages = defineMessages({ cancelscan: 'Cancel Scan', validationHostnameRequired: 'You must provide a valid hostname or IP address', validationPortRequired: 'You must provide a valid port number', + webAppUrl: 'Web App URL', + webAppUrlTip: + 'Optionally direct users to the web app on your server instead of the "hosted" web app', + validationWebAppUrl: 'You must provide a valid Plex Web App URL', }); interface Library { @@ -108,14 +110,18 @@ const SettingsPlex: React.FC = ({ onComplete }) => { const { addToast, removeToast } = useToasts(); const PlexSettingsSchema = Yup.object().shape({ hostname: Yup.string() + .nullable() .required(intl.formatMessage(messages.validationHostnameRequired)) .matches( /^(([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i, intl.formatMessage(messages.validationHostnameRequired) ), port: Yup.number() - .typeError(intl.formatMessage(messages.validationPortRequired)) + .nullable() .required(intl.formatMessage(messages.validationPortRequired)), + webAppUrl: Yup.string() + .nullable() + .url(intl.formatMessage(messages.validationWebAppUrl)), }); const activeLibraries = @@ -282,6 +288,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => { port: data?.port ?? 32400, useSsl: data?.useSsl, selectedPreset: undefined, + webAppUrl: data?.webAppUrl, }} validationSchema={PlexSettingsSchema} onSubmit={async (values) => { @@ -301,6 +308,7 @@ const SettingsPlex: React.FC = ({ onComplete }) => { ip: values.hostname, port: Number(values.port), useSsl: values.useSsl, + webAppUrl: values.webAppUrl, } as PlexSettings); revalidate(); @@ -336,34 +344,12 @@ const SettingsPlex: React.FC = ({ onComplete }) => { }) => { return (
-
- -
-
- -
-
-
-
+