Something went wrong. Try again.
A local, collaborative music player (main-branch-only mirror of forge.ejuarezg.com/ejuarezg/jam-session)
Something went wrong. Try again.
7.2 kB · 169 lines
HTML
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Jam Session</title> <link rel="stylesheet" href="/style.css"></head><body> <header> <h1>Jam Session</h1> <div class="nickname-row"> <span id="nickname-display"></span> <span id="nickname-edit-icon" class="edit-icon" title="Edit nickname">✎</span> <input type="text" id="nickname-input" class="nickname-input" style="display:none" maxlength="20"> <span id="nickname-error" class="nickname-error" style="display:none"></span> <span id="user-count">0 online</span> </div> </header>
<div id="error-toast" style="display:none"></div>
<main> <section id="now-playing"> <h2>Now Playing</h2> <div id="now-playing-info"> <p id="current-title">—</p> <p id="current-duration"></p> <div class="progress-bar" id="progress-bar"> <div class="progress-fill" id="progress-fill" style="width:0%"></div> </div> <div class="progress-time-labels"> <span class="time-elapsed" id="time-elapsed">0:00</span> <span class="time-total" id="time-total">/ 0:00</span> <span class="time-remaining" id="time-remaining">-0:00</span> </div> <div id="host-controls" class="host-controls" style="display:none"> <button id="host-pause" class="host-btn" aria-label="Pause playback"> <span class="host-btn-icon">⏸</span> </button> <button id="host-resume" class="host-btn" aria-label="Resume playback" style="display:none"> <span class="host-btn-icon">▶</span> </button> <button id="host-skip" class="host-btn" aria-label="Skip to next track"> <span class="host-btn-icon">⏩</span> </button> </div> <div id="skip-vote-row" class="skip-vote-row"> <button id="skip-vote-btn" class="vote-btn" aria-label="Vote to skip">⏩</button> <span id="skip-badge" class="skip-badge">0/1 to skip</span> </div> <p id="progress-time">--:--</p> </div> </section>
<div id="disconnect-panel" class="disconnect-panel" style="display:none"> <span class="disconnect-icon">⚠</span> <h2 class="disconnect-heading">Server unreachable</h2> <p class="disconnect-body">Check that the host machine is running and you're on the same network.</p> <button id="reconnect-btn" class="reconnect-btn">Reconnect</button> </div>
<section id="queue-section"> <h2>Queue</h2> <ul id="queue-list"> <li class="empty-state"> <span class="empty-state-icon">♫</span> No songs in the queue yet.<br>Add a YouTube link to get started! </li> </ul> </section>
<section id="add-song"> <h2>Add a Song</h2> <form id="add-form"> <input type="url" id="url-input" placeholder="YouTube URL" required> <button type="submit">Add</button> </form> </section>
<section id="share"> <h2>Share</h2> <img id="qr-image" src="/api/qrcode" alt="QR Code"> <p id="server-url"></p> </section>
<section id="host-settings"> <h2 id="host-settings-heading">Host Settings <span id="host-settings-toggle">−</span></h2> <div id="host-settings-body">
<div class="settings-subsection"> <h3 class="settings-subsection-heading">Session</h3> <div class="settings-row"> <input type="text" id="session-name-input" class="session-name-input" placeholder="Session name…" value=""> <button id="save-session-btn">Save Session</button> <button id="load-session-btn">Load Session</button> </div> <div id="session-picker" class="session-picker" style="display:none"></div> <button id="stop-server-btn" class="stop-server-btn">Stop Server</button> </div>
<div class="settings-subsection"> <h3 class="settings-subsection-heading">Settings</h3>
<div class="settings-row"> <div class="settings-label-col"> <label class="settings-label" for="skip-threshold-slider">Skip Threshold</label> <p class="settings-description">Percentage of connected guests needed to skip the current track. Small groups always need at least 1–2 votes.</p> </div> <div class="settings-control-col"> <div class="slider-group"> <input type="range" id="skip-threshold-slider" min="10" max="100" step="5" value="50"> <input type="number" id="skip-threshold-number" min="10" max="100" value="50"> <span class="settings-unit">%</span> </div> </div> </div>
<div class="settings-row"> <div class="settings-label-col"> <label class="settings-label" for="auto-persist-toggle">Auto-Persist</label> <p class="settings-description">Automatically save the queue on every change. Survives unexpected server exits.</p> </div> <div class="settings-control-col"> <label class="toggle-switch" id="auto-persist-toggle-label" aria-label="Toggle auto-persist"> <input type="checkbox" id="auto-persist-toggle" role="switch"> <span class="toggle-slider"></span> <span class="toggle-label-text">OFF</span> </label> </div> </div>
<div class="settings-row"> <div class="settings-label-col"> <label class="settings-label" for="skip-cooldown-slider">Skip Cooldown</label> <p class="settings-description">Seconds before skip votes can be cast after a track change. Prevents accidental vote spam.</p> </div> <div class="settings-control-col"> <div class="slider-group"> <input type="range" id="skip-cooldown-slider" min="0" max="10" step="1" value="3"> <input type="number" id="skip-cooldown-number" min="0" max="10" value="3"> <span class="settings-unit">s</span> </div> </div> </div> </div>
</div> </section>
<button id="refresh-btn">Refresh</button> </main>
<div id="stop-server-modal" class="modal-overlay" style="display:none"> <div class="modal-card"> <h2 class="modal-heading">Stop Server?</h2> <p id="modal-body" class="modal-body"></p> <input type="text" id="modal-session-name" class="modal-session-name" placeholder="Session name…"> <div class="modal-buttons"> <button id="modal-save-stop" class="modal-btn modal-btn-accent">Save & Stop</button> <button id="modal-stop-no-save" class="modal-btn modal-btn-secondary">Stop Without Saving</button> <button id="modal-cancel" class="modal-btn modal-btn-secondary">Cancel</button> </div> </div> </div>
<script src="/app.js"></script></body></html>