56 lines
2.2 KiB
Python
56 lines
2.2 KiB
Python
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()
|