diff --git a/desktop_engine.py b/desktop_engine.py index 317c09e..7c6a35e 100644 --- a/desktop_engine.py +++ b/desktop_engine.py @@ -120,10 +120,12 @@ def main(): # engine_port.txt (qua lệnh get_engine_port) để kết nối ĐÚNG engine của # chính nó thay vì quét port — quét 8000-8010 có thể dính engine của # instance khác (2 app chạy cùng lúc) → UI mất server khi instance đó tắt. + # Format "port\nengine_exe_path": Tauri đối chiếu path engine với candidate + # của CHÍNH nó → 2 app chạy song song mỗi cái lấy đúng port của mình. try: port_file = os.path.join(os.path.dirname(log_dir), "engine_port.txt") with open(port_file, "w", encoding="utf-8") as f: - f.write(str(port)) + f.write("%d\n%s" % (port, sys.executable)) except OSError: pass diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 30b80f8..d21c5fd 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -911,19 +911,50 @@ fn bridge_status(app: AppHandle) -> Result { /// %APPDATA%/SonicForgeDAW/logs/dbg.log (create dirs as needed). Fire-and-forget /// từ app.jsx (_dbgLog) để định vị delay Play/Stop không cần devtools. /// Port engine đã chọn (desktop_engine.py ghi %APPDATA%/SonicForgeDAW/ -/// engine_port.txt ngay sau _pick_port). UI bootstrap ưu tiên dùng port này -/// để kết nối ĐÚNG engine của chính mình; -1 khi chưa có (engine đang khởi -/// động) → bootstrap retry/fallback quét 8000-8010. +/// engine_port.txt "port\nengine_exe_path" ngay sau _pick_port). UI bootstrap +/// ưu tiên dùng port này để kết nối ĐÚNG engine của chính mình; -1 khi chưa +/// có (engine đang khởi động) hoặc engine ghi file không phải của app này +/// (instance khác) → bootstrap retry/fallback quét 8000-8010. #[tauri::command] -fn get_engine_port() -> i32 { +fn get_engine_port(app: AppHandle) -> i32 { let appdata = std::env::var("APPDATA").unwrap_or_else(|_| ".".into()); let path = std::path::Path::new(&appdata) .join("SonicForgeDAW") .join("engine_port.txt"); - std::fs::read_to_string(path) - .ok() - .and_then(|s| s.trim().parse::().ok()) - .unwrap_or(-1) + let raw = match std::fs::read_to_string(&path) { + Ok(s) => s, + Err(_) => return -1, + }; + let mut lines = raw.lines(); + let Some(port_str) = lines.next() else { return -1 }; + let port: i32 = match port_str.trim().parse() { + Ok(p) => p, + Err(_) => return -1, + }; + // Đối chiếu engine_exe_path với candidate của CHÍNH app này — file có thể + // do instance khác (AppData vs target/release) ghi đè sau khi mình khởi + // động; nếu không khớp thì không dùng (fallback scan sẽ tìm engine mình). + if let Some(exe_line) = lines.next() { + let exe = exe_line.trim().to_string(); + let norm = |s: String| s.replace("\\\\?\\", "").replace('/', "\\").to_lowercase(); + let norm_exe = norm(exe); + let exe_dir = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.to_path_buf())); + let mut bases: Vec = Vec::new(); + if let Ok(res) = app.path().resource_dir() { + bases.push(res); + } + if let Some(d) = exe_dir { + bases.push(d); + } + let name = if cfg!(windows) { "daw_engine.exe" } else { "daw_engine" }; + let matches = bases.iter().any(|base| { + norm(base.join("daw_engine").join(name).to_string_lossy().to_string()) == norm_exe + }); + if !matches { + return -1; + } + } + port } #[tauri::command]