import os, threading from fastapi import FastAPI from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware from app.config import settings from app.api.v1.audio import router as audio_router from app.api.v1.tasks import router as tasks_router from app.api.v1.multitrack import router as multitrack_router from app.api.v1.auth import router as auth_router from app.api.v1.admin import router as admin_router from app.api.v1.projects import router as projects_router from app.api.v1.user_config import router as user_config_router from app.api.v1.ai_proxy import router as ai_proxy_router from app.api.v1.ai_presets import router as ai_presets_router from app.api.v1.plugins import router as plugins_router from app.core.auth import seed_admin from app.core.soundfont_converter import SoundFontConverter from app.core.soundfont_scanner import SoundFontAutoScanner # Ensure storage directories exist os.makedirs(settings.UPLOADS_DIR, exist_ok=True) os.makedirs(settings.PROCESSED_DIR, exist_ok=True) app = FastAPI(title="SonicForge API Engine") from fastapi.middleware.gzip import GZipMiddleware app.add_middleware(GZipMiddleware, minimum_size=500) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Mount storage directory (must come before general /static mount) app.mount("/static/audio", StaticFiles(directory=settings.STORAGE_DIR), name="audio") # Mount app static files (js, css) STATIC_DIR = os.path.join(os.path.dirname(__file__), "static") app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") # Include routers app.include_router(audio_router, prefix="/api/v1/audio", tags=["audio"]) app.include_router(tasks_router, prefix="/api/v1/audio", tags=["tasks"]) app.include_router(multitrack_router, prefix="/api/v1/multitrack", tags=["multitrack"]) app.include_router(auth_router, prefix="/api/v1/auth", tags=["auth"]) app.include_router(admin_router, prefix="/api/v1/admin", tags=["admin"]) app.include_router(projects_router, prefix="/api/v1/projects", tags=["projects"]) app.include_router(user_config_router, prefix="/api/v1/user", tags=["user_config"]) app.include_router(ai_proxy_router, prefix="/api/v1/ai", tags=["ai"]) app.include_router(ai_presets_router, prefix="/api/v1/ai", tags=["ai"]) app.include_router(plugins_router, prefix="/api/v1/plugins", tags=["plugins"]) # Seed admin user on startup @app.on_event("startup") async def startup_seed_admin(): seed_admin() @app.on_event("startup") async def startup_convert_soundfonts(): def _run(): try: SoundFontConverter().batch_convert_all() except Exception as e: print(f"[Startup] SoundFont conversion error: {e}") threading.Thread(target=_run, daemon=True).start() @app.on_event("startup") async def startup_sf_scanner(): scanner = SoundFontAutoScanner() scanner.start_background(interval=30) @app.get("/", response_class=HTMLResponse) async def get_index(): index_path = os.path.join(settings.TEMPLATES_DIR, "index.html") if not os.path.exists(index_path): return HTMLResponse(content=f"