From 47714b698cf4351c1ee38bdf0b672d9f0baed03a Mon Sep 17 00:00:00 2001 From: sct Date: Fri, 30 Oct 2020 10:49:35 +0000 Subject: [PATCH] feat(frontend): plex settings page --- overseerr-api.yml | 2 + package.json | 1 + server/lib/settings.ts | 2 +- src/components/Settings/LibraryItem.tsx | 89 ++++++++++ src/components/Settings/SettingsPlex.tsx | 210 +++++++++++++++++++++++ src/pages/settings/plex.tsx | 14 ++ yarn.lock | 47 +++++ 7 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 src/components/Settings/LibraryItem.tsx create mode 100644 src/components/Settings/SettingsPlex.tsx create mode 100644 src/pages/settings/plex.tsx diff --git a/overseerr-api.yml b/overseerr-api.yml index 6fe35de6e..511920bc3 100644 --- a/overseerr-api.yml +++ b/overseerr-api.yml @@ -789,6 +789,8 @@ paths: nullable: true - in: query name: enable + explode: false + allowReserved: true description: Comma separated list of libraries to enable. Any libraries not passed will be disabled! schema: type: string diff --git a/package.json b/package.json index 2297f355a..0dd14e9ef 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "express": "^4.17.1", "express-openapi-validator": "^3.16.15", "express-session": "^1.17.1", + "formik": "^2.2.1", "intl": "^1.2.5", "lodash": "^4.17.20", "next": "9.5.4", diff --git a/server/lib/settings.ts b/server/lib/settings.ts index 250871ba7..238d5817e 100644 --- a/server/lib/settings.ts +++ b/server/lib/settings.ts @@ -8,7 +8,7 @@ export interface Library { enabled: boolean; } -interface PlexSettings { +export interface PlexSettings { name: string; machineId?: string; ip: string; diff --git a/src/components/Settings/LibraryItem.tsx b/src/components/Settings/LibraryItem.tsx new file mode 100644 index 000000000..6f28c3f38 --- /dev/null +++ b/src/components/Settings/LibraryItem.tsx @@ -0,0 +1,89 @@ +import React from 'react'; + +interface LibraryItemProps { + isEnabled?: boolean; + name: string; + onToggle: () => void; +} + +const LibraryItem: React.FC = ({ + isEnabled, + name, + onToggle, +}) => { + return ( +
  • +
    +
    + {name} +
    +
    + {/* */} + 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`} + > + {/* */} + + +
    +
    +
  • + ); +}; + +export default LibraryItem; diff --git a/src/components/Settings/SettingsPlex.tsx b/src/components/Settings/SettingsPlex.tsx new file mode 100644 index 000000000..5c6e31906 --- /dev/null +++ b/src/components/Settings/SettingsPlex.tsx @@ -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( + '/api/v1/settings/plex' + ); + const [isSyncing, setIsSyncing] = useState(false); + const [isUpdating, setIsUpdating] = useState(false); + const [submitError, setSubmitError] = useState(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 ; + } + return ( + <> +
    +

    + Plex Settings +

    +

    + 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. +

    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +

    + Plex Libraries +

    +

    + 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. +

    +
    + +
    +
      + {data?.libraries.map((library) => ( + toggleLibrary(library.id)} + /> + ))} +
    +
    +
    + + ); +}; + +export default SettingsPlex; diff --git a/src/pages/settings/plex.tsx b/src/pages/settings/plex.tsx new file mode 100644 index 000000000..d05887308 --- /dev/null +++ b/src/pages/settings/plex.tsx @@ -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 ( + + + + ); +}; + +export default PlexSettingsPage; diff --git a/yarn.lock b/yarn.lock index af1535dcb..bb4fa5fc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4021,6 +4021,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -5100,6 +5105,20 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" +formik@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.1.tgz#e09747569e44ffc17263541d2e732cc6568208dc" + integrity sha512-N/8Q1yGlXHibyrM5CyKtC85V8U+mxY04zSfakpyR1e6KpaIC4+A4yo30NBARRprkFoxoT1EV+yK8bo5tjXxfyg== + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.14" + lodash-es "^4.17.14" + react-fast-compare "^2.0.1" + scheduler "^0.18.0" + tiny-warning "^1.0.2" + tslib "^1.10.0" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -6437,6 +6456,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash-es@^4.17.14: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -8462,6 +8486,11 @@ react-dom@16.13.1: prop-types "^15.6.2" scheduler "^0.19.1" +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-intl@^5.8.5: version "5.8.5" resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-5.8.5.tgz#bc5dfab259049830621e129b8bffb1ac33ef4124" @@ -8979,6 +9008,14 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +scheduler@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4" + integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" @@ -9887,6 +9924,11 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -10009,6 +10051,11 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +tslib@^1.10.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tslib@^1.8.1, tslib@^1.9.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"