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 @@
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 {