diff --git a/public/channelSync.js b/public/channelSync.js index 14214c7..dab7142 100644 --- a/public/channelSync.js +++ b/public/channelSync.js @@ -404,6 +404,9 @@ M.setTrackTitle(data.track.title); M.loadingSegments.clear(); + // Auto-scroll queue to current track + setTimeout(() => M.scrollToCurrentTrack(), 100); + // Debug: log cache state for this track const trackCache = M.trackCaches.get(trackId); console.log("[Playback] Starting track:", data.track.title, { diff --git a/public/controls.js b/public/controls.js index 2b665bc..8b70d45 100644 --- a/public/controls.js +++ b/public/controls.js @@ -64,6 +64,7 @@ M.localTimestamp = 0; M.audio.play(); M.renderQueue(); + setTimeout(() => M.scrollToCurrentTrack(), 100); } } diff --git a/public/index.html b/public/index.html index bc914c5..671b35e 100644 --- a/public/index.html +++ b/public/index.html @@ -133,7 +133,7 @@
-

Queue

+

Queue

diff --git a/public/queue.js b/public/queue.js index 1aa7745..a0d7ebb 100644 --- a/public/queue.js +++ b/public/queue.js @@ -231,8 +231,27 @@ if (queueContainer) { queueContainer.render(); } + updateQueueDuration(); }; + function updateQueueDuration() { + const el = M.$("#queue-duration"); + if (!el) return; + const totalSecs = M.queue.reduce((sum, t) => sum + (t.duration || 0), 0); + if (totalSecs === 0) { + el.textContent = ""; + return; + } + const hours = Math.floor(totalSecs / 3600); + const mins = Math.floor((totalSecs % 3600) / 60); + const secs = Math.floor(totalSecs % 60); + let text = ""; + if (hours > 0) text = `${hours}h ${mins}m`; + else if (mins > 0) text = `${mins}m ${secs}s`; + else text = `${secs}s`; + el.textContent = `(${M.queue.length} tracks ยท ${text})`; + } + M.renderLibrary = function() { initContainers(); if (libraryContainer) { diff --git a/public/styles.css b/public/styles.css index 96626b5..f81da0b 100644 --- a/public/styles.css +++ b/public/styles.css @@ -129,6 +129,7 @@ h3 { font-size: 0.8rem; color: #666; margin-bottom: 0.3rem; text-transform: uppe .upload-dropzone.hidden { display: none; } .dropzone-content { color: #4e8; font-size: 1.2rem; font-weight: 600; } #queue-title { margin: 0 0 0.3rem 0; } +#queue-duration { font-size: 0.75rem; color: #888; font-weight: normal; } .now-playing-bar { font-size: 0.75rem; color: #4e8; padding: 0.3rem 0.5rem; background: #1a2a1a; border: 1px solid #2a4a3a; border-radius: 4px; margin-bottom: 0.3rem; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .now-playing-bar:hover { background: #2a3a2a; } .now-playing-bar.hidden { display: none; } diff --git a/public/trackContainer.js b/public/trackContainer.js index 7b69299..3d8cc6e 100644 --- a/public/trackContainer.js +++ b/public/trackContainer.js @@ -261,16 +261,19 @@ const trackId = track.id || track.filename; const index = type === 'queue' ? originalIndex : filteredIndex; - // Click - handle selection (Ctrl = toggle, Shift = range, plain = select only this) + // Click/double-click handling - delay render to allow double-click detection + let clickTimeout = null; div.onclick = (e) => { if (e.target.closest('.track-actions')) return; toggleSelection(index, trackId, e.shiftKey, e.ctrlKey || e.metaKey); - render(); + clearTimeout(clickTimeout); + clickTimeout = setTimeout(() => render(), 200); }; // Double-click - queue: jump to track, library/playlist: add to queue div.ondblclick = (e) => { if (e.target.closest('.track-actions')) return; + clearTimeout(clickTimeout); if (type === 'queue') { M.jumpToTrack(originalIndex); } else {