# 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