From c332ce07cdc3a1e04c384d9f7214a47e4f13c6d2 Mon Sep 17 00:00:00 2001 From: JotaChina Date: Sun, 23 Nov 2025 17:48:27 -0300 Subject: [PATCH] plugin v1.0.1 --- assets/js/creations/file.js | 21 +++++++++- assets/js/creations/socket.js | 21 +++++----- assets/js/creations/state.js | 75 +++++++++++++++++++---------------- 3 files changed, 71 insertions(+), 46 deletions(-) diff --git a/assets/js/creations/file.js b/assets/js/creations/file.js index c787a300..f107b6e0 100644 --- a/assets/js/creations/file.js +++ b/assets/js/creations/file.js @@ -314,11 +314,11 @@ export async function parseMmpContent(xmlString) { console.log("Restaurando estado temporário da sessão..."); const tempState = JSON.parse(tempStateJSON); + // 1. Restaura Pattern (código que já existia) appState.pattern.tracks.forEach((liveTrack) => { const savedTrack = tempState.pattern.tracks.find( (t) => t.id === liveTrack.id ); - if (savedTrack) { liveTrack.name = savedTrack.name; liveTrack.patterns = savedTrack.patterns; @@ -335,14 +335,33 @@ export async function parseMmpContent(xmlString) { } }); + // Filtra tracks deletadas appState.pattern.tracks = appState.pattern.tracks.filter((liveTrack) => tempState.pattern.tracks.some((t) => t.id === liveTrack.id) ); + // 2. 🔥 FIX: Restaura Áudio (Clips e Tracks) + if (tempState.audio) { + console.log("Restaurando faixas de áudio e clips..."); + appState.audio.tracks = tempState.audio.tracks || []; + appState.audio.clips = tempState.audio.clips || []; + } + + // 3. Restaura Global document.getElementById("bpm-input").value = tempState.global.bpm; document.getElementById("compasso-a-input").value = tempState.global.compassoA; document.getElementById("compasso-b-input").value = tempState.global.compassoB; document.getElementById("bars-input").value = tempState.global.bars; + + if (tempState.global.syncMode) { + appState.global.syncMode = tempState.global.syncMode; + // Atualiza visual do botão sync se existir (pode precisar disparar evento ou fazer manual) + const syncBtn = document.getElementById("sync-mode-btn"); + if (syncBtn) { + syncBtn.classList.toggle("active", tempState.global.syncMode === "global"); + syncBtn.textContent = tempState.global.syncMode === "global" ? "Global" : "Local"; + } + } appState.pattern.activeTrackId = tempState.pattern.activeTrackId; } diff --git a/assets/js/creations/socket.js b/assets/js/creations/socket.js index 846015f8..2a798026 100644 --- a/assets/js/creations/socket.js +++ b/assets/js/creations/socket.js @@ -198,35 +198,36 @@ socket.on("load_project_state", async (projectXml) => { showToast("🎵 Projeto carregado com sucesso", "success"); // --- INÍCIO DA CORREÇÃO --- - // Mova a lógica de snapshot para AQUI. - // Só peça um snapshot DEPOIS que o XML foi carregado - // e SE o XML (appState) não tiver áudio. const hasAudio = (appState.audio?.clips?.length || 0) > 0 || (appState.audio?.tracks?.length || 0) > 0; + if (!hasAudio && currentRoom) { console.log( "Projeto XML carregado, sem áudio. Pedindo snapshot de áudio..." ); - // O 'sendAction' agora é chamado daqui, não do joinRoom. + + // 🔥 FIX CRÍTICO: Libera a trava IMEDIATAMENTE antes de pedir + // Caso contrário, a resposta chega muito rápido e cai no bloqueio "justReset=true" + appState.global.justReset = false; + sendAction({ type: "AUDIO_SNAPSHOT_REQUEST" }); } // --- FIM DA CORREÇÃO --- + } catch (e) { console.error("Erro ao carregar projeto:", e); showToast("❌ Erro ao carregar projeto", "error"); } isLoadingProject = false; - // Desativa a flag de reset após um delay. - // Isso dá tempo para a "condição de corrida" (o snapshot sujo) - // ser recebida e ignorada. + + // Mantemos o timeout apenas como segurança extra para outros casos setTimeout(() => { if (appState.global.justReset) { - console.log("Socket: Limpando flag 'justReset'."); + // console.log("Socket: Limpando flag 'justReset' (timeout)."); appState.global.justReset = false; } - }, 250); // 2.5 segundos de "janela de proteção" - // --- FIM DA CORREÇÃO --- + }, 250); }); // ----------------------------------------------------------------------------- diff --git a/assets/js/creations/state.js b/assets/js/creations/state.js index 2b5cedde..76864b57 100644 --- a/assets/js/creations/state.js +++ b/assets/js/creations/state.js @@ -100,49 +100,54 @@ export function resetProjectState() { import { generateXmlFromStateExported } from "./file.js"; // Vamos assumir que você vai exportar isso no file.js export function saveStateToSession() { - // Só salva se estiver em uma sala (contexto colaborativo/online) if (!window.ROOM_NAME) return; - try { - // 1. Snapshot do Áudio (inclui blobs Base64 das gravações) - const audioSnap = getAudioSnapshot(); - - // 2. Snapshot do Pattern (XML) - // Se não conseguirmos o XML exato, salvamos os dados brutos do pattern - let xmlContent = null; - try { - // Tenta usar a função exportada (veja nota abaixo sobre file.js) - if(typeof generateXmlFromStateExported === 'function') { - xmlContent = generateXmlFromStateExported(); - } - } catch(e) { - console.warn("Não foi possível gerar XML para backup:", e); - } - - const stateToSave = { - timestamp: Date.now(), - xml: xmlContent, - // Fallback: Salva o pattern bruto se o XML falhar - patternRaw: !xmlContent ? appState.pattern : null, - audio: audioSnap, - global: { - bpm: appState.global.bpm, // Pega do state, que deve estar syncado com o DOM - compassoA: appState.global.compassoA, - compassoB: appState.global.compassoB, - bars: appState.global.bars, - syncMode: appState.global.syncMode - } + // 1. Crie uma versão "limpa" dos tracks (PATTERN) + const cleanTracks = appState.pattern.tracks.map((track) => { + return { + id: track.id, + name: track.name, + samplePath: track.samplePath, + patterns: track.patterns, + activePatternIndex: track.activePatternIndex, + volume: track.volume, + pan: track.pan, + instrumentName: track.instrumentName, + instrumentXml: track.instrumentXml, }; + }); - const roomName = window.ROOM_NAME; - sessionStorage.setItem(`mmp_backup_${roomName}`, JSON.stringify(stateToSave)); - // console.log("Estado salvo na sessão local (F5 safe)."); + // 2. Construa o objeto de estado final para salvar + const stateToSave = { + pattern: { + ...appState.pattern, + tracks: cleanTracks, + }, + // 🔥 FIX: Salvando o estado de Áudio (Tracks e Clips) + audio: { + tracks: appState.audio.tracks || [], + clips: appState.audio.clips || [], + // Adicione outras props de áudio se necessário (ex: seekTime, etc) + }, + global: { + bpm: document.getElementById("bpm-input").value, + compassoA: document.getElementById("compasso-a-input").value, + compassoB: document.getElementById("compasso-b-input").value, + bars: document.getElementById("bars-input").value, + syncMode: appState.global.syncMode, // É bom salvar isso também + }, + }; + try { + const roomName = window.ROOM_NAME || "default_room"; + sessionStorage.setItem( + `temp_state_${roomName}`, + JSON.stringify(stateToSave) + ); } catch (e) { - console.error("Falha crítica ao salvar sessão (Quota Excedida?):", e); + console.error("Falha ao salvar estado na sessão:", e); } } - export async function loadStateFromSession() { const roomName = window.ROOM_NAME; if (!roomName) return false;