aboutsummaryrefslogtreecommitdiff
path: root/player.js
diff options
context:
space:
mode:
Diffstat (limited to 'player.js')
-rw-r--r--player.js190
1 files changed, 190 insertions, 0 deletions
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.');
}