Quellcode durchsuchen

feat: click canvas to cycle viz / elapsed / remaining

Three modes on canvas click:
  viz       -> spectrum analyser (default)
  actual    -> hh:mm:ss elapsed in white, ELAPSED label
  remaining -> -hh:mm:ss countdown in accent red, REMAINING label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
Jan Svabenik vor 1 Monat
Ursprung
Commit
d8ebc1b8c0
2 geänderte Dateien mit 49 neuen und 3 gelöschten Zeilen
  1. +48
    -3
      web/static/app.js
  2. +1
    -0
      web/static/style.css

+ 48
- 3
web/static/app.js Datei anzeigen

@@ -35,6 +35,12 @@ let rafId = null;
let lastVizAt = 0; // timestamp of last received viz frame
let winampPlaying = false;

// Canvas display mode — cycles on click: viz → actual → remaining → viz
const VIZ_MODES = ['viz', 'actual', 'remaining'];
let vizMode = 'viz';
let currentPosition = 0;
let currentLength = 0;

// ── WebSocket ─────────────────────────────────────────────────────────────────
function connect() {
const proto = location.protocol === 'https:' ? 'wss' : 'ws';
@@ -94,6 +100,8 @@ function applyStatus(st) {
}

if (st.length > 0) {
currentPosition = st.position;
currentLength = st.length;
progressFill.style.width = (st.position / st.length * 100).toFixed(1) + '%';
timeCurrent.textContent = fmtTime(st.position);
timeLength.textContent = '-' + fmtTime(st.length - st.position);
@@ -202,6 +210,10 @@ $('btn-close-killist').addEventListener('click', () =>
killistPanel.classList.add('hidden'));

// ── Visualisation (Canvas) ────────────────────────────────────────────────────
canvas.addEventListener('click', () => {
vizMode = VIZ_MODES[(VIZ_MODES.indexOf(vizMode) + 1) % VIZ_MODES.length];
});

function applyViz(bars) {
if (!bars || bars.length === 0) return;
lastBars = new Float32Array(bars);
@@ -226,13 +238,37 @@ function renderFrame(ts = 0) {
const h = cssH;
if (w === 0 || h === 0) return;

// Check if real viz data is fresh (< 1.5 s old) and Winamp is playing.
const hasSignal = winampPlaying && (performance.now() - lastVizAt) < 1500;

// Background
ctx2d.fillStyle = '#000';
ctx2d.fillRect(0, 0, w, h);

// ── Time display modes ──────────────────────────────────────────────────────
if (vizMode === 'actual' || vizMode === 'remaining') {
const isActual = vizMode === 'actual';
const secs = isActual ? currentPosition : Math.max(0, currentLength - currentPosition);
const prefix = isActual ? '' : '-';
const timeStr = prefix + fmtTimeLong(secs);
const label = isActual ? 'ELAPSED' : 'REMAINING';

ctx2d.textAlign = 'center';
ctx2d.textBaseline = 'middle';

// Large time
ctx2d.font = `bold ${Math.round(h * 0.52)}px monospace`;
ctx2d.fillStyle = isActual ? '#e0e0e0' : '#e94560';
ctx2d.fillText(timeStr, w / 2, h * 0.48);

// Small label below
ctx2d.font = `${Math.round(h * 0.18)}px monospace`;
ctx2d.fillStyle = '#444';
ctx2d.fillText(label, w / 2, h * 0.82);
return;
}

// ── Spectrum mode ───────────────────────────────────────────────────────────
// Check if real viz data is fresh (< 1.5 s old) and Winamp is playing.
const hasSignal = winampPlaying && (performance.now() - lastVizAt) < 1500;

const n = NUM_BARS;
const gap = 1;
const barW = Math.max(1, (w - gap * (n - 1)) / n);
@@ -296,6 +332,15 @@ function fmtTime(secs) {
return `${m}:${s}`;
}

// Like fmtTime but always zero-pads to hh:mm:ss for the canvas display.
function fmtTimeLong(secs) {
secs = Math.floor(secs);
const h = Math.floor(secs / 3600);
const m = Math.floor((secs % 3600) / 60);
const s = secs % 60;
return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
}

function parseTime(str) {
const [m, s] = (str || '0:00').split(':').map(Number);
return m * 60 + (s || 0);


+ 1
- 0
web/static/style.css Datei anzeigen

@@ -69,6 +69,7 @@ html, body {
border-radius: var(--radius);
background: #000;
display: block;
cursor: pointer;
}

/* Progress */


Laden…
Abbrechen
Speichern