Lumi/src/web/views/admin-stream-testing.ejs
2026-07-26 17:56:38 +02:00

104 lines
6.3 KiB
Plaintext

<%- include("partials/layout-top", {
title,
pageWidth: "wide",
pageId: "stream-testing",
extraStyles: ["/stream-testing.css"]
}) %>
<main data-stream-testing>
<section class="card">
<%- include("partials/page-header", {
eyebrow: "Admin-only preview",
pageTitle: "Stream testing",
description: "Inspect the real OBS output through Lumi without publishing it to a normal streaming destination."
}) %>
<div class="callout warning"><strong>Never use this while publicly live.</strong><p>Companion refuses to redirect an active stream. The test uses an expiring destination and restores the exact saved OBS service when it ends.</p></div>
<div class="stream-test-summary" data-stream-summary aria-live="polite"></div>
</section>
<section class="card">
<div class="section-heading">
<div><span class="eyebrow">Managed runtime</span><h2>MediaMTX</h2></div>
<span class="status-pill neutral" data-runtime-state>Checking</span>
</div>
<p class="hint" data-runtime-detail>Lumi checks the private media router without downloading anything.</p>
<div class="inline-actions stream-runtime-actions">
<button class="button" type="button" data-runtime-install>Install MediaMTX</button>
<button class="button subtle" type="button" data-runtime-repair>Repair installation</button>
<button class="button subtle" type="button" data-runtime-health>Run health check</button>
</div>
<div class="callout" data-runtime-result hidden aria-live="polite"></div>
</section>
<section class="card">
<div class="section-heading">
<div><span class="eyebrow">Secure production ingest</span><h2>RTMPS certificate automation</h2></div>
<span class="status-pill <%= streamTestDns.configured ? "success" : "warning" %>"><%= streamTestDns.configured ? "DNS ready" : "HTTP challenge" %></span>
</div>
<% if (streamTestDns.configured) { %>
<p class="hint">Lumi uses encrypted <%= streamTestDns.providerLabel %> credentials to create and remove short-lived DNS challenges. Certificate issuance and renewal do not depend on the HTTPS reverse proxy.</p>
<% } else { %>
<div class="callout warning">
<strong>Using the public WebUI challenge path</strong>
<p>If OpenResty, Nginx Proxy Manager, or another HTTPS proxy reserves <code>/.well-known/acme-challenge/</code>, configure DNS automation here. Lumi will then issue RTMPS certificates without proxy changes or certificate file paths.</p>
</div>
<% } %>
<form method="post" action="/admin/stream-testing/tls/dns" class="stream-test-dns-form">
<label class="field">
<span>DNS provider</span>
<select name="provider" aria-label="DNS provider"><option value="domeneshop" selected>Domeneshop / hyp.net</option></select>
</label>
<label class="field">
<span>API token</span>
<input type="password" name="token" autocomplete="off" maxlength="512" placeholder="<%= streamTestDns.configured ? "Leave blank to keep the saved token" : "Domeneshop API token" %>" />
</label>
<label class="field">
<span>API secret</span>
<input type="password" name="secret" autocomplete="new-password" maxlength="512" placeholder="<%= streamTestDns.configured ? "Leave blank to keep the saved secret" : "Domeneshop API secret" %>" />
</label>
<div class="inline-actions">
<button class="button" type="submit"><%= streamTestDns.configured ? "Verify saved credentials" : "Save and verify" %></button>
<a class="button subtle" href="https://www.domeneshop.no/admin?view=api" target="_blank" rel="noopener noreferrer">Create API credentials</a>
</div>
</form>
<% if (streamTestDns.configured) { %>
<form method="post" action="/admin/stream-testing/tls/dns/remove" data-confirm-mode="modal" data-confirm-title="Remove DNS automation?" data-confirm-text="Lumi will no longer be able to renew its RTMPS certificate through DNS until credentials are configured again." data-confirm-label="Remove credentials">
<button class="button danger" type="submit">Remove DNS credentials</button>
</form>
<% } %>
<p class="hint">Credentials are encrypted with this Lumi installation's secret and are never returned to the browser or written to logs.</p>
</section>
<section class="stream-test-layout">
<div class="card stream-test-player-card">
<div class="section-heading"><div><span class="eyebrow">Private receiver</span><h2>Live output</h2></div><span class="status-pill neutral" data-stream-state>Idle</span></div>
<div class="stream-test-player-shell">
<video data-stream-player controls autoplay muted playsinline crossorigin="use-credentials"><track data-stream-captions kind="captions" srclang="en" label="Lumi captions" default /></video>
<div class="stream-test-caption-overlay" data-stream-caption-overlay hidden aria-hidden="true"></div>
<div class="empty-state" data-stream-empty>Start Stream testing from Lumi Companion on the streaming computer.</div>
</div>
<div class="inline-actions">
<label class="field compact"><span>Quality</span><select data-stream-quality disabled><option value="-1">Source output</option></select></label>
<button class="button subtle" type="button" data-stream-fullscreen disabled>Fullscreen</button>
<button class="button danger" type="button" data-stream-stop disabled>End test</button>
</div>
</div>
<aside class="card">
<div class="section-heading"><div><span class="eyebrow">Real-time facts</span><h2>Diagnostics</h2></div></div>
<div class="stream-test-metrics" data-stream-metrics></div>
<div data-stream-warnings></div>
</aside>
</section>
<section class="card">
<div class="section-heading"><div><span class="eyebrow">Recovery trail</span><h2>Timeline</h2></div></div>
<ol class="stream-test-timeline" data-stream-timeline></ol>
<details class="lumi-expandable-settings"><summary><span><strong>Advanced receiver details</strong><span class="hint">MediaMTX health, ingest safety, and allowlisted metrics</span></span></summary><pre class="log-details" data-stream-advanced></pre></details>
</section>
</main>
<%- include("partials/layout-bottom", {
extraScripts: ["/admin/stream-testing/hls.js", "/stream-testing.js"]
}) %>