V45.46: GUI embed VST full-panel trong DAW - phuc hoi embed (frame-capture ~10fps, relay input), bo toolbar header 'X Dong GUI' trong VstGuiEmbed + bo bar FX RT telemetry trong Mastering (phan tach GUI vs chain FX slot = border chain strip); dong GUI = chain card / module builtin / chon VST khac / onDead; projects API /disk list+open + api.js wrappers; plugins.py them SF_FXGUI_FLOAT=1 khi spawn bridge GUI; FxGuiServer/RenderFxJob fix GUI embed frame capture + show/hide re-anchor theo panel rect. Rebuild daw_vst_bridge+fx_vst_bridge+plugin_host v=202609032200.

This commit is contained in:
2026-09-03 21:28:20 +07:00
parent db9123438d
commit 6a74631cba
14 changed files with 713 additions and 221 deletions
+2 -1
View File
@@ -742,7 +742,8 @@ async def open_fx_gui(req: FxGuiRequest, current_user: dict = Depends(get_curren
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, text=True,
close_fds=os.name != "nt",
env=dict(os.environ, SF_PARENT_PID=str(os.getpid())),
env=dict(os.environ, SF_PARENT_PID=str(os.getpid()),
SF_FXGUI_FLOAT="1"),
creationflags=_gui_creationflags)
# Drain stdout NGAY TU DAU (daemon thread) và log TẤT CẢ các dòng
# bridge in — gồm cả các dòng in TRƯỚC SF_FXGUI_PORT= ("[FxGui]
+48
View File
@@ -295,6 +295,54 @@ async def save_project_to_disk(req: SaveProjectRequest, authorization: Optional[
}
@router.get("/disk")
async def list_disk_projects(authorization: Optional[str] = Header(None)):
"""Danh sách file Ctrl-S desktop (Documents/SonicForgeDAW/Projects),
mới nhất trước. Auth optional."""
try:
if authorization and authorization.startswith("Bearer "):
decode_token(authorization.split(" ")[1])
except Exception:
pass
out_dir = _os_projects_dir()
items = []
try:
names = os.listdir(out_dir)
except Exception:
names = []
for fn in names:
if not fn.endswith(".sonicforge.json"):
continue
pth = os.path.join(out_dir, fn)
try:
mtime = os.path.getmtime(pth)
size = os.path.getsize(pth)
except Exception:
continue
items.append({"filename": fn, "name": fn[: -len(".sonicforge.json")], "mtime": mtime, "size_bytes": size})
items.sort(key=lambda x: x["mtime"], reverse=True)
return items
@router.get("/disk/{filename}")
async def get_disk_project(filename: str, authorization: Optional[str] = Header(None)):
"""Đọc nội dung 1 file dự án trên đĩa (mở lại sau khi restart)."""
try:
if authorization and authorization.startswith("Bearer "):
decode_token(authorization.split(" ")[1])
except Exception:
pass
base = os.path.basename(filename)
if not base.endswith(".sonicforge.json"):
raise HTTPException(status_code=400, detail="Invalid project file")
pth = os.path.join(_os_projects_dir(), base)
if not os.path.isfile(pth):
raise HTTPException(status_code=404, detail="Project not found on disk")
with open(pth, "r", encoding="utf-8") as f:
data_json = f.read()
return {"filename": base, "name": base[: -len(".sonicforge.json")], "data_json": data_json}
@router.post("/cloud")
async def save_cloud_project(req: SaveProjectRequest, current_user: dict = Depends(get_current_user)):
validated_data_json = validate_project_data(req.data_json)