"""qBittorrent tools — torrent queue and transfer stats (read-only).""" from ._common import qbit_login_if_needed, qbit_request def _compact_torrent(t): return { "name": t.get("name"), "state": t.get("state"), # downloading, uploading, stalledDL, pausedDL, completed, ... "progress_pct": round((t.get("progress", 0) or 0) * 100, 1), "size_gb": round((t.get("size", 0) or 0) / 1e9, 2), "dl_speed_mbps": round((t.get("dlspeed", 0) or 0) / 1e6, 2), "up_speed_mbps": round((t.get("upspeed", 0) or 0) / 1e6, 2), "eta_seconds": t.get("eta"), "category": t.get("category"), "ratio": round(t.get("ratio", 0) or 0, 2), "num_seeds": t.get("num_seeds"), "num_peers": t.get("num_leechs") or t.get("num_peers"), "added_on_epoch": t.get("added_on"), } def qbit_torrents(filter="all", category=None, limit=30): """List torrents. Filter is one of: all, downloading, seeding, completed, paused, stalled, errored.""" cookie = qbit_login_if_needed() params = {"filter": filter} if category: params["category"] = category data, _ = qbit_request("/api/v2/torrents/info", params=params, cookies=cookie) if isinstance(data, str): return {"error": data} items = [_compact_torrent(t) for t in data[:limit]] return {"filter": filter, "category": category, "count": len(data), "torrents": items} def qbit_transfer_stats(): """Global transfer stats: overall download / upload speed, session totals, DHT nodes.""" cookie = qbit_login_if_needed() data, _ = qbit_request("/api/v2/transfer/info", cookies=cookie) if isinstance(data, str): return {"error": data} return { "dl_speed_mbps": round((data.get("dl_info_speed", 0) or 0) / 1e6, 2), "up_speed_mbps": round((data.get("up_info_speed", 0) or 0) / 1e6, 2), "dl_session_gb": round((data.get("dl_info_data", 0) or 0) / 1e9, 2), "up_session_gb": round((data.get("up_info_data", 0) or 0) / 1e9, 2), "dht_nodes": data.get("dht_nodes"), "connection_status": data.get("connection_status"), } def qbit_categories(): """Configured torrent categories (e.g., sonarr, radarr, lidarr, whisparr).""" cookie = qbit_login_if_needed() data, _ = qbit_request("/api/v2/torrents/categories", cookies=cookie) return data if isinstance(data, dict) else {"error": str(data)} TOOLS = [ { "name": "qbit_torrents", "description": "List torrents in qBittorrent with their progress, state, speed, and ETA. Filter options: all, downloading, seeding, completed, paused, stalled, errored. Category options: sonarr, radarr, lidarr, whisparr (which *arr requested it).", "input_schema": { "type": "object", "properties": { "filter": {"type": "string", "default": "all", "enum": ["all", "downloading", "seeding", "completed", "paused", "stalled", "errored"]}, "category": {"type": "string", "description": "Optional category filter"}, "limit": {"type": "integer", "default": 30}, }, }, "read_only": True, "fn": qbit_torrents, }, { "name": "qbit_transfer_stats", "description": "Current global download / upload speed and session totals from qBittorrent. Use when user asks 'how fast is the download', 'what's my current download speed', 'total downloaded this session'.", "input_schema": {"type": "object", "properties": {}}, "read_only": True, "fn": qbit_transfer_stats, }, { "name": "qbit_categories", "description": "List the torrent categories configured in qBittorrent. Shows which *arr is pulling what.", "input_schema": {"type": "object", "properties": {}}, "read_only": True, "fn": qbit_categories, }, ]