76 lines
5.8 KiB
Plaintext
76 lines
5.8 KiB
Plaintext
<section class="card transcription-overview" data-transcription-admin>
|
|
<div class="section-header">
|
|
<%- include("../../../src/web/views/partials/page-header", {
|
|
eyebrow: "Experimental companion",
|
|
pageTitle: "Lumi Transcription",
|
|
description: "Server-hosted speech recognition for selected OBS sources, managed through one Lumi Companion application."
|
|
}) %>
|
|
<span class="status-indicator <%= providerHealth.healthy ? 'status-success' : 'status-warning' %>">
|
|
<%= providerHealth.healthy ? 'Inference ready' : 'Setup required' %>
|
|
</span>
|
|
</div>
|
|
<div class="callout warning">
|
|
<strong>Live Twitch delivery is not accepted yet</strong>
|
|
<p>The native OBS 31+ caption path still needs target-machine validation. Lumi will not present a local simulated caption as live Twitch success.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="card setup-path" aria-labelledby="setup-title" data-transcription-admin>
|
|
<div class="section-header">
|
|
<div><span class="eyebrow">First usable path</span><h2 id="setup-title">Setup progress</h2><p class="hint">Complete one clear step at a time. Existing server administration remains in Lumi; streaming-computer controls remain in Companion.</p></div>
|
|
</div>
|
|
<ol>
|
|
<li class="<%= devices.some((device) => !device.revoked_at) ? 'is-complete' : 'is-current' %>"><strong>Download and pair Companion</strong><span>Create a short-lived, single-use package for the streaming computer.</span></li>
|
|
<li><strong>Install the OBS integration</strong><span>Companion owns installation and repair; the bridge has no separate settings.</span></li>
|
|
<li class="<%= providerHealth.model ? 'is-complete' : '' %>"><strong>Load and benchmark a model</strong><span>Small English is the recommended starting point.</span></li>
|
|
<li><strong>Select and test a microphone</strong><span>The real safe test must pass before live delivery is enabled.</span></li>
|
|
</ol>
|
|
<div class="inline-actions">
|
|
<button class="button" type="button" data-create-pairing>Download Companion</button>
|
|
<a class="button subtle" href="#speech-models">Review speech models</a>
|
|
</div>
|
|
<p class="hint" role="status" aria-live="polite" data-status></p>
|
|
</section>
|
|
|
|
<div class="transcription-grid" data-transcription-admin>
|
|
<section class="card" aria-labelledby="runtime-title">
|
|
<div class="section-header"><div><span class="eyebrow">Lumi host</span><h2 id="runtime-title">Inference health</h2><p class="hint">Speech recognition runs on the Lumi server, never on the streaming computer.</p></div></div>
|
|
<div class="dashboard-metric-grid transcription-metrics">
|
|
<div><span>Provider</span><strong>whisper.cpp <%= runtimeManifest.tested_version %></strong></div>
|
|
<div><span>Worker</span><strong><%= providerHealth.state %></strong></div>
|
|
<div><span>Selected model</span><strong><%= providerHealth.model || 'Not loaded' %></strong></div>
|
|
<div><span>Active sessions</span><strong><%= providerHealth.sessions || 0 %></strong></div>
|
|
</div>
|
|
<div class="callout info"><strong>Shared GPU awareness</strong><p>Lumi warns and benchmarks when Lumi AI already occupies GPU memory. It never unloads AI models automatically.</p></div>
|
|
</section>
|
|
|
|
<section class="card" aria-labelledby="devices-title">
|
|
<div class="section-header"><div><span class="eyebrow">Access</span><h2 id="devices-title">Paired devices</h2><p class="hint">Credentials are revocable and scoped to Companion capabilities.</p></div><span class="badge"><%= devices.filter((device) => !device.revoked_at).length %> active</span></div>
|
|
<% if (!devices.length) { %><div class="empty-state"><strong>No Companion is paired</strong><p>Use Download Companion to create a package that expires after 15 minutes and works once.</p></div><% } %>
|
|
<% if (devices.length) { %>
|
|
<ul class="transcription-device-list">
|
|
<% devices.forEach((device) => { %>
|
|
<li><div><strong><%= device.name %></strong><span class="hint"><%= device.revoked_at ? 'Revoked' : 'Last connected ' + new Date(device.last_connected_at).toLocaleString() %></span></div><span class="badge <%= device.revoked_at ? 'danger' : 'success' %>"><%= device.revoked_at ? 'Revoked' : 'Allowed' %></span></li>
|
|
<% }) %>
|
|
</ul>
|
|
<% } %>
|
|
</section>
|
|
</div>
|
|
|
|
<section class="card" id="speech-models" aria-labelledby="models-title" data-transcription-admin>
|
|
<div class="section-header"><div><span class="eyebrow">Curated choices</span><h2 id="models-title">Speech models</h2><p class="hint">Downloads require confirmation and checksum verification before a model can load.</p></div></div>
|
|
<div class="model-row">
|
|
<% models.forEach((model) => { %>
|
|
<article>
|
|
<div><h3><%= model.label %></h3><p class="hint"><%= model.recommended ? 'Recommended starting point' : 'Fallback option' %> · <%= Math.round(model.bytes / 1048576) %> MiB</p></div>
|
|
<div class="model-actions">
|
|
<span class="badge <%= providerHealth.model === model.id ? 'success' : model.status.valid ? 'info' : model.status.installed ? 'danger' : 'muted' %>"><%= providerHealth.model === model.id ? 'Loaded' : model.status.valid ? 'Verified' : model.status.installed ? 'Checksum failed' : 'Not installed' %></span>
|
|
<% if (!model.status.valid) { %><button class="button subtle" type="button" data-download-model="<%= model.id %>" data-model-label="<%= model.label %>" data-model-size="<%= Math.round(model.bytes / 1048576) %>">Download</button><% } %>
|
|
<% if (model.status.valid && providerHealth.model !== model.id) { %><button class="button subtle" type="button" data-load-model="<%= model.id %>">Load model</button><% } %>
|
|
</div>
|
|
</article>
|
|
<% }) %>
|
|
</div>
|
|
<div class="callout info"><strong>Audio privacy</strong><p>Raw audio is held only in bounded memory and is never written to disk by default. Diagnostic caption text is retained for seven days unless disabled.</p></div>
|
|
</section>
|