75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
import os, uuid, json, tempfile
|
|
from fastapi import APIRouter, HTTPException, Depends, UploadFile, File
|
|
from pydantic import BaseModel
|
|
from typing import Optional, Any
|
|
from app.config import settings
|
|
from app.core.vst_engine import PluginManager, HAS_PEDALBOARD, HAS_PYFLUIDSYNTH
|
|
from app.core.render_engine import PythonRenderEngine
|
|
from app.api.v1.auth import get_current_user
|
|
|
|
router = APIRouter()
|
|
|
|
UPLOAD_SF_DIR = os.path.join(settings.STORAGE_DIR, "soundfonts")
|
|
os.makedirs(UPLOAD_SF_DIR, exist_ok=True)
|
|
|
|
|
|
@router.get("/available")
|
|
async def list_plugins(current_user: dict = Depends(get_current_user)):
|
|
pm = PluginManager(upload_sf_dir=UPLOAD_SF_DIR)
|
|
return pm.list_available()
|
|
|
|
|
|
@router.get("/default-soundfonts")
|
|
async def list_default_soundfonts():
|
|
static_sf_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "static", "soundfonts")
|
|
result = []
|
|
if os.path.isdir(static_sf_dir):
|
|
for f in os.listdir(static_sf_dir):
|
|
if f.endswith(".sf2") or f.endswith(".sf3"):
|
|
result.append({
|
|
"id": os.path.splitext(f)[0],
|
|
"name": f,
|
|
"file": f,
|
|
"url": f"/soundfonts/{f}"
|
|
})
|
|
return result
|
|
|
|
|
|
@router.post("/upload-soundfont")
|
|
async def upload_soundfont(
|
|
file: UploadFile = File(...),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
if not (file.filename and (file.filename.endswith(".sf2") or file.filename.endswith(".sf3"))):
|
|
raise HTTPException(status_code=400, detail="Only .sf2 / .sf3 files are allowed")
|
|
|
|
contents = await file.read()
|
|
if not PluginManager.validate_sf2_header(contents):
|
|
raise HTTPException(status_code=400, detail="Invalid SoundFont file: missing RIFF/sfbk header")
|
|
|
|
file_id = str(uuid.uuid4()) + os.path.splitext(file.filename)[1]
|
|
dest_path = os.path.join(UPLOAD_SF_DIR, file_id)
|
|
with open(dest_path, "wb") as f:
|
|
f.write(contents)
|
|
|
|
return {"id": file_id, "name": file.filename, "path": dest_path, "size_bytes": len(contents)}
|
|
|
|
|
|
class RenderRequest(BaseModel):
|
|
project_json: dict
|
|
output_filename: Optional[str] = "render_output.wav"
|
|
|
|
|
|
@router.post("/render")
|
|
async def render_project(
|
|
req: RenderRequest,
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
engine = PythonRenderEngine()
|
|
output_path = os.path.join(settings.PROCESSED_DIR, req.output_filename or "render_output.wav")
|
|
try:
|
|
result_path = engine.render_project(req.project_json, output_path)
|
|
return {"url": f"/static/audio/processed/{os.path.basename(result_path)}", "path": result_path}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Render failed: {str(e)}")
|