Adds Seekbar
This commit is contained in:
190
player.js
190
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.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user