From c908577d3621aab29a40025fb69fd8695e021e57 Mon Sep 17 00:00:00 2001 From: Indrajith K L Date: Tue, 1 Jul 2025 01:33:12 +0530 Subject: Adds Seekbar --- player.js | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) (limited to 'player.js') diff --git a/player.js b/player.js index 135f1f9..1d147ad 100644 --- a/player.js +++ b/player.js @@ -18,6 +18,16 @@ let currentEffectIndex = 0; const modalEl = document.getElementById("intro-modal"); + // Progress bar elements (initialized later to avoid null references) + let progressBarContainer; + let progressBar; + let progressFill; + let progressHandle; + let currentTimeEl; + let totalTimeEl; + let progressUpdateInterval; + let isDragging = false; + // const uploadInfoEl = document.getElementById("upload-info"); const fileUploadEl = document.getElementById("file-upload"); let volume = 1; @@ -111,6 +121,7 @@ }); initHydra(); initControls(); + initProgressBar(); }); document.getElementById("history").addEventListener("click", () => { @@ -223,6 +234,18 @@ onend: function () { playNextTrack(); }, + onload: function () { + updateProgressBar(); + }, + onplay: function () { + startProgressTracking(); + }, + onpause: function () { + stopProgressTracking(); + }, + onstop: function () { + stopProgressTracking(); + }, }); setVolume(); isPlaying = true; @@ -318,6 +341,7 @@ https://retrowave.ru/${musicData.streamUrl} } function resetHowler(destroy = false) { + stopProgressTracking(); howlerInstance.stop(); if (destroy) { howlerInstance.unload(); @@ -325,6 +349,134 @@ https://retrowave.ru/${musicData.streamUrl} } } + // Progress Bar Utility Functions + function formatTime(seconds) { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; + } + + function updateProgressBar() { + if (!howlerInstance || isDragging || !progressFill || !progressHandle || !currentTimeEl || !totalTimeEl) return; + + const seek = howlerInstance.seek() || 0; + const duration = howlerInstance.duration() || 0; + + if (duration > 0) { + const percentage = (seek / duration) * 100; + progressFill.style.width = `${percentage}%`; + progressHandle.style.left = `${percentage}%`; + + currentTimeEl.textContent = formatTime(seek); + totalTimeEl.textContent = formatTime(duration); + } + } + + function startProgressTracking() { + if (progressUpdateInterval) { + clearInterval(progressUpdateInterval); + } + progressUpdateInterval = setInterval(updateProgressBar, 100); + } + + function stopProgressTracking() { + if (progressUpdateInterval) { + clearInterval(progressUpdateInterval); + progressUpdateInterval = null; + } + } + + function seekToPosition(percentage) { + if (!howlerInstance) return; + + const duration = howlerInstance.duration() || 0; + if (duration > 0) { + const seekTime = (percentage / 100) * duration; + howlerInstance.seek(seekTime); + updateProgressBar(); + } + } + + function initProgressBar() { + // Initialize DOM elements + progressBarContainer = document.getElementById("progress-bar-container"); + progressBar = document.getElementById("progress-bar"); + progressFill = document.getElementById("progress-fill"); + progressHandle = document.getElementById("progress-handle"); + currentTimeEl = document.getElementById("current-time"); + totalTimeEl = document.getElementById("total-time"); + + if (!progressBar || !progressFill || !progressHandle || !currentTimeEl || !totalTimeEl) { + console.warn("Progress bar elements not found"); + return; + } + + // Click to seek + progressBar.addEventListener("click", (e) => { + if (isDragging) return; + + const rect = progressBar.getBoundingClientRect(); + const percentage = ((e.clientX - rect.left) / rect.width) * 100; + seekToPosition(Math.max(0, Math.min(100, percentage))); + }); + + // Drag to seek functionality + let startX = 0; + let startPercentage = 0; + + progressHandle.addEventListener("mousedown", (e) => { + isDragging = true; + startX = e.clientX; + const rect = progressBar.getBoundingClientRect(); + startPercentage = ((startX - rect.left) / rect.width) * 100; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + e.preventDefault(); + }); + + function handleMouseMove(e) { + if (!isDragging) return; + + const rect = progressBar.getBoundingClientRect(); + const percentage = ((e.clientX - rect.left) / rect.width) * 100; + const clampedPercentage = Math.max(0, Math.min(100, percentage)); + + progressFill.style.width = `${clampedPercentage}%`; + progressHandle.style.left = `${clampedPercentage}%`; + + if (howlerInstance) { + const duration = howlerInstance.duration() || 0; + if (duration > 0) { + const seekTime = (clampedPercentage / 100) * duration; + currentTimeEl.textContent = formatTime(seekTime); + } + } + } + + function handleMouseUp(e) { + if (!isDragging) return; + + const rect = progressBar.getBoundingClientRect(); + const percentage = ((e.clientX - rect.left) / rect.width) * 100; + const clampedPercentage = Math.max(0, Math.min(100, percentage)); + + seekToPosition(clampedPercentage); + isDragging = false; + + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + } + + // Touch support for mobile + progressBar.addEventListener("touchstart", (e) => { + const touch = e.touches[0]; + const rect = progressBar.getBoundingClientRect(); + const percentage = ((touch.clientX - rect.left) / rect.width) * 100; + seekToPosition(Math.max(0, Math.min(100, percentage))); + }); + } + function initControls() { refreshBtn.addEventListener("click", () => { playNextTrack(); @@ -1267,6 +1419,7 @@ https://retrowave.ru/${musicData.streamUrl} addTerminalLine(' pause - Pause playback'); addTerminalLine(' next - Skip to next track'); addTerminalLine(' volume [0-10] - Set volume (0-10)'); + addTerminalLine(' seek [time] - Seek to time (seconds or mm:ss)'); addTerminalLine(' status - Show current track info'); addTerminalLine(' history - Download playlist history'); addTerminalLine(' effect [list|name]- Change/list visual effects'); @@ -1319,12 +1472,49 @@ https://retrowave.ru/${musicData.streamUrl} } break; + case 'seek': + if (args[1] && howlerInstance) { + let seekTime = 0; + const input = args[1]; + + if (input.includes(':')) { + const [mins, secs] = input.split(':').map(Number); + seekTime = (mins * 60) + secs; + } else { + seekTime = parseFloat(input); + } + + const duration = howlerInstance.duration() || 0; + if (seekTime >= 0 && seekTime <= duration) { + howlerInstance.seek(seekTime); + updateProgressBar(); + addTerminalLine(`Seeked to ${formatTime(seekTime)}`); + } else { + addTerminalLine(`Invalid seek time. Duration: ${formatTime(duration)}`); + } + } else if (!howlerInstance) { + addTerminalLine('No track loaded.'); + } else { + if (howlerInstance) { + const seek = howlerInstance.seek() || 0; + const duration = howlerInstance.duration() || 0; + addTerminalLine(`Current position: ${formatTime(seek)} / ${formatTime(duration)}`); + } + } + break; + case 'status': if (currentTracks.length > 0) { const track = currentTracks[0]; addTerminalLine(`Now playing: ${track.title}`); addTerminalLine(`Status: ${isPlaying ? 'Playing' : 'Paused'}`); addTerminalLine(`Volume: ${Math.round(volume * 10)}/10`); + if (howlerInstance) { + const seek = howlerInstance.seek() || 0; + const duration = howlerInstance.duration() || 0; + const progress = duration > 0 ? ((seek / duration) * 100).toFixed(1) : 0; + addTerminalLine(`Progress: ${formatTime(seek)} / ${formatTime(duration)} (${progress}%)`); + } } else { addTerminalLine('No track loaded.'); } -- cgit v1.2.3