/** * Streamingle Camera Controller - Property Inspector (단순화 버전) * Plugin Main을 통해 Unity와 통신 */ // Global variables let websocket = null; // 전역 uuid와 actionContext를 명확히 구분 let uuid = null; // StreamDeck에서 받은 context(uuid) let actionContext = null; // actionInfo.context let settings = {}; // Context별 설정 관리 const contextSettings = new Map(); let currentActionContext = null; // Unity 연결 상태 (Plugin Main에서 받아옴) let isUnityConnected = false; let cameraData = []; let currentCamera = 0; // DOM elements let statusDot = null; let connectionStatus = null; let cameraSelect = null; let currentCameraDisplay = null; let refreshButton = null; // 화면에 로그를 표시하는 함수 function logToScreen(msg, color = "#fff") { let logDiv = document.getElementById('logArea'); if (!logDiv) { console.log('로그 영역을 찾을 수 없음'); return; } const line = document.createElement('div'); line.style.color = color; line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; logDiv.appendChild(line); logDiv.scrollTop = logDiv.scrollHeight; } // 기존 console.log/console.error를 화면에도 출력 const origLog = console.log; console.log = function(...args) { origLog.apply(console, args); logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#0f0'); }; const origErr = console.error; console.error = function(...args) { origErr.apply(console, args); logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#f55'); }; console.log('🔧 Property Inspector script loaded'); // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', function() { console.log('📋 Property Inspector 초기화'); initializePropertyInspector(); }); // Initialize Property Inspector function initializePropertyInspector() { // Get DOM elements statusDot = document.getElementById('statusDot'); connectionStatus = document.getElementById('connection-status'); cameraSelect = document.getElementById('camera-select'); currentCameraDisplay = document.getElementById('current-camera'); refreshButton = document.getElementById('refresh-button'); // Setup event listeners if (cameraSelect) { cameraSelect.addEventListener('change', onCameraSelectionChanged); } if (refreshButton) { refreshButton.addEventListener('click', onRefreshClicked); } console.log('✅ Property Inspector 준비 완료'); } // Send message to plugin function sendToPlugin(command, data = {}) { if (!websocket) { console.error('❌ WebSocket not available'); return; } if (!uuid) { console.error('❌ UUID not available'); return; } try { const message = { action: 'com.mirabox.streamingle.camera', // manifest.json의 Action UUID event: 'sendToPlugin', context: uuid, // 아이템과 동일한 패턴 payload: { command, ...data } }; websocket.send(JSON.stringify(message)); console.log('📤 Message sent to plugin:', command, data, 'context:', uuid); } catch (error) { console.error('❌ Failed to send message to plugin:', error); } } // Update connection status display function updateConnectionStatus(isConnected) { console.log('🔄 Connection status update:', isConnected); // 전역 변수도 업데이트 isUnityConnected = isConnected; if (statusDot) { statusDot.className = `dot ${isConnected ? 'green' : 'red'}`; } if (connectionStatus) { connectionStatus.textContent = isConnected ? 'Unity 연결됨' : 'Unity 연결 안됨'; connectionStatus.className = isConnected ? 'connected' : 'disconnected'; } if (cameraSelect) { cameraSelect.disabled = !isConnected; } if (refreshButton) { refreshButton.disabled = !isConnected; } } // Update camera data display function updateCameraData(cameraDataParam, currentCamera) { console.log('📹 Camera data update:', cameraDataParam, currentCamera); if (cameraSelect && cameraDataParam) { cameraSelect.innerHTML = ''; let cameras = cameraDataParam; if (cameraDataParam.cameras) { cameras = cameraDataParam.cameras; } else if (Array.isArray(cameraDataParam)) { cameras = cameraDataParam; } console.log('📹 처리할 카메라 배열:', cameras); if (cameras && cameras.length > 0) { cameraData = cameras; console.log('💾 전역 cameraData 저장됨:', cameraData.length + '개'); cameras.forEach((camera, index) => { const option = document.createElement('option'); option.value = index; option.textContent = `카메라 ${index + 1}`; if (camera.name) { option.textContent += ` (${camera.name})`; } cameraSelect.appendChild(option); }); // settings.cameraIndex를 우선 적용 let selectedIndex = currentCamera; if (typeof settings.cameraIndex === 'number') { selectedIndex = settings.cameraIndex; } if (typeof selectedIndex === 'number') { cameraSelect.value = selectedIndex; } cameraSelect.disabled = false; console.log('✅ 카메라 목록 업데이트 완료:', cameras.length + '개'); } else { console.log('⚠️ 카메라 데이터가 없거나 빈 배열'); cameraSelect.disabled = true; } } updateCurrentCameraDisplay(currentCamera); } // Update current camera display function updateCurrentCameraDisplay(currentCamera) { if (currentCameraDisplay) { if (typeof currentCamera === 'number') { currentCameraDisplay.textContent = `현재 카메라: ${currentCamera + 1}`; } else { currentCameraDisplay.textContent = '현재 카메라: -'; } } } // Camera selection changed function onCameraSelectionChanged() { if (!cameraSelect) return; const selectedIndex = parseInt(cameraSelect.value); if (isNaN(selectedIndex)) return; console.log('📹 카메라 선택 변경:', selectedIndex); // Plugin Main에 카메라 변경 요청 sendToPlugin('switch_camera', { cameraIndex: selectedIndex }); // 설정 저장 const currentSettings = getContextSettings(uuid); const newSettings = { ...currentSettings, cameraIndex: selectedIndex }; saveContextSettings(newSettings); // 버튼 제목 업데이트 요청 sendToPlugin('update_title', { cameraIndex: selectedIndex }); } // Refresh button clicked function onRefreshClicked() { console.log('🔄 카메라 목록 새로고침 요청'); sendToPlugin('get_camera_list'); } // Handle messages from plugin function handleMessage(jsonObj) { console.log('📨 Property Inspector 메시지 수신:', jsonObj.event); // 플러그인에서 오는 메시지는 event가 sendToPropertyInspector이고, 실제 타입은 payload.event에 있음 if (jsonObj.event === 'sendToPropertyInspector' && jsonObj.payload && jsonObj.payload.event) { const innerEvent = jsonObj.payload.event; console.log('📨 내부 이벤트:', innerEvent, 'payload:', jsonObj.payload); switch (innerEvent) { case 'unity_connected': console.log('✅ Unity 연결 상태 업데이트'); updateConnectionStatus(true); break; case 'unity_disconnected': console.log('❌ Unity 연결 해제 상태 업데이트'); updateConnectionStatus(false); break; case 'camera_list': console.log('📹 카메라 목록 수신'); updateCameraData(jsonObj.payload.cameras, jsonObj.payload.currentIndex); break; case 'camera_changed': console.log('📹 카메라 변경 알림'); updateCurrentCameraDisplay(jsonObj.payload.cameraIndex); break; case 'item_list': console.log('⚠️ 아이템 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); break; case 'event_list': console.log('⚠️ 이벤트 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); break; case 'avatar_outfit_list': console.log('⚠️ 아바타 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); break; case 'avatar_outfit_changed': console.log('⚠️ 아바타 변경 데이터를 받았지만 카메라 컨트롤러에서는 무시함'); break; default: console.log('❓ 알 수 없는 내부 이벤트:', innerEvent); } return; } // 기존 방식도 유지 switch (jsonObj.event) { case 'unity_connected': console.log('✅ Unity 연결 상태 업데이트'); updateConnectionStatus(true); break; case 'unity_disconnected': console.log('❌ Unity 연결 해제 상태 업데이트'); updateConnectionStatus(false); break; case 'camera_list': console.log('📹 카메라 목록 수신'); if (jsonObj.payload && jsonObj.payload.cameras) { updateCameraData(jsonObj.payload.cameras, jsonObj.payload.currentIndex); } break; case 'camera_changed': console.log('📹 카메라 변경 알림'); if (jsonObj.payload && typeof jsonObj.payload.cameraIndex === 'number') { updateCurrentCameraDisplay(jsonObj.payload.cameraIndex); } break; case 'didReceiveSettings': console.log('⚙️ 설정 수신'); if (jsonObj.payload && jsonObj.context) { const newSettings = jsonObj.payload.settings || {}; contextSettings.set(jsonObj.context, newSettings); // 카메라 인덱스가 있으면 선택 if (typeof newSettings.cameraIndex === 'number' && cameraSelect) { cameraSelect.value = newSettings.cameraIndex; updateCurrentCameraDisplay(newSettings.cameraIndex); } } break; default: console.log('❓ 알 수 없는 메시지 타입:', jsonObj.event); } } // StreamDeck SDK 연결 function connectElgatoStreamDeckSocket(inPort, inPropertyInspectorUUID, inRegisterEvent, inInfo, inActionInfo) { console.log('🔌 Property Inspector StreamDeck 연결'); console.log('📡 포트:', inPort, 'UUID:', inPropertyInspectorUUID); uuid = inPropertyInspectorUUID; // Parse action info try { if (inActionInfo) { const actionInfo = JSON.parse(inActionInfo); actionContext = actionInfo.context; settings = actionInfo.payload?.settings || {}; console.log('⚙️ 초기 설정:', settings); // 카메라 인덱스가 있으면 선택 if (typeof settings.cameraIndex === 'number' && cameraSelect) { cameraSelect.value = settings.cameraIndex; updateCurrentCameraDisplay(settings.cameraIndex); } } } catch (error) { console.error('❌ ActionInfo 파싱 오류:', error); } // WebSocket 연결 if (!websocket) { websocket = new WebSocket('ws://localhost:' + inPort); websocket.onopen = function() { console.log('✅ Property Inspector StreamDeck 연결됨'); // StreamDeck에 등록 websocket.send(JSON.stringify({ event: inRegisterEvent, uuid: inPropertyInspectorUUID })); // Unity 상태 요청 setTimeout(() => { sendToPlugin('get_unity_status'); }, 500); }; websocket.onmessage = function(evt) { try { const jsonObj = JSON.parse(evt.data); handleMessage(jsonObj); } catch (error) { console.error('❌ 메시지 파싱 오류:', error); } }; websocket.onclose = function() { console.log('❌ Property Inspector StreamDeck 연결 끊어짐'); websocket = null; }; websocket.onerror = function(error) { console.error('❌ Property Inspector StreamDeck 연결 오류:', error); }; } } // 설정 관리 헬퍼 함수들 function getContextSettings(context) { if (!context) return {}; return contextSettings.get(context) || {}; } // context별 설정 저장 (StreamDeck 표준 setSettings 사용) function saveContextSettings(newSettings) { if (!uuid || !websocket) return; websocket.send(JSON.stringify({ action: 'com.mirabox.streamingle.camera', event: 'setSettings', context: uuid, payload: newSettings })); contextSettings.set(uuid, newSettings); // 로컬에도 저장 } // context별 설정 불러오기 (StreamDeck 표준 getSettings 사용) function requestSettings() { if (!uuid || !websocket) return; websocket.send(JSON.stringify({ action: 'com.mirabox.streamingle.camera', event: 'getSettings', context: uuid })); } // Property Inspector 초기화 시 context별 설정 요청 function initializePropertyInspector() { // Get DOM elements statusDot = document.getElementById('statusDot'); connectionStatus = document.getElementById('connection-status'); cameraSelect = document.getElementById('camera-select'); currentCameraDisplay = document.getElementById('current-camera'); refreshButton = document.getElementById('refresh-button'); // Setup event listeners if (cameraSelect) { cameraSelect.addEventListener('change', onCameraSelectionChanged); } if (refreshButton) { refreshButton.addEventListener('click', onRefreshClicked); } // 현재 액션의 컨텍스트가 있으면 설정 요청 if (uuid) { requestSettings(); } console.log('✅ Property Inspector 준비 완료'); }