This commit is contained in:
Mateusz Gruszczyński
2026-05-06 23:10:53 +02:00
parent 98f155b53a
commit 2691442fc1
9 changed files with 351 additions and 366 deletions

View File

@@ -1274,73 +1274,6 @@ def start_or_resume_hash(c: ScgiRtorrentClient, torrent_hash: str) -> dict:
result['ok'] = result.get('ok', True)
return result
def move_torrents(profile: dict, torrent_hashes: list[str], payload: dict | None = None) -> dict:
# Note: Shared move implementation keeps API move and automation move-to-path identical.
payload = payload or {}
c = client_for(profile)
path = _remote_clean_path(payload.get("path") or "")
move_data = bool(payload.get("move_data"))
recheck = bool(payload.get("recheck", move_data))
keep_seeding = bool(payload.get("keep_seeding"))
# Note: keep_seeding lets automation move completed data to another path and force the torrent back into seeding.
if not path:
raise ValueError("Missing path")
results = []
if move_data:
_rt_execute_allow_timeout(c, "execute.throw", "mkdir", "-p", path)
for h in torrent_hashes:
item = {"hash": h, "path": path, "move_data": move_data, "keep_seeding": keep_seeding}
try:
was_state = int(c.call("d.state", h) or 0)
except Exception:
was_state = 0
try:
was_active = int(c.call("d.is_active", h) or 0)
except Exception:
was_active = was_state
if move_data:
src = _remote_clean_path(_torrent_data_path(c, h))
if not src:
raise ValueError(f"Cannot determine source path for {h}")
dst = _remote_join(path, posixpath.basename(src.rstrip("/")))
if src != dst:
try:
c.call("d.stop", h)
except Exception:
pass
try:
c.call("d.close", h)
except Exception:
pass
_run_remote_move(c, src, dst)
item["moved_from"] = src
item["moved_to"] = dst
else:
item["skipped"] = "source and destination are the same"
c.call("d.directory.set", h, path)
if recheck:
try:
c.call("d.check_hash", h)
except Exception as exc:
item["recheck_error"] = str(exc)
if keep_seeding or was_state or was_active:
try:
c.call("d.start", h)
item["started_after_move"] = True
except Exception as exc:
item["start_error"] = str(exc)
else:
c.call("d.directory.set", h, path)
if keep_seeding:
try:
c.call("d.start", h)
item["started_after_path_change"] = True
except Exception as exc:
item["start_error"] = str(exc)
results.append(item)
return {"ok": True, "count": len(torrent_hashes), "move_data": move_data, "keep_seeding": keep_seeding, "results": results}
def action(profile: dict, torrent_hashes: list[str], name: str, payload: dict | None = None) -> dict:
payload = payload or {}
c = client_for(profile)
@@ -1361,8 +1294,61 @@ def action(profile: dict, torrent_hashes: list[str], name: str, payload: dict |
c.call("d.custom.set", h, "py_ratio_group", group)
return {"ok": True, "count": len(torrent_hashes), "ratio_group": group}
if name == "move":
# Note: Main move delegates to the shared helper used by automations.
return move_torrents(profile, torrent_hashes, payload)
path = _remote_clean_path(payload.get("path") or "")
move_data = bool(payload.get("move_data"))
recheck = bool(payload.get("recheck", move_data))
keep_seeding = bool(payload.get("keep_seeding"))
# Note: Automations can force seeding after a physical move even if the torrent was not active before.
if not path:
raise ValueError("Missing path")
results = []
if move_data:
_rt_execute_allow_timeout(c, "execute.throw", "mkdir", "-p", path)
for h in torrent_hashes:
item = {"hash": h, "path": path, "move_data": move_data, "keep_seeding": keep_seeding}
try:
was_state = int(c.call("d.state", h) or 0)
except Exception:
was_state = 0
try:
was_active = int(c.call("d.is_active", h) or 0)
except Exception:
was_active = was_state
if move_data:
src = _remote_clean_path(_torrent_data_path(c, h))
if not src:
raise ValueError(f"Cannot determine source path for {h}")
dst = _remote_join(path, posixpath.basename(src.rstrip("/")))
if src != dst:
try:
c.call("d.stop", h)
except Exception:
pass
try:
c.call("d.close", h)
except Exception:
pass
_run_remote_move(c, src, dst)
item["moved_from"] = src
item["moved_to"] = dst
else:
item["skipped"] = "source and destination are the same"
c.call("d.directory.set", h, path)
if recheck:
try:
c.call("d.check_hash", h)
except Exception as exc:
item["recheck_error"] = str(exc)
if keep_seeding or was_state or was_active:
try:
c.call("d.start", h)
item["started_after_move"] = True
except Exception as exc:
item["start_after_move_error"] = str(exc)
else:
c.call("d.directory.set", h, path)
results.append(item)
return {"ok": True, "count": len(torrent_hashes), "move_data": move_data, "keep_seeding": keep_seeding, "results": results}
if name == "pause":
# Note: The app pause action is now a pure d.pause so later resume works without stop/start.
results = [pause_hash(c, h) for h in torrent_hashes]