Files
SonicForgeStudio/app/api/v1/plugins.py
T

92 lines
3.4 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.get("/soundfont-instruments/{sf_id}")
async def list_soundfont_instruments(sf_id: str, current_user: dict = Depends(get_current_user)):
pm = PluginManager(upload_sf_dir=UPLOAD_SF_DIR)
presets = pm.list_soundfont_instruments(sf_id)
return {"presets": presets, "count": len(presets)}
@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_ext = os.path.splitext(file.filename)[1]
file_uuid = str(uuid.uuid4())
# Store original name in a sidecar file
base_name = os.path.splitext(file.filename)[0].replace('/', '_').replace('\\', '_')
file_id = file_uuid + file_ext
dest_path = os.path.join(UPLOAD_SF_DIR, file_id)
with open(dest_path, "wb") as f:
f.write(contents)
# Save metadata with original name
meta_path = os.path.join(UPLOAD_SF_DIR, file_uuid + ".meta")
with open(meta_path, "w", encoding="utf-8") as f:
import json
json.dump({"original_name": file.filename, "uuid": file_uuid, "file": file_id}, f)
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)}")