fix: restore download logic to session_upload — was embedded in upload_raw

This commit is contained in:
Claus Lohmar 2026-07-22 12:25:38 +00:00
parent adfb68fe6b
commit 50e969f95e

View file

@ -196,6 +196,57 @@ def session_upload(
"file_size_gb": file_size_gb, "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") @app.post("/session/upload-raw")
async def upload_raw(request: Request): 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}") @app.get("/session/progress/{filename}")
async def session_progress(filename: str): async def session_progress(filename: str):
"""Poll download progress — returns current file size and phase.""" """Poll download progress — returns current file size and phase."""