from __future__ import annotations from threading import RLock from time import time from . import rtorrent _VOLATILE = {"down_rate", "down_rate_h", "up_rate", "up_rate_h", "progress", "completed_bytes", "peers", "seeds", "ratio", "state", "status", "message", "down_total", "down_total_h", "up_total", "up_total_h"} class TorrentCache: def __init__(self): self._lock = RLock() self._data: dict[int, dict[str, dict]] = {} self._errors: dict[int, str] = {} self._updated_at: dict[int, float] = {} def snapshot(self, profile_id: int) -> list[dict]: with self._lock: return list(self._data.get(profile_id, {}).values()) def error(self, profile_id: int) -> str: with self._lock: return self._errors.get(profile_id, "") def refresh(self, profile: dict) -> dict: profile_id = int(profile["id"]) try: rows = rtorrent.list_torrents(profile) fresh = {t["hash"]: t for t in rows} with self._lock: old = self._data.get(profile_id, {}) added = [v for h, v in fresh.items() if h not in old] removed = [h for h in old.keys() if h not in fresh] updated = [] for h, new in fresh.items(): prev = old.get(h) if not prev: continue patch = {"hash": h} for key, value in new.items(): if prev.get(key) != value: patch[key] = value if len(patch) > 1: updated.append(patch) self._data[profile_id] = fresh self._errors[profile_id] = "" self._updated_at[profile_id] = time() return {"ok": True, "profile_id": profile_id, "added": added, "updated": updated, "removed": removed} except Exception as exc: with self._lock: self._errors[profile_id] = str(exc) return {"ok": False, "profile_id": profile_id, "error": str(exc), "added": [], "updated": [], "removed": []} torrent_cache = TorrentCache()