CameraManager의 CinemachineCamera 프리셋을 라운드 로빈으로 렌더링하여 웹 대시보드에서 실시간 JPEG 프리뷰를 확인할 수 있는 시스템 추가. - StreamDeckServerManager에 프리뷰 렌더링 로직 통합 (카메라 풀, AsyncGPUReadback, 바이너리 WebSocket) - 대시보드 프리뷰 탭: 해상도/품질/갱신간격/그리드열 커스텀, 클릭→카메라 전환, 더블클릭→풀스크린 - 구독 기반 전송 + Page Visibility API로 불필요한 렌더링 방지 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
133 lines
6.0 KiB (Stored with Git LFS)
Plaintext
133 lines
6.0 KiB (Stored with Git LFS)
Plaintext
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
<title>Streamingle Dashboard</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/gridstack@10/dist/gridstack.min.css" rel="stylesheet"/>
|
|
<style>
|
|
{{CSS}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<h1>STREAMINGLE</h1>
|
|
<div class="tab-bar">
|
|
<button class="tab-btn active" id="tabBtnDashboard" onclick="switchTab('dashboard')">Dashboard</button>
|
|
<button class="tab-btn" id="tabBtnRetargeting" onclick="switchTab('retargeting')">Retargeting</button>
|
|
<button class="tab-btn" id="tabBtnPreview" onclick="switchTab('preview')">카메라 프리뷰</button>
|
|
</div>
|
|
<div class="header-controls">
|
|
<div class="connection-status" id="connectionStatus">연결 대기중...</div>
|
|
<div id="dashboardControls">
|
|
<button class="btn-icon" id="btnLock" onclick="toggleLock()" title="레이아웃 잠금/해제">🔓</button>
|
|
<button class="btn-icon" onclick="showSettings()" title="패널 설정">⚙</button>
|
|
<button class="btn-icon" onclick="requestFullState()" title="새로고침">🔄</button>
|
|
</div>
|
|
<div id="retargetingControls" style="display:none;">
|
|
<button class="btn-icon" onclick="rtRefresh()" title="새로고침">🔄</button>
|
|
<button class="btn-icon" onclick="rtExpandAll()" title="모두 펼치기">📂</button>
|
|
<button class="btn-icon" onclick="rtCollapseAll()" title="모두 접기">📁</button>
|
|
</div>
|
|
<div id="previewControls" style="display:none;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dashboard Tab -->
|
|
<div id="tabDashboard" class="tab-content active">
|
|
<div class="health-bar" id="healthBar">
|
|
<div class="health-item" id="health-optitrack">
|
|
<span class="health-dot offline"></span>
|
|
<span>OptiTrack</span>
|
|
</div>
|
|
<div class="health-item" id="health-facial">
|
|
<span class="health-dot offline"></span>
|
|
<span>Facial(0)</span>
|
|
</div>
|
|
<div class="health-item" id="health-recording">
|
|
<span class="health-dot offline"></span>
|
|
<span>REC</span>
|
|
</div>
|
|
<div class="health-item" id="health-remote">
|
|
<span class="health-dot offline"></span>
|
|
<span>Remote</span>
|
|
</div>
|
|
<div class="health-item" id="health-clients">
|
|
<span class="health-dot online"></span>
|
|
<span>WS(1)</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid-stack" id="dashboardGrid"></div>
|
|
</div>
|
|
|
|
<!-- Retargeting Tab -->
|
|
<div id="tabRetargeting" class="tab-content">
|
|
<div class="rt-status-bar" id="rtStatusBar">
|
|
<div class="rt-connection-status" id="rtConnectionStatus">리타게팅 대기중...</div>
|
|
</div>
|
|
<div class="characters-container" id="charactersContainer">
|
|
<div class="loading-message">리타게팅 탭을 선택하면 연결됩니다.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preview Tab -->
|
|
<div id="tabPreview" class="tab-content">
|
|
<div class="preview-toolbar">
|
|
<span class="preview-info" id="previewInfo">카메라 0개</span>
|
|
<div class="preview-controls">
|
|
<label>해상도:</label>
|
|
<select id="previewResolution" onchange="updatePreviewSettings()">
|
|
<option value="240x135">240x135</option>
|
|
<option value="320x180" selected>320x180</option>
|
|
<option value="480x270">480x270</option>
|
|
</select>
|
|
<label>품질:</label>
|
|
<select id="previewQuality" onchange="updatePreviewSettings()">
|
|
<option value="30">낮음</option>
|
|
<option value="50" selected>중간</option>
|
|
<option value="75">높음</option>
|
|
</select>
|
|
<label>갱신 간격:</label>
|
|
<select id="previewRenderInterval" onchange="updatePreviewSettings()">
|
|
<option value="1">매 프레임</option>
|
|
<option value="2">2프레임</option>
|
|
<option value="3" selected>3프레임</option>
|
|
<option value="5">5프레임</option>
|
|
</select>
|
|
<label>열:</label>
|
|
<select id="previewColumns" onchange="updatePreviewColumns()">
|
|
<option value="0" selected>자동</option>
|
|
<option value="4">4</option>
|
|
<option value="5">5</option>
|
|
<option value="6">6</option>
|
|
<option value="7">7</option>
|
|
<option value="8">8</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="preview-grid" id="previewGrid"></div>
|
|
</div>
|
|
|
|
<!-- Settings Overlay -->
|
|
<div class="settings-overlay" id="settingsOverlay" onclick="onSettingsOverlayClick(event)">
|
|
<div class="settings-panel">
|
|
<div class="settings-header">
|
|
<span>패널 설정</span>
|
|
<button class="settings-close" onclick="hideSettings()">✕</button>
|
|
</div>
|
|
<div class="settings-body" id="settingsBody"></div>
|
|
<div class="settings-footer">
|
|
<button class="btn btn-secondary btn-sm" onclick="resetLayout()">레이아웃 초기화</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="toast" id="toast"></div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/gridstack@10/dist/gridstack-all.js"></script>
|
|
<script>
|
|
{{JS}}
|
|
</script>
|
|
</body>
|
|
</html>
|