Spaces:
Sleeping
Sleeping
| # Step 2 β FastAPI Backend | |
| **Status: TODO** | |
| ## Goal | |
| Build an API that accepts an audio file upload, runs Demucs on it, and returns the cleaned vocals file. | |
| --- | |
| ## Install dependencies | |
| ```bash | |
| source venv/bin/activate | |
| pip install fastapi uvicorn python-multipart | |
| ``` | |
| --- | |
| ## Files to create | |
| ``` | |
| backend/ | |
| βββ main.py # FastAPI app β routes and server config | |
| βββ demucs_runner.py # Demucs logic β takes a file path, returns output path | |
| βββ uploads/ # Temp folder for uploaded files | |
| βββ .gitkeep | |
| ``` | |
| --- | |
| ## How to build it | |
| ### 1. Create `backend/demucs_runner.py` | |
| This file has one job: take a file path, run Demucs, return the path to `vocals.wav`. | |
| ```python | |
| import subprocess | |
| import os | |
| def run_demucs(input_path: str) -> str: | |
| subprocess.run([ | |
| "demucs", "--two-stems=vocals", "-n", "mdx_extra", input_path | |
| ], check=True) | |
| filename = os.path.splitext(os.path.basename(input_path))[0] | |
| output_path = f"separated/mdx_extra/{filename}/vocals.wav" | |
| return output_path | |
| ``` | |
| ### 2. Create `backend/main.py` | |
| Two endpoints: | |
| - `POST /upload` β accepts audio file, runs Demucs, returns download URL | |
| - `GET /download/{filename}` β serves the processed `.wav` file | |
| ```python | |
| from fastapi import FastAPI, UploadFile, File, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse | |
| import shutil, os, uuid | |
| from backend.demucs_runner import run_demucs | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| UPLOAD_DIR = "backend/uploads" | |
| @app.post("/upload") | |
| async def upload_audio(file: UploadFile = File(...)): | |
| ext = os.path.splitext(file.filename)[1] | |
| unique_name = f"{uuid.uuid4()}{ext}" | |
| save_path = os.path.join(UPLOAD_DIR, unique_name) | |
| with open(save_path, "wb") as buffer: | |
| shutil.copyfileobj(file.file, buffer) | |
| output_path = run_demucs(save_path) | |
| filename = os.path.basename(output_path) | |
| return {"download_url": f"/download/{filename}", "status": "done"} | |
| @app.get("/download/{filename}") | |
| def download_audio(filename: str): | |
| file_path = f"separated/mdx_extra/{filename}/vocals.wav" | |
| if not os.path.exists(file_path): | |
| raise HTTPException(status_code=404, detail="File not found") | |
| return FileResponse(file_path, media_type="audio/wav") | |
| ``` | |
| ### 3. Run the server | |
| ```bash | |
| source venv/bin/activate | |
| uvicorn backend.main:app --reload | |
| ``` | |
| Server runs at: `http://localhost:8000` | |
| --- | |
| ## Test it manually | |
| ```bash | |
| curl -X POST http://localhost:8000/upload \ | |
| -F "file=@mp3/Recording 1.mp3" | |
| ``` | |
| Expected response: | |
| ```json | |
| {"download_url": "/download/vocals.wav", "status": "done"} | |
| ``` | |
| Then open `http://localhost:8000/download/vocals.wav` in the browser to download the file. | |
| --- | |
| ## Notes | |
| - `uuid4()` on the filename prevents collisions if two people upload at the same time | |
| - CORS is open (`*`) for now β fine for a demo, tighten it before production | |
| - No file size limit set yet β add one later if needed | |
| - Uploaded files in `backend/uploads/` are not cleaned up automatically β add a cleanup step later | |