From 47bc073fb41a8c5590a8cacd4eb321758ffa5db6 Mon Sep 17 00:00:00 2001 From: Ben Phelps Date: Mon, 26 Sep 2022 02:23:02 +0300 Subject: [PATCH] widget refactoring and cleanup --- src/components/services/item.jsx | 2 +- .../services/widgets/service/adguard.jsx | 45 ---- .../services/widgets/service/bazarr.jsx | 36 --- .../widgets/service/coinmarketcap.jsx | 90 ------- .../services/widgets/service/docker.jsx | 63 ----- .../services/widgets/service/emby.jsx | 239 ------------------ .../services/widgets/service/gotify.jsx | 29 --- .../services/widgets/service/jackett.jsx | 37 --- .../services/widgets/service/jellyfin.jsx | 5 - .../services/widgets/service/jellyseerr.jsx | 37 --- .../services/widgets/service/overseerr.jsx | 37 --- .../services/widgets/service/portainer.jsx | 47 ---- .../services/widgets/service/prowlarr.jsx | 55 ---- .../services/widgets/service/qbittorrent.jsx | 69 ----- .../services/widgets/service/radarr.jsx | 38 --- .../services/widgets/service/readarr.jsx | 39 --- .../services/widgets/service/rutorrent.jsx | 43 ---- .../services/widgets/service/sabnzbd.jsx | 37 --- .../services/widgets/service/sonarr.jsx | 39 --- .../services/widgets/service/speedtest.jsx | 46 ---- .../services/widgets/service/strelaysrv.jsx | 44 ---- .../services/widgets/service/tautulli.jsx | 183 -------------- .../services/widgets/service/traefik.jsx | 37 --- .../services/widgets/service/transmission.jsx | 70 ----- src/utils/api-helpers.js | 22 +- src/widgets/components.js | 7 +- .../mastodon/component.jsx} | 7 +- src/widgets/mastodon/widget.js | 14 + .../npm.jsx => widgets/npm/component.jsx} | 7 +- .../proxies/npm.js => widgets/npm/proxy.js} | 0 src/widgets/npm/widget.js | 8 + .../nzbget/component.jsx} | 7 +- .../nzbget.js => widgets/nzbget/proxy.js} | 0 src/widgets/nzbget/widget.js | 7 + .../ombi.jsx => widgets/ombi/component.jsx} | 7 +- src/widgets/ombi/widget.js | 14 + .../pihole/component.jsx} | 7 +- src/widgets/pihole/widget.js | 14 + src/widgets/widgets.js | 14 +- 39 files changed, 92 insertions(+), 1410 deletions(-) delete mode 100644 src/components/services/widgets/service/adguard.jsx delete mode 100644 src/components/services/widgets/service/bazarr.jsx delete mode 100644 src/components/services/widgets/service/coinmarketcap.jsx delete mode 100644 src/components/services/widgets/service/docker.jsx delete mode 100644 src/components/services/widgets/service/emby.jsx delete mode 100644 src/components/services/widgets/service/gotify.jsx delete mode 100644 src/components/services/widgets/service/jackett.jsx delete mode 100644 src/components/services/widgets/service/jellyfin.jsx delete mode 100644 src/components/services/widgets/service/jellyseerr.jsx delete mode 100644 src/components/services/widgets/service/overseerr.jsx delete mode 100644 src/components/services/widgets/service/portainer.jsx delete mode 100644 src/components/services/widgets/service/prowlarr.jsx delete mode 100644 src/components/services/widgets/service/qbittorrent.jsx delete mode 100644 src/components/services/widgets/service/radarr.jsx delete mode 100644 src/components/services/widgets/service/readarr.jsx delete mode 100644 src/components/services/widgets/service/rutorrent.jsx delete mode 100644 src/components/services/widgets/service/sabnzbd.jsx delete mode 100644 src/components/services/widgets/service/sonarr.jsx delete mode 100644 src/components/services/widgets/service/speedtest.jsx delete mode 100644 src/components/services/widgets/service/strelaysrv.jsx delete mode 100644 src/components/services/widgets/service/tautulli.jsx delete mode 100644 src/components/services/widgets/service/traefik.jsx delete mode 100644 src/components/services/widgets/service/transmission.jsx rename src/{components/services/widgets/service/mastodon.jsx => widgets/mastodon/component.jsx} (86%) create mode 100644 src/widgets/mastodon/widget.js rename src/{components/services/widgets/service/npm.jsx => widgets/npm/component.jsx} (85%) rename src/{utils/proxies/npm.js => widgets/npm/proxy.js} (100%) create mode 100644 src/widgets/npm/widget.js rename src/{components/services/widgets/service/nzbget.jsx => widgets/nzbget/component.jsx} (86%) rename src/{utils/proxies/nzbget.js => widgets/nzbget/proxy.js} (100%) create mode 100644 src/widgets/nzbget/widget.js rename src/{components/services/widgets/service/ombi.jsx => widgets/ombi/component.jsx} (83%) create mode 100644 src/widgets/ombi/widget.js rename src/{components/services/widgets/service/pihole.jsx => widgets/pihole/component.jsx} (85%) create mode 100644 src/widgets/pihole/widget.js diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx index 93fa83ac3..7974789f6 100644 --- a/src/components/services/item.jsx +++ b/src/components/services/item.jsx @@ -4,8 +4,8 @@ import { useContext, useState } from "react"; import Status from "./status"; import Widget from "./widget"; -import Docker from "./widgets/service/docker"; +import Docker from "widgets/docker/component"; import { SettingsContext } from "utils/settings-context"; function resolveIcon(icon) { diff --git a/src/components/services/widgets/service/adguard.jsx b/src/components/services/widgets/service/adguard.jsx deleted file mode 100644 index 1befec86d..000000000 --- a/src/components/services/widgets/service/adguard.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function AdGuard({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: adguardData, error: adguardError } = useSWR(formatProxyUrl(config, "stats")); - - if (adguardError) { - return ; - } - - if (!adguardData) { - return ( - - - - - - - ); - } - - const filtered = - adguardData.num_replaced_safebrowsing + adguardData.num_replaced_safesearch + adguardData.num_replaced_parental; - - return ( - - - - - - - ); -} diff --git a/src/components/services/widgets/service/bazarr.jsx b/src/components/services/widgets/service/bazarr.jsx deleted file mode 100644 index 33b4defcb..000000000 --- a/src/components/services/widgets/service/bazarr.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Bazarr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: episodesData, error: episodesError } = useSWR(formatProxyUrl(config, "episodes")); - const { data: moviesData, error: moviesError } = useSWR(formatProxyUrl(config, "movies")); - - if (episodesError || moviesError) { - return ; - } - - if (!episodesData || !moviesData) { - return ( - - - - - ); - } - - return ( - - - - - ); -} diff --git a/src/components/services/widgets/service/coinmarketcap.jsx b/src/components/services/widgets/service/coinmarketcap.jsx deleted file mode 100644 index d775e3fa1..000000000 --- a/src/components/services/widgets/service/coinmarketcap.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import useSWR from "swr"; -import { useState } from "react"; -import { useTranslation } from "next-i18next"; -import classNames from "classnames"; - -import Widget from "../widget"; -import Block from "../block"; - -import Dropdown from "components/services/dropdown"; -import { formatProxyUrl } from "utils/api-helpers"; - -export default function CoinMarketCap({ service }) { - const { t } = useTranslation(); - - const dateRangeOptions = [ - { label: t("coinmarketcap.1hour"), value: "1h" }, - { label: t("coinmarketcap.1day"), value: "24h" }, - { label: t("coinmarketcap.7days"), value: "7d" }, - { label: t("coinmarketcap.30days"), value: "30d" }, - ]; - - const [dateRange, setDateRange] = useState(dateRangeOptions[0].value); - - const config = service.widget; - const currencyCode = config.currency ?? "USD"; - const { symbols } = config; - - const { data: statsData, error: statsError } = useSWR( - formatProxyUrl(config, `v1/cryptocurrency/quotes/latest?symbol=${symbols.join(",")}&convert=${currencyCode}`) - ); - - if (!symbols || symbols.length === 0) { - return ( - - - - ); - } - - if (statsError) { - return ; - } - - if (!statsData || !dateRange) { - return ( - - - - ); - } - - const { data } = statsData; - - return ( - -
- -
- -
- {symbols.map((symbol) => ( -
-
{data[symbol].name}
-
-
- {t("common.number", { - value: data[symbol].quote[currencyCode].price, - style: "currency", - currency: currencyCode, - })} -
-
0 - ? "text-emerald-300" - : "text-rose-300" - }`} - > - {data[symbol].quote[currencyCode][`percent_change_${dateRange}`].toFixed(2)}% -
-
-
- ))} -
-
- ); -} diff --git a/src/components/services/widgets/service/docker.jsx b/src/components/services/widgets/service/docker.jsx deleted file mode 100644 index ca9476db9..000000000 --- a/src/components/services/widgets/service/docker.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import calculateCPUPercent from "widgets/docker/stats-helpers"; - -export default function Docker({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: statusData, error: statusError } = useSWR( - `/api/docker/status/${config.container}/${config.server || ""}`, - { - refreshInterval: 5000, - } - ); - - const { data: statsData, error: statsError } = useSWR( - `/api/docker/stats/${config.container}/${config.server || ""}`, - { - refreshInterval: 5000, - } - ); - - if (statsError || statusError) { - return ; - } - - if (statusData && statusData.status !== "running") { - return ( - - - - ); - } - - if (!statsData || !statusData) { - return ( - - - - - - - ); - } - - return ( - - - - {statsData.stats.networks && ( - <> - - - - )} - - ); -} diff --git a/src/components/services/widgets/service/emby.jsx b/src/components/services/widgets/service/emby.jsx deleted file mode 100644 index 46ea129f5..000000000 --- a/src/components/services/widgets/service/emby.jsx +++ /dev/null @@ -1,239 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; -import { BsVolumeMuteFill, BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; -import { MdOutlineSmartDisplay } from "react-icons/md"; - -import Widget from "../widget"; - -import { formatProxyUrl } from "utils/api-helpers"; - -function ticksToTime(ticks) { - const milliseconds = ticks / 10000; - const seconds = Math.floor((milliseconds / 1000) % 60); - const minutes = Math.floor((milliseconds / (1000 * 60)) % 60); - const hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); - return { hours, minutes, seconds }; -} - -function ticksToString(ticks) { - const { hours, minutes, seconds } = ticksToTime(ticks); - const parts = []; - if (hours > 0) { - parts.push(hours); - } - parts.push(minutes); - parts.push(seconds); - - return parts.map((part) => part.toString().padStart(2, "0")).join(":"); -} - -function SingleSessionEntry({ playCommand, session }) { - const { - NowPlayingItem: { Name, SeriesName, RunTimeTicks }, - PlayState: { PositionTicks, IsPaused, IsMuted }, - } = session; - - const { IsVideoDirect, VideoDecoderIsHardware, VideoEncoderIsHardware } = session?.TranscodingInfo || { - IsVideoDirect: true, - VideoDecoderIsHardware: true, - VideoEncoderIsHardware: true, - }; - - const percent = (PositionTicks / RunTimeTicks) * 100; - - return ( - <> -
-
-
- {Name} - {SeriesName && ` - ${SeriesName}`} -
-
-
- {IsVideoDirect && } - {!IsVideoDirect && (!VideoDecoderIsHardware || !VideoEncoderIsHardware) && } - {!IsVideoDirect && VideoDecoderIsHardware && VideoEncoderIsHardware && ( - - )} -
-
- -
-
-
- {IsPaused && ( - { - playCommand(session, "Unpause"); - }} - className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" - /> - )} - {!IsPaused && ( - { - playCommand(session, "Pause"); - }} - className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" - /> - )} -
-
-
{IsMuted && }
-
- {ticksToString(PositionTicks)} - / - {ticksToString(RunTimeTicks)} -
-
- - ); -} - -function SessionEntry({ playCommand, session }) { - const { - NowPlayingItem: { Name, SeriesName, RunTimeTicks }, - PlayState: { PositionTicks, IsPaused, IsMuted }, - } = session; - - const { IsVideoDirect, VideoDecoderIsHardware, VideoEncoderIsHardware } = session?.TranscodingInfo || {}; - - const percent = (PositionTicks / RunTimeTicks) * 100; - - return ( -
-
-
- {IsPaused && ( - { - playCommand(session, "Unpause"); - }} - className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" - /> - )} - {!IsPaused && ( - { - playCommand(session, "Pause"); - }} - className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" - /> - )} -
-
-
- {Name} - {SeriesName && ` - ${SeriesName}`} -
-
-
{IsMuted && }
-
{ticksToString(PositionTicks)}
-
- {IsVideoDirect && } - {!IsVideoDirect && (!VideoDecoderIsHardware || !VideoEncoderIsHardware) && } - {!IsVideoDirect && VideoDecoderIsHardware && VideoEncoderIsHardware && } -
-
- ); -} - -export default function Emby({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { - data: sessionsData, - error: sessionsError, - mutate: sessionMutate, - } = useSWR(formatProxyUrl(config, "Sessions"), { - refreshInterval: 5000, - }); - - async function handlePlayCommand(session, command) { - const url = formatProxyUrl(config, `Sessions/${session.Id}/Playing/${command}`); - await fetch(url, { - method: "POST", - }).then(() => { - sessionMutate(); - }); - } - - if (sessionsError) { - return ; - } - - if (!sessionsData) { - return ( -
-
- - -
-
- - -
-
- ); - } - - const playing = sessionsData - .filter((session) => session?.NowPlayingItem) - .sort((a, b) => { - if (a.PlayState.PositionTicks > b.PlayState.PositionTicks) { - return 1; - } - if (a.PlayState.PositionTicks < b.PlayState.PositionTicks) { - return -1; - } - return 0; - }); - - if (playing.length === 0) { - return ( -
-
- {t("emby.no_active")} -
-
- - -
-
- ); - } - - if (playing.length === 1) { - const session = playing[0]; - return ( -
- handlePlayCommand(currentSession, command)} - session={session} - /> -
- ); - } - - return ( -
- {playing.map((session) => ( - handlePlayCommand(currentSession, command)} - session={session} - /> - ))} -
- ); -} diff --git a/src/components/services/widgets/service/gotify.jsx b/src/components/services/widgets/service/gotify.jsx deleted file mode 100644 index 292155749..000000000 --- a/src/components/services/widgets/service/gotify.jsx +++ /dev/null @@ -1,29 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Gotify({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: appsData, error: appsError } = useSWR(formatProxyUrl(config, `application`)); - const { data: messagesData, error: messagesError } = useSWR(formatProxyUrl(config, `message`)); - const { data: clientsData, error: clientsError } = useSWR(formatProxyUrl(config, `client`)); - - if (appsError || messagesError || clientsError) { - return ; - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/jackett.jsx b/src/components/services/widgets/service/jackett.jsx deleted file mode 100644 index f9cc2dfb2..000000000 --- a/src/components/services/widgets/service/jackett.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Jackett({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: indexersData, error: indexersError } = useSWR(formatProxyUrl(config, "indexers")); - - if (indexersError) { - return ; - } - - if (!indexersData) { - return ( - - - - - ); - } - - const errored = indexersData.filter((indexer) => indexer.last_error); - - return ( - - - - - ); -} diff --git a/src/components/services/widgets/service/jellyfin.jsx b/src/components/services/widgets/service/jellyfin.jsx deleted file mode 100644 index 195c133c0..000000000 --- a/src/components/services/widgets/service/jellyfin.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import Emby from "./emby"; - -export default function Jellyfin({ service }) { - return ; -} diff --git a/src/components/services/widgets/service/jellyseerr.jsx b/src/components/services/widgets/service/jellyseerr.jsx deleted file mode 100644 index f4d5b71e0..000000000 --- a/src/components/services/widgets/service/jellyseerr.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Jellyseerr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `request/count`)); - - if (statsError) { - return ; - } - - if (!statsData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/overseerr.jsx b/src/components/services/widgets/service/overseerr.jsx deleted file mode 100644 index 834172361..000000000 --- a/src/components/services/widgets/service/overseerr.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Overseerr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `request/count`)); - - if (statsError) { - return ; - } - - if (!statsData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/portainer.jsx b/src/components/services/widgets/service/portainer.jsx deleted file mode 100644 index 1c28333be..000000000 --- a/src/components/services/widgets/service/portainer.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Portainer({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: containersData, error: containersError } = useSWR( - formatProxyUrl(config, `docker/containers/json?all=1`) - ); - - if (containersError) { - return ; - } - - if (!containersData) { - return ( - - - - - - ); - } - - if (containersData.error) { - return ; - } - - const running = containersData.filter((c) => c.State === "running").length; - const stopped = containersData.filter((c) => c.State === "exited").length; - const total = containersData.length; - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/prowlarr.jsx b/src/components/services/widgets/service/prowlarr.jsx deleted file mode 100644 index e80b9e3d6..000000000 --- a/src/components/services/widgets/service/prowlarr.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Prowlarr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: indexersData, error: indexersError } = useSWR(formatProxyUrl(config, "indexer")); - const { data: grabsData, error: grabsError } = useSWR(formatProxyUrl(config, "indexerstats")); - - if (indexersError || grabsError) { - return ; - } - - if (!indexersData || !grabsData) { - return ( - - - - - - - - ); - } - - const indexers = indexersData?.filter((indexer) => indexer.enable === true); - - let numberOfGrabs = 0; - let numberOfQueries = 0; - let numberOfFailedGrabs = 0; - let numberOfFailedQueries = 0; - grabsData?.indexers?.forEach((element) => { - numberOfGrabs += element.numberOfGrabs; - numberOfQueries += element.numberOfQueries; - numberOfFailedGrabs += numberOfFailedGrabs + element.numberOfFailedGrabs; - numberOfFailedQueries += numberOfFailedQueries + element.numberOfFailedQueries; - }); - - return ( - - - - - - - - ); -} diff --git a/src/components/services/widgets/service/qbittorrent.jsx b/src/components/services/widgets/service/qbittorrent.jsx deleted file mode 100644 index e7030cd88..000000000 --- a/src/components/services/widgets/service/qbittorrent.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function QBittorrent({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: torrentData, error: torrentError } = useSWR(formatProxyUrl(config, "torrents/info")); - - if (torrentError) { - return ; - } - - if (!torrentData) { - return ( - - - - - - - ); - } - - let rateDl = 0; - let rateUl = 0; - let completed = 0; - - for (let i = 0; i < torrentData.length; i += 1) { - const torrent = torrentData[i]; - rateDl += torrent.dlspeed; - rateUl += torrent.upspeed; - if (torrent.progress === 1) { - completed += 1; - } - } - - const leech = torrentData.length - completed; - - let unitsDl = "KB/s"; - let unitsUl = "KB/s"; - rateDl /= 1024; - rateUl /= 1024; - - if (rateDl > 1024) { - rateDl /= 1024; - unitsDl = "MB/s"; - } - - if (rateUl > 1024) { - rateUl /= 1024; - unitsUl = "MB/s"; - } - - return ( - - - - - - - ); -} diff --git a/src/components/services/widgets/service/radarr.jsx b/src/components/services/widgets/service/radarr.jsx deleted file mode 100644 index f738ab71b..000000000 --- a/src/components/services/widgets/service/radarr.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Radarr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: moviesData, error: moviesError } = useSWR(formatProxyUrl(config, "movie")); - const { data: queuedData, error: queuedError } = useSWR(formatProxyUrl(config, "queue/status")); - - if (moviesError || queuedError) { - return ; - } - - if (!moviesData || !queuedData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/readarr.jsx b/src/components/services/widgets/service/readarr.jsx deleted file mode 100644 index aab6290a4..000000000 --- a/src/components/services/widgets/service/readarr.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Readarr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: booksData, error: booksError } = useSWR(formatProxyUrl(config, "book")); - const { data: wantedData, error: wantedError } = useSWR(formatProxyUrl(config, "wanted/missing")); - const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue/status")); - - if (booksError || wantedError || queueError) { - return ; - } - - if (!booksData || !wantedData || !queueData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/rutorrent.jsx b/src/components/services/widgets/service/rutorrent.jsx deleted file mode 100644 index 6aba5e67f..000000000 --- a/src/components/services/widgets/service/rutorrent.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Rutorrent({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: statusData, error: statusError } = useSWR(formatProxyUrl(config)); - - if (statusError) { - return ; - } - - if (!statusData) { - return ( - - - - - - ); - } - - const upload = statusData.reduce((acc, torrent) => acc + parseInt(torrent["d.get_up_rate"], 10), 0); - - const download = statusData.reduce((acc, torrent) => acc + parseInt(torrent["d.get_down_rate"], 10), 0); - - const active = statusData.filter((torrent) => torrent["d.get_state"] === "1"); - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/sabnzbd.jsx b/src/components/services/widgets/service/sabnzbd.jsx deleted file mode 100644 index a79e11feb..000000000 --- a/src/components/services/widgets/service/sabnzbd.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function SABnzbd({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue")); - - if (queueError) { - return ; - } - - if (!queueData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/sonarr.jsx b/src/components/services/widgets/service/sonarr.jsx deleted file mode 100644 index ea91388bc..000000000 --- a/src/components/services/widgets/service/sonarr.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Sonarr({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: wantedData, error: wantedError } = useSWR(formatProxyUrl(config, "wanted/missing")); - const { data: queuedData, error: queuedError } = useSWR(formatProxyUrl(config, "queue")); - const { data: seriesData, error: seriesError } = useSWR(formatProxyUrl(config, "series")); - - if (wantedError || queuedError || seriesError) { - return ; - } - - if (!wantedData || !queuedData || !seriesData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/speedtest.jsx b/src/components/services/widgets/service/speedtest.jsx deleted file mode 100644 index def1336ec..000000000 --- a/src/components/services/widgets/service/speedtest.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Speedtest({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: speedtestData, error: speedtestError } = useSWR(formatProxyUrl(config, "speedtest/latest")); - - if (speedtestError) { - return ; - } - - if (!speedtestData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/strelaysrv.jsx b/src/components/services/widgets/service/strelaysrv.jsx deleted file mode 100644 index a1aff9943..000000000 --- a/src/components/services/widgets/service/strelaysrv.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function StRelaySrv({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `status`)); - - if (statsError) { - return ; - } - - if (!statsData) { - return ( - - - - - - ); - } - - return ( - - - - - - - ); -} diff --git a/src/components/services/widgets/service/tautulli.jsx b/src/components/services/widgets/service/tautulli.jsx deleted file mode 100644 index 9ba4306f8..000000000 --- a/src/components/services/widgets/service/tautulli.jsx +++ /dev/null @@ -1,183 +0,0 @@ -/* eslint-disable camelcase */ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; -import { BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; -import { MdOutlineSmartDisplay, MdSmartDisplay } from "react-icons/md"; - -import Widget from "../widget"; - -import { formatProxyUrl } from "utils/api-helpers"; - -function millisecondsToTime(milliseconds) { - const seconds = Math.floor((milliseconds / 1000) % 60); - const minutes = Math.floor((milliseconds / (1000 * 60)) % 60); - const hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); - return { hours, minutes, seconds }; -} - -function millisecondsToString(milliseconds) { - const { hours, minutes, seconds } = millisecondsToTime(milliseconds); - const parts = []; - if (hours > 0) { - parts.push(hours); - } - parts.push(minutes); - parts.push(seconds); - - return parts.map((part) => part.toString().padStart(2, "0")).join(":"); -} - -function SingleSessionEntry({ session }) { - const { full_title, duration, view_offset, progress_percent, state, video_decision, audio_decision } = session; - - return ( - <> -
-
-
{full_title}
-
-
- {video_decision === "direct play" && audio_decision === "direct play" && ( - - )} - {video_decision === "copy" && audio_decision === "copy" && } - {video_decision !== "copy" && - video_decision !== "direct play" && - (audio_decision !== "copy" || audio_decision !== "direct play") && } - {(video_decision === "copy" || video_decision === "direct play") && - audio_decision !== "copy" && - audio_decision !== "direct play" && } -
-
- -
-
-
- {state === "paused" && ( - - )} - {state !== "paused" && ( - - )} -
-
-
- {millisecondsToString(view_offset)} - / - {millisecondsToString(duration)} -
-
- - ); -} - -function SessionEntry({ session }) { - const { full_title, view_offset, progress_percent, state, video_decision, audio_decision } = session; - - return ( -
-
-
- {state === "paused" && ( - - )} - {state !== "paused" && ( - - )} -
-
-
{full_title}
-
-
- {video_decision === "direct play" && audio_decision === "direct play" && ( - - )} - {video_decision === "copy" && audio_decision === "copy" && } - {video_decision !== "copy" && - video_decision !== "direct play" && - (audio_decision !== "copy" || audio_decision !== "direct play") && } - {(video_decision === "copy" || video_decision === "direct play") && - audio_decision !== "copy" && - audio_decision !== "direct play" && } -
-
{millisecondsToString(view_offset)}
-
- ); -} - -export default function Tautulli({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: activityData, error: activityError } = useSWR(formatProxyUrl(config, "get_activity"), { - refreshInterval: 5000, - }); - - if (activityError) { - return ; - } - - if (!activityData) { - return ( -
-
- - -
-
- - -
-
- ); - } - - const playing = activityData.response.data.sessions.sort((a, b) => { - if (a.view_offset > b.view_offset) { - return 1; - } - if (a.view_offset < b.view_offset) { - return -1; - } - return 0; - }); - - if (playing.length === 0) { - return ( -
-
- {t("tautulli.no_active")} -
-
- - -
-
- ); - } - - if (playing.length === 1) { - const session = playing[0]; - return ( -
- -
- ); - } - - return ( -
- {playing.map((session) => ( - - ))} -
- ); -} diff --git a/src/components/services/widgets/service/traefik.jsx b/src/components/services/widgets/service/traefik.jsx deleted file mode 100644 index efef287e7..000000000 --- a/src/components/services/widgets/service/traefik.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Traefik({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: traefikData, error: traefikError } = useSWR(formatProxyUrl(config, "overview")); - - if (traefikError) { - return ; - } - - if (!traefikData) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -} diff --git a/src/components/services/widgets/service/transmission.jsx b/src/components/services/widgets/service/transmission.jsx deleted file mode 100644 index fb449e281..000000000 --- a/src/components/services/widgets/service/transmission.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import useSWR from "swr"; -import { useTranslation } from "next-i18next"; - -import Widget from "../widget"; -import Block from "../block"; - -import { formatProxyUrl } from "utils/api-helpers"; - -export default function Transmission({ service }) { - const { t } = useTranslation(); - - const config = service.widget; - - const { data: torrentData, error: torrentError } = useSWR(formatProxyUrl(config)); - - if (torrentError) { - return ; - } - - if (!torrentData) { - return ( - - - - - - - ); - } - - const { torrents } = torrentData.arguments; - let rateDl = 0; - let rateUl = 0; - let completed = 0; - - for (let i = 0; i < torrents.length; i += 1) { - const torrent = torrents[i]; - rateDl += torrent.rateDownload; - rateUl += torrent.rateUpload; - if (torrent.percentDone === 1) { - completed += 1; - } - } - - const leech = torrents.length - completed; - - let unitsDl = "KB/s"; - let unitsUl = "KB/s"; - rateDl /= 1024; - rateUl /= 1024; - - if (rateDl > 1024) { - rateDl /= 1024; - unitsDl = "MB/s"; - } - - if (rateUl > 1024) { - rateUl /= 1024; - unitsUl = "MB/s"; - } - - return ( - - - - - - - ); -} diff --git a/src/utils/api-helpers.js b/src/utils/api-helpers.js index 9293e9872..17e966ec7 100644 --- a/src/utils/api-helpers.js +++ b/src/utils/api-helpers.js @@ -1,25 +1,5 @@ // const formats = { -// emby: `{url}/emby/{endpoint}?api_key={key}`, -// jellyfin: `{url}/emby/{endpoint}?api_key={key}`, -// pihole: `{url}/admin/{endpoint}`, -// speedtest: `{url}/api/{endpoint}`, -// tautulli: `{url}/api/v2?apikey={key}&cmd={endpoint}`, -// traefik: `{url}/api/{endpoint}`, -// portainer: `{url}/api/endpoints/{env}/{endpoint}`, -// rutorrent: `{url}/plugins/httprpc/action.php`, -// transmission: `{url}/transmission/rpc`, -// qbittorrent: `{url}/api/v2/{endpoint}`, -// jellyseerr: `{url}/api/v1/{endpoint}`, -// ombi: `{url}/api/v1/{endpoint}`, -// npm: `{url}/api/{endpoint}`, // lidarr: `{url}/api/v1/{endpoint}?apikey={key}`, -// readarr: `{url}/api/v1/{endpoint}?apikey={key}`, -// sabnzbd: `{url}/api/?apikey={key}&output=json&mode={endpoint}`, -// gotify: `{url}/{endpoint}`, -// prowlarr: `{url}/api/v1/{endpoint}`, -// jackett: `{url}/api/v2.0/{endpoint}?apikey={key}&configured=true`, -// strelaysrv: `{url}/{endpoint}`, -// mastodon: `{url}/api/v1/{endpoint}`, // }; export function formatApiCall(url, args) { @@ -45,7 +25,7 @@ function getURLSearchParams(widget, endpoint) { export function formatProxyUrlWithSegments(widget, endpoint, segments) { const params = getURLSearchParams(widget, endpoint); if (segments) { - params.append("segments", JSON.stringify(segments)) + params.append("segments", JSON.stringify(segments)); } return `/api/services/proxy?${params.toString()}`; } diff --git a/src/widgets/components.js b/src/widgets/components.js index 0831ba7d5..f40036628 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -10,15 +10,20 @@ const components = { jackett: dynamic(() => import("./jackett/component")), jellyfin: dynamic(() => import("./emby/component")), jellyseerr: dynamic(() => import("./jellyseerr/component")), + mastodon: dynamic(() => import("./mastodon/component")), + npm: dynamic(() => import("./npm/component")), + nzbget: dynamic(() => import("./nzbget/component")), + ombi: dynamic(() => import("./ombi/component")), overseerr: dynamic(() => import("./overseerr/component")), + pihole: dynamic(() => import("./pihole/component")), portainer: dynamic(() => import("./portainer/component")), prowlarr: dynamic(() => import("./prowlarr/component")), qbittorrent: dynamic(() => import("./qbittorrent/component")), radarr: dynamic(() => import("./radarr/component")), - sonarr: dynamic(() => import("./sonarr/component")), readarr: dynamic(() => import("./readarr/component")), rutorrent: dynamic(() => import("./rutorrent/component")), sabnzbd: dynamic(() => import("./sabnzbd/component")), + sonarr: dynamic(() => import("./sonarr/component")), speedtest: dynamic(() => import("./speedtest/component")), strelaysrv: dynamic(() => import("./strelaysrv/component")), tautulli: dynamic(() => import("./tautulli/component")), diff --git a/src/components/services/widgets/service/mastodon.jsx b/src/widgets/mastodon/component.jsx similarity index 86% rename from src/components/services/widgets/service/mastodon.jsx rename to src/widgets/mastodon/component.jsx index d1bb22527..fbee420fa 100644 --- a/src/components/services/widgets/service/mastodon.jsx +++ b/src/widgets/mastodon/component.jsx @@ -1,12 +1,11 @@ import useSWR from "swr"; import { useTranslation } from "next-i18next"; -import Widget from "../widget"; -import Block from "../block"; - +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; import { formatProxyUrl } from "utils/api-helpers"; -export default function Mastodon({ service }) { +export default function Component({ service }) { const { t } = useTranslation(); const config = service.widget; diff --git a/src/widgets/mastodon/widget.js b/src/widgets/mastodon/widget.js new file mode 100644 index 000000000..c9761c5eb --- /dev/null +++ b/src/widgets/mastodon/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/v1/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + instance: { + endpoint: "instance", + }, + }, +}; + +export default widget; diff --git a/src/components/services/widgets/service/npm.jsx b/src/widgets/npm/component.jsx similarity index 85% rename from src/components/services/widgets/service/npm.jsx rename to src/widgets/npm/component.jsx index 93ecf26bd..59a709e8e 100644 --- a/src/components/services/widgets/service/npm.jsx +++ b/src/widgets/npm/component.jsx @@ -1,12 +1,11 @@ import useSWR from "swr"; import { useTranslation } from "next-i18next"; -import Widget from "../widget"; -import Block from "../block"; - +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; import { formatProxyUrl } from "utils/api-helpers"; -export default function Npm({ service }) { +export default function Component({ service }) { const { t } = useTranslation(); const config = service.widget; diff --git a/src/utils/proxies/npm.js b/src/widgets/npm/proxy.js similarity index 100% rename from src/utils/proxies/npm.js rename to src/widgets/npm/proxy.js diff --git a/src/widgets/npm/widget.js b/src/widgets/npm/widget.js new file mode 100644 index 000000000..652cb4a25 --- /dev/null +++ b/src/widgets/npm/widget.js @@ -0,0 +1,8 @@ +import npmProxyHandler from "./proxy"; + +const widget = { + api: "{url}/api/{endpoint}", + proxyHandler: npmProxyHandler, +}; + +export default widget; diff --git a/src/components/services/widgets/service/nzbget.jsx b/src/widgets/nzbget/component.jsx similarity index 86% rename from src/components/services/widgets/service/nzbget.jsx rename to src/widgets/nzbget/component.jsx index 58d088503..fe85cdb50 100644 --- a/src/components/services/widgets/service/nzbget.jsx +++ b/src/widgets/nzbget/component.jsx @@ -1,12 +1,11 @@ import useSWR from "swr"; import { useTranslation } from "next-i18next"; -import Widget from "../widget"; -import Block from "../block"; - +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; import { formatProxyUrl } from "utils/api-helpers"; -export default function Nzbget({ service }) { +export default function Component({ service }) { const { t } = useTranslation("common"); const config = service.widget; diff --git a/src/utils/proxies/nzbget.js b/src/widgets/nzbget/proxy.js similarity index 100% rename from src/utils/proxies/nzbget.js rename to src/widgets/nzbget/proxy.js diff --git a/src/widgets/nzbget/widget.js b/src/widgets/nzbget/widget.js new file mode 100644 index 000000000..975c8dea7 --- /dev/null +++ b/src/widgets/nzbget/widget.js @@ -0,0 +1,7 @@ +import nzbgetProxyHandler from "./proxy"; + +const widget = { + proxyHandler: nzbgetProxyHandler, +}; + +export default widget; diff --git a/src/components/services/widgets/service/ombi.jsx b/src/widgets/ombi/component.jsx similarity index 83% rename from src/components/services/widgets/service/ombi.jsx rename to src/widgets/ombi/component.jsx index 887c7348c..55435b54d 100644 --- a/src/components/services/widgets/service/ombi.jsx +++ b/src/widgets/ombi/component.jsx @@ -1,12 +1,11 @@ import useSWR from "swr"; import { useTranslation } from "next-i18next"; -import Widget from "../widget"; -import Block from "../block"; - +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; import { formatProxyUrl } from "utils/api-helpers"; -export default function Ombi({ service }) { +export default function Component({ service }) { const { t } = useTranslation(); const config = service.widget; diff --git a/src/widgets/ombi/widget.js b/src/widgets/ombi/widget.js new file mode 100644 index 000000000..d0dbea934 --- /dev/null +++ b/src/widgets/ombi/widget.js @@ -0,0 +1,14 @@ +import credentialedProxyHandler from "utils/proxies/credentialed"; + +const widget = { + api: "{url}/api/v1/{endpoint}", + proxyHandler: credentialedProxyHandler, + + mappings: { + "Request/count": { + endpoint: "Request/count", + }, + }, +}; + +export default widget; diff --git a/src/components/services/widgets/service/pihole.jsx b/src/widgets/pihole/component.jsx similarity index 85% rename from src/components/services/widgets/service/pihole.jsx rename to src/widgets/pihole/component.jsx index 720cd681e..fea8e68c3 100644 --- a/src/components/services/widgets/service/pihole.jsx +++ b/src/widgets/pihole/component.jsx @@ -1,12 +1,11 @@ import useSWR from "swr"; import { useTranslation } from "next-i18next"; -import Widget from "../widget"; -import Block from "../block"; - +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; import { formatProxyUrl } from "utils/api-helpers"; -export default function Pihole({ service }) { +export default function Component({ service }) { const { t } = useTranslation(); const config = service.widget; diff --git a/src/widgets/pihole/widget.js b/src/widgets/pihole/widget.js new file mode 100644 index 000000000..5198ea2cd --- /dev/null +++ b/src/widgets/pihole/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/admin/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + "api.php": { + endpoint: "api.php", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 241c78d91..8466a2c53 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -5,15 +5,20 @@ import emby from "./emby/widget"; import gotify from "./gotify/widget"; import jackett from "./jackett/widget"; import jellyseerr from "./jellyseerr/widget"; +import mastodon from "./mastodon/widget"; +import npm from "./npm/widget"; +import nzbget from "./nzbget/widget"; +import ombi from "./ombi/widget"; import overseerr from "./overseerr/widget"; +import pihole from "./pihole/widget"; import portainer from "./portainer/widget"; import prowlarr from "./prowlarr/widget"; import qbittorrent from "./qbittorrent/widget"; import radarr from "./radarr/widget"; -import sonarr from "./sonarr/widget"; import readarr from "./readarr/widget"; import rutorrent from "./rutorrent/widget"; import sabnzbd from "./sabnzbd/widget"; +import sonarr from "./sonarr/widget"; import speedtest from "./speedtest/widget"; import strelaysrv from "./strelaysrv/widget"; import tautulli from "./tautulli/widget"; @@ -29,15 +34,20 @@ const widgets = { jackett, jellyfin: emby, jellyseerr, + mastodon, + npm, + nzbget, + ombi, overseerr, + pihole, portainer, prowlarr, qbittorrent, radarr, - sonarr, readarr, rutorrent, sabnzbd, + sonarr, speedtest, strelaysrv, tautulli,