Files
pyTorrent/pytorrent/services/websocket.py
2026-05-03 23:46:41 +02:00

92 lines
4.4 KiB
Python

from __future__ import annotations
import psutil
from flask_socketio import emit
from ..config import POLL_INTERVAL
from .preferences import active_profile, get_profile
from .torrent_cache import torrent_cache
from .torrent_summary import cached_summary
from . import rtorrent, smart_queue, traffic_history, automation_rules
_started = False
def register_socketio_handlers(socketio):
global _started
def poller():
tick = 0
while True:
profile = active_profile()
if profile:
diff = torrent_cache.refresh(profile)
heartbeat = {"ok": bool(diff.get("ok")), "profile_id": profile["id"], "tick": tick, "error": diff.get("error", "")}
if diff.get("ok") and (diff["added"] or diff["updated"] or diff["removed"]):
socketio.emit("torrent_patch", {**diff, "summary": cached_summary(profile["id"], torrent_cache.snapshot(profile["id"]), force=True)})
elif not diff.get("ok"):
socketio.emit("rtorrent_error", diff)
try:
status = rtorrent.system_status(profile)
if bool(profile.get("is_remote")):
status["usage_source"] = "remote-hidden"
status["usage_available"] = False
else:
status["cpu"] = psutil.cpu_percent(interval=None)
status["ram"] = psutil.virtual_memory().percent
status["usage_source"] = "local"
status["usage_available"] = True
status["profile_id"] = profile["id"]
traffic_history.record(profile["id"], status.get("down_rate", 0), status.get("up_rate", 0), status.get("total_down", 0), status.get("total_up", 0))
socketio.emit("system_stats", status)
heartbeat["ok"] = True
except Exception as exc:
heartbeat["ok"] = False
heartbeat["error"] = str(exc)
socketio.emit("rtorrent_error", {"profile_id": profile["id"], "error": str(exc)})
if tick % max(1, int(30 / POLL_INTERVAL)) == 0:
try:
result = smart_queue.check(profile, force=False)
if result.get("enabled"):
socketio.emit("smart_queue_update", result)
except Exception as exc:
socketio.emit("smart_queue_update", {"ok": False, "error": str(exc)})
try:
auto_result = automation_rules.check(profile, force=False)
if auto_result.get("applied"):
socketio.emit("automation_update", auto_result)
except Exception as exc:
socketio.emit("automation_update", {"ok": False, "error": str(exc)})
socketio.emit("heartbeat", heartbeat)
tick += 1
socketio.sleep(POLL_INTERVAL)
@socketio.on("connect")
def handle_connect():
global _started
if not _started:
socketio.start_background_task(poller)
_started = True
profile = active_profile()
emit("connected", {"ok": True, "profile": profile})
if not profile:
# Note: Fresh installs have no rTorrent yet; tell the client to show setup instead of waiting for a snapshot.
emit("profile_required", {"ok": True, "profiles": []})
return
rows = torrent_cache.snapshot(profile["id"])
emit("torrent_snapshot", {"profile_id": profile["id"], "torrents": rows, "summary": cached_summary(profile["id"], rows)})
@socketio.on("select_profile")
def handle_select_profile(data):
profile_id = int((data or {}).get("profile_id") or 0)
if not profile_id:
# Note: Ignore empty profile selections created before the first rTorrent profile exists.
emit("profile_required", {"ok": True, "profiles": []})
return
profile = get_profile(profile_id)
if not profile:
emit("rtorrent_error", {"error": "Profile does not exist"})
return
diff = torrent_cache.refresh(profile)
rows = torrent_cache.snapshot(profile_id)
emit("torrent_snapshot", {"profile_id": profile_id, "torrents": rows, "summary": cached_summary(profile_id, rows, force=True), "error": diff.get("error", "")})