fix: 3 bug standalone — soundfont preview/play, Carla bridge play item, realtime sync LAN/Tauri
Bug1: dispatcher CREATE_MIDI_ITEM track_id number vs state string -> String(); play+preview native soundfont verified. Bug2: Carla play item — chờ OSC ready (poll carla-status, queue nốt khi cold start), chống spawn trùng (carla-status dò port bind, open-in-carla skip khi đã chạy). Bug3: realtime sync — GET /temp/revision (updated_at+client_id), poll 3s, apply qua deserializeProjectFromSchema; hash-skip autosave + client_id chống ping-pong.
This commit is contained in:
@@ -45,8 +45,9 @@ window.API_BASE_URL = window.API_BASE_URL || window.location.origin;
|
||||
deleteUser: (userId) => apiRequest(`/api/v1/admin/users/${userId}`, { method: 'DELETE' }),
|
||||
createUser: (username, email, password, role = 'standard') => apiRequest('/api/v1/admin/users', { method: 'POST', body: JSON.stringify({ username, email, password, role }) }),
|
||||
|
||||
saveTempProject: (dataJson) => apiRequest('/api/v1/projects/temp', { method: 'POST', body: JSON.stringify({ data_json: dataJson }) }),
|
||||
saveTempProject: (dataJson, clientId) => apiRequest('/api/v1/projects/temp', { method: 'POST', body: JSON.stringify({ data_json: dataJson, client_id: clientId || '' }) }),
|
||||
getTempProject: () => apiRequest('/api/v1/projects/temp', { method: 'GET' }),
|
||||
getTempRevision: () => apiRequest('/api/v1/projects/temp/revision', { method: 'GET' }),
|
||||
saveCloudProject: (name, dataJson) => apiRequest('/api/v1/projects/cloud', { method: 'POST', body: JSON.stringify({ name, data_json: dataJson }) }),
|
||||
listCloudProjects: () => apiRequest('/api/v1/projects/cloud', { method: 'GET' }),
|
||||
getCloudProject: (projectId) => apiRequest(`/api/v1/projects/cloud/${projectId}`, { method: 'GET' }),
|
||||
|
||||
@@ -61,6 +61,21 @@
|
||||
|
||||
let autoSaveTimer = null;
|
||||
let lastGetProjectStateCallback = null;
|
||||
// ⚠️ FIX (Bug 3): hash-skip — nếu state serialize GIỐNG HỆT bản đã lưu
|
||||
// thì KHÔNG ghi lại (server + localStorage). Chặn ping-pong realtime sync:
|
||||
// client B apply state của A rồi autosave lại → server bump updated_at →
|
||||
// A tưởng mới → apply → ... vô hạn. Bản giống hệt → bỏ qua → hội tụ.
|
||||
let lastSavedJson = '';
|
||||
function getClientId() {
|
||||
try {
|
||||
let c = localStorage.getItem('sonic_client_id');
|
||||
if (!c) {
|
||||
c = 'c_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 10);
|
||||
localStorage.setItem('sonic_client_id', c);
|
||||
}
|
||||
return c;
|
||||
} catch (e) { return ''; }
|
||||
}
|
||||
function scheduleTempAutoSave(getProjectStateCallback) {
|
||||
if (getProjectStateCallback) lastGetProjectStateCallback = getProjectStateCallback;
|
||||
if (autoSaveTimer) clearTimeout(autoSaveTimer);
|
||||
@@ -69,9 +84,11 @@
|
||||
const state = lastGetProjectStateCallback ? lastGetProjectStateCallback() : null;
|
||||
if (!state || (!state.tracks && !state.main_session)) return;
|
||||
const dataJson = JSON.stringify(state);
|
||||
if (dataJson === lastSavedJson) return; // không đổi → không ghi
|
||||
lastSavedJson = dataJson;
|
||||
localStorage.setItem('sonic_temp_project', dataJson);
|
||||
if (window.SonicAPI && localStorage.getItem('sonic_token')) {
|
||||
await window.SonicAPI.saveTempProject(dataJson).catch(() => {});
|
||||
await window.SonicAPI.saveTempProject(dataJson, getClientId()).catch(() => {});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Auto-save temp project warning:", e);
|
||||
@@ -85,9 +102,11 @@
|
||||
const state = lastGetProjectStateCallback ? lastGetProjectStateCallback() : null;
|
||||
if (!state || (!state.tracks && !state.main_session)) return;
|
||||
const dataJson = JSON.stringify(state);
|
||||
if (dataJson === lastSavedJson) return; // không đổi → không ghi
|
||||
lastSavedJson = dataJson;
|
||||
localStorage.setItem('sonic_temp_project', dataJson);
|
||||
if (window.SonicAPI && localStorage.getItem('sonic_token')) {
|
||||
await window.SonicAPI.saveTempProject(dataJson).catch(() => {});
|
||||
await window.SonicAPI.saveTempProject(dataJson, getClientId()).catch(() => {});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("Flush temp project warning:", e);
|
||||
@@ -98,6 +117,7 @@
|
||||
exportProjectToSFS,
|
||||
importProjectFromSFSFile,
|
||||
scheduleTempAutoSave,
|
||||
flushTempAutoSave
|
||||
flushTempAutoSave,
|
||||
getClientId
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user