- Added run ID + unique segment naming (r-{ISOTIME}/seg-{seq}.m4s)
- Added fsnotify watcher for real-time R2 segment upload (~7s)
- Added atomic manifest publication (temp key → CopyObject)
- Fixed manifest Cache-Control: public, max-age=1, s-maxage=2, must-revalidate
- Added hls.js error recovery: bounded retries (5x), exponential backoff, reconnecting UI
- Enriched /health endpoint: run_id, active_item, last_segment_seq, last_r2_pub, consecutive_failures, unhealthy detection
- Updated tests: HealthHandler (new fields + unhealthy), player error recovery test
- Updated TODO.md, BACKLOG.md, DONE.md, MEMORY.md
82 lines
3.0 KiB
HTML
82 lines
3.0 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Linear Void - 24/7 Synchronized Channel</title>
|
|
<link rel="stylesheet" href="/src/style.css" />
|
|
</head>
|
|
|
|
<body class="lv-body">
|
|
<main class="viewer-shell" aria-label="Live player and chat">
|
|
<header class="viewer-header">
|
|
<div class="viewer-brand-group">
|
|
<div class="brand-badge" aria-hidden="true">LV</div>
|
|
<div>
|
|
<h1 class="viewer-title">Linear Void</h1>
|
|
<p class="viewer-subtitle">24/7 Synchronized Channel • Live</p>
|
|
</div>
|
|
</div>
|
|
<button
|
|
id="chat-toggle"
|
|
type="button"
|
|
class="btn btn-secondary chat-toggle-btn"
|
|
onclick="toggleChatPanel()"
|
|
aria-controls="chat-panel"
|
|
aria-expanded="true"
|
|
>
|
|
Hide Chat
|
|
</button>
|
|
</header>
|
|
|
|
<section class="viewer-content" aria-label="Live stream and chat layout">
|
|
<section class="player-stage" aria-label="Video stage">
|
|
<video id="player" class="player" controls autoplay muted playsinline></video>
|
|
<div id="now-playing" class="now-playing-overlay" aria-live="polite">
|
|
<span id="np-title">Loading...</span>
|
|
<span id="np-next" class="np-next"></span>
|
|
</div>
|
|
</section>
|
|
|
|
<aside id="chat-panel" class="chat-sidebar" aria-label="Chat panel">
|
|
<header class="chat-header">
|
|
<div class="chat-room-title">
|
|
<span class="chat-live-marker" aria-hidden="true">●</span>
|
|
<span id="room-name">Main Channel</span>
|
|
</div>
|
|
<button type="button" class="btn btn-secondary" onclick="togglePrivateDM()">New DM</button>
|
|
</header>
|
|
|
|
<section id="messages" class="chat-messages" aria-live="polite" aria-label="Message stream"></section>
|
|
|
|
<section id="presence-panel" class="presence-panel" aria-label="Online users">
|
|
<div id="presence-list" class="presence-list"></div>
|
|
</section>
|
|
|
|
<div class="chat-input-area">
|
|
<div class="chat-input-row">
|
|
<label for="message-input" class="sr-only">Message input</label>
|
|
<input
|
|
id="message-input"
|
|
type="text"
|
|
placeholder="Type a message..."
|
|
class="input"
|
|
onkeydown="if (event.key === 'Enter') sendMessage();"
|
|
/>
|
|
<button type="button" class="btn btn-primary" onclick="sendMessage()">Send</button>
|
|
</div>
|
|
<p class="chat-note">Synced with video • Moderation active</p>
|
|
</div>
|
|
</aside>
|
|
</section>
|
|
</main>
|
|
|
|
<div id="reconnecting-overlay" class="reconnecting-overlay hidden" role="alert" aria-live="assertive">
|
|
<div class="reconnecting-spinner"></div>
|
|
<span id="reconnecting-text">Reconnecting...</span>
|
|
</div>
|
|
|
|
<script type="module" src="/src/main.ts"></script>
|
|
</body>
|
|
</html>
|