diff --git a/pytorrent/services/workers.py b/pytorrent/services/workers.py index 69dbbc1..fb813d5 100644 --- a/pytorrent/services/workers.py +++ b/pytorrent/services/workers.py @@ -220,6 +220,29 @@ def _job_event_meta(payload: dict) -> dict: return meta + + +def _remove_job_deletes_data(action_name: str, payload: dict, result: dict | None = None) -> bool: + # Note: Disk usage refreshes only when a remove job actually requested data deletion. + if str(action_name or "") != "remove": + return False + if bool((payload or {}).get("remove_data")): + return True + ctx = (payload or {}).get("job_context") or {} + return bool(ctx.get("remove_data") or (result or {}).get("remove_data")) + + +def _emit_disk_refresh_requested(profile_id: int, action_name: str, payload: dict, result: dict | None = None) -> None: + if not _remove_job_deletes_data(action_name, payload, result): + return + # Note: The browser performs the fresh /api/system/disk read so user-specific disk monitor preferences stay respected. + _emit("disk_refresh_requested", { + "profile_id": int(profile_id), + "action": str(action_name or ""), + "hash_count": len((payload or {}).get("hashes") or []), + "reason": "remove_data_done", + }) + def _execute(profile: dict, action_name: str, payload: dict, user_id: int | None = None): if action_name == "smart_queue_check": from . import smart_queue @@ -350,8 +373,10 @@ def _run(job_id: str): _set_job(job_id, "done", result=result, finished=True) operation_logs.record_job_event(profile["id"], job["action"], "done", payload, result=result or {}, job_id=job_id, user_id=int(job.get("user_id") or 0)) _emit("operation_finished", {"job_id": job_id, "action": job["action"], "profile_id": profile["id"], "hashes": payload.get("hashes") or [], "hash_count": len(payload.get("hashes") or []), "bulk": len(payload.get("hashes") or []) > 1, "result": result, **event_meta}) - # Note: Completed jobs must publish a fresh torrent snapshot/patch so removed or moved torrents disappear without a page reload. + # Note: Remove-with-data jobs ask connected browsers to refresh disk usage immediately after filesystem deletion finishes. action_name = str(job["action"] or "") + _emit_disk_refresh_requested(int(profile["id"]), action_name, payload, result or {}) + # Note: Completed jobs must publish a fresh torrent snapshot/patch so removed or moved torrents disappear without a page reload. _emit_torrent_refresh(profile, action_name) _schedule_delayed_torrent_refresh(profile, action_name) _emit("job_update", {"id": job_id, "profile_id": profile["id"], "status": "done", "result": result}) diff --git a/pytorrent/static/js/systemStatsSocket.js b/pytorrent/static/js/systemStatsSocket.js index 10051c8..b86c819 100644 --- a/pytorrent/static/js/systemStatsSocket.js +++ b/pytorrent/static/js/systemStatsSocket.js @@ -1 +1 @@ -export const systemStatsSocketSource = " socket.on('system_stats',s=>{\n if(!isActiveProfilePayload(s)) return;\n const usageAvailable=s.usage_available!==false && s.cpu!==undefined && s.ram!==undefined;\n $('statCpuBox')?.classList.toggle('d-none',!usageAvailable);\n $('statRamBox')?.classList.toggle('d-none',!usageAvailable);\n $('systemChart')?.classList.toggle('d-none',!usageAvailable);\n if(usageAvailable){\n $('statCpu').textContent=s.cpu??'-';\n $('statRam').textContent=s.ram??'-';\n drawSystemUsage(s.cpu,s.ram);\n }\n $('statVersion').textContent=s.version||'-';\n applyLiveSpeedStats(s);\n lastLimits={down:Number(s.down_limit||0),up:Number(s.up_limit||0)};\n $('statDlLimit').textContent=s.down_limit_h||'∞';\n $('statUlLimit').textContent=s.up_limit_h||'∞';\n $('statTotalDl').textContent=compactTransferText(s.total_down_h);\n $('statTotalUl').textContent=compactTransferText(s.total_up_h);\n updateSpeedPeaks(s.speed_peaks||{});\n drawTraffic(s.down_rate,s.up_rate);\n if(diskMonitorMode==='default'){\n drawDiskUsage(s.disk);\n }else{\n refreshUserDiskUsage(false);\n }\n updateRtorrentFooterStats(s, false);\n saveFooterStatusCache(s);\n if(s.poller) fillPoller(null,s.poller);\n applyFooterPreferences();\n });\n"; +export const systemStatsSocketSource = " socket.on('system_stats',s=>{\n if(!isActiveProfilePayload(s)) return;\n const usageAvailable=s.usage_available!==false && s.cpu!==undefined && s.ram!==undefined;\n $('statCpuBox')?.classList.toggle('d-none',!usageAvailable);\n $('statRamBox')?.classList.toggle('d-none',!usageAvailable);\n $('systemChart')?.classList.toggle('d-none',!usageAvailable);\n if(usageAvailable){\n $('statCpu').textContent=s.cpu??'-';\n $('statRam').textContent=s.ram??'-';\n drawSystemUsage(s.cpu,s.ram);\n }\n $('statVersion').textContent=s.version||'-';\n applyLiveSpeedStats(s);\n lastLimits={down:Number(s.down_limit||0),up:Number(s.up_limit||0)};\n $('statDlLimit').textContent=s.down_limit_h||'∞';\n $('statUlLimit').textContent=s.up_limit_h||'∞';\n $('statTotalDl').textContent=compactTransferText(s.total_down_h);\n $('statTotalUl').textContent=compactTransferText(s.total_up_h);\n updateSpeedPeaks(s.speed_peaks||{});\n drawTraffic(s.down_rate,s.up_rate);\n if(diskMonitorMode==='default'){\n drawDiskUsage(s.disk);\n }else{\n refreshUserDiskUsage(false);\n }\n updateRtorrentFooterStats(s, false);\n saveFooterStatusCache(s);\n if(s.poller) fillPoller(null,s.poller);\n applyFooterPreferences();\n });\n\n socket.on('disk_refresh_requested',msg=>{\n if(!isActiveProfilePayload(msg)) return;\n // Note: Remove-with-data jobs can free space between normal poller ticks, so refresh the footer immediately and once more after filesystem metadata settles.\n refreshUserDiskUsage(true);\n setTimeout(()=>refreshUserDiskUsage(true), 1500);\n });\n";