diff --git a/apps/sandbox/templates/spf-segment-loading/index.html b/apps/sandbox/templates/spf-segment-loading/index.html index 9c3b862a..2b157a8f 100644 --- a/apps/sandbox/templates/spf-segment-loading/index.html +++ b/apps/sandbox/templates/spf-segment-loading/index.html @@ -168,12 +168,27 @@ #resolution-list { display: flex; flex-wrap: wrap; gap: 8px; } .resolution-item { - padding: 4px 10px; + 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; } @@ -236,11 +251,24 @@ + +
+ + + + +
+
+
+

Live / DVR

+
Waiting for presentation…
+
+

Video Renditions

Waiting for presentation…
diff --git a/apps/sandbox/templates/spf-segment-loading/main.ts b/apps/sandbox/templates/spf-segment-loading/main.ts index 85915122..9dfcfeb1 100644 --- a/apps/sandbox/templates/spf-segment-loading/main.ts +++ b/apps/sandbox/templates/spf-segment-loading/main.ts @@ -28,6 +28,12 @@ const mutedToggle = document.getElementById('muted-toggle') as HTMLInputElement; const autoplayToggle = document.getElementById('autoplay-toggle') as HTMLInputElement; const preloadSelect = document.getElementById('preload-select') as HTMLSelectElement; const shareLink = document.getElementById('share-link') as HTMLAnchorElement; +const liveInfoDiv = document.getElementById('live-info') as HTMLDivElement; +const seekLiveBtn = document.getElementById('seek-live') as HTMLButtonElement; +const seekBack10Btn = document.getElementById('seek-back-10') as HTMLButtonElement; +const seekBack30Btn = document.getElementById('seek-back-30') as HTMLButtonElement; +const seekWindowStartBtn = document.getElementById('seek-window-start') as HTMLButtonElement; +const liveSeekButtons = [seekLiveBtn, seekBack10Btn, seekBack30Btn, seekWindowStartBtn]; // ── Query params ────────────────────────────────────────────────────────────── const DEFAULT_STREAM = 'https://stream.mux.com/JX01bG8eB4uaoV3OpDuK602rBfvdSgrMObjwuUOBn4JrQ.m3u8'; @@ -142,6 +148,100 @@ function updateThroughputDisplay() { throughputDiv.className = 'has-data'; } +// ── Live / DVR panel ──────────────────────────────────────────────────────── +// All values are derived sandbox-side from the resolved presentation's +// streamType plus the native