Add ad-free YouTube player built on zero-native
Desktop YouTube player with no login and no ads. Extracts direct video/audio streams with yt-dlp and plays them in a native window via zero-native (Zig + system WebView) for a tiny footprint vs Electron. - Dual-stream engine: muted video synced with separate audio track for high quality without ffmpeg muxing; progressive fallback - On-device playlists, watch history, audio-only mode - Full custom controls: seek, volume, speed, quality, fullscreen, queue - Zig bridge handlers spawn yt-dlp and return slimmed JSON to the web UI - Cinematic dark UI: Bricolage/Hanken/JetBrains Mono, vermilion accent
This commit is contained in:
147
frontend/index.html
Normal file
147
frontend/index.html
Normal file
@@ -0,0 +1,147 @@
|
||||
<!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:; media-src https: blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com data:; script-src 'self'; connect-src 'self' https:;"
|
||||
/>
|
||||
<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="history">🕘 History</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="ph-logo">▶</div>
|
||||
<p>Search and pick a video to start watching — ad-free.</p>
|
||||
</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="fsBtn" class="ctrl" title="Fullscreen">⛶</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="nowPlayingMeta" class="now-meta hidden">
|
||||
<div class="np-title" id="npTitle"></div>
|
||||
<div class="np-channel" id="npChannel"></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="toast" class="toast hidden"></div>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user