From 035dd25ece828b7f2212cb1cb0ff0705b701275b Mon Sep 17 00:00:00 2001 From: Ben Phelps Date: Mon, 26 Sep 2022 00:35:54 +0300 Subject: [PATCH] widget refactoring --- src/pages/api/services/proxy.js | 7 +- src/widgets/components.js | 11 ++ src/widgets/portainer/component.jsx | 48 +++++ src/widgets/portainer/widget.js | 15 ++ src/widgets/prowlarr/component.jsx | 54 ++++++ src/widgets/prowlarr/widget.js | 17 ++ src/widgets/qbittorrent/component.jsx | 68 +++++++ .../qbittorrent/proxy.js} | 0 src/widgets/qbittorrent/widget.js | 8 + src/widgets/readarr/component.jsx | 38 ++++ src/widgets/readarr/widget.js | 24 +++ src/widgets/rutorrent/component.jsx | 42 ++++ .../rutorrent/proxy.js} | 0 src/widgets/rutorrent/widget.js | 8 + src/widgets/sabnzbd/component.jsx | 36 ++++ src/widgets/sabnzbd/widget.js | 14 ++ src/widgets/sonarr/widget.js | 8 +- src/widgets/speedtest/component.jsx | 45 +++++ src/widgets/speedtest/widget.js | 14 ++ src/widgets/strelaysrv/component.jsx | 43 +++++ src/widgets/strelaysrv/widget.js | 14 ++ src/widgets/tautulli/component.jsx | 182 ++++++++++++++++++ src/widgets/tautulli/widget.js | 14 ++ src/widgets/traefik/component.jsx | 36 ++++ src/widgets/traefik/widget.js | 14 ++ src/widgets/transmission/component.jsx | 69 +++++++ .../transmission/proxy.js} | 0 src/widgets/transmission/widget.js | 8 + src/widgets/widgets.js | 24 ++- 29 files changed, 851 insertions(+), 10 deletions(-) create mode 100644 src/widgets/portainer/component.jsx create mode 100644 src/widgets/portainer/widget.js create mode 100644 src/widgets/prowlarr/component.jsx create mode 100644 src/widgets/prowlarr/widget.js create mode 100644 src/widgets/qbittorrent/component.jsx rename src/{utils/proxies/qbittorrent.js => widgets/qbittorrent/proxy.js} (100%) create mode 100644 src/widgets/qbittorrent/widget.js create mode 100644 src/widgets/readarr/component.jsx create mode 100644 src/widgets/readarr/widget.js create mode 100644 src/widgets/rutorrent/component.jsx rename src/{utils/proxies/rutorrent.js => widgets/rutorrent/proxy.js} (100%) create mode 100644 src/widgets/rutorrent/widget.js create mode 100644 src/widgets/sabnzbd/component.jsx create mode 100644 src/widgets/sabnzbd/widget.js create mode 100644 src/widgets/speedtest/component.jsx create mode 100644 src/widgets/speedtest/widget.js create mode 100644 src/widgets/strelaysrv/component.jsx create mode 100644 src/widgets/strelaysrv/widget.js create mode 100644 src/widgets/tautulli/component.jsx create mode 100644 src/widgets/tautulli/widget.js create mode 100644 src/widgets/traefik/component.jsx create mode 100644 src/widgets/traefik/widget.js create mode 100644 src/widgets/transmission/component.jsx rename src/{utils/proxies/transmission.js => widgets/transmission/proxy.js} (100%) create mode 100644 src/widgets/transmission/widget.js diff --git a/src/pages/api/services/proxy.js b/src/pages/api/services/proxy.js index 43a9a3d08..2efb01c26 100644 --- a/src/pages/api/services/proxy.js +++ b/src/pages/api/services/proxy.js @@ -1,5 +1,3 @@ -import { URLSearchParams } from "next/dist/compiled/@edge-runtime/primitives/url"; - import createLogger from "utils/logger"; import genericProxyHandler from "utils/proxies/generic"; import widgets from "widgets/widgets"; @@ -35,10 +33,9 @@ export default async function handler(req, res) { if (req.query.params) { const queryParams = JSON.parse(req.query.params); - const query = new URLSearchParams(mappingParams.map(p => [p, queryParams[p]])); + const query = new URLSearchParams(mappingParams.map((p) => [p, queryParams[p]])); req.query.endpoint = `${endpoint}?${query}`; - } - else { + } else { req.query.endpoint = endpoint; } diff --git a/src/widgets/components.js b/src/widgets/components.js index 5827a575e..07b1e1fa1 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -5,8 +5,19 @@ const components = { bazarr: dynamic(() => import("./bazarr/component")), coinmarketcap: dynamic(() => import("./coinmarketcap/component")), overseerr: dynamic(() => import("./overseerr/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")), + speedtest: dynamic(() => import("./speedtest/component")), + strelaysrv: dynamic(() => import("./strelaysrv/component")), + tautulli: dynamic(() => import("./tautulli/component")), + traefik: dynamic(() => import("./traefik/component")), + transmission: dynamic(() => import("./transmission/component")), }; export default components; diff --git a/src/widgets/portainer/component.jsx b/src/widgets/portainer/component.jsx new file mode 100644 index 000000000..272b8449a --- /dev/null +++ b/src/widgets/portainer/component.jsx @@ -0,0 +1,48 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/portainer/widget.js b/src/widgets/portainer/widget.js new file mode 100644 index 000000000..58d6c3068 --- /dev/null +++ b/src/widgets/portainer/widget.js @@ -0,0 +1,15 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/endpoints/{env}/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + "docker/containers/json": { + endpoint: "docker/containers/json", + params: ["all"], + }, + }, +}; + +export default widget; diff --git a/src/widgets/prowlarr/component.jsx b/src/widgets/prowlarr/component.jsx new file mode 100644 index 000000000..c7ebdf96e --- /dev/null +++ b/src/widgets/prowlarr/component.jsx @@ -0,0 +1,54 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/prowlarr/widget.js b/src/widgets/prowlarr/widget.js new file mode 100644 index 000000000..a19b83a34 --- /dev/null +++ b/src/widgets/prowlarr/widget.js @@ -0,0 +1,17 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/v1/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + indexer: { + endpoint: "indexer", + }, + indexerstats: { + endpoint: "indexerstats", + }, + }, +}; + +export default widget; diff --git a/src/widgets/qbittorrent/component.jsx b/src/widgets/qbittorrent/component.jsx new file mode 100644 index 000000000..1c98541be --- /dev/null +++ b/src/widgets/qbittorrent/component.jsx @@ -0,0 +1,68 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/utils/proxies/qbittorrent.js b/src/widgets/qbittorrent/proxy.js similarity index 100% rename from src/utils/proxies/qbittorrent.js rename to src/widgets/qbittorrent/proxy.js diff --git a/src/widgets/qbittorrent/widget.js b/src/widgets/qbittorrent/widget.js new file mode 100644 index 000000000..9c892848f --- /dev/null +++ b/src/widgets/qbittorrent/widget.js @@ -0,0 +1,8 @@ +import qbittorrentProxyHandler from "./proxy"; + +const widget = { + api: "{url}/api/v2/{endpoint}", + proxyHandler: qbittorrentProxyHandler, +}; + +export default widget; diff --git a/src/widgets/readarr/component.jsx b/src/widgets/readarr/component.jsx new file mode 100644 index 000000000..131d94d7c --- /dev/null +++ b/src/widgets/readarr/component.jsx @@ -0,0 +1,38 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/readarr/widget.js b/src/widgets/readarr/widget.js new file mode 100644 index 000000000..f826cf1ff --- /dev/null +++ b/src/widgets/readarr/widget.js @@ -0,0 +1,24 @@ +import genericProxyHandler from "utils/proxies/generic"; +import { jsonArrayFilter } from "utils/api-helpers"; + +const widget = { + api: "{url}/api/v1/{endpoint}?apikey={key}", + proxyHandler: genericProxyHandler, + + mappings: { + book: { + endpoint: "book", + map: (data) => ({ + have: jsonArrayFilter(data, (item) => item?.statistics?.bookFileCount > 0).length, + }), + }, + "queue/status": { + endpoint: "queue/status", + }, + "wanted/missing": { + endpoint: "wanted/missing", + }, + }, +}; + +export default widget; diff --git a/src/widgets/rutorrent/component.jsx b/src/widgets/rutorrent/component.jsx new file mode 100644 index 000000000..5766fbab6 --- /dev/null +++ b/src/widgets/rutorrent/component.jsx @@ -0,0 +1,42 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/utils/proxies/rutorrent.js b/src/widgets/rutorrent/proxy.js similarity index 100% rename from src/utils/proxies/rutorrent.js rename to src/widgets/rutorrent/proxy.js diff --git a/src/widgets/rutorrent/widget.js b/src/widgets/rutorrent/widget.js new file mode 100644 index 000000000..cde092f60 --- /dev/null +++ b/src/widgets/rutorrent/widget.js @@ -0,0 +1,8 @@ +import rutorrentProxyHandler from "./proxy"; + +const widget = { + api: "{url}/plugins/httprpc/action.php", + proxyHandler: rutorrentProxyHandler, +}; + +export default widget; diff --git a/src/widgets/sabnzbd/component.jsx b/src/widgets/sabnzbd/component.jsx new file mode 100644 index 000000000..7e77ae04f --- /dev/null +++ b/src/widgets/sabnzbd/component.jsx @@ -0,0 +1,36 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/sabnzbd/widget.js b/src/widgets/sabnzbd/widget.js new file mode 100644 index 000000000..67e64e973 --- /dev/null +++ b/src/widgets/sabnzbd/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/?apikey={key}&output=json&mode={endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + queue: { + endpoint: "queue", + }, + }, +}; + +export default widget; diff --git a/src/widgets/sonarr/widget.js b/src/widgets/sonarr/widget.js index c17182058..45d6e533a 100644 --- a/src/widgets/sonarr/widget.js +++ b/src/widgets/sonarr/widget.js @@ -6,19 +6,19 @@ const widget = { proxyHandler: genericProxyHandler, mappings: { - "series": { + series: { endpoint: "series", map: (data) => ({ total: asJson(data).length, }), }, - "queue": { + queue: { endpoint: "queue", }, "wanted/missing": { - endpoint: "wanted/missing", - }, + endpoint: "wanted/missing", }, + }, }; export default widget; diff --git a/src/widgets/speedtest/component.jsx b/src/widgets/speedtest/component.jsx new file mode 100644 index 000000000..6e30231fa --- /dev/null +++ b/src/widgets/speedtest/component.jsx @@ -0,0 +1,45 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/speedtest/widget.js b/src/widgets/speedtest/widget.js new file mode 100644 index 000000000..6c89b913c --- /dev/null +++ b/src/widgets/speedtest/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + "speedtest/latest": { + endpoint: "speedtest/latest", + }, + }, +}; + +export default widget; diff --git a/src/widgets/strelaysrv/component.jsx b/src/widgets/strelaysrv/component.jsx new file mode 100644 index 000000000..1e0578308 --- /dev/null +++ b/src/widgets/strelaysrv/component.jsx @@ -0,0 +1,43 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/strelaysrv/widget.js b/src/widgets/strelaysrv/widget.js new file mode 100644 index 000000000..b0c8139bb --- /dev/null +++ b/src/widgets/strelaysrv/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + status: { + endpoint: "status", + }, + }, +}; + +export default widget; diff --git a/src/widgets/tautulli/component.jsx b/src/widgets/tautulli/component.jsx new file mode 100644 index 000000000..084d457ba --- /dev/null +++ b/src/widgets/tautulli/component.jsx @@ -0,0 +1,182 @@ +/* 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 "components/services/widgets/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 Component({ 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/widgets/tautulli/widget.js b/src/widgets/tautulli/widget.js new file mode 100644 index 000000000..4f7239947 --- /dev/null +++ b/src/widgets/tautulli/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/v2?apikey={key}&cmd={endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + get_activity: { + endpoint: "get_activity", + }, + }, +}; + +export default widget; diff --git a/src/widgets/traefik/component.jsx b/src/widgets/traefik/component.jsx new file mode 100644 index 000000000..a87b35ee0 --- /dev/null +++ b/src/widgets/traefik/component.jsx @@ -0,0 +1,36 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/widgets/traefik/widget.js b/src/widgets/traefik/widget.js new file mode 100644 index 000000000..ed39af2c7 --- /dev/null +++ b/src/widgets/traefik/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxies/generic"; + +const widget = { + api: "{url}/api/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + overview: { + endpoint: "overview", + }, + }, +}; + +export default widget; diff --git a/src/widgets/transmission/component.jsx b/src/widgets/transmission/component.jsx new file mode 100644 index 000000000..b935f4b57 --- /dev/null +++ b/src/widgets/transmission/component.jsx @@ -0,0 +1,69 @@ +import useSWR from "swr"; +import { useTranslation } from "next-i18next"; + +import Widget from "components/services/widgets/widget"; +import Block from "components/services/widgets/block"; +import { formatProxyUrl } from "utils/api-helpers"; + +export default function Component({ 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/proxies/transmission.js b/src/widgets/transmission/proxy.js similarity index 100% rename from src/utils/proxies/transmission.js rename to src/widgets/transmission/proxy.js diff --git a/src/widgets/transmission/widget.js b/src/widgets/transmission/widget.js new file mode 100644 index 000000000..321f25baa --- /dev/null +++ b/src/widgets/transmission/widget.js @@ -0,0 +1,8 @@ +import transmissionProxyHandler from "./proxy"; + +const widget = { + api: "{url}/transmission/rpc", + proxyHandler: transmissionProxyHandler, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 03a8e4a5b..78b177261 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -2,16 +2,38 @@ import adguard from "./adguard/widget"; import bazarr from "./bazarr/widget"; import coinmarketcap from "./coinmarketcap/widget"; import overseerr from "./overseerr/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 sonarr from "./sonarr/widget"; +import readarr from "./readarr/widget"; +import rutorrent from "./rutorrent/widget"; +import sabnzbd from "./sabnzbd/widget"; +import speedtest from "./speedtest/widget"; +import strelaysrv from "./strelaysrv/widget"; +import tautulli from "./tautulli/widget"; +import traefik from "./traefik/widget"; +import transmission from "./transmission/widget"; const widgets = { adguard, bazarr, coinmarketcap, overseerr, + portainer, + prowlarr, + qbittorrent, radarr, sonarr, + readarr, + rutorrent, + sabnzbd, + speedtest, + strelaysrv, + tautulli, + traefik, + transmission, }; export default widgets;