upload zip archive from strava
This commit is contained in:
+44
-1
@@ -11,7 +11,7 @@ from typing import Any
|
||||
from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, StreamingResponse
|
||||
|
||||
from bincio.edit.ops import SPORTS, STAT_PANELS, VALID_ACTIVITY_ID
|
||||
|
||||
@@ -783,3 +783,46 @@ async def strava_reset(request: Request) -> JSONResponse:
|
||||
token["last_sync_at"] = last_ts
|
||||
save_token(dd, token)
|
||||
return JSONResponse({"ok": True, "mode": "soft", "last_sync_at": last_ts})
|
||||
|
||||
|
||||
@app.post("/api/upload/strava-zip")
|
||||
async def upload_strava_zip(file: UploadFile = File(...)) -> StreamingResponse:
|
||||
"""Accept a Strava bulk export ZIP and stream SSE progress while processing.
|
||||
|
||||
The ZIP is written to a temp file, processed activity-by-activity, then deleted.
|
||||
Originals are never kept — the UI informs the user of this upfront.
|
||||
"""
|
||||
if not file.filename or not file.filename.lower().endswith(".zip"):
|
||||
raise HTTPException(400, "Please upload a .zip file")
|
||||
|
||||
dd = _get_data_dir()
|
||||
import tempfile
|
||||
tmp = tempfile.NamedTemporaryFile(suffix=".zip", delete=False, dir=dd)
|
||||
zip_path = Path(tmp.name)
|
||||
try:
|
||||
while chunk := await file.read(1024 * 1024): # 1 MB chunks
|
||||
tmp.write(chunk)
|
||||
finally:
|
||||
tmp.close()
|
||||
|
||||
from bincio.extract.strava_zip import strava_zip_iter
|
||||
from bincio.render.merge import merge_all
|
||||
|
||||
def event_stream():
|
||||
any_imported = False
|
||||
try:
|
||||
for event in strava_zip_iter(zip_path, dd):
|
||||
yield f"data: {json.dumps(event)}\n\n"
|
||||
if event.get("type") == "progress" and event.get("status") == "imported":
|
||||
any_imported = True
|
||||
if event.get("type") == "done" and any_imported:
|
||||
merge_all(dd)
|
||||
except Exception as exc:
|
||||
zip_path.unlink(missing_ok=True)
|
||||
yield f"data: {json.dumps({'type': 'error', 'message': str(exc)})}\n\n"
|
||||
|
||||
return StreamingResponse(
|
||||
event_stream(),
|
||||
media_type="text/event-stream",
|
||||
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user