Adds Seekbar

This commit is contained in:
2025-07-01 01:33:12 +05:30
parent b304a682e8
commit c908577d36
3 changed files with 269 additions and 0 deletions

69
hg.css
View File

@@ -427,6 +427,75 @@ canvas {
top: 0;
}
.progress-bar-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 55px;
background: linear-gradient(145deg, rgba(0, 0, 0, 0.95), rgba(0, 0, 0, 0.85));
border-top: 2px solid #ff2975;
z-index: 1000;
padding: 10px 20px;
box-shadow:
0 -2px 10px rgba(0, 0, 0, 0.6),
0 0 15px rgba(255, 41, 117, 0.2);
}
.progress-info {
display: flex;
justify-content: space-between;
margin-bottom: 6px;
font-family: "VCR", monospace;
font-size: 10px;
color: #00ffff;
text-shadow: 0 0 5px rgba(0, 255, 255, 0.5);
}
.progress-bar {
position: relative;
height: 6px;
background: linear-gradient(145deg, rgba(0, 0, 0, 0.8), rgba(20, 20, 20, 0.9));
border: 1px solid #333;
border-radius: 3px;
cursor: pointer;
overflow: hidden;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.8);
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #ff2975, #00ffff);
border-radius: 3px;
width: 0%;
transition: width 0.1s ease-out;
box-shadow: 0 0 8px rgba(255, 41, 117, 0.4);
}
.progress-handle {
position: absolute;
top: -4px;
width: 14px;
height: 14px;
background: radial-gradient(circle, #00ffff, #ff2975);
border: 1px solid #fff;
border-radius: 50%;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease;
box-shadow: 0 0 8px rgba(0, 255, 255, 0.6);
transform: translateX(-50%);
}
.progress-bar:hover .progress-handle {
opacity: 1;
}
.progress-handle:hover {
transform: translateX(-50%) scale(1.1);
box-shadow: 0 0 12px rgba(0, 255, 255, 0.8);
}
.player-controls {
position: absolute;
top: 0;

View File

@@ -129,6 +129,16 @@
</div>
</div>
</div>
<div id="progress-bar-container" class="progress-bar-container">
<div class="progress-info">
<span id="current-time">00:00</span>
<span id="total-time">00:00</span>
</div>
<div id="progress-bar" class="progress-bar">
<div id="progress-fill" class="progress-fill"></div>
<div id="progress-handle" class="progress-handle"></div>
</div>
</div>
<div id="codef-canvas"></div>
<div class="OVR hidden"></div>
<div class="ERRORS hidden"></div>

190
player.js
View File

@@ -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.');
}