- src/streaming/ module: camera detection (USB webcam + Pi Camera Module via v4l2/libcamera), GStreamer pipeline builder with V4L2/OMX/x264 h264 encoders, platform presets (YouTube, Twitch, Facebook, custom RTMP), streamer lifecycle manager with auto-reconnect, scene management, stream statistics - Keyboard shortcut controller (evdev + pynput fallback) for stream control: Ctrl+Shift+S (start/stop), Ctrl+Shift+1-9 (scene switch), Ctrl+Shift+R (reconnect) - REST API: /api/v1/stream/* endpoints for start/stop/status, scenes, cameras, platforms, and hotkey listing — integrated into NetworkServer - StreamStateProvider bridging layer connects REST routes to Streamer instance - GStreamer pipeline supports ALSA/JACK/PulseAudio audio capture and V4L2/libcamera video capture with H.264 hardware encoding on RPi4B - Low-latency streaming settings for live performance (zerolatency, no B-frames) - 89 streaming tests (709 total project tests pass)
This commit is contained in:
+62
-3
@@ -109,13 +109,72 @@
|
||||
<!-- ── Sessions View ── -->
|
||||
<section id="view-sessions" class="view">
|
||||
<div class="sessions-header">
|
||||
<h3>Saved Sessions</h3>
|
||||
<h3>Mixer Sessions</h3>
|
||||
<div class="sessions-actions">
|
||||
<input type="text" id="scene-name-input" placeholder="Scene name" class="text-input">
|
||||
<button class="btn btn-primary" onclick="App.saveScene()">💾 Save Current</button>
|
||||
<input type="text" id="session-name-input" placeholder="Session name" class="text-input">
|
||||
<input type="text" id="session-notes-input" placeholder="Notes (optional)" class="text-input" style="width:160px">
|
||||
<button class="btn btn-primary" onclick="App.saveSession()">💾 Save Session</button>
|
||||
<button class="btn" onclick="App.refreshSessions()">🔄 Refresh</button>
|
||||
<select id="session-filter" onchange="App.refreshSessions()" style="width:100px">
|
||||
<option value="all">All</option>
|
||||
<option value="sessions">Sessions</option>
|
||||
<option value="snapshots">Snapshots</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Auto-save status -->
|
||||
<div id="autosave-status" class="autosave-status"><span id="autosave-indicator">● Auto-save active</span></div>
|
||||
|
||||
<!-- Sub-tabs for Sessions / Setlists / Snapshots -->
|
||||
<div class="sessions-subtabs">
|
||||
<button class="subtab active" id="subtab-sessions" onclick="App.showSessionsSubtab('sessions')">📁 Sessions</button>
|
||||
<button class="subtab" id="subtab-setlists" onclick="App.showSessionsSubtab('setlists')">📋 Setlists</button>
|
||||
<button class="subtab" id="subtab-snapshots" onclick="App.showSessionsSubtab('snapshots')">📸 Snapshots</button>
|
||||
</div>
|
||||
|
||||
<!-- Sessions list -->
|
||||
<div id="sessions-list" class="sessions-grid"></div>
|
||||
|
||||
<!-- Setlist view (hidden by default) -->
|
||||
<div id="setlists-view" style="display:none">
|
||||
<div class="sessions-actions" style="margin-bottom:0.5rem">
|
||||
<input type="text" id="setlist-name-input" placeholder="Setlist name" class="text-input">
|
||||
<button class="btn btn-primary" onclick="App.createSetlist()">+ New Setlist</button>
|
||||
</div>
|
||||
<div id="setlists-list" class="sessions-grid"></div>
|
||||
<div id="setlist-editor" style="display:none">
|
||||
<h4>Setlist: <span id="setlist-editor-title"></span></h4>
|
||||
<div id="setlist-entries"></div>
|
||||
<div class="sessions-actions">
|
||||
<input type="text" id="setlist-entry-session" placeholder="Session name" class="text-input">
|
||||
<select id="setlist-entry-transition"><option value="cut">Cut</option><option value="crossfade">Crossfade</option><option value="wait">Wait</option></select>
|
||||
<button class="btn" onclick="App.addSetlistEntry()">+ Add Entry</button>
|
||||
<button class="btn btn-primary" onclick="App.saveSetlist()">💾 Save Setlist</button>
|
||||
<button class="btn" onclick="App.activateSetlist()">▶ Activate</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Snapshots view (hidden by default) -->
|
||||
<div id="snapshots-view" style="display:none">
|
||||
<div class="sessions-actions" style="margin-bottom:0.5rem">
|
||||
<input type="text" id="snapshot-name-input" placeholder="Snapshot name" class="text-input">
|
||||
<select id="snapshot-category-input" style="width:100px">
|
||||
<option value="">No Category</option><option value="intro">Intro</option><option value="verse">Verse</option><option value="chorus">Chorus</option><option value="bridge">Bridge</option><option value="outro">Outro</option>
|
||||
</select>
|
||||
<button class="btn btn-primary" onclick="App.captureSnapshot()">📸 Capture</button>
|
||||
<button class="btn" onclick="App.snapshotNext()">▶ Next</button>
|
||||
<button class="btn" onclick="App.snapshotPrev()">◀ Prev</button>
|
||||
</div>
|
||||
<div id="snapshots-list" class="sessions-grid"></div>
|
||||
</div>
|
||||
|
||||
<!-- Import/Export -->
|
||||
<div class="sessions-actions" style="margin-top:1rem; border-top:1px solid var(--border-color); padding-top:0.5rem">
|
||||
<button class="btn" onclick="App.importSession()">📥 Import</button>
|
||||
<button class="btn" onclick="App.exportAllSessions()">📤 Export All</button>
|
||||
<input type="file" id="session-import-file" accept=".json" style="display:none" onchange="App.handleImportFile(this)">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user