Files
ytplayer/frontend/index.html
2026-06-21 16:15:30 +08:00

222 lines
9.6 KiB
HTML
Executable File
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" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' https: data: asset: http://asset.localhost; media-src 'self' https: blob: asset: http://asset.localhost; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com data:; script-src 'self'; connect-src 'self' https: ipc: http://ipc.localhost;"
/>
<title>YT Player</title>
<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=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,700;12..96,800&family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="app">
<!-- Sidebar -->
<aside class="sidebar">
<div class="brand">
<span class="brand-mark"></span> YT Player
</div>
<nav class="nav">
<button class="nav-item active" data-view="search">🔍 Search</button>
<button class="nav-item" data-view="queue">▶ Queue <span id="navQueueCount" class="nav-badge hidden">0</span></button>
<button class="nav-item" data-view="history">🕘 History</button>
<button class="nav-item" data-view="saved">💾 Saved</button>
<button class="nav-item" data-view="downloads">⬇ Downloads <span id="navDlCount" class="nav-badge hidden">0</span></button>
<button class="nav-item" data-view="settings">⚙ Settings</button>
</nav>
<div class="pl-header">
<span>Playlists</span>
<button id="newPlaylistBtn" class="icon-btn" title="New playlist"></button>
</div>
<div id="playlistList" class="playlist-list"></div>
<div class="sidebar-footer">
<label class="switch">
<input type="checkbox" id="audioOnlyToggle" />
<span>Audio-only mode</span>
</label>
</div>
</aside>
<!-- Main -->
<main class="main">
<header class="topbar">
<form id="searchForm" class="search-form">
<input
id="searchInput"
type="text"
placeholder="Search YouTube… (no login, no ads)"
autocomplete="off"
/>
<button type="submit" class="search-btn">Search</button>
</form>
</header>
<div class="body">
<!-- Player pane -->
<section id="playerPane" class="player-pane empty">
<div class="player-stage">
<video id="video" playsinline></video>
<audio id="audio"></audio>
<div id="artFallback" class="art-fallback hidden">
<img id="artImg" alt="" />
</div>
<div id="playerPlaceholder" class="player-placeholder">
<div class="hero">
<div class="ph-logo"></div>
<h1 class="hero-title">YT Player</h1>
<p class="hero-tagline">Ad-free YouTube — no login, no tracking. Search, play, and keep playlists right on your device.</p>
<div class="hero-pills">
<span class="hero-pill">No ads</span>
<span class="hero-pill">No sign-in</span>
<span class="hero-pill">Offline playlists</span>
</div>
<div class="hero-suggests">
<span class="hero-suggests-label">Try</span>
<button class="chip" data-q="lofi hip hop radio">lofi beats</button>
<button class="chip" data-q="live news">live news</button>
<button class="chip" data-q="relaxing music">relaxing music</button>
<button class="chip" data-q="podcast highlights">podcasts</button>
</div>
</div>
</div>
<div id="bufferSpinner" class="spinner hidden"></div>
</div>
<div id="controls" class="controls hidden">
<div class="seek-row">
<span id="curTime" class="time">0:00</span>
<input id="seek" class="seek" type="range" min="0" max="1000" value="0" />
<span id="durTime" class="time">0:00</span>
</div>
<div class="btn-row">
<button id="prevBtn" class="ctrl" title="Previous"></button>
<button id="playBtn" class="ctrl play" title="Play/Pause"></button>
<button id="nextBtn" class="ctrl" title="Next"></button>
<div class="vol">
<button id="muteBtn" class="ctrl" title="Mute">🔊</button>
<input id="volume" type="range" min="0" max="1" step="0.01" value="1" />
</div>
<div class="spacer"></div>
<label class="sel">
Speed
<select id="speedSelect">
<option value="0.5">0.5×</option>
<option value="0.75">0.75×</option>
<option value="1" selected>1×</option>
<option value="1.25">1.25×</option>
<option value="1.5">1.5×</option>
<option value="2">2×</option>
</select>
</label>
<label class="sel">
Quality
<select id="qualitySelect"></select>
</label>
<button id="loopBtn" class="ctrl" title="Loop current video (L)">🔂</button>
<button id="repeatBtn" class="ctrl" title="Repeat list when finished (R)">🔁</button>
<button id="fsBtn" class="ctrl" title="Fullscreen"></button>
</div>
</div>
<div id="nowPlayingMeta" class="now-meta hidden">
<div class="np-text">
<div class="np-title" id="npTitle"></div>
<div class="np-channel" id="npChannel"></div>
</div>
<div class="np-actions">
<button id="queueBtn" class="np-btn" title="Add to the temporary queue"> Queue</button>
<button id="saveBtn" class="np-btn" title="Save this video for offline playback">⬇ Save</button>
<button id="addPlaylistBtn" class="np-btn" title="Add to a playlist"> Playlist</button>
</div>
</div>
<!-- Up next queue -->
<div id="upnext" class="upnext hidden">
<div class="upnext-header">
<span>Up next</span>
<span id="upnextCount" class="upnext-count">0</span>
</div>
<div id="upnextList" class="upnext-list"></div>
</div>
</section>
<!-- List pane -->
<section class="list-pane">
<div class="list-header">
<h2 id="listTitle">Search</h2>
<div id="listActions" class="list-actions"></div>
</div>
<div id="status" class="status hidden"></div>
<div id="cards" class="cards"></div>
</section>
</div>
</main>
</div>
<!-- Generic modal -->
<div id="modal" class="modal-backdrop hidden">
<div class="modal">
<h3 id="modalTitle"></h3>
<div id="modalBody"></div>
<div class="modal-actions" id="modalActions"></div>
</div>
</div>
<div id="toastContainer" class="toast-container"></div>
<!-- Mini now-playing bar -->
<div id="miniBar" class="mini-bar hidden">
<div class="mini-bar-inner">
<button id="miniPlayBtn" class="mini-btn"></button>
<div class="mini-info">
<div id="miniTitle" class="mini-title"></div>
<div id="miniChannel" class="mini-channel"></div>
</div>
<span id="miniTime" class="mini-time">0:00</span>
<button id="miniCloseBtn" class="mini-btn mini-close"></button>
</div>
<div id="miniProgress" class="mini-progress"><div id="miniProgressFill" class="mini-progress-fill"></div></div>
</div>
<!-- Keyboard shortcuts help overlay -->
<div id="shortcutHelp" class="shortcut-overlay hidden">
<div class="shortcut-panel">
<div class="shortcut-header">
<h2>Keyboard Shortcuts</h2>
<button id="shortcutClose" class="shortcut-close"></button>
</div>
<div class="shortcut-grid">
<div class="shortcut-row"><kbd>Space</kbd><span>Play / Pause</span></div>
<div class="shortcut-row"><kbd></kbd><kbd></kbd><span>Seek backward / forward 5s</span></div>
<div class="shortcut-row"><kbd></kbd><kbd></kbd><span>Volume up / down</span></div>
<div class="shortcut-row"><kbd>F</kbd><span>Toggle fullscreen</span></div>
<div class="shortcut-row"><kbd>M</kbd><span>Toggle mute</span></div>
<div class="shortcut-row"><kbd>L</kbd><span>Loop current video</span></div>
<div class="shortcut-row"><kbd>R</kbd><span>Repeat list when finished</span></div>
<div class="shortcut-row"><kbd>Q</kbd><span>Add current video to queue</span></div>
<div class="shortcut-row"><kbd>?</kbd><span>Show this help</span></div>
<div class="shortcut-row"><kbd>Esc</kbd><span>Close help</span></div>
</div>
</div>
</div>
<script src="async-guard.js"></script>
<script src="app.js"></script>
</body>
</html>