103 lines
5.0 KiB
Python
103 lines
5.0 KiB
Python
from __future__ import annotations
|
|
|
|
import threading
|
|
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, torrent_stats
|
|
|
|
_started = False
|
|
_start_lock = threading.Lock()
|
|
|
|
|
|
def register_socketio_handlers(socketio):
|
|
|
|
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(15 * 60 / POLL_INTERVAL)) == 0:
|
|
# Note: Queue heavier torrent statistics outside the fast system_stats poller.
|
|
torrent_stats.queue_refresh(socketio, profile, force=False)
|
|
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)
|
|
|
|
def ensure_poller_started():
|
|
global _started
|
|
with _start_lock:
|
|
if not _started:
|
|
# Note: Poller startuje przy starcie aplikacji, więc Smart Queue i automatyzacje działają bez otwartego UI.
|
|
socketio.start_background_task(poller)
|
|
_started = True
|
|
|
|
ensure_poller_started()
|
|
|
|
@socketio.on("connect")
|
|
def handle_connect():
|
|
ensure_poller_started()
|
|
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", "")})
|