From f57368519fbfcc649ee5a1086e6fb29171ffe99f Mon Sep 17 00:00:00 2001 From: MaximilianKos Date: Sat, 18 May 2024 22:41:33 +0200 Subject: [PATCH] Feature: fireshare widget --- docs/widgets/services/fireshare.md | 16 +++++++ public/locales/en/common.json | 5 ++ src/widgets/components.js | 1 + src/widgets/fireshare/component.jsx | 44 +++++++++++++++++ src/widgets/fireshare/proxy.js | 74 +++++++++++++++++++++++++++++ src/widgets/fireshare/widget.js | 8 ++++ src/widgets/widgets.js | 2 + 7 files changed, 150 insertions(+) create mode 100644 docs/widgets/services/fireshare.md create mode 100644 src/widgets/fireshare/component.jsx create mode 100644 src/widgets/fireshare/proxy.js create mode 100644 src/widgets/fireshare/widget.js diff --git a/docs/widgets/services/fireshare.md b/docs/widgets/services/fireshare.md new file mode 100644 index 00000000..ab26e403 --- /dev/null +++ b/docs/widgets/services/fireshare.md @@ -0,0 +1,16 @@ +--- +title: Fireshare +description: Fireshare Widget Configuration +--- + +Learn more about [Fireshare](https://github.com/ShaneIsrael/fireshare). + +Allowed fields: `["total", "categories", "views"]`. + +```yaml +widget: + type: fireshare + url: http://fireshare.host.or.ip + username: username + password: password +``` diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 15de0ee9..f4ccde6a 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -882,5 +882,10 @@ "enabled": "Enabled", "disabled": "Disabled", "total": "Total" + }, + "fireshare": { + "total": "Total", + "categories": "Categories", + "views": "Views" } } diff --git a/src/widgets/components.js b/src/widgets/components.js index 1b5c4b68..b89b201f 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -27,6 +27,7 @@ const components = { esphome: dynamic(() => import("./esphome/component")), evcc: dynamic(() => import("./evcc/component")), fileflows: dynamic(() => import("./fileflows/component")), + fireshare: dynamic(() => import("./fireshare/component")), flood: dynamic(() => import("./flood/component")), freshrss: dynamic(() => import("./freshrss/component")), fritzbox: dynamic(() => import("./fritzbox/component")), diff --git a/src/widgets/fireshare/component.jsx b/src/widgets/fireshare/component.jsx new file mode 100644 index 00000000..353f72d1 --- /dev/null +++ b/src/widgets/fireshare/component.jsx @@ -0,0 +1,44 @@ +import Container from "components/services/widget/container"; +import Block from "components/services/widget/block"; +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { widget } = service; + + const { data: infoData, error: infoError } = useWidgetAPI(widget); + + if (!widget.fields) { + widget.fields = ["total", "categories", "views"]; + } + + if (infoError) { + return ; + } + + if (!infoData) { + return ( + + + + + + ); + } + + const total = infoData.videos.length; + const categoriesSet = new Set(); + infoData.videos.forEach(video => { + const category = video.path.split('/')[0]; + categoriesSet.add(category); + }); + const categoriesCount = categoriesSet.size; + const totalViews = infoData.videos.reduce((acc, video) => acc + video.view_count, 0); + + return ( + + + + + + ); +} diff --git a/src/widgets/fireshare/proxy.js b/src/widgets/fireshare/proxy.js new file mode 100644 index 00000000..d736228b --- /dev/null +++ b/src/widgets/fireshare/proxy.js @@ -0,0 +1,74 @@ +import cache from "memory-cache"; + +import getServiceWidget from "utils/config/service-helpers"; +import { formatApiCall } from "utils/proxy/api-helpers"; +import { httpProxy } from "utils/proxy/http"; +import widgets from "widgets/widgets"; +import createLogger from "utils/logger"; + +const proxyName = "fireshareProxyHandler"; +const logger = createLogger(proxyName); +const sessionTokenCacheKey = `${proxyName}__sessionToken`; + +async function login(widget, service) { + const url = formatApiCall(widgets[widget.type].api, { ...widget, endpoint: "login" }); + logger.info(`url: ${url}`); + logger.info(`username: ${widget.username}, password: ${widget.password}`); + const [, , , responseHeaders] = await httpProxy(url, { + method: "POST", + body: JSON.stringify({ username: widget.username, password: widget.password }), + headers: { + "Content-Type": "application/json" + } + }); + + try { + logger.info(responseHeaders); + const rememberTokenCookie = responseHeaders["set-cookie"] + .find((cookie) => cookie.startsWith("remember_token=")) + .split(";")[0] + .replace("remember_token=", ""); + logger.info(`remember_token: ${rememberTokenCookie}`); + cache.put(`${sessionTokenCacheKey}.${service}`, rememberTokenCookie); + return rememberTokenCookie; + } catch (e) { + logger.error(`Error retrieving 'remember_token' cookie for service: ${service}`); + cache.del(`${sessionTokenCacheKey}.${service}`); + return null; + } +} + +export default async function fireshareProxyHandler(req, res) { + const { group, service } = req.query; + + if (group && service) { + const widget = await getServiceWidget(group, service); + + if (!widgets?.[widget.type]?.api) { + return res.status(403).json({ error: "Service does not support API calls" }); + } + + if (widget) { + let token = cache.get(`${sessionTokenCacheKey}.${service}`); + if (!token) { + token = await login(widget, service); + if (!token) { + return res.status(500).json({ error: "Failed to authenticate with Fireshare" }); + } + } + const [, , data] = await httpProxy( + formatApiCall(widgets[widget.type].api, { ...widget, endpoint: "videos?sort=updated_at+desc" }), + { + headers: { + "Content-Type": "application/json", + Cookie: `remember_token=${token}` + } + } + ); + + return res.json(JSON.parse(data)); + } + } + + return res.status(400).json({ error: "Invalid proxy service type" }); +} diff --git a/src/widgets/fireshare/widget.js b/src/widgets/fireshare/widget.js new file mode 100644 index 00000000..b27e2816 --- /dev/null +++ b/src/widgets/fireshare/widget.js @@ -0,0 +1,8 @@ +import fireshareProxyHandler from "./proxy"; + +const widget = { + api: "{url}/api/{endpoint}", + proxyHandler: fireshareProxyHandler, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index d6965f50..76b5f6df 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -21,6 +21,7 @@ import emby from "./emby/widget"; import esphome from "./esphome/widget"; import evcc from "./evcc/widget"; import fileflows from "./fileflows/widget"; +import fireshare from "./fireshare/widget"; import flood from "./flood/widget"; import freshrss from "./freshrss/widget"; import fritzbox from "./fritzbox/widget"; @@ -136,6 +137,7 @@ const widgets = { esphome, evcc, fileflows, + fireshare, flood, freshrss, fritzbox,