import os 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.api.v1.media import router as media_router from app.core.auth import seed_admin 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"]) app.include_router(media_router, prefix="/api/v1/media", tags=["media"]) # 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(): # SF2 -> SF3 conversion is disabled: the client FluidSynth WASM cannot decode # Ogg Vorbis (SF3) samples, so converted SF3s would play silence. The download # endpoint serves SF2 when available and converts SF3 -> SF2 on demand instead. pass @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"

SonicForge Studio: index.html not found at {index_path}

", status_code=404) with open(index_path, "r", encoding="utf-8") as file: return HTMLResponse(content=file.read(), status_code=200) @app.get("/favicon.svg") async def get_favicon(): import os favicon_path = os.path.join(settings.TEMPLATES_DIR, "favicon.svg") if os.path.exists(favicon_path): from fastapi.responses import FileResponse return FileResponse(favicon_path, media_type="image/svg+xml") return HTMLResponse(content="", status_code=404) @app.get("/ai-prompt-generator", response_class=HTMLResponse) async def get_ai_prompt_generator(): md_path = os.path.join(settings.BASE_DIR, "md", "49_AI_PROMPT_GENERATOR.md") if not os.path.exists(md_path): return HTMLResponse(content="

File not found

", status_code=404) with open(md_path, "r", encoding="utf-8") as file: return HTMLResponse(content=file.read(), status_code=200)