Files
v10/apps/sandbox/templates/spf-segment-loading/index.html
T
Christian PillsburyandClaude Opus 4.8 6888b83abe feat(sandbox): add live/DVR diagnostics + seek controls to spf-segment-loading
Adds a Live/DVR panel (stream type, window classification, live gap,
DVR window length, buffer-ahead, presentation anchor + wall-clock) and
seek controls (seek-to-live, -10/-30s, jump to window start) to the SPF
segment-loading harness. All derived sandbox-side from presentation
streamType + the native video; degrades cleanly to on-demand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:55:15 -07:00

305 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sandbox — SPF Segment Loading POC</title>
<style>
body {
max-width: 1200px;
padding: 20px;
margin: 0 auto;
font-family: monospace;
}
h1 { margin-bottom: 6px; }
p { margin: 0 0 12px; }
video {
display: block;
width: 100%;
max-width: 640px;
margin: 12px 0 4px;
border: 2px solid #333;
}
/* ── Src controls ── */
#src-controls {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 10px;
}
#src-input {
flex: 1;
max-width: 540px;
padding: 6px 10px;
font-family: monospace;
font-size: 12px;
border: 1px solid #999;
border-radius: 4px;
}
/* ── Playback config ── */
#playback-config {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
margin-bottom: 10px;
font-size: 12px;
}
#playback-config label { display: flex; gap: 4px; align-items: center; cursor: pointer; }
#playback-config select {
padding: 2px 6px;
font-family: monospace;
font-size: 12px;
border: 1px solid #999;
border-radius: 4px;
}
/* ── Share URL ── */
#share-url-section {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 10px;
font-family: monospace;
font-size: 11px;
}
#share-link {
max-width: 520px;
overflow: hidden;
text-overflow: ellipsis;
color: #0070f3;
white-space: nowrap;
text-decoration: none;
}
#share-link:hover { text-decoration: underline; }
/* ── Main controls ── */
#controls { margin-bottom: 10px; }
button {
padding: 8px 16px;
margin-right: 8px;
font-family: monospace;
font-size: 12px;
cursor: pointer;
background: #f0f0f0;
border: 1px solid #333;
border-radius: 4px;
}
button:hover { background: #e0e0e0; }
/* ── Video info strip ── */
#now-playing-quality {
margin-bottom: 2px;
font-family: monospace;
font-size: 12px;
color: #555;
}
#now-playing-quality.has-quality { font-weight: bold; color: #0070f3; }
#throughput-display {
margin-bottom: 12px;
font-family: monospace;
font-size: 12px;
color: #555;
}
#throughput-display.has-data { color: #16a34a; }
#throughput-display.warming { color: #d97706; }
/* ── Rendition / resolution / audio panels ── */
#rendition-picker,
#audio-track-picker,
#resolution-status {
padding: 12px 15px;
margin: 16px 0;
font-size: 12px;
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 4px;
}
#rendition-picker h2, #audio-track-picker h2, #text-track-picker h2, #resolution-status h2 { margin-top: 0; font-size: 14px; }
#audio-track-buttons, #text-track-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.audio-track-btn {
padding: 6px 12px;
font-family: monospace;
font-size: 11px;
cursor: pointer;
background: #fff;
border: 2px solid #999;
border-radius: 4px;
}
.audio-track-btn:hover { background: #eee; border-color: #555; }
.audio-track-btn.selected-default { font-weight: bold; background: #e6f0ff; border-color: #0070f3; }
.audio-track-btn.selected-pinned { font-weight: bold; background: #fef3c7; border-color: #d97706; }
.audio-status { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; }
.mode-default { font-weight: bold; color: #0070f3; }
.mode-pinned { font-weight: bold; color: #d97706; }
.clear-filter-btn {
padding: 4px 10px; font-size: 11px; color: #d97706; cursor: pointer; background: #fef3c7;
border: 1px solid #d97706; border-radius: 4px;
}
.clear-filter-btn:hover { background: #fde68a; }
#rendition-buttons { display: flex; flex-wrap: wrap; gap: 8px; }
.rendition-btn {
padding: 6px 12px;
font-family: monospace;
font-size: 11px;
cursor: pointer;
background: #fff;
border: 2px solid #999;
border-radius: 4px;
}
.rendition-btn:hover { background: #eee; border-color: #555; }
.rendition-btn.selected-abr { font-weight: bold; background: #e6f0ff; border-color: #0070f3; }
.rendition-btn.selected-manual { font-weight: bold; background: #fef3c7; border-color: #d97706; }
.abr-status { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; }
.mode-abr { font-weight: bold; color: #0070f3; }
.mode-manual { font-weight: bold; color: #d97706; }
.enable-abr-btn {
padding: 4px 10px; font-size: 11px; color: #0070f3; cursor: pointer;background: #e6f0ff;
border: 1px solid #0070f3; border-radius: 4px;
}
.enable-abr-btn:hover { background: #cce0ff; }
#resolution-list { display: flex; flex-wrap: wrap; gap: 8px; }
.resolution-item {
padding: 4px 10px;
font-family: monospace; font-size: 11px;border: 1px solid #ccc; border-radius: 4px;
}
.resolution-item.resolved { color: green; border-color: green; }
.resolution-item.unresolved { color: #888; border-color: #aaa; }
/* ── Live / DVR ── */
#live-controls { margin-bottom: 10px; }
#live-controls button:disabled { color: #aaa; cursor: not-allowed; background: #f7f7f7; border-color: #ccc; }
#live-panel {
padding: 12px 15px; margin: 16px 0; font-size: 12px;
background: #f5f5f5; border: 1px solid #ddd; border-radius: 4px;
}
#live-panel h2 { margin-top: 0; font-size: 14px; }
#live-info { display: grid; grid-template-columns: max-content 1fr; gap: 2px 16px; }
#live-info .k { color: #777; }
#live-info .v { color: #0070f3; }
#live-info .edge { font-weight: bold; color: #16a34a; }
#live-info .warn { font-weight: bold; color: #d97706; }
#live-info .muted { color: #888; }
/* ── Logs ── */
#logs-section { margin: 16px 0; }
#logs-section h2 { margin-bottom: 6px; font-size: 14px; }
#logs {
max-height: 400px; padding: 10px; overflow-y: auto; font-size: 12px;
background: #f5f5f5; border: 1px solid #ddd; border-radius: 4px;
}
#logs div { padding: 2px 0; border-bottom: 1px solid #eee; }
/* ── State inspector ── */
#state {
padding: 15px; margin: 16px 0;font-size: 12px;
background: #f5f5f5;
border: 1px solid #ddd; border-radius: 4px;
}
#state h2 { margin-top: 0; font-size: 14px; }
#state pre {
padding: 10px;
margin: 10px 0; overflow-x: auto;background: #fff;
border: 1px solid #ddd; border-radius: 4px;
}
/* ── Log colours ── */
.error { font-weight: bold; color: red; }
.success { color: green; }
.warning { color: orange; }
</style>
</head>
<body>
<h1>SPF Segment Loading POC Test</h1>
<p>Testing segment loading pipeline with real Mux HLS stream</p>
<div id="src-controls">
<input type="text" id="src-input" placeholder="Stream URL…" />
<button type="button" id="set-src">Set Src</button>
</div>
<div id="playback-config">
<label><input type="checkbox" id="muted-toggle" /> Muted</label>
<label><input type="checkbox" id="autoplay-toggle" /> Autoplay</label>
<label>
Preload:
<select id="preload-select">
<option value="none">none</option>
<option value="metadata">metadata</option>
<option value="auto">auto</option>
</select>
</label>
</div>
<div id="share-url-section">
🔗 <a id="share-link" href="#" target="_blank" rel="noopener">current page</a>
<button type="button" id="open-new-tab">Open in new tab ↗</button>
</div>
<div id="controls">
<button type="button" id="play">Play</button>
<button type="button" id="pause">Pause</button>
<button type="button" id="inspect">Inspect State</button>
<button type="button" id="clearLogs">Clear Logs</button>
</div>
<!-- Live/DVR seek controls — disabled on non-live sources (see renderLivePanel). -->
<div id="live-controls">
<button type="button" id="seek-live">⏭ Seek to live</button>
<button type="button" id="seek-back-10">10s</button>
<button type="button" id="seek-back-30">30s</button>
<button type="button" id="seek-window-start">⏮ Window start</button>
</div>
<!-- biome-ignore lint/a11y/useMediaCaption: test harness, captions managed by SPF -->
<video id="video" controls preload="none"></video>
<div id="now-playing-quality"></div>
<div id="throughput-display"></div>
<div id="live-panel">
<h2>Live / DVR</h2>
<div id="live-info">Waiting for presentation…</div>
</div>
<div id="rendition-picker">
<h2>Video Renditions</h2>
<div id="rendition-buttons">Waiting for presentation…</div>
</div>
<div id="audio-track-picker">
<h2>Audio Tracks</h2>
<div id="audio-track-buttons">Waiting for presentation…</div>
</div>
<div id="text-track-picker">
<h2>Subtitles / Captions</h2>
<div id="text-track-buttons">Waiting for presentation…</div>
</div>
<div id="resolution-status">
<h2>Track Resolution Status</h2>
<div id="resolution-list">Waiting for presentation…</div>
</div>
<div id="logs-section">
<h2>Logs</h2>
<div id="logs"></div>
</div>
<div id="state">
<h2>State Inspector</h2>
<div>Click "Inspect State" to view engine state</div>
</div>
<script type="module" src="./main.ts"></script>
</body>
</html>