diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 13a8f90c8..3c61aa00e 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -123,8 +123,6 @@ export default async function credentialedProxyHandler(req, res, map) { // v1 does not require a key headers.Authorization = `Bearer ${widget.key}`; } - } else if (widget.type === "jellyfin") { - headers["Authorization"] = `MediaBrowser Token=${widget.key}`; } else { headers["X-API-Key"] = `${widget.key}`; } diff --git a/src/widgets/jellyfin/proxy.js b/src/widgets/jellyfin/proxy.js new file mode 100644 index 000000000..d175d9030 --- /dev/null +++ b/src/widgets/jellyfin/proxy.js @@ -0,0 +1,66 @@ +import getServiceWidget from "utils/config/service-helpers"; +import createLogger from "utils/logger"; +import { formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers"; +import { httpProxy } from "utils/proxy/http"; +import validateWidgetData from "utils/proxy/validate-widget-data"; +import widgets from "widgets/widgets"; + +const logger = createLogger("jellyfinProxyHandler"); + +export default async function jellyfinProxyHandler(req, res, map) { + const { group, service, endpoint, index } = req.query; + + if (!group || !service) { + logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group); + return res.status(400).json({ error: "Invalid proxy service type" }); + } + + const widget = await getServiceWidget(group, service, index); + + if (!widget || !widgets?.[widget.type]?.api) { + logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group); + return res.status(403).json({ error: "Service does not support API calls" }); + } + + const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget })); + + const headers = { + Authorization: `MediaBrowser Token=${widget.key}`, + "Content-Type": "application/json", + "X-Emby-Token": widget.key, + "X-MediaBrowser-Token": widget.key, + }; + + const params = { + method: req.method, + withCredentials: true, + credentials: "include", + headers, + }; + + const [status, contentType, data] = await httpProxy(url, params); + + let resultData = data; + + if (resultData.error?.url) { + resultData.error.url = sanitizeErrorURL(url); + } + + if (status === 204 || status === 304) { + return res.status(status).end(); + } + + if (status >= 400) { + logger.error("HTTP Error %d calling %s", status, url.toString()); + } + + if (status === 200) { + if (!validateWidgetData(widget, endpoint, resultData)) { + return res.status(500).json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } }); + } + if (map) resultData = map(resultData); + } + + if (contentType) res.setHeader("Content-Type", contentType); + return res.status(status).send(resultData); +} diff --git a/src/widgets/jellyfin/widget.js b/src/widgets/jellyfin/widget.js index b1f603e4d..a11121359 100644 --- a/src/widgets/jellyfin/widget.js +++ b/src/widgets/jellyfin/widget.js @@ -1,8 +1,8 @@ -import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; +import jellyfinProxyHandler from "./proxy"; const widget = { api: "{url}/{endpoint}", - proxyHandler: credentialedProxyHandler, + proxyHandler: jellyfinProxyHandler, mappings: { Sessions: { endpoint: "emby/Sessions?api_key={key}", @@ -20,6 +20,7 @@ const widget = { endpoint: "emby/Sessions/{sessionId}/Playing/Pause?api_key={key}", segments: ["sessionId"], }, + // V2 Endpoints SessionsV2: { endpoint: "Sessions", },