const { OBSWebSocket } = require('obs-websocket-js'); let obs = null; let isConnecting = false; let connectionPromise = null; async function ensureConnected() { // If already connected, return the existing client if (obs && obs.identified) { return obs; } // If already in the process of connecting, wait for it if (isConnecting && connectionPromise) { return connectionPromise; } // Start new connection isConnecting = true; connectionPromise = connectToOBS(); try { await connectionPromise; return obs; } finally { isConnecting = false; connectionPromise = null; } } async function connectToOBS() { const OBS_HOST = process.env.OBS_WEBSOCKET_HOST || '127.0.0.1'; const OBS_PORT = process.env.OBS_WEBSOCKET_PORT || '4455'; const OBS_PASSWORD = process.env.OBS_WEBSOCKET_PASSWORD || ''; // Create new client if needed if (!obs) { obs = new OBSWebSocket(); // Set up event handlers for connection management obs.on('ConnectionClosed', () => { console.log('OBS WebSocket connection closed'); obs = null; }); obs.on('ConnectionError', (err) => { console.error('OBS WebSocket connection error:', err); obs = null; }); obs.on('Identified', () => { console.log('OBS WebSocket successfully identified'); }); } try { console.log('Connecting to OBS WebSocket...'); console.log('Host:', OBS_HOST); console.log('Port:', OBS_PORT); console.log('Password:', OBS_PASSWORD ? '***' : '(none)'); await obs.connect(`ws://${OBS_HOST}:${OBS_PORT}`, OBS_PASSWORD); console.log('Connected to OBS WebSocket.'); return obs; } catch (err) { console.error('Failed to connect to OBS WebSocket:', err.message); obs = null; throw err; } } async function getOBSClient() { return await ensureConnected(); } function getConnectionStatus() { return { connected: obs && obs.identified, client: obs }; } async function disconnectFromOBS() { if (obs) { try { await obs.disconnect(); console.log('Disconnected from OBS WebSocket.'); } catch (err) { console.error('Error disconnecting from OBS:', err.message); } finally { obs = null; } } } async function addSourceToSwitcher(inputName, newSources) { try { const obsClient = await getOBSClient(); // Step 1: Get current input settings const { inputSettings } = await obsClient.call('GetInputSettings', { inputName }); // console.log('Current Settings:', inputSettings); // Step 2: Add new sources to the sources array const updatedSources = [...inputSettings.sources, ...newSources]; // Step 3: Update the settings with the new sources array await obsClient.call('SetInputSettings', { inputName, inputSettings: { ...inputSettings, sources: updatedSources, }, }); console.log('Updated settings successfully for', inputName); } catch (error) { console.error('Error updating settings:', error.message); throw error; } } async function createGroupIfNotExists(groupName) { try { const obsClient = await getOBSClient(); // Check if the group (scene) exists const { scenes } = await obsClient.call('GetSceneList'); const groupExists = scenes.some((scene) => scene.sceneName === groupName); if (!groupExists) { console.log(`Creating group "${groupName}"`); await obsClient.call('CreateScene', { sceneName: groupName }); return { created: true, message: `Group "${groupName}" created successfully` }; } else { console.log(`Group "${groupName}" already exists`); return { created: false, message: `Group "${groupName}" already exists` }; } } catch (error) { console.error('Error creating group:', error.message); throw error; } } async function addSourceToGroup(groupName, sourceName, url) { try { const obsClient = await getOBSClient(); // Ensure group exists await createGroupIfNotExists(groupName); // Check if source already exists in the group const { sceneItems } = await obsClient.call('GetSceneItemList', { sceneName: groupName }); const sourceExists = sceneItems.some(item => item.sourceName === sourceName); if (!sourceExists) { // Create the browser source in the group console.log(`Adding source "${sourceName}" to group "${groupName}"`); await obsClient.call('CreateInput', { sceneName: groupName, inputName: sourceName, inputKind: 'browser_source', inputSettings: { width: 1600, height: 900, url, control_audio: true, }, }); // Ensure audio control is enabled await obsClient.call('SetInputSettings', { inputName: sourceName, inputSettings: { control_audio: true, }, overlay: true, }); console.log(`Source "${sourceName}" successfully added to group "${groupName}"`); return { success: true, message: `Source added to group successfully` }; } else { console.log(`Source "${sourceName}" already exists in group "${groupName}"`); return { success: false, message: `Source already exists in group` }; } } catch (error) { console.error('Error adding source to group:', error.message); throw error; } } // Export all functions module.exports = { connectToOBS, getOBSClient, disconnectFromOBS, addSourceToSwitcher, ensureConnected, getConnectionStatus, createGroupIfNotExists, addSourceToGroup };