276 lines
15 KiB
HTML
276 lines
15 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<link rel="icon" href="/public/assets/waifuboards.ico" type="image/x-icon" />
|
||
<title>Watch Party - WaifuBoard</title>
|
||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||
<script type="module">
|
||
import JASSUB from 'https://cdn.jsdelivr.net/npm/jassub@1.8.8/dist/jassub.es.js';
|
||
window.JASSUB = JASSUB;
|
||
</script>
|
||
<link rel="stylesheet" href="/views/css/globals.css" />
|
||
<link rel="stylesheet" href="/views/css/anime/player.css" />
|
||
<link rel="stylesheet" href="/views/css/room.css" />
|
||
</head>
|
||
<body>
|
||
|
||
<div id="room-view">
|
||
<div class="room-layout" id="room-layout">
|
||
<div class="video-area">
|
||
<div class="room-header">
|
||
<div class="header-left">
|
||
<div class="room-info">
|
||
<h2 id="room-name">Loading...</h2>
|
||
<div id="now-playing-info" class="np-fade">
|
||
<span id="np-title" class="np-title">Waiting selection...</span>
|
||
<span class="np-sep">•</span>
|
||
<span id="np-episode" class="np-badge">Episode --</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="header-center" id="host-controls" style="display: none;">
|
||
<div class="quick-controls-group">
|
||
<div class="sd-toggle small" id="room-sd-toggle" data-state="sub">
|
||
<div class="sd-bg"></div>
|
||
<div class="sd-option active" data-val="sub">Sub</div>
|
||
<div class="sd-option" data-val="dub">Dub</div>
|
||
</div>
|
||
|
||
<select id="room-ext-select" class="glass-select-sm" title="Extension">
|
||
<option value="" disabled selected>Ext</option>
|
||
</select>
|
||
|
||
<select id="room-server-select" class="glass-select-sm" title="Server">
|
||
<option value="" disabled selected>Server</option>
|
||
</select>
|
||
<button
|
||
id="copy-invite-btn"
|
||
class="btn-icon-glass"
|
||
title="Copy invite link"
|
||
style="display:none;"
|
||
>
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M10 13a5 5 0 0 0 7.07 0l1.41-1.41a5 5 0 0 0-7.07-7.07L10 5"/>
|
||
<path d="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="header-right">
|
||
<div class="viewers-pill">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
|
||
<span id="room-viewers">0</span>
|
||
</div>
|
||
|
||
<button id="select-anime-btn" class="btn-glass-primary" style="display: none;">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg>
|
||
<span>Change Anime</span>
|
||
</button>
|
||
|
||
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="player-wrapper">
|
||
<div class="player-container show-cursor">
|
||
<div class="video-frame">
|
||
<canvas id="subtitles-canvas"></canvas>
|
||
<div id="video-toast-container" class="video-toast-container"></div>
|
||
<video id="player" crossorigin="anonymous" playsinline></video>
|
||
|
||
<div id="player-loading" class="player-loading-overlay">
|
||
<div class="spinner"></div>
|
||
<p id="player-loading-text">Waiting for host...</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="custom-controls">
|
||
<div class="progress-container">
|
||
<div class="progress-buffer"></div>
|
||
<div class="progress-played"></div>
|
||
<div class="progress-handle"></div>
|
||
</div>
|
||
|
||
<div class="controls-row">
|
||
<div class="controls-left">
|
||
<button class="control-btn play-pause" id="play-pause-btn">
|
||
<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
|
||
</button>
|
||
<button class="control-btn volume" id="volume-btn">
|
||
<svg viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/></svg>
|
||
</button>
|
||
<input type="range" class="volume-slider" id="volume-slider" min="0" max="100" value="100">
|
||
<span class="time-display" id="time-display">0:00 / 0:00</span>
|
||
</div>
|
||
|
||
<div class="controls-center"></div>
|
||
|
||
<div class="controls-right">
|
||
<button class="control-btn settings" id="settings-btn">
|
||
<svg viewBox="0 0 24 24"><path d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"/></svg>
|
||
</button>
|
||
<div class="settings-panel" id="settings-panel"></div>
|
||
<button class="control-btn fullscreen" id="fullscreen-btn">
|
||
<svg viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="chat-sidebar">
|
||
<div class="sidebar-tabs">
|
||
<button class="tab-btn active" id="tab-chat-btn">Chat</button>
|
||
<button class="tab-btn" id="tab-queue-btn">Queue <span id="queue-count" class="badge">0</span></button>
|
||
</div>
|
||
|
||
<div id="tab-content-chat" class="tab-content active" style="display: flex; flex-direction: column; height: 100%;">
|
||
<div style="padding: 10px; border-bottom: 1px solid rgba(255,255,255,0.1); display: flex; justify-content: flex-end;">
|
||
<button id="toggle-users-btn" class="btn-icon-glass" title="Toggle User List" style="width: 32px; height: 32px;">
|
||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||
<circle cx="9" cy="7" r="4"></circle>
|
||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
<div class="users-list" id="users-list" style="display: none;"></div>
|
||
<div class="chat-messages" id="chat-messages"></div>
|
||
<form class="chat-input" id="chat-form" autocomplete="off">
|
||
<input type="text" id="chat-input" placeholder="Type a message..." maxlength="500" autocomplete="off" />
|
||
<button type="submit">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
<div id="tab-content-queue" class="tab-content" style="display: none;">
|
||
<div class="queue-list" id="queue-list">
|
||
<div class="queue-empty">Queue is empty</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-overlay" id="join-room-modal">
|
||
<div class="modal-content">
|
||
<div id="join-host-info" class="join-host-info" style="display: none;">
|
||
<div class="join-avatar-container">
|
||
<img id="join-host-avatar" src="" alt="Host">
|
||
</div>
|
||
<p id="join-host-text" class="join-text"></p>
|
||
</div>
|
||
|
||
<h2 class="modal-title" style="text-align: center;">Join Room</h2>
|
||
|
||
<form id="join-room-form">
|
||
<div class="form-group">
|
||
<label>Your Name</label>
|
||
<input type="text" id="guest-name-input" placeholder="Enter your name" maxlength="30" />
|
||
</div>
|
||
|
||
<div class="form-group" id="password-group" style="display: none;">
|
||
<label>Room Password</label>
|
||
<input type="password" id="join-password-input" placeholder="Enter password" maxlength="50" />
|
||
</div>
|
||
|
||
<div class="form-actions" style="justify-content: center;">
|
||
<button type="button" class="btn-cancel" id="cancel-join-btn">Go Back</button>
|
||
<button type="submit" class="btn-confirm">Join Party</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-overlay" id="anime-search-modal">
|
||
<div class="modal-content anime-search-content">
|
||
<button class="modal-close" id="close-search-modal">✕</button>
|
||
|
||
<div id="step-search">
|
||
<h2 class="modal-title">Select Anime</h2>
|
||
<div class="search-bar">
|
||
<input type="text" id="anime-search-input" placeholder="Search anime..." />
|
||
<button id="anime-search-btn">Search</button>
|
||
</div>
|
||
<div id="anime-results" class="anime-results"></div>
|
||
</div>
|
||
|
||
<div id="step-config" style="display: none;">
|
||
<div class="modal-header-row">
|
||
<button class="btn-icon-small" id="back-to-search" title="Back">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||
</button>
|
||
<h2 class="modal-title" id="selected-anime-title">Configure Stream</h2>
|
||
</div>
|
||
|
||
<div class="config-layout">
|
||
<div class="config-sidebar">
|
||
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
||
|
||
<div style="width: 100%">
|
||
<div class="cfg-section-title">Episode</div>
|
||
<div class="ep-control">
|
||
<button class="ep-btn" id="ep-dec">−</button>
|
||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||
<button class="ep-btn" id="ep-inc">+</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="config-main">
|
||
<div style="display:flex; justify-content:space-between; align-items:end; gap:10px; flex-wrap:wrap;">
|
||
<div>
|
||
<div class="cfg-section-title">Source</div>
|
||
<div class="chips-grid" id="ext-chips-container">
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="cfg-section-title">Audio</div>
|
||
<div class="cat-toggle" id="modal-sd-toggle">
|
||
<div class="cat-opt active" data-val="sub">Subtitles</div>
|
||
<div class="cat-opt" data-val="dub">Dubbed</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div class="cfg-section-title">Select Server</div>
|
||
<div class="chips-grid" id="server-chips-container">
|
||
<div class="grid-loader">Select a source first</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="config-error" style="color:#ff6b6b; font-size:0.9rem; display:none; background:rgba(255,0,0,0.1); padding:10px; border-radius:8px;"></div>
|
||
|
||
<div class="form-actions" style="margin-top:auto; display:flex; gap:10px;">
|
||
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;" disabled>
|
||
+ Add to Queue
|
||
</button>
|
||
|
||
<button id="btn-launch-stream" class="btn-confirm" style="flex:1;" disabled>
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
||
Play Now
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/src/scripts/utils/auth-utils.js"></script>
|
||
<script src="/src/scripts/utils/search-manager.js"></script>
|
||
<script src="/src/scripts/anime/subtitle-renderer.js"></script>
|
||
<script src="/src/scripts/anime/player.js"></script>
|
||
<script src="/src/scripts/room.js"></script>
|
||
|
||
</body>
|
||
</html> |