From 7e8752243cc2ee1f7c818f69e2664326061ca0ae Mon Sep 17 00:00:00 2001 From: Zlendy Date: Sat, 3 May 2025 17:26:56 +0200 Subject: [PATCH] Feature: Jellystat widget (#5185) Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/widgets/services/jellystat.md | 18 +++++++++++ public/locales/en/common.json | 6 ++++ src/utils/config/service-helpers.js | 6 ++++ src/utils/proxy/handlers/credentialed.js | 2 +- src/widgets/components.js | 1 + src/widgets/jellystat/component.jsx | 38 ++++++++++++++++++++++++ src/widgets/jellystat/widget.js | 15 ++++++++++ src/widgets/widgets.js | 2 ++ 8 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 docs/widgets/services/jellystat.md create mode 100644 src/widgets/jellystat/component.jsx create mode 100644 src/widgets/jellystat/widget.js diff --git a/docs/widgets/services/jellystat.md b/docs/widgets/services/jellystat.md new file mode 100644 index 000000000..175bc6c5b --- /dev/null +++ b/docs/widgets/services/jellystat.md @@ -0,0 +1,18 @@ +--- +title: Jellystat +description: Jellystat Widget Configuration +--- + +Learn more about [Jellystat](https://github.com/CyferShepard/Jellystat). The widget supports (at least) Jellystat version 1.1.6 + +You can create an API key from inside Jellystat at `Settings > API Key`. + +Allowed fields: `["songs", "movies", "episodes", "other"]`. + +```yaml +widget: + type: jellystat + url: http://jellystat.host.or.ip + key: apikeyapikeyapikeyapikeyapikey + days: 30 # optional, defaults to 30 +``` diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 09b9c2d3b..5eab037df 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -1042,5 +1042,11 @@ "downloads": "Downloads", "uploads": "Uploads", "sharedFiles": "Files" + }, + "jellystat": { + "songs": "Songs", + "movies": "Movies", + "episodes": "Episodes", + "other": "Other" } } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 297e55a70..0fade417a 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -331,6 +331,9 @@ export function cleanServiceGroups(groups) { referrerPolicy, src, + // jellystat + days, + // kopia snapshotHost, snapshotPath, @@ -563,6 +566,9 @@ export function cleanServiceGroups(groups) { if (type === "spoolman") { if (spoolIds !== undefined) widget.spoolIds = spoolIds; } + if (type === "jellystat") { + if (days !== undefined) widget.days = parseInt(days, 10); + } return widget; }); return cleanedService; diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 017d44c93..779dbe332 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -65,7 +65,7 @@ export default async function credentialedProxyHandler(req, res, map) { } else if (widget.type === "proxmoxbackupserver") { delete headers["Content-Type"]; headers.Authorization = `PBSAPIToken=${widget.username}:${widget.password}`; - } else if (widget.type === "autobrr") { + } else if (["autobrr", "jellystat"].includes(widget.type)) { headers["X-API-Token"] = `${widget.key}`; } else if (widget.type === "tubearchivist") { headers.Authorization = `Token ${widget.key}`; diff --git a/src/widgets/components.js b/src/widgets/components.js index 880c8222e..7f1bbd852 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -59,6 +59,7 @@ const components = { jdownloader: dynamic(() => import("./jdownloader/component")), jellyfin: dynamic(() => import("./emby/component")), jellyseerr: dynamic(() => import("./jellyseerr/component")), + jellystat: dynamic(() => import("./jellystat/component")), kavita: dynamic(() => import("./kavita/component")), komga: dynamic(() => import("./komga/component")), kopia: dynamic(() => import("./kopia/component")), diff --git a/src/widgets/jellystat/component.jsx b/src/widgets/jellystat/component.jsx new file mode 100644 index 000000000..d62e643cd --- /dev/null +++ b/src/widgets/jellystat/component.jsx @@ -0,0 +1,38 @@ +import Block from "components/services/widget/block"; +import Container from "components/services/widget/container"; + +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { widget } = service; + + // Days validation + if (!(Number.isInteger(widget.days) && 0 < widget.days)) widget.days = 30; + + const { data: viewsData, error: viewsError } = useWidgetAPI(widget, "getViewsByLibraryType", { days: widget.days }); + + const error = viewsError || viewsData?.message; + if (error) { + return ; + } + + if (!viewsData) { + return ( + + + + + + + ); + } + + return ( + + + + + + + ); +} diff --git a/src/widgets/jellystat/widget.js b/src/widgets/jellystat/widget.js new file mode 100644 index 000000000..116826fb5 --- /dev/null +++ b/src/widgets/jellystat/widget.js @@ -0,0 +1,15 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "{url}/{endpoint}", + proxyHandler: credentialedProxyHandler, + + mappings: { + getViewsByLibraryType: { + endpoint: "stats/getViewsByLibraryType", + params: ["days"], + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index e183a9c6f..8f143a934 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -49,6 +49,7 @@ import immich from "./immich/widget"; import jackett from "./jackett/widget"; import jdownloader from "./jdownloader/widget"; import jellyseerr from "./jellyseerr/widget"; +import jellystat from "./jellystat/widget"; import karakeep from "./karakeep/widget"; import kavita from "./kavita/widget"; import komga from "./komga/widget"; @@ -190,6 +191,7 @@ const widgets = { jdownloader, jellyfin: emby, jellyseerr, + jellystat, kavita, komga, kopia,