Files
video-sync/public/index.html
2026-03-02 21:28:24 +11:00

202 lines
9.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VideoSync — Watch Together</title>
<meta name="description"
content="Sync video playback with friends in real-time. Load a local file, create a room, and watch together.">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<!-- ===== LOBBY VIEW ===== -->
<div id="lobby" class="view active">
<div class="lobby-container">
<div class="lobby-brand">
<div class="logo-icon">
<svg viewBox="0 0 48 48" width="56" height="56" fill="none">
<rect x="4" y="8" width="40" height="32" rx="4" fill="#3ea6ff" opacity="0.15" />
<rect x="4" y="8" width="40" height="32" rx="4" stroke="#3ea6ff" stroke-width="2.5" />
<polygon points="20,16 34,24 20,32" fill="#3ea6ff" />
</svg>
</div>
<h1>VideoSync</h1>
<p class="tagline">Watch together, anywhere</p>
</div>
<div class="lobby-card">
<div class="input-group">
<label for="username-input">Your Name</label>
<input type="text" id="username-input" placeholder="Enter a display name" maxlength="20" autocomplete="off">
</div>
<div class="lobby-divider"><span>then</span></div>
<div class="lobby-actions">
<div class="action-panel create-panel">
<h3>Create a Room</h3>
<p class="action-desc">Select a video file to start watching</p>
<label class="file-select-btn" id="create-file-label">
<input type="file" id="create-file-input" accept="video/*" hidden>
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm4 18H6V4h7v5h5v11z" />
</svg>
<span>Choose Video File</span>
</label>
<div id="create-file-info" class="file-info hidden"></div>
<button id="create-room-btn" class="btn-primary" disabled>Create Room</button>
</div>
<div class="lobby-or"><span>or</span></div>
<div class="action-panel join-panel">
<h3>Join a Room</h3>
<p class="action-desc">Enter a room code from a friend</p>
<input type="text" id="room-code-input" placeholder="e.g. ABC123" maxlength="6" autocomplete="off"
style="text-transform: uppercase">
<button id="join-room-btn" class="btn-secondary" disabled>Join Room</button>
</div>
</div>
</div>
<div id="lobby-error" class="error-toast hidden"></div>
</div>
</div>
<!-- ===== FILE CHECK MODAL ===== -->
<div id="file-check-modal" class="modal hidden">
<div class="modal-backdrop"></div>
<div class="modal-content">
<h2>File Required</h2>
<p>To join this room, you need the following file:</p>
<div id="required-file-info" class="file-info-card"></div>
<p class="modal-instruction">Select your copy of this file:</p>
<label class="file-select-btn" id="join-file-label">
<input type="file" id="join-file-input" accept="video/*" hidden>
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
<path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm4 18H6V4h7v5h5v11z" />
</svg>
<span>Choose File</span>
</label>
<div id="join-file-status" class="file-match-status hidden"></div>
<div class="modal-actions">
<button id="modal-cancel-btn" class="btn-secondary">Cancel</button>
<button id="modal-confirm-btn" class="btn-primary" disabled>Join Room</button>
</div>
</div>
</div>
<!-- ===== ROOM VIEW ===== -->
<div id="room" class="view">
<div class="room-layout">
<!-- Connection Status Banner -->
<div id="connection-status" class="connection-status hidden"></div>
<!-- Video Area -->
<div class="video-area">
<div class="video-topbar">
<div class="topbar-left">
<span class="room-badge">Room: <strong id="room-code-display"></strong></span>
<button id="copy-code-btn" class="icon-btn" title="Copy room code">
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
<path
d="M16 1H4a2 2 0 00-2 2v14h2V3h12V1zm3 4H8a2 2 0 00-2 2v14a2 2 0 002 2h11a2 2 0 002-2V7a2 2 0 00-2-2zm0 16H8V7h11v14z" />
</svg>
</button>
</div>
<div class="topbar-right">
<div class="file-badge" id="room-file-badge"></div>
<button id="leave-room-btn" class="btn-leave">Leave</button>
</div>
</div>
<div class="video-wrapper" id="video-wrapper">
<video id="video-player" preload="auto"></video>
<div class="video-overlay" id="video-overlay">
<div class="big-play-btn" id="big-play-btn">
<svg viewBox="0 0 68 68" width="68" height="68" fill="none">
<circle cx="34" cy="34" r="33" fill="rgba(0,0,0,0.6)" stroke="white" stroke-width="2" />
<polygon points="27,20 50,34 27,48" fill="white" />
</svg>
</div>
</div>
<div class="controls-bar" id="controls-bar">
<div class="seek-bar-container">
<input type="range" id="seek-bar" class="seek-bar" value="0" min="0" max="1000" step="1">
</div>
<div class="controls-row">
<div class="controls-left">
<button id="play-pause-btn" class="ctrl-btn" title="Play/Pause">
<svg id="play-icon" viewBox="0 0 24 24" width="28" height="28" fill="white">
<polygon points="6,4 20,12 6,20" />
</svg>
<svg id="pause-icon" viewBox="0 0 24 24" width="28" height="28" fill="white" class="hidden">
<rect x="5" y="4" width="4" height="16" />
<rect x="15" y="4" width="4" height="16" />
</svg>
</button>
<div class="volume-control">
<button id="volume-btn" class="ctrl-btn" title="Mute/Unmute">
<svg id="vol-on-icon" viewBox="0 0 24 24" width="22" height="22" fill="white">
<path
d="M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0014 8.5v7a4.49 4.49 0 002.5-3.5zM14 3.23v2.06a6.51 6.51 0 010 13.42v2.06A8.51 8.51 0 0014 3.23z" />
</svg>
<svg id="vol-off-icon" viewBox="0 0 24 24" width="22" height="22" fill="white" class="hidden">
<path
d="M16.5 12A4.5 4.5 0 0014 8.5v2.09l2.44 2.44c.03-.17.06-.34.06-.53zm2.5 0a6.5 6.5 0 01-.55 2.63l1.52 1.52A8.46 8.46 0 0021 12a8.51 8.51 0 00-7-8.77v2.06A6.51 6.51 0 0119 12zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.51-1.42.93-2.25 1.18v2.06a8.46 8.46 0 003.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z" />
</svg>
</button>
<input type="range" id="volume-slider" class="volume-slider" min="0" max="100" value="100">
</div>
<span class="time-display">
<span id="current-time">0:00</span> / <span id="duration">0:00</span>
</span>
</div>
<div class="controls-right">
<button id="fullscreen-btn" class="ctrl-btn" title="Fullscreen">
<svg viewBox="0 0 24 24" width="22" height="22" fill="white">
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Chat Sidebar -->
<div class="chat-area">
<div class="chat-header">
<h3>Live Chat</h3>
<div class="users-indicator">
<span class="online-dot"></span>
<span id="user-count">0</span> watching
</div>
</div>
<div class="users-list" id="users-list"></div>
<div class="chat-messages" id="chat-messages">
<div class="chat-welcome" id="chat-welcome">
<p>Welcome to the room! 👋</p>
</div>
</div>
<div class="chat-input-area">
<input type="text" id="chat-input" placeholder="Send a message..." autocomplete="off" maxlength="500">
<button id="chat-send-btn" class="chat-send-btn" title="Send">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
</svg>
</button>
</div>
</div>
</div>
</div>
<script src="/app.js"></script>
</body>
</html>