Files
ytplayer/frontend/index.html
Claude Worker 8c0776f97d feat: Add support for editing video
Add an Edit
2026-07-18 16:43:15 +00:00

319 lines
15 KiB
HTML
Executable File
Raw Permalink 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, viewport-fit=cover" />
<meta name="theme-color" content="#1a1311" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="YT Player" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' https: data: asset: http://asset.localhost blob:; 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 blob:; worker-src 'self';"
/>
<title>YT Player</title>
<link rel="manifest" href="/manifest.webmanifest" />
<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 id="sidebar" 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="pl-header smart-pl-header">
<span>Auto Playlists</span>
</div>
<div id="smartPlaylistList" class="playlist-list smart-pl-list"></div>
<div class="sidebar-footer">
<label class="switch">
<input type="checkbox" id="audioOnlyToggle" />
<span>Audio-only mode</span>
</label>
</div>
</aside>
<!-- Sidebar backdrop (mobile drawer) -->
<div id="sidebarBackdrop" class="sidebar-backdrop"></div>
<!-- Main -->
<main class="main">
<header class="topbar">
<button
id="sidebarToggle"
class="sidebar-toggle"
type="button"
aria-label="Toggle sidebar"
aria-controls="sidebar"
aria-expanded="false"
></button>
<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>
<div class="seek-wrap">
<input id="seek" class="seek" type="range" min="0" max="1000" value="0" />
<div id="abIndicator" class="ab-indicator hidden">
<span id="abALabel" class="ab-label">A: --</span>
<span style="flex:1"></span>
<span id="abBLabel" class="ab-label">B: --</span>
</div>
</div>
<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>
<button id="abABtn" class="ctrl ab-ctrl" title="Set A-B loop start [A]">A</button>
<button id="abBBtn" class="ctrl ab-ctrl" title="Set A-B loop end [B]">B</button>
<button id="abClearBtn" class="ctrl ab-ctrl hidden" title="Clear A-B loop"></button>
<button id="rememberPosBtn" class="ctrl" title="Remember playback position for this song (per playlist)">📍</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="sleepTimerBtn" class="ctrl" title="Sleep timer"></button>
<button id="shuffleBtn" class="ctrl" title="Shuffle upcoming tracks (S)">🔀</button>
<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="sleepStatus" class="sleep-status hidden">
<span id="sleepCountdown" class="sleep-countdown"></span>
<button id="sleepCancelBtn" class="sleep-cancel">✕ Cancel</button>
</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="editBtn" class="np-btn" title="Cut parts out and save a custom edited copy">✂ Edit &amp; download</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>
<!-- Related videos -->
<div id="relatedPanel" class="related-panel hidden">
<div class="related-header">
<span class="related-label">Related</span>
<button id="relatedToggleBtn" class="related-toggle-btn"></button>
</div>
<div id="relatedList" class="related-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="listFilterBar" class="list-filter-bar hidden">
<input id="listFilterInput" type="text" placeholder="Filter…" autocomplete="off" />
</div>
<div id="batchBar" class="batch-bar hidden">
<span id="batchCount" class="batch-count">0 selected</span>
<button id="batchQueueBtn" class="batch-btn"> Queue</button>
<button id="batchAddPlaylistBtn" class="batch-btn"> Playlist</button>
<button id="batchDeleteBtn" class="batch-btn batch-danger">Delete</button>
<button id="batchCancelBtn" class="batch-btn">Cancel</button>
</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>
<!-- YouTube-style bottom navigation bar (portrait PWA mode only) -->
<nav id="bottomNav" class="bottom-nav" aria-label="Main navigation">
<button class="bottom-nav-btn active" data-view="search" aria-label="Search">
<span class="bottom-nav-icon">🔍</span>
<span class="bottom-nav-label">Search</span>
</button>
<button class="bottom-nav-btn" data-view="queue" aria-label="Queue">
<span class="bottom-nav-icon"></span>
<span class="bottom-nav-label">Queue</span>
<span id="bnQueueCount" class="bottom-nav-badge hidden">0</span>
</button>
<button class="bottom-nav-btn" data-view="history" aria-label="History">
<span class="bottom-nav-icon">🕘</span>
<span class="bottom-nav-label">History</span>
</button>
<button class="bottom-nav-btn" data-view="saved" aria-label="Saved">
<span class="bottom-nav-icon">💾</span>
<span class="bottom-nav-label">Saved</span>
</button>
<button class="bottom-nav-btn" data-view="downloads" aria-label="Downloads">
<span class="bottom-nav-icon"></span>
<span class="bottom-nav-label">Downloads</span>
<span id="bnDlCount" class="bottom-nav-badge hidden">0</span>
</button>
<button class="bottom-nav-btn" data-view="settings" aria-label="Settings">
<span class="bottom-nav-icon"></span>
<span class="bottom-nav-label">More</span>
</button>
</nav>
<!-- 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>S</kbd><span>Shuffle upcoming tracks</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>A</kbd><span>Set A-B loop start point</span></div>
<div class="shortcut-row"><kbd>B</kbd><span>Set A-B loop end point</span></div>
<div class="shortcut-row"><kbd>?</kbd><span>Show this help</span></div>
<div class="shortcut-row"><kbd>Esc</kbd><span>Close overlay / cancel selection</span></div>
</div>
</div>
</div>
<script src="fingerprint.js"></script>
<script src="opfs.js"></script>
<script src="video-edit.js"></script>
<script src="async-guard.js"></script>
<script src="sw-update.js"></script>
<script src="app.js"></script>
</body>
</html>