fix: restore download logic to session_upload — was embedded in upload_raw
This commit is contained in:
parent
adfb68fe6b
commit
50e969f95e
1 changed files with 51 additions and 57 deletions
108
frontend/app.py
108
frontend/app.py
|
|
@ -196,6 +196,57 @@ def session_upload(
|
|||
"file_size_gb": file_size_gb,
|
||||
})
|
||||
|
||||
else:
|
||||
# ── Download ────────────────────────────────────────────────
|
||||
url = (source_url or "").strip()
|
||||
if not url:
|
||||
return JSONResponse({"phase": "error", "error": "No URL provided."}, status_code=400)
|
||||
|
||||
filename = Path(url).name or f"download_{uuid.uuid4().hex[:8]}"
|
||||
dest = STAGING / filename
|
||||
|
||||
_active_downloads.pop(filename, None)
|
||||
|
||||
usage = shutil.disk_usage(STAGING)
|
||||
free_gb = usage.free / (1024**3)
|
||||
if free_gb < 50:
|
||||
logger.warning("Low disk: %.1f GB free — download may fail", free_gb)
|
||||
|
||||
logger.info("Starting background download: %s → %s", url, dest)
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
["wget", "--progress=dot:giga", "-O", str(dest), url],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except Exception as exc:
|
||||
return JSONResponse({"phase": "error", "error": f"Failed to start download: {exc}"}, status_code=500)
|
||||
|
||||
_active_downloads[filename] = {
|
||||
"proc": proc, "dest": dest, "vmid": vmid, "vm_name": vm_name,
|
||||
"start_time": time.time(), "content_length": 0, "_last_logged_bytes": 0,
|
||||
}
|
||||
|
||||
content_length = 0
|
||||
def _fetch_cl():
|
||||
nonlocal content_length
|
||||
try:
|
||||
hr = http_requests.head(url, timeout=5, allow_redirects=True)
|
||||
cl = hr.headers.get("Content-Length")
|
||||
if cl:
|
||||
content_length = int(cl)
|
||||
if filename in _active_downloads:
|
||||
_active_downloads[filename]["content_length"] = int(cl)
|
||||
logger.info("Download size: %.1f GiB", int(cl) / (1024**3))
|
||||
except Exception:
|
||||
pass
|
||||
threading.Thread(target=_fetch_cl, daemon=True).start()
|
||||
|
||||
return JSONResponse({
|
||||
"phase": "downloading",
|
||||
"filename": filename, "vmid": vmid, "vm_name": vm_name,
|
||||
"content_length_gb": round(content_length / (1024**3), 1) if content_length else None,
|
||||
})
|
||||
|
||||
|
||||
@app.post("/session/upload-raw")
|
||||
async def upload_raw(request: Request):
|
||||
|
|
@ -247,63 +298,6 @@ async def upload_raw(request: Request):
|
|||
})
|
||||
|
||||
|
||||
# ── Download ────────────────────────────────────────────────────
|
||||
url = (source_url or "").strip()
|
||||
if not url:
|
||||
return JSONResponse({"phase": "error", "error": "No URL provided."}, status_code=400)
|
||||
|
||||
filename = Path(url).name or f"download_{uuid.uuid4().hex[:8]}"
|
||||
dest = STAGING / filename
|
||||
|
||||
# Clean up any stale download with same name
|
||||
_active_downloads.pop(filename, None)
|
||||
|
||||
usage = shutil.disk_usage(STAGING)
|
||||
free_gb = usage.free / (1024**3)
|
||||
if free_gb < 50:
|
||||
logger.warning("Low disk: %.1f GB free — download may fail", free_gb)
|
||||
|
||||
# Start wget immediately (HEAD request for size happens in background)
|
||||
logger.info("Starting background download: %s → %s", url, dest)
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
["wget", "--progress=dot:giga", "-O", str(dest), url],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except Exception as exc:
|
||||
return JSONResponse({"phase": "error", "error": f"Failed to start download: {exc}"}, status_code=500)
|
||||
|
||||
_active_downloads[filename] = {
|
||||
"proc": proc,
|
||||
"dest": dest,
|
||||
"vmid": vmid,
|
||||
"vm_name": vm_name,
|
||||
"start_time": time.time(),
|
||||
"content_length": 0,
|
||||
"_last_logged_bytes": 0,
|
||||
}
|
||||
|
||||
# Fire-and-forget HEAD request to get file size for ETA (non-blocking)
|
||||
def _fetch_content_length():
|
||||
try:
|
||||
head_resp = http_requests.head(url, timeout=5, allow_redirects=True)
|
||||
cl = head_resp.headers.get("Content-Length")
|
||||
if cl and filename in _active_downloads:
|
||||
_active_downloads[filename]["content_length"] = int(cl)
|
||||
logger.info("Download size from HEAD: %.1f GiB", int(cl) / (1024**3))
|
||||
except Exception:
|
||||
pass
|
||||
threading.Thread(target=_fetch_content_length, daemon=True).start()
|
||||
|
||||
return JSONResponse({
|
||||
"phase": "downloading",
|
||||
"filename": filename,
|
||||
"vmid": vmid,
|
||||
"vm_name": vm_name,
|
||||
"content_length_gb": round(content_length / (1024**3), 1) if content_length else None,
|
||||
})
|
||||
|
||||
|
||||
@app.get("/session/progress/{filename}")
|
||||
async def session_progress(filename: str):
|
||||
"""Poll download progress — returns current file size and phase."""
|
||||
|
|
|
|||
Loading…
Reference in a new issue