feat: auto-scan soundfont for Synth instrument list

Background daemon (30s interval) tracks scanned files via
sf_scan_state.json (size+mtime). Only inspects new/changed SFs.
Catalog served from scanner cache, no full re-scan per request.
Upload/delete trigger immediate re-scan.
This commit is contained in:
2026-07-28 07:06:37 +07:00
parent e68fe27d7f
commit d0def4bf1e
6 changed files with 150 additions and 11 deletions
+17 -8
View File
@@ -8,6 +8,7 @@ from app.core.vst_engine import PluginManager, HAS_PEDALBOARD, HAS_PYFLUIDSYNTH
from app.core.render_engine import PythonRenderEngine
from app.core.soundfont_inspector import SoundFontInspector
from app.core.soundfont_converter import SoundFontConverter
from app.core.soundfont_scanner import SoundFontAutoScanner
from app.api.v1.auth import get_current_user
router = APIRouter()
@@ -18,6 +19,7 @@ os.makedirs(UPLOAD_SF_DIR, exist_ok=True)
SYSTEM_SF_DIR = "/opt/daw_engine/soundfonts"
_inspector = None
_scanner = None
def get_inspector():
global _inspector
@@ -25,6 +27,13 @@ def get_inspector():
_inspector = SoundFontInspector(system_sf_dir=SYSTEM_SF_DIR, upload_sf_dir=UPLOAD_SF_DIR)
return _inspector
def get_scanner():
global _scanner
if _scanner is None:
_scanner = SoundFontAutoScanner(system_sf_dir=SYSTEM_SF_DIR, upload_sf_dir=UPLOAD_SF_DIR)
_scanner.scan_once()
return _scanner
@router.get("/available")
async def list_plugins(current_user: dict = Depends(get_current_user)):
@@ -50,9 +59,10 @@ async def list_default_soundfonts():
@router.get("/soundfonts/catalog")
async def soundfont_catalog(current_user: dict = Depends(get_current_user)):
scanner = get_scanner()
full_catalog = scanner.get_catalog()
inspector = get_inspector()
full_catalog = inspector.get_catalog()
condensed_catalog = inspector.get_condensed_catalog_summary()
condensed_catalog = inspector.get_condensed_catalog_summary(full_catalog)
return {"full_catalog": full_catalog, "condensed_catalog": condensed_catalog}
@@ -91,11 +101,11 @@ async def upload_soundfont(
import json
json.dump({"original_name": file.filename, "uuid": file_uuid, "file": file_id}, f)
inspector = get_inspector()
inspector.invalidate_catalog_cache()
scanner = get_scanner()
if background_tasks:
background_tasks.add_task(inspector.generate_full_catalog)
background_tasks.add_task(scanner.scan_once)
else:
scanner.scan_once()
return {"id": file_id, "name": file.filename, "path": dest_path, "size_bytes": len(contents)}
@@ -122,8 +132,7 @@ async def delete_soundfont(sf_id: str, current_user: dict = Depends(get_current_
break
if not deleted:
raise HTTPException(status_code=404, detail="SoundFont not found")
inspector = get_inspector()
inspector.invalidate_catalog_cache()
get_scanner().scan_once()
return {"deleted": True, "sf_id": sf_id}