[extension_name] MickFX Poof [extension_info] Poof effect for MickFX. Makes webcam disappear behind smoke cloud. [extension_version] 1.0.0 [insert_external]
Starting MickFX Poof in 5...
`; statusDiv.parentNode.insertBefore(completeContainer, statusDiv.nextSibling); let timeLeft = 5; const countdownElement = document.getElementById('MickFX-Poof-Countdown'); const timer = setInterval(() => { timeLeft--; if (timeLeft > 0) { countdownElement.textContent = `Starting MickFX Poof in ${timeLeft}...`; } else { clearInterval(timer); MickFXPoofCompleteInstall(); } }, 1000); statusDiv.dataset.completeTimer = timer; } } catch (error) { console.error('[MickFX Poof] Error processing download stage:', error); } }); } function MickFXPoofInstallClicked() { MickFX_PoofInstalling = true; let statusDiv = document.getElementById('MickFX-Poof-Popup'); const installButton = document.getElementById('MickFX-Poof-InstallButton'); // Create or reset status elements if (!statusDiv) { statusDiv = document.createElement('div'); statusDiv.id = 'MickFX-Poof-Popup'; statusDiv.className = 'MickFX-Popup-ContainerFX'; statusDiv.innerHTML = ` Please wait. Setting up MickFX Poof `; installButton.parentNode.insertBefore(statusDiv, installButton); } else { statusDiv.innerHTML = ` Please wait. Setting up MickFX Poof `; } statusDiv.style.display = 'block'; installButton.style.display = 'none'; SAMMI.triggerButton('MickFXPoofInstall') .catch(error => console.error('[MickFX Poof] Install error:', error)); } function MickFXPoofCompleteInstall() { MickFX_PoofInstalling = false; MickFX_PoofInstalled = 1; // Clear any existing timer const timer = document.getElementById('MickFX-Poof-Popup')?.dataset.completeTimer; if (timer) clearInterval(Number(timer)); // Remove the complete container (which includes button and countdown) const completeContainer = document.getElementById('MickFX-Poof-CompleteContainer'); if (completeContainer) completeContainer.remove(); // Let BaseCheck handle the rest of the UI state MickFXPoofInit(); } function MickFXPoofContinueInstall() { MickFX_PoofInstalling = true; const runButton = document.getElementById('MickFX-Poof-RunButton'); if (runButton) runButton.style.display = 'none'; const popup = document.getElementById('MickFX-Poof-Popup'); popup.style.display = 'block'; // Add the complete install listener sammiclient.on('MickFXPoofCompleteInstall', (payload) => { console.log('[MickFX Poof] Received complete payload:', payload); try { const data = JSON.parse(payload.Data.json); if (data.result === true) { const popup = document.getElementById('MickFX-Poof-Popup'); const settingsPath = MickFX_MainDirectory + "/Information/fxsettings.ini"; popup.innerHTML = ` Poof is now fully set up and good to go! `; MickFX_PoofInstalling = false; SAMMI.saveIni(settingsPath, "Poof", "Install Complete", 1, "number") // This could be unecessary as we do it in sammi button but lets leave it .then(() => { setTimeout(() => { popup.style.display = 'none'; const runButton = document.getElementById('MickFX-Poof-RunButton'); if (runButton) runButton.style.display = 'block'; MickFXPoofInit(); }, 5000); }); } } catch (error) { console.error('[MickFX Poof] Complete Install Error:', error); } }); // Always show voice setup first MickFXPoofVoiceManager('popup', 'assess'); } function MickFXPoofGetCurrentVoiceState() { const vstPath = MickFX_MainDirectory + "/Information/pluginsinstalled.ini"; const settingsPath = MickFX_MainDirectory + "/Information/fxsettings.ini"; return Promise.all([ SAMMI.loadIni(vstPath, "VST Installed", "Dragonfly Hall", "number"), SAMMI.loadIni(settingsPath, "Poof", "VoiceFX", "number") ]).then(([vstStatus, voiceFX]) => ({ mic: MickFX_Microphone === 1, vst: vstStatus.Value === 1, voiceFX: voiceFX.Value || 0 })); } function MickFXPoofCalculateNextAction(state, context, flowState = null) { // Auto-fix invalid VoiceFX state if (!state.vst && state.voiceFX === 1) { const settingsPath = MickFX_MainDirectory + "/Information/fxsettings.ini"; SAMMI.saveIni(settingsPath, "Poof", "VoiceFX", 0, "number"); state.voiceFX = 0; } if (context === 'popup') { // Handle flow progression - if we just completed mic setup, go to VST if (flowState === 'mic-completed' && !state.vst) { return { action: 'vst-prompt', showUI: 'vst-form', setupMessage: 'Now you have to set up Dragonfly Hall Reverb using the tutorial that should have opened in your browser. Once done click the install button to add it to OBS.', linkUrl: 'https://mickfx.com/vst-installation/#reverb' }; } if (!state.vst) { return { action: 'voicefx-choice', message: 'MickFX Poof features an optional voice changer to add reverb to your voice during the effect. This requires installing a VST (audio software plugin) called Dragonfly Reverb. Do you want to set it up?', buttons: ['No', 'Yes'], yesAction: state.mic ? 'vst-prompt' : 'mic-prompt', inputMessage: 'To add voice effects to your microphone, enter your microphone\'s exact name as it appears in OBS.', setupMessage: 'The Dragonfly Reverb tutorial page should have opened in your browser. Follow along to download and install the VST, then click the install button to add it to OBS.', linkUrl: 'https://mickfx.com/vst-installation/#reverb' }; } else if (!state.mic) { return { action: 'mic-prompt', message: 'You already have the required VST for the MickFX Poof voice changer. Do you want to add it to your microphone?', buttons: ['No', 'Yes'], yesAction: 'mic-prompt', inputMessage: 'To add voice effects to your microphone, enter your microphone\'s exact name as it appears in OBS.' }; } else { return { action: 'auto-complete', message: 'Adding filters', autoComplete: true }; } } else { if (!state.mic) { return { action: 'mic-prompt', message: 'Set up your microphone to unlock voice effects for MickFX Poof.', buttons: ['Set up Microphone'], inputMessage: 'Enter your microphone\'s exact name as it appears in OBS.' }; } else if (!state.vst) { if (flowState === 'mic-completed') { return { action: 'vst-prompt', showUI: 'vst-form', setupMessage: 'Now you need to set up Dragonfly Hall Reverb using the tutorial, then click the install button to add it to OBS.', linkUrl: 'https://mickfx.com/vst-installation/#reverb' }; } else { return { action: 'vst-prompt', message: 'Install a VST filter to add reverb effects during MickFX Poof.', linkUrl: 'https://mickfx.com/vst-installation/#reverb', buttons: ['Add Reverb Filter'], setupMessage: 'Set up Dragonfly Hall Reverb using the tutorial, then click the install button to add it to OBS.' }; } } else { return { action: 'voice-on', message: 'Dragonfly Hall Reverb is installed!', buttons: ['Re-add Reverb Filter', 'Remove Reverb Filter'] }; } } } function MickFXPoofVoiceManager(context = 'fx', action = 'assess', data = null, flowState = null) { MickFXPoofGetCurrentVoiceState().then((currentState) => { let uiState; switch(action) { case 'assess': uiState = MickFXPoofCalculateNextAction(currentState, context, flowState); if (uiState.showUI === 'vst-form') { SAMMI.openURL(uiState.linkUrl || 'https://mickfx.com/vst-installation/#reverb'); } break; case 'mic-validate': MickFXPoofValidateMic(context, data) .then((success) => { if (success) { MickFXPoofVoiceManager(context, 'assess', null, 'mic-completed'); } }); return; case 'vst-validate': MickFXPoofValidateVST(context, data) .then((success) => { if (success && context === 'popup') { } else if (success) { MickFXPoofVoiceManager(context, 'assess', null, 'voice-on'); } }); return; case 'mic-prompt': uiState = MickFXPoofCalculateNextAction(currentState, context); uiState.showUI = 'mic-form'; break; case 'vst-prompt': uiState = MickFXPoofCalculateNextAction(currentState, context); uiState.showUI = 'vst-form'; // Open tutorial immediately SAMMI.openURL(uiState.linkUrl || 'https://mickfx.com/vst-installation/#reverb'); break; case 'mic-error': uiState = MickFXPoofCalculateNextAction(currentState, context); uiState.showUI = 'mic-form'; uiState.errorMessage = data; break; case 'vst-error': uiState = MickFXPoofCalculateNextAction(currentState, context); uiState.showUI = 'vst-form'; uiState.errorMessage = 'Filter installation failed. Make sure you\'ve downloaded and installed Dragonfly Hall Reverb correctly, then try again.'; break; case 'loading': uiState = { message: data, isLoading: true }; break; case 'skip': MickFXPoofHandleSkip(context); return; case 're-add-vst': MickFXPoofReAddVST(context); return; case 'remove-vst': MickFXPoofRemoveVST(context); return; } MickFXPoofRenderInterface(context, currentState, uiState); }); } function MickFXPoofValidateMic(context, micName) { return new Promise((resolve) => { if (!micName?.trim()) { MickFXPoofVoiceManager(context, 'mic-error', 'Microphone name cannot be empty. Please enter the exact name as it appears in OBS.'); resolve(false); return; } MickFXPoofVoiceManager(context, 'loading', 'Checking microphone'); const installPath = MickFX_MainDirectory + "/Information/installer.ini"; MickFXMicCallback = (exists) => { if (exists) { MickFX_Microphone = 1; if (context === 'fx') { /*Auto-add VST after Mic added for fx section if it exists. This is to provide smooth UX. Don't worry about popup, it handles VST setup through seperate flow. ValidateVST: Verifies file exists FIRST, then adds to OBS ValidateMic: Assumes VST exists (already checked in state), directly adds to OBS. Auto-add VST probably unecessary, could just do the flow as usual without VST stuff. Will come back to this one day.*/ const vstPath = MickFX_MainDirectory + "/Information/pluginsinstalled.ini"; SAMMI.loadIni(vstPath, "VST Installed", "Dragonfly Hall", "number") .then((vstStatus) => { if (vstStatus.Value === 1) { sammiclient.removeAllListeners('MickFXPoofVSTAdded'); sammiclient.on('MickFXPoofVSTAdded', (payload) => { setTimeout(() => resolve(true), 1500); // ← Fixed race condition }); SAMMI.triggerButton('MickFXPoofAddVST'); MickFXPoofVoiceManager(context, 'loading', 'Voice effects added successfully!'); } else { resolve(true); } }); } else { resolve(true); } } else { MickFX_Microphone = 0; MickFXPoofVoiceManager(context, 'mic-error', 'No microphone found with that name. Please check the exact spelling and capitalization as it appears in OBS.'); resolve(false); } }; SAMMI.saveIni(installPath, "Name Checker", "Microphone", micName.trim(), "string") .then(() => SAMMI.triggerButton('MickFXMicExists')); }); } function MickFXPoofValidateVST(context) { return new Promise((resolve) => { MickFXPoofVoiceManager(context, 'loading', 'Verifying VST installation'); const installPath = MickFX_MainDirectory + "/Information/installer.ini"; MickFXFileCallback = (exists) => { if (exists) { const vstPath = MickFX_MainDirectory + "/Information/pluginsinstalled.ini"; SAMMI.saveIni(vstPath, "VST Installed", "Dragonfly Hall", 1, "number") .then(() => { if (context === 'popup') { const settingsPath = MickFX_MainDirectory + "/Information/fxsettings.ini"; SAMMI.saveIni(settingsPath, "Poof", "VoiceFX", 1, "number") .then(() => { SAMMI.triggerButton('MickFXPoofCompleteInstall'); resolve(true); }); } else { sammiclient.removeAllListeners('MickFXPoofVSTAdded'); sammiclient.on('MickFXPoofVSTAdded', (payload) => { MickFXPoofVoiceManager(context, 'loading', 'Voice effects added successfully!'); setTimeout(() => { MickFXPoofVoiceManager(context, 'assess'); // Refresh UI resolve(true); }, 1500); }); SAMMI.triggerButton('MickFXPoofAddVST'); } }); } else { MickFXPoofVoiceManager(context, 'vst-error'); resolve(false); } }; SAMMI.saveIni(installPath, "Name Checker", "File Name", "C://Program Files//VSTPlugins//DragonflyHallReverb-vst.dll", "string") .then(() => SAMMI.triggerButton('MickFXFileExists')); }); } function MickFXPoofRenderInterface(context, state, uiState) { if (context === 'popup') { MickFXPoofRenderPopup(state, uiState); } else { MickFXPoofRenderFX(state, uiState); } // Update VoiceFX dropdown visibility MickFXPoofUpdateVoiceFXDropdown(state); } function MickFXPoofRenderPopup(state, uiState) { const popup = document.getElementById('MickFX-Poof-Popup'); // Handle loading state - preserve existing form structure if (uiState.isLoading) { const statusText = popup.querySelector('#MickFX-Poof-Popup-Text'); const buttons = popup.querySelectorAll('.MickFX-Button'); const inputs = popup.querySelectorAll('.MickFX-Input'); if (statusText) { statusText.innerHTML = uiState.message; // Disable all interactive elements during loading buttons.forEach(button => button.disabled = true); inputs.forEach(input => input.disabled = true); } return; } // Re-enable elements when not loading const buttons = popup.querySelectorAll('.MickFX-Button'); const inputs = popup.querySelectorAll('.MickFX-Input'); buttons.forEach(button => button.disabled = false); inputs.forEach(input => input.disabled = false); // Handle auto-complete if (uiState.autoComplete) { popup.innerHTML = ` ${uiState.message} `; const settingsPath = MickFX_MainDirectory + "/Information/fxsettings.ini"; SAMMI.saveIni(settingsPath, "Poof", "VoiceFX", 1, "number") .then(() => SAMMI.triggerButton('MickFXPoofCompleteInstall')); return; } // Handle mic input form if (uiState.showUI === 'mic-form') { const messageText = uiState.errorMessage || uiState.inputMessage || 'To add voice effects to your microphone, enter your microphone\'s exact name as it appears in OBS.'; popup.innerHTML = ` ${messageText}