888 lines
38 KiB
JavaScript
888 lines
38 KiB
JavaScript
// MiraBox StreamDock 플러그인 - 브라우저 기반
|
|
console.log('🚀 Streamingle 플러그인 시작 (브라우저 기반)');
|
|
|
|
// Global variables
|
|
let websocket = null;
|
|
let buttonContexts = new Map(); // 각 버튼의 컨텍스트별 설정 저장
|
|
let unitySocket = null;
|
|
let isUnityConnected = false;
|
|
let cameraList = []; // 카메라 목록 저장
|
|
let itemList = []; // 아이템 목록 저장
|
|
|
|
const fs = require('fs');
|
|
|
|
function getBase64Image(filePath) {
|
|
try {
|
|
const data = fs.readFileSync(filePath);
|
|
return 'data:image/png;base64,' + data.toString('base64');
|
|
} catch (e) {
|
|
console.error('이미지 파일 읽기 실패:', filePath, e);
|
|
return '';
|
|
}
|
|
}
|
|
|
|
// StreamDock 연결 함수 (브라우저 기반)
|
|
function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inActionInfo) {
|
|
console.log('🔌 StreamDock 연결 시작 (브라우저)');
|
|
console.log('📡 포트:', inPort, 'UUID:', inUUID, '이벤트:', inEvent);
|
|
|
|
// Parse action info to get initial settings
|
|
try {
|
|
if (inActionInfo) {
|
|
const actionInfo = JSON.parse(inActionInfo);
|
|
const context = actionInfo.context;
|
|
const settings = actionInfo.payload?.settings || {};
|
|
|
|
// 각 컨텍스트별로 설정 저장
|
|
buttonContexts.set(context, settings);
|
|
console.log('⚙️ 초기 설정 저장:', settings);
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ ActionInfo 파싱 오류:', error);
|
|
}
|
|
|
|
// WebSocket 연결
|
|
if (!websocket) {
|
|
websocket = new WebSocket('ws://localhost:' + inPort);
|
|
|
|
websocket.onopen = function() {
|
|
console.log('✅ StreamDock 연결됨 (브라우저)');
|
|
|
|
// StreamDock에 등록
|
|
websocket.send(JSON.stringify({
|
|
event: inEvent,
|
|
uuid: inUUID
|
|
}));
|
|
|
|
// Unity 연결 시도
|
|
setTimeout(() => {
|
|
connectToUnity();
|
|
}, 1000);
|
|
|
|
// Unity 연결 재시도 (5초마다)
|
|
setInterval(() => {
|
|
if (!isUnityConnected) {
|
|
console.log('🔄 Unity 재연결 시도...');
|
|
connectToUnity();
|
|
}
|
|
}, 5000);
|
|
};
|
|
|
|
websocket.onmessage = function(evt) {
|
|
try {
|
|
const jsonObj = JSON.parse(evt.data);
|
|
console.log('📨 StreamDock 메시지 수신:', jsonObj.event);
|
|
console.log('📋 전체 메시지 데이터:', JSON.stringify(jsonObj, null, 2));
|
|
|
|
switch(jsonObj.event) {
|
|
case 'didReceiveSettings':
|
|
if (jsonObj.payload && jsonObj.context) {
|
|
const newSettings = jsonObj.payload.settings || {};
|
|
buttonContexts.set(jsonObj.context, newSettings);
|
|
console.log('⚙️ 설정 업데이트:', newSettings);
|
|
updateButtonTitle(jsonObj.context);
|
|
}
|
|
break;
|
|
case 'willAppear':
|
|
console.log('👀 버튼 나타남:', jsonObj.context);
|
|
|
|
let settings = jsonObj.payload?.settings || {};
|
|
|
|
// action UUID로 actionType 결정
|
|
if (jsonObj.action === 'com.mirabox.streamingle.item') {
|
|
settings.actionType = 'item';
|
|
console.log('🎯 아이템 컨트롤러 등록:', jsonObj.context);
|
|
} else {
|
|
settings.actionType = 'camera';
|
|
console.log('📹 카메라 컨트롤러 등록:', jsonObj.context);
|
|
}
|
|
|
|
buttonContexts.set(jsonObj.context, settings);
|
|
updateButtonTitle(jsonObj.context);
|
|
|
|
// Unity가 이미 연결되어 있다면 Property Inspector에 상태 전송
|
|
if (isUnityConnected) {
|
|
const settings = getCurrentSettings(jsonObj.context);
|
|
const actionType = settings.actionType || 'camera';
|
|
let actionUUID = 'com.mirabox.streamingle.camera';
|
|
if (actionType === 'item') {
|
|
actionUUID = 'com.mirabox.streamingle.item';
|
|
}
|
|
|
|
sendToPropertyInspector(jsonObj.context, 'unity_connected', { connected: true }, actionUUID);
|
|
}
|
|
break;
|
|
|
|
case 'keyUp':
|
|
console.log('🔘 버튼 클릭됨!');
|
|
handleButtonClick(jsonObj.context);
|
|
break;
|
|
|
|
case 'sendToPlugin':
|
|
handlePropertyInspectorMessage(jsonObj.payload, jsonObj.context, jsonObj.action);
|
|
break;
|
|
|
|
default:
|
|
console.log('❓ 알 수 없는 이벤트:', jsonObj.event);
|
|
break;
|
|
}
|
|
} catch (error) {
|
|
console.error('❌ 메시지 파싱 오류:', error);
|
|
}
|
|
};
|
|
|
|
websocket.onclose = function() {
|
|
console.log('❌ StreamDock 연결 끊어짐');
|
|
websocket = null;
|
|
};
|
|
|
|
websocket.onerror = function(error) {
|
|
console.error('❌ StreamDock 연결 오류:', error);
|
|
};
|
|
}
|
|
}
|
|
|
|
// Unity 연결 함수 (브라우저 네이티브 WebSocket 사용)
|
|
function connectToUnity() {
|
|
console.log('🔌 Unity 연결 시도 (브라우저)...');
|
|
|
|
if (unitySocket) {
|
|
unitySocket.close();
|
|
}
|
|
|
|
try {
|
|
unitySocket = new WebSocket('ws://localhost:10701');
|
|
|
|
unitySocket.onopen = function() {
|
|
isUnityConnected = true;
|
|
console.log('✅ Unity 연결 성공 (브라우저)!');
|
|
|
|
// 카메라 목록 요청
|
|
setTimeout(() => {
|
|
const message = JSON.stringify({ type: 'get_camera_list' });
|
|
unitySocket.send(message);
|
|
console.log('📋 카메라 목록 요청:', message);
|
|
}, 100);
|
|
|
|
// 아이템 목록 요청
|
|
setTimeout(() => {
|
|
const message = JSON.stringify({ type: 'get_item_list' });
|
|
unitySocket.send(message);
|
|
console.log('📋 아이템 목록 요청:', message);
|
|
}, 200);
|
|
};
|
|
|
|
unitySocket.onmessage = function(event) {
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
console.log('📨 Unity 메시지 수신:', data.type);
|
|
console.log('📋 Unity 메시지 전체 데이터:', JSON.stringify(data, null, 2));
|
|
handleUnityMessage(data);
|
|
} catch (error) {
|
|
console.error('❌ Unity 메시지 파싱 오류:', error);
|
|
}
|
|
};
|
|
|
|
unitySocket.onclose = function() {
|
|
isUnityConnected = false;
|
|
console.log('❌ Unity 연결 끊어짐');
|
|
|
|
// Property Inspector들에게 Unity 연결 해제 알림
|
|
for (const context of buttonContexts.keys()) {
|
|
// context별로 적절한 action UUID 결정
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
let actionUUID = 'com.mirabox.streamingle.camera';
|
|
if (actionType === 'item') {
|
|
actionUUID = 'com.mirabox.streamingle.item';
|
|
}
|
|
sendToPropertyInspector(context, 'unity_disconnected', { connected: false }, actionUUID);
|
|
}
|
|
};
|
|
|
|
unitySocket.onerror = function(error) {
|
|
console.error('❌ Unity 연결 오류:', error);
|
|
console.log('🔍 Unity가 실행 중인지 확인하세요 (포트 10701)');
|
|
};
|
|
} catch (error) {
|
|
console.error('❌ Unity 연결 설정 오류:', error);
|
|
}
|
|
}
|
|
|
|
// 버튼 클릭 처리
|
|
function handleButtonClick(context) {
|
|
console.log('🎯 버튼 클릭 처리 시작');
|
|
console.log('📍 컨텍스트:', context);
|
|
console.log('🔌 Unity 연결 상태:', isUnityConnected);
|
|
|
|
if (!isUnityConnected || !unitySocket) {
|
|
console.error('❌ Unity 연결되지 않음');
|
|
return;
|
|
}
|
|
|
|
// context별 settings 사용
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera'; // 기본값은 camera
|
|
|
|
console.log('🎯 액션 타입:', actionType);
|
|
|
|
switch (actionType) {
|
|
case 'camera':
|
|
handleCameraAction(settings);
|
|
break;
|
|
case 'item':
|
|
handleItemAction(settings);
|
|
break;
|
|
default:
|
|
console.log('⚠️ 알 수 없는 액션 타입:', actionType);
|
|
// 기본적으로 카메라 액션으로 처리
|
|
handleCameraAction(settings);
|
|
}
|
|
}
|
|
|
|
// 카메라 액션 처리
|
|
function handleCameraAction(settings) {
|
|
let cameraIndex = settings.cameraIndex;
|
|
|
|
// 카메라 인덱스가 설정되지 않았으면 0 사용
|
|
if (typeof cameraIndex !== 'number') {
|
|
cameraIndex = 0;
|
|
console.log('⚠️ 카메라 인덱스가 설정되지 않음, 기본값 0 사용');
|
|
}
|
|
|
|
console.log('📹 전환할 카메라 인덱스:', cameraIndex);
|
|
|
|
// Unity에 카메라 전환 요청
|
|
const message = JSON.stringify({
|
|
type: 'switch_camera',
|
|
data: {
|
|
camera_index: cameraIndex
|
|
}
|
|
});
|
|
|
|
console.log('📤 Unity에 카메라 전환 요청 전송:', message);
|
|
console.log('🔍 Unity 연결 상태:', isUnityConnected);
|
|
console.log('🔍 Unity 소켓 상태:', !!unitySocket);
|
|
|
|
if (unitySocket && unitySocket.readyState === WebSocket.OPEN) {
|
|
unitySocket.send(message);
|
|
console.log('✅ 메시지 전송 완료');
|
|
} else {
|
|
console.error('❌ Unity 소켓이 연결되지 않음');
|
|
}
|
|
}
|
|
|
|
// 아이템 액션 처리
|
|
function handleItemAction(settings) {
|
|
let itemIndex = settings.itemIndex;
|
|
const itemAction = settings.itemAction || 'toggle'; // 기본값은 toggle
|
|
|
|
// 아이템 인덱스가 설정되지 않았으면 0 사용
|
|
if (typeof itemIndex !== 'number') {
|
|
itemIndex = 0;
|
|
console.log('⚠️ 아이템 인덱스가 설정되지 않음, 기본값 0 사용');
|
|
}
|
|
|
|
console.log('🎯 아이템 액션:', itemAction, '인덱스:', itemIndex);
|
|
|
|
let messageType = 'toggle_item';
|
|
if (itemAction === 'set') {
|
|
messageType = 'set_item';
|
|
}
|
|
|
|
// Unity에 아이템 액션 요청
|
|
const message = JSON.stringify({
|
|
type: messageType,
|
|
data: {
|
|
item_index: itemIndex
|
|
}
|
|
});
|
|
|
|
console.log('📤 Unity에 아이템 액션 요청 전송:', message);
|
|
console.log('🔍 Unity 연결 상태:', isUnityConnected);
|
|
console.log('🔍 Unity 소켓 상태:', !!unitySocket);
|
|
|
|
if (unitySocket && unitySocket.readyState === WebSocket.OPEN) {
|
|
unitySocket.send(message);
|
|
console.log('✅ 메시지 전송 완료');
|
|
} else {
|
|
console.error('❌ Unity 소켓이 연결되지 않음');
|
|
}
|
|
}
|
|
|
|
// Property Inspector 메시지 처리
|
|
function handlePropertyInspectorMessage(payload, context, actionUUID) {
|
|
const command = payload.command;
|
|
console.log('📤 Property Inspector 명령 처리:', command, 'action:', actionUUID);
|
|
switch (command) {
|
|
case 'get_unity_status':
|
|
console.log('📡 Unity 상태 요청 - context:', context, 'action:', actionUUID);
|
|
console.log('📡 Unity 연결 상태:', isUnityConnected);
|
|
console.log('📡 카메라 목록 길이:', cameraList.length);
|
|
console.log('📡 아이템 목록 길이:', itemList.length);
|
|
|
|
sendToPropertyInspector(context, 'unity_connected', { connected: isUnityConnected }, actionUUID);
|
|
if (isUnityConnected) {
|
|
if (cameraList.length > 0) {
|
|
// 현재 활성 카메라 인덱스 찾기
|
|
const currentCameraIndex = cameraList.findIndex(cam => cam.isActive) || 0;
|
|
console.log('📡 현재 활성 카메라 인덱스:', currentCameraIndex);
|
|
sendToPropertyInspector(context, 'camera_list', {
|
|
cameras: cameraList,
|
|
currentIndex: currentCameraIndex
|
|
}, actionUUID);
|
|
}
|
|
if (itemList.length > 0) {
|
|
// 현재 활성 아이템 인덱스 찾기
|
|
const currentItemIndex = itemList.findIndex(item => item.isActive) || 0;
|
|
console.log('📡 현재 활성 아이템 인덱스:', currentItemIndex);
|
|
sendToPropertyInspector(context, 'item_list', {
|
|
items: itemList,
|
|
currentIndex: currentItemIndex
|
|
}, actionUUID);
|
|
}
|
|
} else {
|
|
// Unity가 연결되지 않은 경우에도 빈 목록 전송
|
|
console.log('📡 Unity 연결 안됨 - 빈 목록 전송');
|
|
sendToPropertyInspector(context, 'camera_list', {
|
|
cameras: [],
|
|
currentIndex: 0
|
|
}, actionUUID);
|
|
sendToPropertyInspector(context, 'item_list', {
|
|
items: [],
|
|
currentIndex: 0
|
|
}, actionUUID);
|
|
}
|
|
break;
|
|
case 'get_camera_list':
|
|
console.log('📹 카메라 목록 요청');
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({ type: 'get_camera_list' });
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 카메라 목록 요청 전송');
|
|
}
|
|
break;
|
|
case 'get_item_list':
|
|
console.log('🎯 아이템 목록 요청');
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({ type: 'get_item_list' });
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 아이템 목록 요청 전송');
|
|
}
|
|
break;
|
|
case 'switch_camera':
|
|
console.log('📹 카메라 전환 요청:', payload.cameraIndex);
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({
|
|
type: 'switch_camera',
|
|
data: { camera_index: payload.cameraIndex }
|
|
});
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 카메라 전환 요청 전송');
|
|
}
|
|
break;
|
|
case 'toggle_item':
|
|
console.log('🎯 아이템 토글 요청:', payload.itemIndex);
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({
|
|
type: 'toggle_item',
|
|
data: { item_index: payload.itemIndex }
|
|
});
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 아이템 토글 요청 전송');
|
|
}
|
|
break;
|
|
case 'set_item':
|
|
console.log('🎯 아이템 설정 요청:', payload.itemIndex);
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({
|
|
type: 'set_item',
|
|
data: { item_index: payload.itemIndex }
|
|
});
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 아이템 설정 요청 전송');
|
|
}
|
|
break;
|
|
case 'refresh_camera_list':
|
|
console.log('🔄 카메라 목록 새로고침 요청');
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({ type: 'get_camera_list' });
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 카메라 목록 요청 전송');
|
|
}
|
|
break;
|
|
case 'refresh_item_list':
|
|
console.log('🔄 아이템 목록 새로고침 요청');
|
|
if (isUnityConnected && unitySocket) {
|
|
const message = JSON.stringify({ type: 'get_item_list' });
|
|
unitySocket.send(message);
|
|
console.log('📤 Unity에 아이템 목록 요청 전송');
|
|
}
|
|
break;
|
|
case 'update_title':
|
|
console.log('🏷️ 버튼 제목 업데이트 요청');
|
|
updateButtonTitle(context);
|
|
break;
|
|
default:
|
|
console.log('❓ 알 수 없는 Property Inspector 명령:', command);
|
|
}
|
|
}
|
|
|
|
// Property Inspector로 메시지 전송 (action UUID 지정 가능)
|
|
function sendToPropertyInspector(context, event, payload, actionUUID = null) {
|
|
console.log('📤 Property Inspector로 메시지 전송 시작 - context:', context, 'event:', event);
|
|
|
|
if (!websocket || !context) {
|
|
console.log('🚫 WebSocket 또는 context 없음 - Property Inspector 메시지 전송 건너뜀');
|
|
console.log('🔍 websocket 상태:', !!websocket);
|
|
console.log('🔍 context:', context);
|
|
return;
|
|
}
|
|
|
|
// action UUID 결정
|
|
let action = actionUUID;
|
|
if (!action) {
|
|
// Property Inspector에서 보내는 메시지의 action UUID를 그대로 사용
|
|
// 카메라 컨트롤러 Property Inspector는 'com.mirabox.streamingle.camera'를 사용
|
|
// 아이템 컨트롤러 Property Inspector는 'com.mirabox.streamingle.item'을 사용
|
|
action = 'com.mirabox.streamingle.camera'; // 기본값
|
|
|
|
// context별로 적절한 action 결정
|
|
const settings = getCurrentSettings(context);
|
|
console.log('📤 context 설정:', settings);
|
|
const actionType = settings.actionType || 'camera';
|
|
|
|
if (actionType === 'item') {
|
|
action = 'com.mirabox.streamingle.item';
|
|
}
|
|
}
|
|
|
|
console.log('📤 결정된 action:', action);
|
|
|
|
const message = {
|
|
action: action,
|
|
event: 'sendToPropertyInspector',
|
|
context: context,
|
|
payload: {
|
|
event: event,
|
|
...payload
|
|
}
|
|
};
|
|
console.log('📤 Property Inspector로 전송할 메시지:', JSON.stringify(message, null, 2));
|
|
websocket.send(JSON.stringify(message));
|
|
console.log('✅ Property Inspector로 메시지 전송 완료:', event, payload);
|
|
}
|
|
|
|
// Unity 메시지 처리
|
|
function handleUnityMessage(data) {
|
|
console.log('📨 Unity 메시지 수신:', data.type);
|
|
console.log('📋 전체 메시지 데이터:', JSON.stringify(data, null, 2));
|
|
|
|
switch (data.type) {
|
|
case 'connection_established':
|
|
console.log('🎉 Unity 연결 확인됨');
|
|
console.log('📋 connection_established 데이터:', JSON.stringify(data, null, 2));
|
|
|
|
// 연결 시 카메라 및 아이템 데이터 저장
|
|
if (data.data) {
|
|
// 카메라 데이터 처리
|
|
if (data.data.camera_data) {
|
|
let cameras = data.data.camera_data.presets || data.data.camera_data;
|
|
if (Array.isArray(cameras)) {
|
|
cameraList = cameras;
|
|
console.log('📹 카메라 목록 저장됨:', cameraList.length, '개');
|
|
} else {
|
|
cameraList = [];
|
|
console.log('⚠️ 카메라 데이터가 배열이 아님:', cameras);
|
|
}
|
|
}
|
|
|
|
// 아이템 데이터 처리
|
|
if (data.data.item_data) {
|
|
let items = data.data.item_data.items || data.data.item_data;
|
|
if (Array.isArray(items)) {
|
|
itemList = items;
|
|
console.log('🎯 아이템 목록 저장됨:', itemList.length, '개');
|
|
} else {
|
|
itemList = [];
|
|
console.log('⚠️ 아이템 데이터가 배열이 아님:', items);
|
|
}
|
|
}
|
|
|
|
updateAllButtonTitles();
|
|
|
|
// Property Inspector들에게 Unity 연결 상태 알림
|
|
console.log('📤 Property Inspector들에게 Unity 연결 알림 전송 시작');
|
|
console.log('📤 현재 등록된 컨텍스트들:', Array.from(buttonContexts.keys()));
|
|
|
|
if (buttonContexts.size === 0) {
|
|
console.log('⚠️ 등록된 컨텍스트가 없음 - Property Inspector가 아직 연결되지 않았을 수 있음');
|
|
} else {
|
|
console.log('📤 등록된 컨텍스트들:', Array.from(buttonContexts.keys()));
|
|
for (const context of buttonContexts.keys()) {
|
|
console.log('📤 Property Inspector로 전송할 컨텍스트:', context);
|
|
|
|
// context별로 적절한 action UUID 결정
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
let actionUUID = 'com.mirabox.streamingle.camera';
|
|
if (actionType === 'item') {
|
|
actionUUID = 'com.mirabox.streamingle.item';
|
|
}
|
|
|
|
console.log('🔍 컨텍스트 분석:', context, 'Action Type:', actionType, 'Action UUID:', actionUUID);
|
|
sendToPropertyInspector(context, 'unity_connected', { connected: true }, actionUUID);
|
|
|
|
// action UUID에 따라 적절한 데이터만 전송
|
|
if (actionUUID === 'com.mirabox.streamingle.camera') {
|
|
// 카메라 컨트롤러에는 카메라 데이터만 전송
|
|
let currentCameraIndex = 0;
|
|
if (typeof data.data.camera_data?.current_index === 'number' && data.data.camera_data.current_index >= 0) {
|
|
currentCameraIndex = data.data.camera_data.current_index;
|
|
}
|
|
console.log('📹 카메라 컨트롤러에 카메라 데이터 전송:', context, '카메라 수:', cameraList.length);
|
|
sendToPropertyInspector(context, 'camera_list', {
|
|
cameras: cameraList,
|
|
currentIndex: currentCameraIndex
|
|
}, actionUUID);
|
|
} else if (actionUUID === 'com.mirabox.streamingle.item') {
|
|
// 아이템 컨트롤러에는 아이템 데이터만 전송
|
|
let currentItemIndex = 0;
|
|
if (typeof data.data.item_data?.current_index === 'number' && data.data.item_data.current_index >= 0) {
|
|
currentItemIndex = data.data.item_data.current_index;
|
|
}
|
|
console.log('🎯 아이템 컨트롤러에 아이템 데이터 전송:', context, '아이템 수:', itemList.length);
|
|
sendToPropertyInspector(context, 'item_list', {
|
|
items: itemList,
|
|
currentIndex: currentItemIndex
|
|
}, actionUUID);
|
|
}
|
|
}
|
|
console.log('✅ Property Inspector들에게 Unity 연결 알림 전송 완료');
|
|
}
|
|
} else {
|
|
console.log('⚠️ Unity 연결 시 데이터가 없음');
|
|
console.log('📋 data.data:', data.data);
|
|
}
|
|
break;
|
|
|
|
case 'camera_list_response':
|
|
console.log('📹 카메라 목록 수신');
|
|
|
|
if (data.data && data.data.camera_data) {
|
|
let cameras = data.data.camera_data.presets || data.data.camera_data;
|
|
|
|
if (Array.isArray(cameras)) {
|
|
cameraList = cameras;
|
|
const currentIndex = data.data.current_camera ?? data.data.camera_data?.current_index ?? 0;
|
|
console.log('📹 카메라 목록 업데이트됨:', cameraList.length, '개');
|
|
updateAllButtonTitles();
|
|
|
|
// Property Inspector들에게 카메라 목록 전송 (카메라 컨트롤러만)
|
|
for (const context of buttonContexts.keys()) {
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
|
|
if (actionType === 'camera') {
|
|
sendToPropertyInspector(context, 'camera_list', {
|
|
cameras: cameraList,
|
|
currentIndex: currentIndex
|
|
}, 'com.mirabox.streamingle.camera');
|
|
|
|
// 카메라 상태에 따라 버튼 상태도 업데이트
|
|
const cameraIndex = settings.cameraIndex || 0;
|
|
const isActive = (cameraIndex === currentIndex);
|
|
setButtonState(context, isActive);
|
|
console.log('🎨 카메라 변경으로 상태 업데이트:', context, '카메라 인덱스:', cameraIndex, '활성:', isActive);
|
|
}
|
|
}
|
|
} else {
|
|
console.log('⚠️ 카메라 목록 응답에서 카메라 데이터가 배열이 아님');
|
|
console.log('📋 cameras:', cameras);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'camera_changed':
|
|
console.log('📹 카메라 변경 알림');
|
|
if (data.data && data.data.camera_data) {
|
|
let cameras = data.data.camera_data.presets || data.data.camera_data;
|
|
|
|
if (Array.isArray(cameras)) {
|
|
cameraList = cameras;
|
|
console.log('📹 카메라 목록 업데이트됨:', cameraList.length, '개');
|
|
updateAllButtonTitles();
|
|
|
|
// Property Inspector들에게 카메라 목록 전송 (카메라 컨트롤러만)
|
|
for (const context of buttonContexts.keys()) {
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
|
|
if (actionType === 'camera') {
|
|
sendToPropertyInspector(context, 'camera_list', {
|
|
cameras: cameraList,
|
|
currentIndex: data.data.camera_data?.current_index || 0
|
|
}, 'com.mirabox.streamingle.camera');
|
|
|
|
// 카메라 상태에 따라 버튼 상태도 업데이트
|
|
const cameraIndex = settings.cameraIndex || 0;
|
|
let isActive = false;
|
|
if (cameraList && cameraList[cameraIndex]) {
|
|
isActive = cameraList[cameraIndex].isActive === true;
|
|
}
|
|
setButtonState(context, isActive);
|
|
console.log('🎨 카메라 변경으로 상태 업데이트:', context, '카메라 인덱스:', cameraIndex, 'isActive:', isActive);
|
|
}
|
|
}
|
|
} else {
|
|
console.log('⚠️ 카메라 변경 응답에서 카메라 데이터가 배열이 아님');
|
|
console.log('📋 cameras:', cameras);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'item_changed':
|
|
console.log('🎯 아이템 변경 알림');
|
|
if (data.data && data.data.item_data) {
|
|
let items = data.data.item_data.items || data.data.item_data;
|
|
|
|
if (Array.isArray(items)) {
|
|
itemList = items;
|
|
console.log('🎯 아이템 목록 업데이트됨:', itemList.length, '개');
|
|
updateAllButtonTitles();
|
|
|
|
// Property Inspector들에게 아이템 목록 전송 (아이템 컨트롤러만)
|
|
for (const context of buttonContexts.keys()) {
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
|
|
if (actionType === 'item') {
|
|
sendToPropertyInspector(context, 'item_list', {
|
|
items: itemList,
|
|
currentIndex: data.data.item_data?.current_index || 0
|
|
}, 'com.mirabox.streamingle.item');
|
|
|
|
// 아이템 상태에 따라 버튼 상태도 업데이트
|
|
const itemIndex = settings.itemIndex || 0;
|
|
if (itemList[itemIndex]) {
|
|
const isActive = itemList[itemIndex].isActive !== false; // 기본값은 true
|
|
setButtonState(context, isActive);
|
|
console.log('🎨 아이템 변경으로 상태 업데이트:', context, '아이템 인덱스:', itemIndex, '활성:', isActive);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
console.log('⚠️ 아이템 목록 응답에서 아이템 데이터가 배열이 아님');
|
|
console.log('📋 items:', items);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'item_list_response':
|
|
console.log('🎯 아이템 목록 수신');
|
|
|
|
if (data.data && data.data.item_data) {
|
|
let items = data.data.item_data.items || data.data.item_data;
|
|
|
|
if (Array.isArray(items)) {
|
|
itemList = items;
|
|
const currentIndex = data.data.current_item ?? data.data.item_data?.current_index ?? 0;
|
|
console.log('🎯 아이템 목록 업데이트됨:', itemList.length, '개');
|
|
updateAllButtonTitles();
|
|
|
|
// Property Inspector들에게 아이템 목록 전송 (아이템 컨트롤러만)
|
|
for (const context of buttonContexts.keys()) {
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
|
|
if (actionType === 'item') {
|
|
sendToPropertyInspector(context, 'item_list', {
|
|
items: itemList,
|
|
currentIndex: currentIndex
|
|
}, 'com.mirabox.streamingle.item');
|
|
|
|
// 아이템 상태에 따라 버튼 상태도 업데이트
|
|
const itemIndex = settings.itemIndex || 0;
|
|
if (itemList[itemIndex]) {
|
|
const isActive = itemList[itemIndex].isActive !== false; // 기본값은 true
|
|
setButtonState(context, isActive);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
console.log('⚠️ 아이템 목록 응답에서 아이템 데이터가 배열이 아님');
|
|
console.log('📋 items:', items);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
console.log('❓ 알 수 없는 Unity 메시지 타입:', data.type);
|
|
}
|
|
}
|
|
|
|
// 모든 버튼의 제목 업데이트
|
|
function updateAllButtonTitles() {
|
|
for (const context of buttonContexts.keys()) {
|
|
updateButtonTitle(context);
|
|
}
|
|
}
|
|
|
|
// StreamDock 버튼 제목 업데이트
|
|
function updateButtonTitle(context) {
|
|
if (!websocket || !context) {
|
|
console.log('🚫 WebSocket 또는 context 없음 - 제목 업데이트 건너뜀');
|
|
return;
|
|
}
|
|
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
let title = 'Camera';
|
|
let isActive = true; // 아이템 활성화 상태 (기본값: 활성)
|
|
|
|
if (actionType === 'camera') {
|
|
const cameraIndex = typeof settings.cameraIndex === 'number' ? settings.cameraIndex : 0;
|
|
title = `카메라 ${cameraIndex + 1}`;
|
|
|
|
// 카메라 목록에서 이름 찾기
|
|
if (cameraList && cameraList.length > cameraIndex) {
|
|
const camera = cameraList[cameraIndex];
|
|
if (camera && camera.name) {
|
|
// 카메라 이름에서 불필요한 부분 제거하고 짧게 만들기
|
|
let shortName = camera.name
|
|
.replace('Cam0', '')
|
|
.replace('Cam', '')
|
|
.replace('_', ' ')
|
|
.substring(0, 10); // 최대 10글자
|
|
|
|
title = shortName || `카메라 ${cameraIndex + 1}`;
|
|
}
|
|
|
|
// 카메라 활성화 상태 확인
|
|
if (typeof camera.isActive === 'boolean') {
|
|
isActive = camera.isActive;
|
|
}
|
|
}
|
|
} else if (actionType === 'item') {
|
|
const itemIndex = typeof settings.itemIndex === 'number' ? settings.itemIndex : 0;
|
|
title = `아이템 ${itemIndex + 1}`;
|
|
|
|
// 아이템 목록에서 이름 찾기
|
|
if (itemList && itemList.length > itemIndex) {
|
|
const item = itemList[itemIndex];
|
|
if (item && (item.name || item.groupName)) {
|
|
// 아이템 이름에서 불필요한 부분 제거하고 짧게 만들기
|
|
let itemName = item.name || item.groupName;
|
|
let shortName = itemName
|
|
.replace('Item', '')
|
|
.replace('Group', '')
|
|
.replace('_', ' ')
|
|
.substring(0, 10); // 최대 10글자
|
|
|
|
title = shortName || `아이템 ${itemIndex + 1}`;
|
|
|
|
// 아이템 활성화 상태 확인
|
|
if (typeof item.isActive === 'boolean') {
|
|
isActive = item.isActive;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// StreamDock에 제목 업데이트 요청
|
|
const message = {
|
|
event: 'setTitle',
|
|
context: context,
|
|
payload: {
|
|
title: title,
|
|
target: 0, // hardware and software
|
|
titleParameters: {
|
|
fontSize: 36, // 기존 24 → 36으로 키움
|
|
showTitle: true,
|
|
titleAlignment: "bottom" // 중앙(middle) → 아래(bottom)으로 변경
|
|
}
|
|
}
|
|
};
|
|
|
|
websocket.send(JSON.stringify(message));
|
|
console.log('🏷️ 버튼 제목 업데이트:', title, '(액션 타입:', actionType, ', 활성:', isActive, ')');
|
|
|
|
// 아이템이나 카메라가 비활성화되어 있으면 아이콘을 어둡게 표시
|
|
if ((actionType === 'item' || actionType === 'camera') && !isActive) {
|
|
setButtonState(context, false); // 비활성 상태로 설정
|
|
} else {
|
|
setButtonState(context, true); // 활성 상태로 설정
|
|
}
|
|
}
|
|
|
|
// 버튼 상태 설정 (활성/비활성)
|
|
function setButtonState(context, isActive) {
|
|
if (!websocket || !context) {
|
|
console.log('🚫 WebSocket 또는 context 없음 - 버튼 상태 업데이트 건너뜀');
|
|
return;
|
|
}
|
|
|
|
const settings = getCurrentSettings(context);
|
|
const actionType = settings.actionType || 'camera';
|
|
|
|
// 아이템 컨트롤러와 카메라 컨트롤러 모두 상태 변경 적용
|
|
if (actionType === 'item' || actionType === 'camera') {
|
|
// 방법 1: setState 이벤트 사용
|
|
const stateMessage = {
|
|
event: 'setState',
|
|
context: context,
|
|
payload: {
|
|
state: isActive ? 0 : 1, // 0: 활성 상태, 1: 비활성 상태
|
|
target: 0 // hardware and software
|
|
}
|
|
};
|
|
|
|
websocket.send(JSON.stringify(stateMessage));
|
|
console.log('🎨 버튼 상태 업데이트 (setState):', context, '(활성:', isActive, ', 상태:', isActive ? 0 : 1, ')');
|
|
|
|
// 방법 2: setImage 이벤트로 아이콘 직접 변경 (base64)
|
|
let imagePath;
|
|
if (actionType === 'item') {
|
|
imagePath = isActive ? 'images/item_icon.png' : 'images/item_icon_inactive.png';
|
|
} else if (actionType === 'camera') {
|
|
imagePath = isActive ? 'images/camera_icon.png' : 'images/camera_icon_inactive.png';
|
|
}
|
|
const imageBase64 = getBase64Image(imagePath);
|
|
const imageMessage = {
|
|
event: 'setImage',
|
|
context: context,
|
|
payload: {
|
|
image: imageBase64,
|
|
target: 0 // hardware and software
|
|
}
|
|
};
|
|
|
|
websocket.send(JSON.stringify(imageMessage));
|
|
console.log('🖼️ 버튼 아이콘 업데이트 (setImage):', context, '(활성:', isActive, ', 이미지:', imagePath, ')');
|
|
|
|
// 추가 디버깅을 위한 로그
|
|
setTimeout(() => {
|
|
console.log('🔍 상태 변경 후 확인 - Context:', context, 'Action Type:', actionType, '활성:', isActive);
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
// 설정 관리 헬퍼 함수들
|
|
function getCurrentSettings(context) {
|
|
if (!context) return {};
|
|
return buttonContexts.get(context) || {};
|
|
}
|
|
|
|
function setCurrentSettings(context, newSettings) {
|
|
if (!context) return;
|
|
buttonContexts.set(context, newSettings);
|
|
}
|
|
|
|
function updateCurrentSettings(context, partialSettings) {
|
|
if (!context) return;
|
|
const currentSettings = getCurrentSettings(context);
|
|
setCurrentSettings(context, { ...currentSettings, ...partialSettings });
|
|
}
|
|
|
|
// 브라우저 환경에서 자동 시작
|
|
console.log('🌐 브라우저 기반 플러그인 준비 완료'); |