move to anther profile
This commit is contained in:
@@ -408,7 +408,8 @@ def _automation_profile_transfer_payload(profile: dict[str, Any], eff: dict[str,
|
||||
if not target_profile:
|
||||
raise ValueError('Automation target profile does not exist')
|
||||
default_path = _safe_remote_path(rtorrent.default_download_path(target_profile))
|
||||
target_path = _safe_remote_path(str(eff.get('target_path') or eff.get('path') or default_path))
|
||||
requested_target_path = _safe_remote_path(str(eff.get('target_path') or eff.get('path') or ''))
|
||||
target_path = requested_target_path or default_path
|
||||
roots = [default_path]
|
||||
try:
|
||||
prefs = get_disk_monitor_preferences(target_id, user_id=user_id)
|
||||
@@ -423,6 +424,8 @@ def _automation_profile_transfer_payload(profile: dict[str, Any], eff: dict[str,
|
||||
pass
|
||||
target_roots = [r for r in roots if r]
|
||||
if not any(_path_inside_root(target_path, root) for root in target_roots):
|
||||
if requested_target_path:
|
||||
raise ValueError('Automation target path is outside the target profile download roots')
|
||||
target_path = default_path
|
||||
requested_move_data = bool(eff.get('move_data'))
|
||||
move_data = False
|
||||
|
||||
@@ -577,3 +577,77 @@ def save_preferences(data: dict, user_id: int | None = None, profile_id: int | N
|
||||
if disk_payload is not None:
|
||||
save_disk_monitor_preferences(profile_id, disk_payload, user_id)
|
||||
return get_preferences(user_id, profile_id)
|
||||
|
||||
|
||||
def _row_int(row: dict, key: str) -> int:
|
||||
try:
|
||||
return int(float(row.get(key) or 0))
|
||||
except (TypeError, ValueError):
|
||||
return 0
|
||||
|
||||
|
||||
def profile_runtime_stats_from_rows(profile: dict, rows: list[dict], user_id: int | None = None) -> dict:
|
||||
# Note: Stored profile stats are intentionally approximate and updated only when the user switches to that profile.
|
||||
user_id = user_id or auth.current_user_id() or default_user_id()
|
||||
total_size = completed = downloaded = uploaded = active = seeding = downloading = stopped = 0
|
||||
for row in rows or []:
|
||||
size = _row_int(row, 'size')
|
||||
total_size += size
|
||||
completed += min(size, _row_int(row, 'completed_bytes')) if size else _row_int(row, 'completed_bytes')
|
||||
downloaded += _row_int(row, 'down_total')
|
||||
uploaded += _row_int(row, 'up_total')
|
||||
status = str(row.get('status') or '').strip().lower()
|
||||
state = bool(row.get('state'))
|
||||
complete = bool(row.get('complete'))
|
||||
if state:
|
||||
active += 1
|
||||
if complete and state:
|
||||
seeding += 1
|
||||
if not complete and state and status != 'queued':
|
||||
downloading += 1
|
||||
if not state:
|
||||
stopped += 1
|
||||
return {
|
||||
'profile_id': int(profile.get('id') or 0),
|
||||
'user_id': int(user_id),
|
||||
'torrent_count': len(rows or []),
|
||||
'total_size_bytes': total_size,
|
||||
'completed_bytes': completed,
|
||||
'downloaded_bytes': downloaded,
|
||||
'uploaded_bytes': uploaded,
|
||||
'active_count': active,
|
||||
'seeding_count': seeding,
|
||||
'downloading_count': downloading,
|
||||
'stopped_count': stopped,
|
||||
'updated_at': utcnow(),
|
||||
}
|
||||
|
||||
|
||||
def save_profile_runtime_stats(profile: dict, rows: list[dict], user_id: int | None = None) -> dict:
|
||||
stats = profile_runtime_stats_from_rows(profile, rows, user_id=user_id)
|
||||
with connect() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO profile_runtime_stats(
|
||||
profile_id,user_id,torrent_count,total_size_bytes,completed_bytes,downloaded_bytes,uploaded_bytes,
|
||||
active_count,seeding_count,downloading_count,stopped_count,updated_at
|
||||
) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
ON CONFLICT(profile_id) DO UPDATE SET
|
||||
user_id=excluded.user_id, torrent_count=excluded.torrent_count, total_size_bytes=excluded.total_size_bytes,
|
||||
completed_bytes=excluded.completed_bytes, downloaded_bytes=excluded.downloaded_bytes, uploaded_bytes=excluded.uploaded_bytes,
|
||||
active_count=excluded.active_count, seeding_count=excluded.seeding_count, downloading_count=excluded.downloading_count,
|
||||
stopped_count=excluded.stopped_count, updated_at=excluded.updated_at
|
||||
""",
|
||||
(
|
||||
stats['profile_id'], stats['user_id'], stats['torrent_count'], stats['total_size_bytes'], stats['completed_bytes'],
|
||||
stats['downloaded_bytes'], stats['uploaded_bytes'], stats['active_count'], stats['seeding_count'],
|
||||
stats['downloading_count'], stats['stopped_count'], stats['updated_at'],
|
||||
),
|
||||
)
|
||||
return stats
|
||||
|
||||
|
||||
def get_profile_runtime_stats(profile_id: int) -> dict | None:
|
||||
with connect() as conn:
|
||||
row = conn.execute("SELECT * FROM profile_runtime_stats WHERE profile_id=?", (int(profile_id),)).fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
@@ -289,6 +289,12 @@ def _emit_disk_refresh_requested(profile_id: int, action_name: str, payload: dic
|
||||
_schedule_profile_disk_refresh(int(profile_id), len((payload or {}).get("hashes") or []))
|
||||
|
||||
def _execute(profile: dict, action_name: str, payload: dict, user_id: int | None = None):
|
||||
def checkpoint(next_state: dict, current: int, total: int):
|
||||
# Note: Checkpoint is defined before every action branch so profile-transfer jobs can resume safely.
|
||||
job_id = payload.get("__job_id")
|
||||
if job_id:
|
||||
_checkpoint_job(str(job_id), next_state, current, total)
|
||||
|
||||
if action_name == "smart_queue_check":
|
||||
from . import smart_queue
|
||||
return smart_queue.check(profile, user_id=user_id or default_user_id(), force=True)
|
||||
@@ -315,11 +321,6 @@ def _execute(profile: dict, action_name: str, payload: dict, user_id: int | None
|
||||
disk_guard.assert_can_start_download(profile)
|
||||
state = payload.get("__resume_state") or {}
|
||||
|
||||
def checkpoint(next_state: dict, current: int, total: int):
|
||||
job_id = payload.get("__job_id")
|
||||
if job_id:
|
||||
_checkpoint_job(str(job_id), next_state, current, total)
|
||||
|
||||
return rtorrent.action(profile, hashes, action_name, payload, checkpoint=checkpoint, resume_state=state)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user