679 lines
46 KiB
HTML
Executable File
679 lines
46 KiB
HTML
Executable File
<!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" />
|
||
<!-- Build this page was served from — the server stamps it at request time;
|
||
app.js compares it with /api/version before offering an update. -->
|
||
<meta name="ytp-build" content="__BUILD_TAG__" />
|
||
<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 'self' https://fonts.gstatic.com data:; script-src 'self' 'wasm-unsafe-eval' https://esm.sh; connect-src 'self' https: ipc: http://ipc.localhost blob:; worker-src 'self';"
|
||
/>
|
||
<title>YT Player</title>
|
||
<link rel="manifest" href="/manifest.webmanifest" />
|
||
<link rel="preload" href="fonts/BricolageGrotesque-latin.woff2" as="font" type="font/woff2" crossorigin />
|
||
<link rel="preload" href="fonts/HankenGrotesk-latin.woff2" as="font" type="font/woff2" crossorigin />
|
||
<link rel="stylesheet" href="fonts/fonts.css" />
|
||
<link rel="preconnect" href="https://i.ytimg.com" crossorigin />
|
||
<link rel="stylesheet" href="styles.css" />
|
||
<link rel="stylesheet" href="service-ui.css" />
|
||
<link rel="stylesheet" href="refresh.css" />
|
||
<link rel="stylesheet" href="theme-glass.css" />
|
||
<link rel="stylesheet" href="theme-classic.css" />
|
||
<link rel="stylesheet" href="recommendations.css" />
|
||
<link rel="stylesheet" href="settings-sections.css" />
|
||
<link rel="stylesheet" href="lower-third.css" />
|
||
<link rel="stylesheet" href="car-mode.css" />
|
||
<link rel="stylesheet" href="offline-pages.css" />
|
||
<link rel="stylesheet" href="saved-page.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"><span class="legacy-nav-icon" aria-hidden="true">🔍</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5"/></svg> Search</button>
|
||
<button class="nav-item" data-view="queue"><span class="legacy-nav-icon" aria-hidden="true">▶</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="m8 5 11 7-11 7z"/></svg> Queue <span id="navQueueCount" class="nav-badge hidden">0</span></button>
|
||
<button class="nav-item" data-view="history"><span class="legacy-nav-icon" aria-hidden="true">🕘</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg> History</button>
|
||
<button class="nav-item" data-view="saved"><span class="legacy-nav-icon" aria-hidden="true">💾</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3h12l4 4v14H3V3h2zm2 0v6h10V3M7 21v-8h10v8"/></svg> Saved</button>
|
||
<button class="nav-item" data-view="downloads"><span class="legacy-nav-icon" aria-hidden="true">⬇</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5"/></svg> Downloads <span id="navDlCount" class="nav-badge hidden">0</span></button>
|
||
<button class="nav-item" data-view="library"><span class="legacy-nav-icon" aria-hidden="true">📁</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h7l2 3h9v11H3z"/></svg> Library</button>
|
||
<button class="nav-item" data-view="notes"><span class="legacy-nav-icon" aria-hidden="true">📝</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M14 3H4v18h16V11M9 15l2-5 7-7 3 3-7 7z"/></svg> Notes</button>
|
||
<button class="nav-item" data-view="stats"><span class="legacy-nav-icon" aria-hidden="true">📊</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M4 21V11h4v10M10 21V3h4v18M16 21V7h4v14"/></svg> Stats</button>
|
||
<button class="nav-item" data-view="settings"><span class="legacy-nav-icon" aria-hidden="true">⚙</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="m9 3-1 3-3 1-2 3 2 2-1 3 2 3 3-1 2 3h3l1-3 3-1 2-3-2-2 1-3-2-3-3 1-2-3z"/></svg> Settings</button>
|
||
<button class="nav-item" id="navPartyBtn" type="button" title="Watch together with friends"><span class="legacy-nav-icon" aria-hidden="true">🎉</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="m4 20 4-13 9 9zM14 3v4m4 1 3-2m-2 5 3 2"/></svg> <span class="nav-label">Watch party</span></button>
|
||
<button class="nav-item" id="navRemoteBtn" type="button" title="Control this screen from a phone, or use this phone as a remote"><span class="legacy-nav-icon" aria-hidden="true">📱</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><rect x="6" y="2" width="12" height="20" rx="2"/><path d="M10 18h4"/></svg> Remote</button>
|
||
</nav>
|
||
|
||
<div class="pl-header">
|
||
<span>Playlists</span>
|
||
<span id="syncSpinner" class="sync-spinner hidden" title="Syncing with your profile…" aria-live="polite" role="status"></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">
|
||
<!-- Shown only when an online profile is linked (see updateProfileStatus) -->
|
||
<div id="sidebarProfile" class="sb-profile hidden">
|
||
<div class="sb-profile-id">
|
||
<span class="sb-profile-mark">👤</span>
|
||
<span id="sidebarProfileName" class="sb-profile-name"></span>
|
||
</div>
|
||
<div class="sb-profile-actions">
|
||
<button id="sidebarProfileCopy" class="sb-profile-btn" type="button" title="Copy profile link">⧉ Copy link</button>
|
||
<button id="sidebarProfileShare" class="sb-profile-btn" type="button" title="Share profile link">↗ Share</button>
|
||
</div>
|
||
</div>
|
||
<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"
|
||
aria-label="Search YouTube"
|
||
type="text"
|
||
placeholder="Search YouTube… (no login, no ads)"
|
||
autocomplete="off"
|
||
/>
|
||
<!-- Shown after "Later" on the update dialog: one tap updates -->
|
||
<button id="updateIconBtn" type="button" class="update-icon-btn hidden" title="Update available — tap to update" aria-label="Update available — tap to update">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 12a8 8 0 1 1-2.34-5.66M20 4v4.5h-4.5"/><path d="M12 8v5M9.5 11l2.5 2.5 2.5-2.5"/></svg>
|
||
</button>
|
||
<button type="submit" class="search-btn">Search</button>
|
||
<div id="searchRecent" class="search-recent hidden" role="listbox" aria-label="Recent searches"></div>
|
||
</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>
|
||
<!-- Lyrics shown in place of the video (watch party / sing-along) -->
|
||
<div id="stageLyrics" class="stage-lyrics hidden">
|
||
<div id="stageLyricsList" class="sl-list"></div>
|
||
<div class="stage-lyrics-bar">
|
||
<button id="stageLyricsAuto" class="np-btn" type="button" aria-pressed="true">⇅ Auto-scroll on</button>
|
||
<button id="stageLyricsOff" class="np-btn" type="button">📺 Show video</button>
|
||
</div>
|
||
</div>
|
||
<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 needed. Search, play, and discover videos shaped by what you listen to.</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>
|
||
<!-- Replaces the hero once this device has playlists (renderHomePlaylists) -->
|
||
<div id="homePlaylists" class="home-pl hidden">
|
||
<div class="home-pl-head">
|
||
<span class="home-pl-title">Your playlists</span>
|
||
<button id="homePlNewBtn" class="home-pl-new" type="button">+ New</button>
|
||
</div>
|
||
<div id="homePlGrid" class="home-pl-grid"></div>
|
||
</div>
|
||
<section id="homeRecommendations" class="recommendations hidden" aria-label="Recommended videos"></section>
|
||
</div>
|
||
<div id="bufferSpinner" class="spinner hidden"></div>
|
||
<!-- Touch gesture feedback (Gestures in app.js) -->
|
||
<div id="gestureHud" class="gesture-hud hidden"></div>
|
||
<div id="stgOverlay" class="stg-overlay" aria-hidden="true">
|
||
<div class="stg-top">
|
||
<span class="stg-chip"><i class="stg-dot"></i><b id="stgQuality">Auto</b></span>
|
||
<div class="stg-seg" role="group" aria-label="Video or audio only">
|
||
<button id="stgVideo" type="button">Video</button>
|
||
<button id="stgAudio" type="button">Audio-only</button>
|
||
</div>
|
||
</div>
|
||
<div class="stg-mid">
|
||
<button id="stgBack" class="stg-skip" type="button" aria-label="Back 10 seconds"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12a8 8 0 1 0 3-6.2M4 4v4.5h4.5"/><text x="12" y="15.5" text-anchor="middle" font-size="7.5" font-weight="700" fill="currentColor" stroke="none">10</text></svg></button>
|
||
<button id="stgPlay" class="stg-play" type="button" aria-label="Play or pause"><svg viewBox="0 0 24 24" aria-hidden="true" class="fill"><path d="M9 5.5v13l10-6.5z"/></svg></button>
|
||
<button id="stgFwd" class="stg-skip" type="button" aria-label="Forward 10 seconds"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 12a8 8 0 1 1-3-6.2M20 4v4.5h-4.5"/><text x="12" y="15.5" text-anchor="middle" font-size="7.5" font-weight="700" fill="currentColor" stroke="none">10</text></svg></button>
|
||
</div>
|
||
<div class="stg-bot"><span class="stg-chip"><b id="stgZoom">Pinch 1.0×</b></span><span class="stg-chip"><i class="stg-dot"></i><b id="stgBuf">Buffered</b></span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="controls" class="controls hidden">
|
||
<div class="seek-row">
|
||
<span id="curTime" class="time">0:00</span>
|
||
<div class="seek-wrap">
|
||
<!-- Audio-only: loudness waveform drawn behind the transparent range input -->
|
||
<canvas id="waveCanvas" class="wave-canvas" aria-hidden="true"></canvas>
|
||
<span id="waveTip" class="wave-tip hidden"></span>
|
||
<input id="seek" aria-label="Playback position" class="seek" type="range" min="0" max="1000" value="0" />
|
||
<div id="seekMarkers" class="seek-markers" aria-hidden="true"></div>
|
||
<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>
|
||
<button id="chBadge" type="button" class="ch-badge" hidden title="Chapters — tap to jump" aria-haspopup="dialog"></button>
|
||
<button id="fsMini" class="fs-mini" type="button" title="Fullscreen" aria-label="Fullscreen"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></svg></button>
|
||
</div>
|
||
<button id="waveLoud" class="wave-loud hidden" type="button">⚡ Jump to the loudest part</button>
|
||
<div class="deck-main">
|
||
<button id="prevBtn" class="ctrl" title="Previous" aria-label="Previous"><svg viewBox="0 0 24 24" aria-hidden="true" class="fill"><rect x="5" y="5" width="2.8" height="14" rx="1.2"/><path d="M19 5.5v13L9.5 12z"/></svg></button>
|
||
<button id="back10Btn" class="ctrl skip10" type="button" title="Back 10 seconds" aria-label="Back 10 seconds"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12a8 8 0 1 0 3-6.2M4 4v4.5h4.5"/><text x="12" y="15.5" text-anchor="middle" font-size="7.5" font-weight="700" fill="currentColor" stroke="none">10</text></svg></button>
|
||
<button id="playBtn" class="ctrl play" title="Play/Pause" aria-label="Play or pause"><svg viewBox="0 0 24 24" aria-hidden="true" class="fill"><path d="M9 5.5v13l10-6.5z"/></svg></button>
|
||
<button id="fwd10Btn" class="ctrl skip10" type="button" title="Forward 10 seconds" aria-label="Forward 10 seconds"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 12a8 8 0 1 1-3-6.2M20 4v4.5h-4.5"/><text x="12" y="15.5" text-anchor="middle" font-size="7.5" font-weight="700" fill="currentColor" stroke="none">10</text></svg></button>
|
||
<button id="nextBtn" class="ctrl" title="Next" aria-label="Next"><svg viewBox="0 0 24 24" aria-hidden="true" class="fill"><rect x="16.2" y="5" width="2.8" height="14" rx="1.2"/><path d="M5 5.5v13l9.5-6.5z"/></svg></button>
|
||
<div id="dkModeHost" hidden><button id="shuffleBtn" class="ctrl" title="Shuffle upcoming tracks (S)" aria-label="Shuffle"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M16 3h5v5M4 20 21 3M21 16v5h-5M15 15l6 6M4 4l5 5"/></svg></button><button id="repeatBtn" class="ctrl" title="Repeat list when finished (R)" aria-label="Repeat list"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M17 2l4 4-4 4M3 12v-2a4 4 0 0 1 4-4h14M7 22l-4-4 4-4M21 12v2a4 4 0 0 1-4 4H3"/></svg></button><button id="loopBtn" class="ctrl" title="Loop this video (L)"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M17 2l4 4-4 4M3 12v-2a4 4 0 0 1 4-4h14M7 22l-4-4 4-4M21 12v2a4 4 0 0 1-4 4H3M11 10.2l1.6-.9v5.4"/></svg></button></div>
|
||
</div>
|
||
<div class="deck-vol vol">
|
||
<div class="vol-pod">
|
||
<input id="volume" type="range" min="0" max="1" step="0.01" value="1" aria-label="Volume" />
|
||
<button id="muteBtn" class="ctrl" title="Mute" aria-label="Mute"></button>
|
||
<span id="volPct" class="vol-pct">100%</span>
|
||
</div>
|
||
<button id="modeBtn" class="ctrl" type="button" title="Playback mode" aria-label="Playback mode"></button>
|
||
</div>
|
||
<div class="dk-quick">
|
||
<button id="fsBtn" class="ctrl" title="Fullscreen" aria-label="Fullscreen"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></svg><span class="fs-label">Fullscreen</span></button>
|
||
<div class="ab-group">
|
||
<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>
|
||
</div>
|
||
<button id="ctlMoreBtn" class="dk-more" type="button" aria-haspopup="dialog" title="More controls" aria-label="More controls"><span><svg class="mc-tune" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h9M17 7h3M4 17h3M11 17h9M13 4v6M7 14v6"/></svg><em class="mc-dots">⋯</em><i class="mc-label"> More<i class="mc-rest"> controls</i></i></span><small>Speed · Quality · A-B · Sleep · EQ</small></button>
|
||
</div>
|
||
<div id="deckChips" class="deck-chips" aria-label="Quick settings"></div>
|
||
<div id="dkMoreHost" hidden>
|
||
<button id="rememberPosBtn" class="ctrl" title="Remember playback position for this song (per playlist)">📍</button>
|
||
<button id="sleepTimerBtn" class="ctrl" title="Sleep timer">⏱</button>
|
||
<button id="transitionBtn" class="ctrl" title="Between songs in a playlist: Normal — click to change">↦</button>
|
||
<button id="eqBtn" class="ctrl" title="Equalizer (E)">🎚</button>
|
||
<button id="pipBtn" class="ctrl hidden" title="Picture-in-picture (I)">⧉</button>
|
||
<label class="sel dk-sel">
|
||
<span>Speed</span>
|
||
<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 dk-sel">
|
||
<span>Quality</span>
|
||
<select id="qualitySelect"></select>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="sleepStatus" class="sleep-status hidden">
|
||
<span class="sl-ic" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M20 14.5A8.5 8.5 0 0 1 9.5 4a8.5 8.5 0 1 0 10.5 10.5z"/></svg></span>
|
||
<span class="sl-txt"><span id="sleepCountdown" class="sleep-countdown"></span><small class="sl-sub">Audio fades out smoothly</small></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 class="np-avail hidden" id="npAvail" aria-live="polite"></div>
|
||
</div>
|
||
<div class="np-actions">
|
||
<button id="addBtn" class="np-btn" title="Add to the queue or to a playlist"><svg class="ti" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h10M4 11h10M4 16h6M17 13v8M13 17h8"/></svg><span class="tl">Add</span></button>
|
||
<button id="saveBtn" class="np-btn" title="Save this video for offline playback"><svg class="ti" viewBox="0 0 24 24" aria-hidden="true"><path class="i-save" d="M12 4v11M7 11l5 5 5-5M5 20h14"/><path class="i-done" d="M20 12a8 8 0 1 1-16 0 8 8 0 0 1 16 0zM8.5 12.2l2.4 2.4 4.6-4.8"/></svg><span class="tl">Save</span></button>
|
||
<button id="exportBtn" class="np-btn" title="Save this video to your gallery, Files app or Downloads folder — works while streaming too"><svg class="ti" viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="3"/><circle cx="9" cy="10.5" r="1.6"/><path d="M4 17l5-4.5 4 3.5 3-2.5 4 3.5"/></svg><span class="tl">Gallery</span></button>
|
||
<button id="serviceBtn" class="np-btn" title="Service mode — set-list view with the screen kept awake"><svg class="ti" viewBox="0 0 24 24" aria-hidden="true"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5.5 11a6.5 6.5 0 0 0 13 0M12 17.5V21"/></svg><span class="tl">Service</span></button>
|
||
<div id="npMoreHost" class="np-more-host" hidden>
|
||
<button id="queueBtn" class="np-btn" title="Add to the temporary queue">+ Queue</button>
|
||
<button id="addPlaylistBtn" class="np-btn" title="Add to a playlist">+ Playlist</button>
|
||
<button id="editBtn" class="np-btn" title="Cut parts out and save a custom edited copy">✂ Edit & download</button>
|
||
<button id="shareMomentBtn" class="np-btn" title="Share a link or GIF of this exact moment">🔗 Share moment</button>
|
||
<button id="externalBtn" class="np-btn" title="Play this in VLC, MX Player or another app">📺 Open in…</button>
|
||
<button id="brokenBtn" class="np-btn" title="Video won't play or looks broken? Re-download the server's copy" hidden>⚠ Broken</button>
|
||
</div>
|
||
<button id="moreBtn" class="np-more" type="button" aria-haspopup="dialog"><span>⋯ More actions</span><small>Edit & download · Share moment · Open in… · Report broken</small></button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Lyrics · chapters · bookmarks (shared notes, notes.js on the server) -->
|
||
<div id="notesPanel" class="notes-panel hidden">
|
||
<div class="notes-tabs" role="tablist">
|
||
<button class="notes-tab active" data-tab="lyrics" role="tab" type="button">Lyrics</button>
|
||
<button class="notes-tab" data-tab="markers" role="tab" type="button">Chapters & bookmarks <span id="markersCount" class="notes-count"></span></button>
|
||
<button class="notes-tab" data-tab="search" role="tab" type="button">🔎 Search</button>
|
||
<span id="notesStatus" class="notes-status"></span>
|
||
<button id="notesFoldBtn" class="notes-fold" type="button" aria-expanded="true" title="Fold this card">▾</button>
|
||
</div>
|
||
|
||
<div id="lyricsPane" class="notes-pane">
|
||
<div class="lyrics-toolbar">
|
||
<button id="lyricsEditBtn" class="np-btn" type="button" title="Edit the shared lyrics">✎ Edit</button>
|
||
<button id="lyricsSyncBtn" class="np-btn" type="button" title="Tap along to time each line">⏱ Tap-sync</button>
|
||
<button id="lyricsHistoryBtn" class="np-btn" type="button" title="Earlier versions">🕘 History</button>
|
||
<span class="notes-spacer"></span>
|
||
<span class="lyr-offset" title="Your personal sync offset for this song — positive shows lyrics later">
|
||
<button id="lyrOffMinus" class="lyr-off-btn" type="button">−0.5s</button>
|
||
<span id="lyrOffVal" class="lyr-off-val">0.0s</span>
|
||
<button id="lyrOffPlus" class="lyr-off-btn" type="button">+0.5s</button>
|
||
</span>
|
||
<label class="lyr-auto"><input type="checkbox" id="lyrAutoscroll" checked /> Autoscroll</label>
|
||
<button id="lyricsFlagBtn" class="lyr-flag hidden" type="button" aria-pressed="false" title="Mark a line with wrong words or timing — an admin reviews it"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 21V4M5 4h11l-2 4 2 4H5"/></svg><span>Report a wrong line</span></button>
|
||
</div>
|
||
<div id="lyricsSyncBar" class="lyrics-syncbar hidden">
|
||
<span id="lyricsSyncHint" class="lyrics-sync-hint"></span>
|
||
<button id="lyricsStampBtn" class="np-btn primary-btn" type="button">Stamp line ⏎</button>
|
||
<button id="lyricsUndoBtn" class="np-btn" type="button">↶ Undo</button>
|
||
<button id="lyricsSyncCancel" class="np-btn" type="button">Cancel</button>
|
||
<button id="lyricsSyncDone" class="np-btn" type="button">✓ Save timings</button>
|
||
</div>
|
||
<div id="lyricsTags" class="lyrics-tags"></div>
|
||
<div id="lyricsList" class="lyrics-list"></div>
|
||
</div>
|
||
|
||
<div id="searchPane" class="notes-pane hidden">
|
||
<div class="ts-bar">
|
||
<input id="tsQuery" class="ts-query" type="search" placeholder="Search what's said or sung…" autocomplete="off" />
|
||
<select id="tsSource" class="ts-source" aria-label="Search in">
|
||
<option value="lyrics">Lyrics</option>
|
||
<option value="captions">YouTube transcript</option>
|
||
</select>
|
||
</div>
|
||
<div id="tsInfo" class="ts-info"></div>
|
||
<div id="tsResults" class="ts-results"></div>
|
||
</div>
|
||
|
||
<div id="markersPane" class="notes-pane hidden">
|
||
<div id="markerChips" class="marker-chips"></div>
|
||
<div class="markers-toolbar">
|
||
<button id="addChapterBtn" class="np-btn" type="button">+ Chapter</button>
|
||
<button id="addBookmarkBtn" class="np-btn" type="button" title="Pin a private bookmark here [P]">📌 Bookmark</button>
|
||
<button id="addNoteBtn" class="np-btn" type="button" title="Write a note tied to this moment [N]">📝 Note</button>
|
||
<span class="notes-spacer"></span>
|
||
<button id="chaptersHistoryBtn" class="np-btn" type="button" title="Earlier versions of the shared chapters">🕘</button>
|
||
<button id="exportMarkersBtn" class="np-btn" type="button">⤓ Export</button>
|
||
</div>
|
||
<div class="markers-section">
|
||
<div class="markers-heading">Chapters <span class="markers-scope">shared with everyone</span></div>
|
||
<div id="chapterList" class="marker-list"></div>
|
||
</div>
|
||
<div class="markers-section">
|
||
<div class="markers-heading">My notes & bookmarks <span class="markers-scope">only you</span></div>
|
||
<div id="bookmarkList" class="marker-list"></div>
|
||
</div>
|
||
</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>
|
||
<button id="upnextClear" class="upnext-clear" type="button">Clear</button>
|
||
</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 id="listPane" class="list-pane">
|
||
<div class="list-header">
|
||
<div class="list-title-row">
|
||
<h2 id="listTitle">Search</h2>
|
||
<div id="listTitleActions" class="list-title-actions"></div>
|
||
</div>
|
||
<p id="listSub" class="list-sub"></p>
|
||
<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 id="listFilterActions" class="list-filter-actions"></div>
|
||
</div>
|
||
<div id="batchBar" class="batch-bar hidden">
|
||
<span id="batchCount" class="batch-count">0 selected</span>
|
||
<button id="batchAllBtn" class="batch-btn">Select all</button>
|
||
<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="searchMeta" class="search-meta hidden"></div>
|
||
<div id="status" class="status hidden"></div>
|
||
<div id="cards" class="cards"></div>
|
||
<div id="p2pStrip" class="p2p-strip hidden"></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>
|
||
|
||
<!-- Service mode (set-list view) overlay -->
|
||
<!-- Mobile: floating rail that jumps between the cards on this page and
|
||
follows the scroll. Tapping the active icon folds that card. -->
|
||
<nav id="sectionRail" class="sec-rail hidden" aria-label="Jump to a section"></nav>
|
||
|
||
<section id="serviceMode" class="service-mode hidden" aria-label="Service Mode">
|
||
<div class="svc-top">
|
||
<div class="svc-head">
|
||
<div id="serviceWakeState" class="svc-wake" role="status">Screen may sleep</div>
|
||
<button id="serviceExitBtn" class="svc-exit" type="button" aria-label="Exit Service Mode">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>Exit
|
||
</button>
|
||
</div>
|
||
<div class="svc-dock" role="toolbar" aria-label="Service view">
|
||
<button id="serviceViewBtn" class="svc-tile svc-view" type="button" aria-label="Service view: Default — tap to change">
|
||
<svg class="svc-ico" viewBox="0 0 24 24" aria-hidden="true"></svg>
|
||
<span class="svc-label">Default</span><i class="svc-led"></i>
|
||
</button>
|
||
<button id="serviceAutoscrollBtn" class="svc-tile hidden" type="button" aria-pressed="true" aria-label="Keep the sung line centred">
|
||
<svg class="svc-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v16M7 9l5-5 5 5M7 15l5 5 5-5"/></svg>
|
||
<span class="svc-label">Auto-scroll</span><i class="svc-led"></i>
|
||
</button>
|
||
<button id="serviceFlagBtn" class="svc-tile svc-flag hidden" type="button" aria-pressed="false" aria-label="Report a wrong lyric line">
|
||
<svg class="svc-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 21V4M5 4h11l-2 4 2 4H5"/></svg>
|
||
<span class="svc-label">Report</span><i class="svc-led"></i>
|
||
</button>
|
||
<div class="svc-font" role="group" aria-label="Lyrics text size">
|
||
<button id="svcFontDown" type="button" aria-label="Smaller text">A−</button>
|
||
<span id="svcFontVal" aria-live="polite">0</span>
|
||
<button id="svcFontUp" type="button" aria-label="Larger text">A+</button>
|
||
</div>
|
||
</div>
|
||
<div id="serviceFlagHint" class="svc-hint hidden" role="status">
|
||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 21V4M5 4h11l-2 4 2 4H5"/></svg>
|
||
<span>Tap the line that’s wrong</span>
|
||
</div>
|
||
</div>
|
||
<div class="service-center">
|
||
<div class="service-meta">
|
||
<h1 id="serviceTitle" class="service-title"></h1>
|
||
<div id="serviceChannel" class="service-channel"></div>
|
||
<div id="serviceNote" class="service-note hidden"></div>
|
||
</div>
|
||
<!-- Album art / visualizer views -->
|
||
<div id="serviceVisual" class="service-visual hidden" aria-hidden="true">
|
||
<img id="serviceArt" class="svc-art" alt="" decoding="async" />
|
||
<canvas id="serviceViz" class="svc-viz"></canvas>
|
||
</div>
|
||
<div id="serviceUpNext" class="service-upnext">Last song</div>
|
||
<!-- Lyrics-only view: the sung line is centred, largest and highlighted -->
|
||
<div id="serviceLyrics" class="service-lyrics hidden" aria-live="off">
|
||
<div id="serviceLyricsList" class="sl-list"></div>
|
||
</div>
|
||
</div>
|
||
<div class="service-bottom">
|
||
<div class="svc-progress" aria-hidden="true"><i id="serviceProgressFill"></i></div>
|
||
<div id="serviceTime" class="service-time">
|
||
<span id="serviceCurTime">0:00</span> / <span id="serviceDurTime">0:00</span>
|
||
</div>
|
||
<div class="service-controls">
|
||
<button id="servicePrevBtn" class="service-btn" type="button" aria-label="Previous track">⏮</button>
|
||
<button id="servicePlayBtn" class="service-btn service-btn-play" type="button" aria-label="Play or Pause">▶</button>
|
||
<button id="serviceNextBtn" class="service-btn" type="button" aria-label="Next track">⏭</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Sleep timer "screen off": black curtain; tap to wake -->
|
||
<div id="sleepCurtain" class="sleep-curtain hidden" role="button" aria-label="Wake">
|
||
<span id="sleepCurtainMsg">Sleep timer ended · tap to wake</span>
|
||
</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="miniServiceBtn" class="mini-btn mini-service" type="button"
|
||
title="Service mode" aria-label="Service mode">🎤</button>
|
||
<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) -->
|
||
<!-- Watch party panel (Party in app.js) -->
|
||
<aside id="partyPanel" class="party-panel hidden" aria-label="Watch party">
|
||
<div class="pt-head"><span id="ptTitle">🎉 Watch party</span><span id="ptStatus" class="pt-status"></span><button id="ptMin" class="rv-icon" type="button" aria-label="Minimise">▁</button></div>
|
||
<div class="pt-body"></div>
|
||
</aside>
|
||
|
||
<!-- Desktop: shown while phones are driving this screen (remote.js) -->
|
||
<button id="remoteChip" class="remote-chip hidden" type="button" title="Phone remote settings">
|
||
<span class="remote-chip-dot"></span><span id="remoteChipText">Remote connected</span>
|
||
</button>
|
||
|
||
<!-- Presenter / projector screen: lyrics of whatever the paired screen plays -->
|
||
<section id="presenterView" class="presenter-view hidden" aria-label="Presenter view">
|
||
<div id="pvList" class="sl-list pv-list"></div>
|
||
<div id="pvTitle" class="pv-title hidden"></div>
|
||
<div id="pvBar" class="pv-bar">
|
||
<span id="pvStatus" class="pv-status">Connecting…</span>
|
||
<span class="pv-hint">Double-click or F = fullscreen · B = blank screen</span>
|
||
<button id="pvFull" class="pv-btn" type="button">⛶ Fullscreen</button>
|
||
</div>
|
||
<div id="pvPair" class="pv-pair hidden">
|
||
<div class="pv-pair-title">Presenter screen</div>
|
||
<p>Enter the 6-digit code shown on the playing computer (📱 Remote → Control this screen → Presenter).</p>
|
||
<input id="pvCode" inputmode="numeric" maxlength="7" placeholder="000 000" />
|
||
<button id="pvPairBtn" class="rv-primary" type="button">Connect</button>
|
||
<div id="pvMsg" class="rv-pair-msg"></div>
|
||
</div>
|
||
<div id="pvBlank" class="pv-blank hidden"></div>
|
||
</section>
|
||
|
||
<!-- Phone: remote control for a desktop instance -->
|
||
<section id="remoteView" class="remote-view hidden" aria-label="Phone remote">
|
||
<div class="rv-top">
|
||
<button id="rvClose" class="rv-icon" type="button" aria-label="Close remote">✕</button>
|
||
<div class="rv-host">
|
||
<div id="rvHostName" class="rv-host-name">Remote</div>
|
||
<div id="rvStatus" class="rv-status">Not connected</div>
|
||
</div>
|
||
<button id="rvMenu" class="rv-icon" type="button" aria-label="Remote options">⋯</button>
|
||
</div>
|
||
|
||
<div id="rvPair" class="rv-pair">
|
||
<div class="rv-pair-title">Connect to a screen</div>
|
||
<p class="rv-pair-help">On the computer that plays to the TV or speakers, open <b>📱 Remote</b> → <b>Control from a phone</b>, then enter the 6-digit code shown there (or scan its QR code with your camera).</p>
|
||
<input id="rvCode" class="rv-code" inputmode="numeric" autocomplete="one-time-code" maxlength="7" placeholder="000 000" />
|
||
<input id="rvName" class="rv-name" maxlength="40" placeholder="This phone's name" />
|
||
<button id="rvPairBtn" class="rv-primary" type="button">Pair</button>
|
||
<div id="rvPairMsg" class="rv-pair-msg"></div>
|
||
</div>
|
||
|
||
<div id="rvControl" class="rv-control hidden">
|
||
<div class="rv-now">
|
||
<img id="rvThumb" class="rv-thumb" alt="" />
|
||
<div class="rv-now-text">
|
||
<div id="rvTitle" class="rv-title">Nothing playing</div>
|
||
<div id="rvChannel" class="rv-channel"></div>
|
||
</div>
|
||
</div>
|
||
<div class="rv-seek">
|
||
<input id="rvSeek" type="range" min="0" max="1000" value="0" />
|
||
<div class="rv-times"><span id="rvCur">0:00</span><span id="rvDur">0:00</span></div>
|
||
</div>
|
||
<div class="rv-transport">
|
||
<button id="rvPrev" class="rv-tbtn" type="button" aria-label="Previous">⏮</button>
|
||
<button id="rvToggle" class="rv-tbtn rv-play" type="button" aria-label="Play or pause">▶</button>
|
||
<button id="rvNext" class="rv-tbtn" type="button" aria-label="Next">⏭</button>
|
||
</div>
|
||
<div class="rv-volume">
|
||
<span>🔊</span><input id="rvVolume" type="range" min="0" max="100" value="100" /><span id="rvVolVal">100%</span>
|
||
</div>
|
||
<div class="rv-queue-head"><span>Up next</span><span id="rvQueueCount"></span></div>
|
||
<div id="rvQueue" class="rv-queue"></div>
|
||
<div class="rv-queue-hint">Tap a song to play it · drag ⠿ to reorder · ✕ removes</div>
|
||
<div class="rv-actions">
|
||
<button id="rvAdd" class="rv-action" type="button">+ Add to queue</button>
|
||
<button id="rvSleep" class="rv-action" type="button">⏱ Sleep</button>
|
||
<button id="rvService" class="rv-action" type="button">🎤 Service mode</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<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 class="legacy-nav-icon" aria-hidden="true">🔍</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5"/></svg></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 class="legacy-nav-icon" aria-hidden="true">▶</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="m8 5 11 7-11 7z"/></svg></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 class="legacy-nav-icon" aria-hidden="true">🕘</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></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 class="legacy-nav-icon" aria-hidden="true">💾</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3h12l4 4v14H3V3h2zm2 0v6h10V3M7 21v-8h10v8"/></svg></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 class="legacy-nav-icon" aria-hidden="true">⬇</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5"/></svg></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 class="legacy-nav-icon" aria-hidden="true">⚙</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="m9 3-1 3-3 1-2 3 2 2-1 3 2 3 3-1 2 3h3l1-3 3-1 2-3-2-2 1-3-2-3-3 1-2-3z"/></svg></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>P</kbd><span>Pin a bookmark at the current time</span></div>
|
||
<div class="shortcut-row"><kbd>[</kbd><kbd>]</kbd><span>Previous / next chapter</span></div>
|
||
<div class="shortcut-row"><kbd>N</kbd><span>Write a note at this moment</span></div>
|
||
<div class="shortcut-row"><kbd>I</kbd><span>Picture-in-picture</span></div>
|
||
<div class="shortcut-row"><kbd>E</kbd><span>Equalizer</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="lyrics-core.js"></script>
|
||
<script src="lyrics-display.js"></script>
|
||
<script src="flag-ui.js"></script>
|
||
<script src="export.js"></script>
|
||
<script src="stats-core.js"></script>
|
||
<script src="sha256-wasm.js"></script>
|
||
<script src="sha256.js"></script>
|
||
<script src="loudness-wasm.js"></script>
|
||
<script src="loudness.js"></script>
|
||
<script src="device-db.js"></script>
|
||
<script src="p2p-client.js"></script>
|
||
<script src="p2p-core.js"></script>
|
||
<script src="p2p-transfer.js"></script>
|
||
<script src="async-guard.js"></script>
|
||
<script src="sw-update.js"></script>
|
||
<script src="settings-sections.js"></script>
|
||
<script src="downloads.js"></script>
|
||
<script src="downloads-page.js"></script>
|
||
<script src="saved-page.js"></script>
|
||
<script src="server-backfill.js"></script>
|
||
<script src="party-dj.js"></script>
|
||
<script src="lyrics-window.js"></script>
|
||
<script src="midi.js"></script>
|
||
<script src="lower-third.js"></script>
|
||
<script src="vocal-reducer.js"></script>
|
||
<script src="eq-core.js"></script>
|
||
<script src="piano-core.js"></script>
|
||
<script src="piano.js"></script>
|
||
<script src="setlist-import.js"></script>
|
||
<script src="car-mode.js"></script>
|
||
<script src="app.js"></script>
|
||
</body>
|
||
</html>
|