diff --git a/index.html b/index.html index 9fd29ac..e8976ab 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,10 @@

Queue

- +
+ + +
diff --git a/js/events.js b/js/events.js index 6206fc6..9f0c5a5 100644 --- a/js/events.js +++ b/js/events.js @@ -250,9 +250,14 @@ function initializeSmoothSliders(audioPlayer, player) { progressBar.addEventListener('click', e => { if (!isSeeking) { seek(progressBar, e, position => { - if (!isNaN(audioPlayer.duration)) { + if (!isNaN(audioPlayer.duration) && audioPlayer.duration > 0 && audioPlayer.duration !== Infinity) { audioPlayer.currentTime = position * audioPlayer.duration; player.updateMediaSessionPositionState(); + } else if (player.currentTrack && player.currentTrack.duration) { + const targetTime = position * player.currentTrack.duration; + const progressFill = document.querySelector('.progress-fill'); + if (progressFill) progressFill.style.width = `${position * 100}%`; + player.playTrackFromQueue(targetTime); } }); } @@ -310,7 +315,7 @@ export async function handleTrackAction(action, item, player, api, lyricsManager // Update all instances of this item's like button on the page const id = type === 'playlist' ? item.uuid : item.id; const selector = type === 'track' - ? `.track-item[data-track-id="${id}"] .like-btn` + ? `[data-track-id="${id}"] .like-btn` : `.card[data-${type}-id="${id}"] .like-btn, .card[data-playlist-id="${id}"] .like-btn`; // Also check header buttons diff --git a/js/player.js b/js/player.js index 932947b..7d41efe 100644 --- a/js/player.js +++ b/js/player.js @@ -52,6 +52,8 @@ export class Player { if (coverEl) coverEl.src = this.api.getCoverUrl(track.album?.cover, '1280'); if (titleEl) titleEl.textContent = trackTitle; if (artistEl) artistEl.textContent = trackArtists; + const totalDurationEl = document.getElementById('total-duration'); + if (totalDurationEl) totalDurationEl.textContent = formatTime(track.duration); document.title = `${trackTitle} • ${track.artist?.name || 'Unknown'}`; this.updatePlayingTrackIndicator(); @@ -154,7 +156,7 @@ export class Player { } } - async playTrackFromQueue() { + async playTrackFromQueue(startTime = 0) { const currentQueue = this.shuffleActive ? this.shuffledQueue : this.queue; if (this.currentQueueIndex < 0 || this.currentQueueIndex >= currentQueue.length) { return; @@ -193,6 +195,9 @@ export class Player { } this.audio.src = streamUrl; + if (startTime > 0) { + this.audio.currentTime = startTime; + } await this.audio.play(); this.updateMediaSessionPlaybackState(); diff --git a/js/ui-interactions.js b/js/ui-interactions.js index 82df0f5..fcc95fb 100644 --- a/js/ui-interactions.js +++ b/js/ui-interactions.js @@ -1,5 +1,5 @@ //js/ui-interactions.js -import { SVG_CLOSE, formatTime, trackDataStore, getTrackTitle, getTrackArtists } from './utils.js'; +import { SVG_CLOSE, SVG_BIN, formatTime, trackDataStore, getTrackTitle, getTrackArtists } from './utils.js'; export function initializeUIInteractions(player, api) { const sidebar = document.querySelector('.sidebar'); @@ -8,6 +8,7 @@ export function initializeUIInteractions(player, api) { const queueBtn = document.getElementById('queue-btn'); const queueModalOverlay = document.getElementById('queue-modal-overlay'); const closeQueueBtn = document.getElementById('close-queue-btn'); + const clearQueueBtn = document.getElementById('clear-queue-btn'); const queueList = document.getElementById('queue-list'); let draggedQueueIndex = null; @@ -40,6 +41,13 @@ export function initializeUIInteractions(player, api) { closeQueueBtn.addEventListener('click', () => { queueModalOverlay.style.display = 'none'; }); + + if (clearQueueBtn) { + clearQueueBtn.addEventListener('click', () => { + player.clearQueue(); + renderQueue(); + }); + } queueModalOverlay.addEventListener('click', e => { if (e.target === queueModalOverlay) { @@ -50,6 +58,10 @@ export function initializeUIInteractions(player, api) { function renderQueue() { const currentQueue = player.getCurrentQueue(); + if (clearQueueBtn) { + clearQueueBtn.style.display = currentQueue.length > 0 ? 'block' : 'none'; + } + if (currentQueue.length === 0) { queueList.innerHTML = '
Queue is empty.
'; return; @@ -78,7 +90,7 @@ export function initializeUIInteractions(player, api) {
${formatTime(track.duration)}
`; diff --git a/js/utils.js b/js/utils.js index dbd77cc..b225985 100644 --- a/js/utils.js +++ b/js/utils.js @@ -34,6 +34,7 @@ export const SVG_DOWNLOAD = ' { if (isNaN(seconds)) return '0:00'; diff --git a/styles.css b/styles.css index 108cf4b..a93f018 100644 --- a/styles.css +++ b/styles.css @@ -1520,7 +1520,7 @@ input:checked + .slider::before { margin: 0; } -#queue-modal-header button { +#queue-modal-header #close-queue-btn { background: transparent; border: none; color: var(--muted-foreground); @@ -1535,7 +1535,12 @@ input:checked + .slider::before { transition: all var(--transition); } -#queue-modal-header button:hover { +#queue-modal-header #clear-queue-btn { + background-color: transparent; +} + +#queue-modal-header #clear-queue-btn:hover, +#queue-modal-header #close-queue-btn:hover { background-color: var(--secondary); color: var(--foreground); }