Spaces:
Sleeping
Sleeping
Create app/main.py
Browse files- app/main.py +82 -0
app/main.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, UploadFile, File, HTTPException, BackgroundTasks
|
| 2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
import uuid
|
| 4 |
+
from . import processing
|
| 5 |
+
from .models import TaskStatus
|
| 6 |
+
import os
|
| 7 |
+
import shutil
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
app = FastAPI()
|
| 11 |
+
|
| 12 |
+
# Configure CORS - In production, restrict this to your frontend's domain
|
| 13 |
+
origins = ["*"]
|
| 14 |
+
app.add_middleware(
|
| 15 |
+
CORSMiddleware,
|
| 16 |
+
allow_origins=origins,
|
| 17 |
+
allow_credentials=True,
|
| 18 |
+
allow_methods=["*"],
|
| 19 |
+
allow_headers=["*"],
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
tasks = {}
|
| 23 |
+
TEMP_DIR = Path("temp_audio")
|
| 24 |
+
TEMP_DIR.mkdir(exist_ok=True)
|
| 25 |
+
|
| 26 |
+
# Define allowed file extensions and max size (50MB)
|
| 27 |
+
ALLOWED_EXTENSIONS = {".wav", ".mp3", ".m4a", ".ogg", ".flac"}
|
| 28 |
+
MAX_FILE_SIZE = 50 * 1024 * 1024
|
| 29 |
+
|
| 30 |
+
@app.post("/upload")
|
| 31 |
+
async def upload_audio_file(background_tasks: BackgroundTasks, file: UploadFile = File(...)):
|
| 32 |
+
"""
|
| 33 |
+
Receives an audio file, saves it temporarily to disk, and starts the
|
| 34 |
+
processing pipeline in the background. Includes validation for file type and size.
|
| 35 |
+
"""
|
| 36 |
+
# Validation: File Extension
|
| 37 |
+
file_ext = Path(file.filename).suffix.lower()
|
| 38 |
+
if file_ext not in ALLOWED_EXTENSIONS:
|
| 39 |
+
raise HTTPException(
|
| 40 |
+
status_code=400,
|
| 41 |
+
detail=f"Invalid audio format. Allowed formats: {', '.join(ALLOWED_EXTENSIONS)}"
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
# Validation: File Size
|
| 45 |
+
# We read the file into memory once to check size and then save it.
|
| 46 |
+
# This is a trade-off for validation. For extremely large files (>1GB),
|
| 47 |
+
# a streaming validator would be better, but this is fine for a 50MB limit.
|
| 48 |
+
file_content = await file.read()
|
| 49 |
+
if len(file_content) > MAX_FILE_SIZE:
|
| 50 |
+
raise HTTPException(status_code=413, detail="File too large. Limit is 50MB.")
|
| 51 |
+
|
| 52 |
+
# Reset file pointer before saving
|
| 53 |
+
await file.seek(0)
|
| 54 |
+
|
| 55 |
+
task_id = str(uuid.uuid4())
|
| 56 |
+
tasks[task_id] = {"status": "processing", "result": None}
|
| 57 |
+
|
| 58 |
+
file_path = TEMP_DIR / f"{task_id}{file_ext}"
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
with open(file_path, "wb") as buffer:
|
| 62 |
+
shutil.copyfileobj(file.file, buffer)
|
| 63 |
+
finally:
|
| 64 |
+
file.file.close()
|
| 65 |
+
|
| 66 |
+
background_tasks.add_task(processing.run_pipeline, task_id, file_path, tasks)
|
| 67 |
+
|
| 68 |
+
return {"task_id": task_id}
|
| 69 |
+
|
| 70 |
+
@app.get("/status/{task_id}", response_model=TaskStatus)
|
| 71 |
+
async def get_task_status(task_id: str):
|
| 72 |
+
task = tasks.get(task_id)
|
| 73 |
+
if not task:
|
| 74 |
+
raise HTTPException(status_code=404, detail="Task not found")
|
| 75 |
+
return TaskStatus(**task)
|
| 76 |
+
|
| 77 |
+
@app.get("/result/{task_id}")
|
| 78 |
+
async def get_task_result(task_id: str):
|
| 79 |
+
task = tasks.get(task_id)
|
| 80 |
+
if not task or task.get("status") != "complete":
|
| 81 |
+
raise HTTPException(status_code=404, detail="Result not yet available or task not found")
|
| 82 |
+
return task.get("result")
|