fix: add support for ssl when connecting to plex

fixes #275
This commit is contained in:
sct
2020-12-15 03:22:54 +00:00
parent f998873fc5
commit 3ba09d07eb
5 changed files with 165 additions and 110 deletions

View File

@@ -62,6 +62,7 @@ class PlexAPI {
this.plexClient = new NodePlexAPI({ this.plexClient = new NodePlexAPI({
hostname: settings.plex.ip, hostname: settings.plex.ip,
port: settings.plex.port, port: settings.plex.port,
https: settings.plex.useSsl,
token: plexToken, token: plexToken,
authenticator: { authenticator: {
authenticate: ( authenticate: (

View File

@@ -14,6 +14,7 @@ export interface PlexSettings {
machineId?: string; machineId?: string;
ip: string; ip: string;
port: number; port: number;
useSsl?: boolean;
libraries: Library[]; libraries: Library[];
} }
@@ -109,6 +110,7 @@ class Settings {
name: '', name: '',
ip: '127.0.0.1', ip: '127.0.0.1',
port: 32400, port: 32400,
useSsl: false,
libraries: [], libraries: [],
}, },
radarr: [], radarr: [],

View File

@@ -4,6 +4,7 @@ declare module 'plex-api' {
hostname: string; hostname: string;
port: number; port: number;
token?: string; token?: string;
https?: boolean;
authenticator: { authenticator: {
authenticate: ( authenticate: (
_plexApi: PlexAPI, _plexApi: PlexAPI,

View File

@@ -2,21 +2,23 @@ import React, { useState } from 'react';
import LoadingSpinner from '../Common/LoadingSpinner'; import LoadingSpinner from '../Common/LoadingSpinner';
import type { PlexSettings } from '../../../server/lib/settings'; import type { PlexSettings } from '../../../server/lib/settings';
import useSWR from 'swr'; import useSWR from 'swr';
import { useFormik } from 'formik'; import { Formik, Field } from 'formik';
import Button from '../Common/Button'; import Button from '../Common/Button';
import axios from 'axios'; import axios from 'axios';
import LibraryItem from './LibraryItem'; import LibraryItem from './LibraryItem';
import Badge from '../Common/Badge'; import Badge from '../Common/Badge';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import * as Yup from 'yup';
const messages = defineMessages({ const messages = defineMessages({
plexsettings: 'Plex Settings', plexsettings: 'Plex Settings',
plexsettingsDescription: plexsettingsDescription:
'Configure the settings for your Plex server. Overseerr uses your Plex server to scan your library at an interval and see what content is available.', 'Configure the settings for your Plex server. Overseerr uses your Plex server to scan your library at an interval and see what content is available.',
servername: 'Server Name (Automatically Set)', servername: 'Server Name (Automatically set after you save)',
servernamePlaceholder: 'Plex Server Name', servernamePlaceholder: 'Plex Server Name',
hostname: 'Hostname/IP', hostname: 'Hostname/IP',
port: 'Port', port: 'Port',
ssl: 'SSL',
save: 'Save Changes', save: 'Save Changes',
saving: 'Saving...', saving: 'Saving...',
plexlibraries: 'Plex Libraries', plexlibraries: 'Plex Libraries',
@@ -32,6 +34,8 @@ const messages = defineMessages({
librariesRemaining: 'Libraries Remaining: {count}', librariesRemaining: 'Libraries Remaining: {count}',
startscan: 'Start Scan', startscan: 'Start Scan',
cancelscan: 'Cancel Scan', cancelscan: 'Cancel Scan',
validationHostnameRequired: 'You must provide a hostname/IP',
validationPortRequired: 'You must provide a port',
}); });
interface Library { interface Library {
@@ -64,33 +68,15 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
} }
); );
const [isSyncing, setIsSyncing] = useState(false); const [isSyncing, setIsSyncing] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
const [submitError, setSubmitError] = useState<string | null>(null); const [submitError, setSubmitError] = useState<string | null>(null);
const formik = useFormik({
initialValues: {
hostname: data?.ip,
port: data?.port,
},
enableReinitialize: true,
onSubmit: async (values) => {
setSubmitError(null);
setIsUpdating(true);
try {
await axios.post('/api/v1/settings/plex', {
ip: values.hostname,
port: Number(values.port),
} as PlexSettings);
revalidate(); const PlexSettingsSchema = Yup.object().shape({
if (onComplete) { hostname: Yup.string().required(
onComplete(); intl.formatMessage(messages.validationHostnameRequired)
} ),
} catch (e) { port: Yup.number().required(
setSubmitError(e.response.data.message); intl.formatMessage(messages.validationPortRequired)
} finally { ),
setIsUpdating(false);
}
},
}); });
const activeLibraries = const activeLibraries =
@@ -164,91 +150,154 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
<FormattedMessage {...messages.plexsettingsDescription} /> <FormattedMessage {...messages.plexsettingsDescription} />
</p> </p>
</div> </div>
<form onSubmit={formik.handleSubmit}> <Formik
<div className="mt-6 sm:mt-5"> initialValues={{
{submitError && ( hostname: data?.ip,
<div className="bg-red-700 text-white p-4 rounded-md mb-6"> port: data?.port,
{submitError} useSsl: data?.useSsl,
</div> }}
)} enableReinitialize
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5"> validationSchema={PlexSettingsSchema}
<label onSubmit={async (values) => {
htmlFor="name" setSubmitError(null);
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2" try {
> await axios.post('/api/v1/settings/plex', {
<FormattedMessage {...messages.servername} /> ip: values.hostname,
</label> port: Number(values.port),
<div className="mt-1 sm:mt-0 sm:col-span-2"> useSsl: values.useSsl,
<div className="max-w-lg flex rounded-md shadow-sm"> } as PlexSettings);
<input
type="text" revalidate();
id="name" if (onComplete) {
name="name" onComplete();
placeholder={intl.formatMessage( }
messages.servernamePlaceholder } catch (e) {
)} setSubmitError(e.response.data.message);
value={data?.name} }
readOnly }}
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500" >
/> {({
errors,
touched,
values,
handleSubmit,
setFieldValue,
isSubmitting,
}) => {
return (
<form onSubmit={handleSubmit}>
<div className="mt-6 sm:mt-5">
{submitError && (
<div className="bg-red-700 text-white p-4 rounded-md mb-6">
{submitError}
</div>
)}
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
<label
htmlFor="name"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
>
<FormattedMessage {...messages.servername} />
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<div className="max-w-lg flex rounded-md shadow-sm">
<input
type="text"
id="name"
name="name"
placeholder={intl.formatMessage(
messages.servernamePlaceholder
)}
value={data?.name}
readOnly
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
/>
</div>
</div>
</div>
<div className="mt-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
<label
htmlFor="hostname"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
>
<FormattedMessage {...messages.hostname} />
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<div className="max-w-lg flex rounded-md shadow-sm">
<Field
type="text"
id="hostname"
name="hostname"
placeholder="127.0.0.1"
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
/>
</div>
{errors.hostname && touched.hostname && (
<div className="text-red-500 mt-2">{errors.hostname}</div>
)}
</div>
</div>
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
<label
htmlFor="port"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
>
<FormattedMessage {...messages.port} />
</label>
<div className="mt-1 sm:mt-0 sm:col-span-2">
<div className="max-w-lg rounded-md shadow-sm sm:max-w-xs">
<Field
type="text"
id="port"
name="port"
placeholder="32400"
className="form-input block w-24 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
/>
</div>
{errors.port && touched.port && (
<div className="text-red-500 mt-2">{errors.port}</div>
)}
</div>
</div>
</div> </div>
</div> <div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5">
</div> <label
<div className="mt-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5"> htmlFor="ssl"
<label className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
htmlFor="hostname" >
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2" {intl.formatMessage(messages.ssl)}
> </label>
<FormattedMessage {...messages.hostname} /> <div className="mt-1 sm:mt-0 sm:col-span-2">
</label> <Field
<div className="mt-1 sm:mt-0 sm:col-span-2"> type="checkbox"
<div className="max-w-lg flex rounded-md shadow-sm"> id="useSsl"
<input name="useSsl"
type="text" onChange={() => {
id="hostname" setFieldValue('useSsl', !values.useSsl);
name="hostname" }}
placeholder="127.0.0.1" className="form-checkbox h-6 w-6 rounded-md text-indigo-600 transition duration-150 ease-in-out"
value={formik.values.hostname} />
onChange={formik.handleChange} </div>
className="flex-1 form-input block w-full min-w-0 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
/>
</div> </div>
</div> <div className="mt-8 border-t border-gray-700 pt-5">
</div> <div className="flex justify-end">
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5"> <span className="ml-3 inline-flex rounded-md shadow-sm">
<label <Button
htmlFor="port" buttonType="primary"
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2" type="submit"
> disabled={isSubmitting}
<FormattedMessage {...messages.port} /> >
</label> {isSubmitting
<div className="mt-1 sm:mt-0 sm:col-span-2"> ? intl.formatMessage(messages.saving)
<div className="max-w-lg rounded-md shadow-sm sm:max-w-xs"> : intl.formatMessage(messages.save)}
<input </Button>
type="text" </span>
id="port" </div>
name="port"
placeholder="32400"
value={formik.values.port}
onChange={formik.handleChange}
className="form-input block w-24 rounded-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
/>
</div> </div>
</div> </form>
</div> );
</div> }}
<div className="mt-8 border-t border-gray-700 pt-5"> </Formik>
<div className="flex justify-end">
<span className="ml-3 inline-flex rounded-md shadow-sm">
<Button buttonType="primary" type="submit" disabled={isUpdating}>
{isUpdating
? intl.formatMessage(messages.saving)
: intl.formatMessage(messages.save)}
</Button>
</span>
</div>
</div>
</form>
<div className="mt-10"> <div className="mt-10">
<h3 className="text-lg leading-6 font-medium text-gray-200"> <h3 className="text-lg leading-6 font-medium text-gray-200">
<FormattedMessage {...messages.plexlibraries} /> <FormattedMessage {...messages.plexlibraries} />

View File

@@ -210,7 +210,7 @@
"components.Settings.runnow": "Run Now", "components.Settings.runnow": "Run Now",
"components.Settings.save": "Save Changes", "components.Settings.save": "Save Changes",
"components.Settings.saving": "Saving...", "components.Settings.saving": "Saving...",
"components.Settings.servername": "Server Name (Automatically Set)", "components.Settings.servername": "Server Name (Automatically set after you save)",
"components.Settings.servernamePlaceholder": "Plex Server Name", "components.Settings.servernamePlaceholder": "Plex Server Name",
"components.Settings.sonarrSettingsDescription": "Set up your Sonarr connection below. You can have multiple, but only two active as defaults at any time (one for standard HD and one for 4K). Administrators can override which server is used for new requests.", "components.Settings.sonarrSettingsDescription": "Set up your Sonarr connection below. You can have multiple, but only two active as defaults at any time (one for standard HD and one for 4K). Administrators can override which server is used for new requests.",
"components.Settings.sonarrsettings": "Sonarr Settings", "components.Settings.sonarrsettings": "Sonarr Settings",
@@ -218,6 +218,8 @@
"components.Settings.startscan": "Start Scan", "components.Settings.startscan": "Start Scan",
"components.Settings.sync": "Sync Plex Libraries", "components.Settings.sync": "Sync Plex Libraries",
"components.Settings.syncing": "Syncing…", "components.Settings.syncing": "Syncing…",
"components.Settings.validationHostnameRequired": "You must provide a hostname/IP",
"components.Settings.validationPortRequired": "You must provide a port",
"components.Setup.configureplex": "Configure Plex", "components.Setup.configureplex": "Configure Plex",
"components.Setup.configureservices": "Configure Services", "components.Setup.configureservices": "Configure Services",
"components.Setup.continue": "Continue", "components.Setup.continue": "Continue",