Add Transmission widget

- Update http.js to support writing request bodies
- Update http.js to support returning all response headers

resolves: #104
pull/141/head
Jason Fischer 2 years ago
parent 406358aae9
commit b3db549a65

@ -63,6 +63,12 @@
"upload": "Upload",
"download": "Download"
},
"transmission": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"sonarr": {
"wanted": "Wanted",
"queued": "Queued",

@ -8,6 +8,7 @@ import Portainer from "./widgets/service/portainer";
import Emby from "./widgets/service/emby";
import Nzbget from "./widgets/service/nzbget";
import SABnzbd from "./widgets/service/sabnzbd";
import Transmission from "./widgets/service/transmission";
import Docker from "./widgets/service/docker";
import Pihole from "./widgets/service/pihole";
import Rutorrent from "./widgets/service/rutorrent";
@ -31,6 +32,8 @@ const widgetMappings = {
emby: Emby,
jellyfin: Jellyfin,
nzbget: Nzbget,
sabnzbd: SABnzbd,
transmission: Transmission,
pihole: Pihole,
rutorrent: Rutorrent,
speedtest: Speedtest,
@ -41,7 +44,6 @@ const widgetMappings = {
npm: Npm,
tautulli: Tautulli,
gotify: Gotify,
sabnzbd: SABnzbd
};
export default function Widget({ service }) {

@ -29,7 +29,7 @@ export default function SABnzbd({ service }) {
return (
<Widget>
<Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}bps`} />
<Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}B/s`} />
<Block label={t("sabnzbd.queue")} value={queueData.queue.noofslots} />
<Block label={t("sabnzbd.timeleft")} value={queueData.queue.timeleft} />
</Widget>

@ -0,0 +1,69 @@
import useSWR from "swr";
import { useTranslation } from "react-i18next";
import Widget from "../widget";
import Block from "../block";
import { formatApiUrl } from "utils/api-helpers";
export default function Transmission({ service }) {
const { t } = useTranslation();
const config = service.widget;
const { data: torrentData, error: torrentError } = useSWR(formatApiUrl(config));
if (torrentError) {
return <Widget error={t("widget.api_error")} />;
}
if (!torrentData) {
return (
<Widget>
<Block label={t("transmission.leech")} />
<Block label={t("transmission.download")} />
<Block label={t("transmission.seed")} />
<Block label={t("transmission.upload")} />
</Widget>
);
}
const torrents = torrentData.arguments.torrents;
let rateDl = 0;
let rateUl = 0;
let completed = 0;
for (let torrent of torrents) {
rateDl += torrent.rateDownload;
rateUl += torrent.rateUpload;
if (torrent.percentDone === 1) {
completed++;
}
}
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 (
<Widget>
<Block label={t("transmission.leech")} value={leech} />
<Block label={t("transmission.download")} value={`${rateDl.toFixed(2)} ${unitsDl}`} />
<Block label={t("transmission.seed")} value={completed} />
<Block label={t("transmission.upload")} value={`${rateUl.toFixed(2)} ${unitsUl}`} />
</Widget>
);
}

@ -3,6 +3,7 @@ import credentialedProxyHandler from "utils/proxies/credentialed";
import rutorrentProxyHandler from "utils/proxies/rutorrent";
import nzbgetProxyHandler from "utils/proxies/nzbget";
import npmProxyHandler from "utils/proxies/npm";
import transmissionProxyHandler from "utils/proxies/transmission";
const serviceProxyHandlers = {
// uses query param auth
@ -27,6 +28,7 @@ const serviceProxyHandlers = {
rutorrent: rutorrentProxyHandler,
nzbget: nzbgetProxyHandler,
npm: npmProxyHandler,
transmission: transmissionProxyHandler,
};
export default async function handler(req, res) {

@ -9,6 +9,7 @@ const formats = {
traefik: `{url}/api/{endpoint}`,
portainer: `{url}/api/endpoints/{env}/{endpoint}`,
rutorrent: `{url}/plugins/httprpc/action.php`,
transmission: `{url}/transmission/rpc`,
jellyseerr: `{url}/api/v1/{endpoint}`,
overseerr: `{url}/api/v1/{endpoint}`,
ombi: `{url}/api/v1/{endpoint}`,

@ -12,7 +12,7 @@ export function httpsRequest(url, params) {
});
response.on("end", () => {
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data)]);
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data), response.headers]);
});
});
@ -20,6 +20,10 @@ export function httpsRequest(url, params) {
reject([500, error]);
});
if (params.body) {
request.write(params.body);
}
request.end();
});
}
@ -34,7 +38,7 @@ export function httpRequest(url, params) {
});
response.on("end", () => {
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data)]);
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data), response.headers]);
});
});
@ -42,6 +46,10 @@ export function httpRequest(url, params) {
reject([500, error]);
});
if (params.body) {
request.write(params.body);
}
request.end();
});
}

@ -0,0 +1,56 @@
import { httpProxy } from "utils/http";
import { formatApiCall } from "utils/api-helpers";
import getServiceWidget from "utils/service-helpers";
export default async function transmissionProxyHandler(req, res) {
const { group, service, endpoint } = req.query;
if (!group || !service) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
const widget = await getServiceWidget(group, service);
if (!widget) {
return res.status(400).json({ error: "Invalid proxy service type" });
}
const url = new URL(formatApiCall(widget.type, { endpoint, ...widget }));
const csrfHeaderName = "x-transmission-session-id";
const method = "POST";
const body = JSON.stringify({
method: "torrent-get",
arguments: {
fields: ["percentDone", "status", "rateDownload", "rateUpload"]
}
});
const reqHeaders = {
"content-type": "application/json",
};
let [status, contentType, data, responseHeaders] = await httpProxy(url, {
method: method,
auth: `${widget.username}:${widget.password}`,
body: body,
headers: reqHeaders,
});
if (status === 409) {
// Transmission is rejecting the request, but returning a CSRF token
reqHeaders[csrfHeaderName] = responseHeaders[csrfHeaderName];
// retry the request, now with the CSRF token
[status, contentType, data] = await httpProxy(url, {
method: method,
auth: `${widget.username}:${widget.password}`,
body: body,
headers: reqHeaders,
});
}
if (contentType) res.setHeader("Content-Type", contentType);
return res.status(status).send(data);
}
Loading…
Cancel
Save