diff --git a/src/widgets/qbittorrent/component.jsx b/src/widgets/qbittorrent/component.jsx
index c8f9f6ead..73dfacb5c 100644
--- a/src/widgets/qbittorrent/component.jsx
+++ b/src/widgets/qbittorrent/component.jsx
@@ -10,13 +10,28 @@ export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
- const { data: torrentData, error: torrentError } = useWidgetAPI(widget, "torrents");
+ const { data: transferData, error: transferError } = useWidgetAPI(widget, "transfer");
+ const { data: totalCountData, error: totalCountError } = useWidgetAPI(widget, "torrentCount");
+ const { data: completedCountData, error: completedCountError } = useWidgetAPI(widget, "torrentCount", {
+ filter: "completed",
+ });
+ const { data: leechTorrentData, error: leechTorrentError } = useWidgetAPI(
+ widget,
+ widget?.enableLeechProgress ? "torrents" : "",
+ widget?.enableLeechProgress ? { filter: "downloading" } : undefined,
+ );
- if (torrentError) {
- return ;
+ const apiError = transferError || totalCountError || completedCountError || leechTorrentError;
+ if (apiError) {
+ return ;
}
- if (!torrentData) {
+ if (
+ !transferData ||
+ totalCountData === undefined ||
+ completedCountData === undefined ||
+ (widget?.enableLeechProgress && !leechTorrentData)
+ ) {
return (
@@ -27,24 +42,15 @@ export default function Component({ service }) {
);
}
- let rateDl = 0;
- let rateUl = 0;
- let completed = 0;
- const leechTorrents = [];
+ const rateDl = Number(transferData?.dl_info_speed ?? 0);
+ const rateUl = Number(transferData?.up_info_speed ?? 0);
+ const totalCount = Number(totalCountData?.all ?? totalCountData?.count ?? totalCountData ?? 0);
+ const completedCount = Number(
+ completedCountData?.completed ?? completedCountData?.count ?? completedCountData?.all ?? completedCountData ?? 0,
+ );
+ const leech = Math.max(0, totalCount - completedCount);
- 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;
- }
- if (torrent.state.includes("DL") || torrent.state === "downloading") {
- leechTorrents.push(torrent);
- }
- }
-
- const leech = torrentData.length - completed;
+ const leechTorrents = Array.isArray(leechTorrentData) ? [...leechTorrentData] : [];
const statePriority = [
"downloading",
"forcedDL",
@@ -55,7 +61,6 @@ export default function Component({ service }) {
"queuedDL",
"pausedDL",
];
-
leechTorrents.sort((firstTorrent, secondTorrent) => {
const firstStateIndex = statePriority.indexOf(firstTorrent.state);
const secondStateIndex = statePriority.indexOf(secondTorrent.state);
@@ -70,7 +75,7 @@ export default function Component({ service }) {
-
+
{widget?.enableLeechProgress &&
diff --git a/src/widgets/qbittorrent/component.test.jsx b/src/widgets/qbittorrent/component.test.jsx
index a4e222b75..df8ae51c8 100644
--- a/src/widgets/qbittorrent/component.test.jsx
+++ b/src/widgets/qbittorrent/component.test.jsx
@@ -34,19 +34,38 @@ describe("widgets/qbittorrent/component", () => {
expect(screen.getByText("qbittorrent.upload")).toBeInTheDocument();
});
- it("computes leech/seed counts and upload/download rates, and can render leech progress entries", () => {
- useWidgetAPI.mockReturnValue({
- data: [
- { name: "A", dlspeed: 10, upspeed: 1, progress: 1, state: "uploading" },
- { name: "B", dlspeed: 5, upspeed: 2, progress: 0.5, state: "downloading", eta: 60, size: 100, amount_left: 50 },
- ],
- error: undefined,
+ it("uses lightweight endpoints for counts/rates and filtered torrents for leech progress", () => {
+ useWidgetAPI.mockImplementation((_widget, endpoint, query) => {
+ if (endpoint === "transfer") {
+ return { data: { dl_info_speed: 15, up_info_speed: 3 }, error: undefined };
+ }
+ if (endpoint === "torrentCount" && !query) {
+ return { data: 2, error: undefined };
+ }
+ if (endpoint === "torrentCount" && query?.filter === "completed") {
+ return { data: 1, error: undefined };
+ }
+ if (endpoint === "torrents" && query?.filter === "downloading") {
+ return {
+ data: [
+ {
+ name: "B",
+ progress: 0.5,
+ state: "downloading",
+ eta: 60,
+ size: 100,
+ amount_left: 50,
+ },
+ ],
+ error: undefined,
+ };
+ }
+ return { data: undefined, error: undefined };
});
const service = { widget: { type: "qbittorrent", enableLeechProgress: true } };
const { container } = renderWithProviders(, { settings: { hideErrors: false } });
- // total=2, completed=1 => leech=1
expectBlockValue(container, "qbittorrent.leech", 1);
expectBlockValue(container, "qbittorrent.seed", 1);
expectBlockValue(container, "qbittorrent.download", 15);
diff --git a/src/widgets/qbittorrent/widget.js b/src/widgets/qbittorrent/widget.js
index 182ac9d1b..9ec167faf 100644
--- a/src/widgets/qbittorrent/widget.js
+++ b/src/widgets/qbittorrent/widget.js
@@ -4,8 +4,16 @@ const widget = {
proxyHandler: qbittorrentProxyHandler,
mappings: {
+ transfer: {
+ endpoint: "transfer/info",
+ },
+ torrentCount: {
+ endpoint: "torrents/count",
+ optionalParams: ["filter"],
+ },
torrents: {
endpoint: "torrents/info",
+ optionalParams: ["filter"],
},
},
};