new functions
This commit is contained in:
@@ -371,6 +371,66 @@ def browse_path(profile: dict, path: str | None = None) -> dict:
|
|||||||
return {"path": base, "parent": parent, "dirs": dirs[:300], "source": "rtorrent"}
|
return {"path": base, "parent": parent, "dirs": dirs[:300], "source": "rtorrent"}
|
||||||
|
|
||||||
|
|
||||||
|
POST_CHECK_DOWNLOAD_LABEL = "To download after check"
|
||||||
|
|
||||||
|
|
||||||
|
def _label_names(value: str) -> list[str]:
|
||||||
|
names: list[str] = []
|
||||||
|
for part in str(value or "").replace(";", ",").replace("|", ",").split(","):
|
||||||
|
label = part.strip()
|
||||||
|
if label and label not in names:
|
||||||
|
names.append(label)
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def _label_value(labels: list[str]) -> str:
|
||||||
|
return ", ".join([label for label in labels if str(label or "").strip()])
|
||||||
|
|
||||||
|
|
||||||
|
def _row_progress_complete(row: dict) -> bool:
|
||||||
|
size = int(row.get("size") or 0)
|
||||||
|
completed = int(row.get("completed_bytes") or 0)
|
||||||
|
return bool(row.get("complete")) or (size > 0 and completed >= size) or float(row.get("progress") or 0) >= 100.0
|
||||||
|
|
||||||
|
|
||||||
|
def apply_post_check_policy(profile: dict, rows: list[dict], previous_rows: dict[str, dict] | None = None) -> list[dict]:
|
||||||
|
"""Start complete torrents after check; pause and label incomplete ones."""
|
||||||
|
previous_rows = previous_rows or {}
|
||||||
|
c = client_for(profile)
|
||||||
|
changes: list[dict] = []
|
||||||
|
for row in rows:
|
||||||
|
h = str(row.get("hash") or "")
|
||||||
|
prev = previous_rows.get(h) or {}
|
||||||
|
was_checking = str(prev.get("status") or "") == "Checking" or int(prev.get("hashing") or 0) > 0
|
||||||
|
is_checking = str(row.get("status") or "") == "Checking" or int(row.get("hashing") or 0) > 0
|
||||||
|
if not h or not was_checking or is_checking:
|
||||||
|
continue
|
||||||
|
complete = _row_progress_complete(row)
|
||||||
|
try:
|
||||||
|
if complete:
|
||||||
|
# Note: Po zakonczonym checku pelny torrent jest automatycznie startowany, zeby od razu seedowal.
|
||||||
|
c.call("d.start", h)
|
||||||
|
labels = [label for label in _label_names(str(row.get("label") or "")) if label != POST_CHECK_DOWNLOAD_LABEL]
|
||||||
|
if _label_value(labels) != str(row.get("label") or ""):
|
||||||
|
c.call("d.custom1.set", h, _label_value(labels))
|
||||||
|
row["label"] = _label_value(labels)
|
||||||
|
row.update({"state": 1, "active": 1, "paused": False, "status": "Seeding"})
|
||||||
|
changes.append({"hash": h, "action": "start", "complete": True})
|
||||||
|
else:
|
||||||
|
# Note: Niepelny torrent po checku trafia do pauzy i dostaje etykiete informujaca, ze wymaga dalszego pobierania.
|
||||||
|
c.call("d.start", h)
|
||||||
|
c.call("d.pause", h)
|
||||||
|
labels = _label_names(str(row.get("label") or ""))
|
||||||
|
if POST_CHECK_DOWNLOAD_LABEL not in labels:
|
||||||
|
labels.append(POST_CHECK_DOWNLOAD_LABEL)
|
||||||
|
c.call("d.custom1.set", h, _label_value(labels))
|
||||||
|
row.update({"state": 1, "active": 0, "paused": True, "status": "Paused", "label": _label_value(labels)})
|
||||||
|
changes.append({"hash": h, "action": "pause_and_label", "complete": False, "label": POST_CHECK_DOWNLOAD_LABEL})
|
||||||
|
except Exception as exc:
|
||||||
|
changes.append({"hash": h, "action": "post_check_policy_failed", "error": str(exc)})
|
||||||
|
return changes
|
||||||
|
|
||||||
|
|
||||||
TORRENT_FIELDS = [
|
TORRENT_FIELDS = [
|
||||||
"d.hash=", "d.name=", "d.state=", "d.complete=", "d.size_bytes=", "d.completed_bytes=",
|
"d.hash=", "d.name=", "d.state=", "d.complete=", "d.size_bytes=", "d.completed_bytes=",
|
||||||
"d.ratio=", "d.up.rate=", "d.down.rate=", "d.up.total=", "d.down.total=", "d.peers_connected=",
|
"d.ratio=", "d.up.rate=", "d.down.rate=", "d.up.total=", "d.down.total=", "d.peers_connected=",
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ class TorrentCache:
|
|||||||
profile_id = int(profile["id"])
|
profile_id = int(profile["id"])
|
||||||
try:
|
try:
|
||||||
rows = rtorrent.list_torrents(profile)
|
rows = rtorrent.list_torrents(profile)
|
||||||
|
with self._lock:
|
||||||
|
old = dict(self._data.get(profile_id, {}))
|
||||||
|
post_check_changes = rtorrent.apply_post_check_policy(profile, rows, old)
|
||||||
fresh = {t["hash"]: t for t in rows}
|
fresh = {t["hash"]: t for t in rows}
|
||||||
with self._lock:
|
with self._lock:
|
||||||
old = self._data.get(profile_id, {})
|
|
||||||
added = [v for h, v in fresh.items() if h not in old]
|
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]
|
removed = [h for h in old.keys() if h not in fresh]
|
||||||
updated = []
|
updated = []
|
||||||
@@ -45,7 +47,7 @@ class TorrentCache:
|
|||||||
self._data[profile_id] = fresh
|
self._data[profile_id] = fresh
|
||||||
self._errors[profile_id] = ""
|
self._errors[profile_id] = ""
|
||||||
self._updated_at[profile_id] = time()
|
self._updated_at[profile_id] = time()
|
||||||
return {"ok": True, "profile_id": profile_id, "added": added, "updated": updated, "removed": removed}
|
return {"ok": True, "profile_id": profile_id, "added": added, "updated": updated, "removed": removed, "post_check_changes": post_check_changes}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._errors[profile_id] = str(exc)
|
self._errors[profile_id] = str(exc)
|
||||||
|
|||||||
@@ -36,22 +36,28 @@ def _has_error(row: dict) -> bool:
|
|||||||
return bool(message and any(pattern in message for pattern in _ERROR_PATTERNS))
|
return bool(message and any(pattern in message for pattern in _ERROR_PATTERNS))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_checking(row: dict) -> bool:
|
||||||
|
return str(row.get("status") or "") == "Checking" or _number(row, "hashing") > 0
|
||||||
|
|
||||||
|
|
||||||
def _matches(row: dict, summary_type: str) -> bool:
|
def _matches(row: dict, summary_type: str) -> bool:
|
||||||
status = str(row.get("status") or "")
|
status = str(row.get("status") or "")
|
||||||
|
checking = _is_checking(row)
|
||||||
if summary_type == "all":
|
if summary_type == "all":
|
||||||
return True
|
return True
|
||||||
if summary_type == "downloading":
|
if summary_type == "downloading":
|
||||||
return not bool(row.get("complete")) and bool(row.get("state")) and not bool(row.get("paused"))
|
return not checking and not bool(row.get("complete")) and bool(row.get("state")) and not bool(row.get("paused"))
|
||||||
if summary_type == "seeding":
|
if summary_type == "seeding":
|
||||||
return status != "Checking" and bool(row.get("complete")) and bool(row.get("state")) and not bool(row.get("paused"))
|
return not checking and bool(row.get("complete")) and bool(row.get("state")) and not bool(row.get("paused"))
|
||||||
if summary_type == "paused":
|
if summary_type == "paused":
|
||||||
return bool(row.get("paused")) or status == "Paused"
|
return not checking and (bool(row.get("paused")) or status == "Paused")
|
||||||
if summary_type == "checking":
|
if summary_type == "checking":
|
||||||
return status == "Checking" or _number(row, "hashing") > 0
|
return checking
|
||||||
if summary_type == "error":
|
if summary_type == "error":
|
||||||
return _has_error(row)
|
return _has_error(row)
|
||||||
if summary_type == "stopped":
|
if summary_type == "stopped":
|
||||||
return not bool(row.get("state"))
|
# Note: Stopped count follows the UI filter exactly, so torrents being hash-checked do not inflate an empty Stopped list.
|
||||||
|
return not checking and not bool(row.get("state"))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user