fix: show active qBittorrent transfers

This commit is contained in:
Developer
2026-07-14 16:07:04 +00:00
parent 70511d97f9
commit a541a4fd16
11 changed files with 376 additions and 187 deletions
@@ -410,6 +410,26 @@ def _qbittorrent_client(cache_key: tuple[str, str, str, str, int]) -> Qbittorren
return QbittorrentClient(base_url, username, password, timeout=timeout)
_QBITTORRENT_DOWNLOAD_STATES = frozenset(
{"downloading", "forceddl", "stalleddl", "metadl", "allocating"}
)
_QBITTORRENT_UPLOAD_STATES = frozenset({"uploading", "forcedup", "stalledup"})
def _qbit_torrent_direction(torrent: dict[str, Any]) -> str | None:
"""Return the transfer direction for active qBittorrent states or speeds."""
state = str(torrent.get("state") or "").lower()
if state in _QBITTORRENT_DOWNLOAD_STATES:
return "downloading"
if state in _QBITTORRENT_UPLOAD_STATES:
return "uploading"
if _safe_int(torrent.get("dlspeed")) > 0:
return "downloading"
if _safe_int(torrent.get("upspeed")) > 0:
return "uploading"
return None
class QbittorrentWidgetSource:
"""Fetch qBittorrent data for totals, active, and speed widgets."""
@@ -456,24 +476,36 @@ class QbittorrentWidgetSource:
if widget_kind == "totals":
by_state: dict[str, int] = {}
for t in torrents.values():
state = str(t.get("state", "unknown"))
by_direction = {"downloading": 0, "uploading": 0}
for torrent in torrents.values():
state = str(torrent.get("state") or "unknown")
by_state[state] = by_state.get(state, 0) + 1
return {"total": len(torrents), "by_state": by_state}
direction = _qbit_torrent_direction(torrent)
if direction:
by_direction[direction] += 1
return {
"total": len(torrents),
"by_state": by_state,
"by_direction": by_direction,
}
if widget_kind == "active":
active = [
{
"name": t.get("name"),
"state": t.get("state"),
"size": t.get("size"),
"progress": t.get("progress"),
"dl_speed": t.get("dlspeed"),
"up_speed": t.get("upspeed"),
}
for t in torrents.values()
if str(t.get("state", "")) in {"downloading", "uploading"}
]
active = []
for torrent in torrents.values():
direction = _qbit_torrent_direction(torrent)
if not direction:
continue
active.append(
{
"name": torrent.get("name"),
"state": torrent.get("state"),
"direction": direction,
"size": torrent.get("size"),
"progress": torrent.get("progress"),
"dl_speed": torrent.get("dlspeed"),
"up_speed": torrent.get("upspeed"),
}
)
return {"torrents": active}
return {"error": f"Unknown qBittorrent widget kind: {widget_kind}"}
+21 -2
View File
@@ -1086,6 +1086,22 @@ def _fake_qbit_maindata():
"dlspeed": 0,
"upspeed": 0,
},
"h5": {
"name": "Forced download",
"state": "forcedDL",
"size": 5000,
"progress": 0.4,
"dlspeed": 0,
"upspeed": 0,
},
"h6": {
"name": "Stalled upload",
"state": "stalledUP",
"size": 6000,
"progress": 1.0,
"dlspeed": 0,
"upspeed": 0,
},
},
}
@@ -1107,11 +1123,12 @@ async def test_qbittorrent_totals_counts_all_torrents():
mock_client.return_value.maindata.return_value = _fake_qbit_maindata()
result = await adapter.fetch(service, "totals", {})
assert result["total"] == 4
assert result["total"] == 6
assert result["by_state"]["downloading"] == 1
assert result["by_state"]["uploading"] == 1
assert result["by_state"]["queuedDL"] == 1
assert result["by_state"]["pausedDL"] == 1
assert result["by_direction"] == {"downloading": 2, "uploading": 2}
@pytest.mark.asyncio
@@ -1132,10 +1149,12 @@ async def test_qbittorrent_active_filters_dl_ul_only():
result = await adapter.fetch(service, "active", {})
active = result["torrents"]
assert len(active) == 2
assert len(active) == 4
names = [t["name"] for t in active]
assert "Movie.mkv" in names
assert "Show.mkv" in names
assert "Forced download" in names
assert "Stalled upload" in names
# Queued and paused are excluded
assert "Queued" not in names
assert "Paused" not in names