-
-
Notifications
You must be signed in to change notification settings - Fork 407
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
364 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React from 'react'; | ||
|
||
interface LibraryItemProps { | ||
isEnabled?: boolean; | ||
name: string; | ||
onToggle: () => void; | ||
} | ||
|
||
const LibraryItem: React.FC<LibraryItemProps> = ({ | ||
isEnabled, | ||
name, | ||
onToggle, | ||
}) => { | ||
return ( | ||
<li className="col-span-1 flex shadow-sm rounded-md"> | ||
<div className="flex-1 flex items-center justify-between border-t border-r border-b border-cool-gray-700 bg-cool-gray-600 rounded-md truncate"> | ||
<div className="flex-1 px-4 py-6 text-sm leading-5 truncate cursor-default"> | ||
{name} | ||
</div> | ||
<div className="flex-shrink-0 pr-2"> | ||
{/* <!-- On: "bg-indigo-600", Off: "bg-gray-200" --> */} | ||
<span | ||
role="checkbox" | ||
tabIndex={0} | ||
aria-checked={isEnabled} | ||
onClick={() => onToggle()} | ||
onKeyDown={(e) => { | ||
if (e.key === 'Enter') { | ||
onToggle(); | ||
} | ||
}} | ||
className={`${ | ||
isEnabled ? 'bg-indigo-600' : 'bg-cool-gray-700' | ||
} relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:shadow-outline`} | ||
> | ||
{/* <!-- On: "translate-x-5", Off: "translate-x-0" --> */} | ||
<span | ||
aria-hidden="true" | ||
className={`${ | ||
isEnabled ? 'translate-x-5' : 'translate-x-0' | ||
} relative inline-block h-5 w-5 rounded-full bg-white shadow transform transition ease-in-out duration-200`} | ||
> | ||
{/* <!-- On: "opacity-0 ease-out duration-100", Off: "opacity-100 ease-in duration-200" --> */} | ||
<span | ||
className={`${ | ||
isEnabled | ||
? 'opacity-0 ease-out duration-100' | ||
: 'opacity-100 ease-in duration-200' | ||
} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity`} | ||
> | ||
<svg | ||
className="h-3 w-3 text-gray-400" | ||
fill="none" | ||
viewBox="0 0 12 12" | ||
> | ||
<path | ||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" | ||
stroke="currentColor" | ||
strokeWidth="2" | ||
strokeLinecap="round" | ||
strokeLinejoin="round" | ||
/> | ||
</svg> | ||
</span> | ||
{/* <!-- On: "opacity-100 ease-in duration-200", Off: "opacity-0 ease-out duration-100" --> */} | ||
<span | ||
className={`${ | ||
isEnabled | ||
? 'opacity-100 ease-in duration-200' | ||
: 'opacity-0 ease-out duration-100' | ||
} absolute inset-0 h-full w-full flex items-center justify-center transition-opacity`} | ||
> | ||
<svg | ||
className="h-3 w-3 text-indigo-600" | ||
fill="currentColor" | ||
viewBox="0 0 12 12" | ||
> | ||
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" /> | ||
</svg> | ||
</span> | ||
</span> | ||
</span> | ||
</div> | ||
</div> | ||
</li> | ||
); | ||
}; | ||
|
||
export default LibraryItem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import React, { useState } from 'react'; | ||
import LoadingSpinner from '../Common/LoadingSpinner'; | ||
import type { PlexSettings } from '../../../server/lib/settings'; | ||
import useSWR from 'swr'; | ||
import { useFormik } from 'formik'; | ||
import Button from '../Common/Button'; | ||
import axios from 'axios'; | ||
import LibraryItem from './LibraryItem'; | ||
|
||
const SettingsPlex: React.FC = () => { | ||
const { data, error, revalidate } = useSWR<PlexSettings>( | ||
'/api/v1/settings/plex' | ||
); | ||
const [isSyncing, setIsSyncing] = useState(false); | ||
const [isUpdating, setIsUpdating] = useState(false); | ||
const [submitError, setSubmitError] = useState<string | null>(null); | ||
const formik = useFormik({ | ||
initialValues: { | ||
hostname: data?.ip, | ||
port: data?.port, | ||
}, | ||
onSubmit: async (values) => { | ||
setIsUpdating(true); | ||
try { | ||
await axios.post('/api/v1/settings/plex', { | ||
ip: values.hostname, | ||
port: values.port, | ||
} as PlexSettings); | ||
|
||
revalidate(); | ||
} catch (e) { | ||
setSubmitError(e.message); | ||
} finally { | ||
setIsUpdating(false); | ||
} | ||
}, | ||
}); | ||
|
||
const activeLibraries = | ||
data?.libraries | ||
.filter((library) => library.enabled) | ||
.map((library) => library.id) ?? []; | ||
|
||
const syncLibraries = async () => { | ||
setIsSyncing(true); | ||
await axios.get('/api/v1/settings/plex/library', { | ||
params: { | ||
sync: true, | ||
enable: | ||
activeLibraries.length > 0 ? activeLibraries.join(',') : undefined, | ||
}, | ||
}); | ||
setIsSyncing(false); | ||
revalidate(); | ||
}; | ||
|
||
const toggleLibrary = async (libraryId: string) => { | ||
setIsSyncing(true); | ||
if (activeLibraries.includes(libraryId)) { | ||
await axios.get('/api/v1/settings/plex/library', { | ||
params: { | ||
enable: | ||
activeLibraries.length > 0 | ||
? activeLibraries.filter((id) => id !== libraryId).join(',') | ||
: undefined, | ||
}, | ||
}); | ||
} else { | ||
await axios.get('/api/v1/settings/plex/library', { | ||
params: { | ||
enable: [...activeLibraries, libraryId].join(','), | ||
}, | ||
}); | ||
} | ||
setIsSyncing(false); | ||
revalidate(); | ||
}; | ||
|
||
if (!data && !error) { | ||
return <LoadingSpinner />; | ||
} | ||
return ( | ||
<> | ||
<div> | ||
<h3 className="text-lg leading-6 font-medium text-cool-gray-200"> | ||
Plex Settings | ||
</h3> | ||
<p className="mt-1 max-w-2xl text-sm leading-5 text-cool-gray-500"> | ||
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. | ||
</p> | ||
</div> | ||
<form onSubmit={formik.handleSubmit}> | ||
<div className="mt-6 sm:mt-5"> | ||
<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-cool-gray-400 sm:mt-px sm:pt-2" | ||
> | ||
Server Name (Automatically set) | ||
</label> | ||
<div className="mt-1 sm:mt-0 sm:col-span-2"> | ||
<div className="max-w-lg flex rounded-md shadow-sm"> | ||
<input | ||
id="name" | ||
name="name" | ||
placeholder="Plex Server Name (will be set automatically)" | ||
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-cool-gray-700 border border-cool-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-cool-gray-400 sm:mt-px sm:pt-2" | ||
> | ||
Hostname/IP | ||
</label> | ||
<div className="mt-1 sm:mt-0 sm:col-span-2"> | ||
<div className="max-w-lg flex rounded-md shadow-sm"> | ||
<input | ||
id="hostname" | ||
name="hostname" | ||
placeholder="127.0.0.1" | ||
value={formik.values.hostname} | ||
onChange={formik.handleChange} | ||
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-cool-gray-700 border border-cool-gray-500" | ||
/> | ||
</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-cool-gray-400 sm:mt-px sm:pt-2" | ||
> | ||
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"> | ||
<input | ||
id="port" | ||
name="port" | ||
placeholder="32400" | ||
value={formik.values.port} | ||
onChange={formik.handleChange} | ||
className="form-input block w-24 transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-cool-gray-700 border border-cool-gray-500" | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="mt-8 border-t border-cool-gray-700 pt-5"> | ||
<div className="flex justify-end"> | ||
<span className="ml-3 inline-flex rounded-md shadow-sm"> | ||
<Button buttonType="primary" type="submit" disabled={isUpdating}> | ||
{isUpdating ? 'Saving...' : 'Save Changes'} | ||
</Button> | ||
</span> | ||
</div> | ||
</div> | ||
<div className="mt-10"> | ||
<h3 className="text-lg leading-6 font-medium text-cool-gray-200"> | ||
Plex Libraries | ||
</h3> | ||
<p className="mt-1 max-w-2xl text-sm leading-5 text-cool-gray-500"> | ||
These are the libraries Overseerr will scan for titles. If you see | ||
no libraries listed, you will need to run at least one sync by | ||
clicking the button below. You must first configure and save your | ||
plex connection settings before you will be able to retrieve your | ||
libraries. | ||
</p> | ||
<div className="mt-6"> | ||
<Button onClick={() => syncLibraries()} disabled={isSyncing}> | ||
<svg | ||
className={`${isSyncing ? 'animate-spin' : ''} w-5 h-5 mr-1`} | ||
fill="currentColor" | ||
viewBox="0 0 20 20" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
fillRule="evenodd" | ||
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" | ||
clipRule="evenodd" | ||
/> | ||
</svg> | ||
{isSyncing ? 'Syncing...' : 'Sync Plex Libraries'} | ||
</Button> | ||
</div> | ||
<ul className="mt-6 grid grid-cols-1 gap-5 sm:gap-6 sm:grid-cols-2 lg:grid-cols-4"> | ||
{data?.libraries.map((library) => ( | ||
<LibraryItem | ||
name={library.name} | ||
isEnabled={library.enabled} | ||
key={`setting-library-${library.id}`} | ||
onToggle={() => toggleLibrary(library.id)} | ||
/> | ||
))} | ||
</ul> | ||
</div> | ||
</form> | ||
</> | ||
); | ||
}; | ||
|
||
export default SettingsPlex; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import type { NextPage } from 'next'; | ||
import SettingsLayout from '../../components/Settings/SettingsLayout'; | ||
import SettingsPlex from '../../components/Settings/SettingsPlex'; | ||
|
||
const PlexSettingsPage: NextPage = () => { | ||
return ( | ||
<SettingsLayout> | ||
<SettingsPlex /> | ||
</SettingsLayout> | ||
); | ||
}; | ||
|
||
export default PlexSettingsPage; |
Oops, something went wrong.