From 6924cc80feeddd8e88b92c4594c29e973b90d2aa Mon Sep 17 00:00:00 2001 From: Wolf Beckmann Date: Fri, 27 Mar 2026 17:42:19 +0100 Subject: [PATCH] feat(diarization-ui): multi-file audio upload with project create-or-select - Upload page accepts multiple audio files at once - Project field is a combobox: pick existing or type new name (auto-created) - Document titles derived from filename without extension - Files uploaded sequentially with progress bar - New POST /projects/create-api endpoint (idempotent: returns existing if name matches) - POST /upload now returns JSON {job_id} instead of HTML redirect Co-Authored-By: Claude Sonnet 4.6 --- app.py | 126 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 105 insertions(+), 21 deletions(-) diff --git a/app.py b/app.py index f0d7c69..5466743 100644 --- a/app.py +++ b/app.py @@ -470,32 +470,102 @@ def healthz(): @app.get("/", response_class=HTMLResponse) def upload_page(msg: str = ""): projects = get_projects() - opts = "".join([f"" for p in projects]) + existing_names = json.dumps([p["name"] for p in projects], ensure_ascii=False) + existing_map = json.dumps({p["name"]: p["id"] for p in projects}, ensure_ascii=False) body = f"""

Audio Upload

-

Audio wird transkribiert + mit Sprechern angereichert und als Dokument gespeichert.

+

Mehrere Audiodateien gleichzeitig hochladen — je Datei wird ein Transkriptions-Job erstellt.

{f"
{msg}
" if msg else ""} -
-
-
- - +
+
+
+ + +
-
- - -
-
- - +
+ +
-
- +
+
Warteschlange:
+
    - + +
    +
    +
    +
    +
    +
    +
    + """ return layout("Upload", body) @@ -530,24 +600,38 @@ def delete_project(project_id: int): return HTMLResponse("") -@app.post("/upload", response_class=HTMLResponse) -async def upload(project_id: int = Form(...), title: str = Form(""), file: UploadFile = File(...)): +@app.post("/projects/create-api") +def create_project_api(name: str = Form(...)): + name = name.strip() + if not name: + raise HTTPException(400, "Name darf nicht leer sein") + with db() as c: + existing = c.execute("SELECT id FROM projects WHERE name=?", (name,)).fetchone() + if existing: + return {"id": existing["id"]} + cur = c.execute("INSERT INTO projects(name, created_at) VALUES (?,?)", (name, now_iso())) + return {"id": cur.lastrowid} + + +@app.post("/upload") +async def upload(project_id: int = Form(...), file: UploadFile = File(...)): data = await file.read() if not data: raise HTTPException(400, "Leere Datei") JOB_DIR.mkdir(parents=True, exist_ok=True) filename = (file.filename or "audio.bin").replace("/", "_") + stem = filename.rsplit(".", 1)[0] or filename temp_path = JOB_DIR / f"{now_iso().replace(':','-')}_{filename}" temp_path.write_bytes(data) job_id = enqueue_job( "upload", project_id=project_id, - title=(title or "").strip() or filename, + title=stem, file_path=str(temp_path), ) - return HTMLResponse(f"") + return {"job_id": job_id} @app.get("/library", response_class=HTMLResponse)