feat: dodano pasek postępu odtwarzania w czasie rzeczywistym w panelu webowym
This commit is contained in:
5 files changed
+132
-1
No files matched your search
@@ -4,6 +4,11 @@ let currentStatus = null;
|
||||
let currentVolume = 100;
|
||||
let isUpdatingVolume = false;
|
||||
|
||||
// Progress Bar Variables
|
||||
let localElapsed = 0;
|
||||
let totalDuration = 0;
|
||||
let isPlaying = false;
|
||||
|
||||
// DOM Elements
|
||||
const statusDot = document.getElementById('status-dot');
|
||||
const statusText = document.getElementById('status-text');
|
||||
@@ -34,6 +39,12 @@ const emptyQueueMsg = document.getElementById('empty-queue-msg');
|
||||
const queueCountBadge = document.getElementById('queue-count');
|
||||
const toastContainer = document.getElementById('toast-container');
|
||||
|
||||
// Progress Bar DOM Elements
|
||||
const progressContainer = document.getElementById('progress-container');
|
||||
const timeElapsed = document.getElementById('time-elapsed');
|
||||
const timeTotal = document.getElementById('time-total');
|
||||
const progressBarFill = document.getElementById('progress-bar-fill');
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Initial fetch
|
||||
@@ -45,6 +56,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchStatus();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Start Smooth Progress Tracker (every 100ms)
|
||||
setInterval(() => {
|
||||
if (isPlaying && currentStatus && currentStatus.current) {
|
||||
localElapsed += 0.1;
|
||||
if (totalDuration > 0 && localElapsed > totalDuration) {
|
||||
localElapsed = totalDuration;
|
||||
}
|
||||
updateProgressBar();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Bind Event Listeners
|
||||
bindEvents();
|
||||
@@ -139,6 +161,28 @@ async function fetchStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
// Progress Bar Helpers
|
||||
function formatTime(secs) {
|
||||
if (secs === null || secs === undefined || isNaN(secs) || secs < 0) return "0:00";
|
||||
const h = Math.floor(secs / 3600);
|
||||
const m = Math.floor((secs % 3600) / 60);
|
||||
const s = Math.floor(secs % 60);
|
||||
const formattedSecs = s < 10 ? `0${s}` : s;
|
||||
if (h > 0) {
|
||||
const formattedMins = m < 10 ? `0${m}` : m;
|
||||
return `${h}:${formattedMins}:${formattedSecs}`;
|
||||
}
|
||||
return `${m}:${formattedSecs}`;
|
||||
}
|
||||
|
||||
function updateProgressBar() {
|
||||
timeElapsed.textContent = formatTime(localElapsed);
|
||||
if (totalDuration > 0) {
|
||||
const pct = Math.min((localElapsed / totalDuration) * 100, 100);
|
||||
progressBarFill.style.width = `${pct}%`;
|
||||
}
|
||||
}
|
||||
|
||||
// Update UI States
|
||||
function updateUI(status) {
|
||||
// 1. Connection Status & Available Channels
|
||||
@@ -219,6 +263,24 @@ function updateUI(status) {
|
||||
trackLink.href = status.current.url || '#';
|
||||
trackLink.style.display = status.current.url ? 'inline-flex' : 'none';
|
||||
|
||||
// Sync progress bar values
|
||||
totalDuration = status.current.duration || 0;
|
||||
localElapsed = status.current.elapsed || 0;
|
||||
isPlaying = status.playing; // Update playing state
|
||||
|
||||
// Show progress bar
|
||||
progressContainer.style.display = 'flex';
|
||||
timeElapsed.textContent = formatTime(localElapsed);
|
||||
|
||||
if (totalDuration > 0) {
|
||||
timeTotal.textContent = formatTime(totalDuration);
|
||||
const pct = Math.min((localElapsed / totalDuration) * 100, 100);
|
||||
progressBarFill.style.width = `${pct}%`;
|
||||
} else {
|
||||
timeTotal.textContent = "Live";
|
||||
progressBarFill.style.width = "100%";
|
||||
}
|
||||
|
||||
// Vinyl animation
|
||||
if (status.playing) {
|
||||
vinylRecord.classList.add('playing');
|
||||
@@ -233,6 +295,12 @@ function updateUI(status) {
|
||||
trackLink.style.display = 'none';
|
||||
vinylRecord.classList.remove('playing');
|
||||
musicWave.classList.remove('active');
|
||||
|
||||
// Hide progress bar
|
||||
progressContainer.style.display = 'none';
|
||||
isPlaying = false;
|
||||
localElapsed = 0;
|
||||
totalDuration = 0;
|
||||
}
|
||||
|
||||
// 3. Play/Pause Button State
|
||||
|
||||
@@ -90,6 +90,15 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Track Progress Bar -->
|
||||
<div class="track-progress-container" id="progress-container" style="display: none;">
|
||||
<span class="time-text" id="time-elapsed">0:00</span>
|
||||
<div class="progress-bar-bg" id="progress-bar-bg">
|
||||
<div class="progress-bar-fill" id="progress-bar-fill" style="width: 0%;"></div>
|
||||
</div>
|
||||
<span class="time-text" id="time-total">0:00</span>
|
||||
</div>
|
||||
|
||||
<!-- Progress / State (Simple status text) -->
|
||||
<div class="playback-state-bar">
|
||||
<span class="badge" id="loop-badge"><i data-lucide="repeat-1"></i> Zapętlenie aktywne</span>
|
||||
|
||||
@@ -438,6 +438,50 @@ body {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Progress Bar styling */
|
||||
.track-progress-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-bar-bg {
|
||||
flex-grow: 1;
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--transition-smooth);
|
||||
border: 1px solid rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.progress-bar-bg:hover {
|
||||
height: 8px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.progress-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
border-radius: 4px;
|
||||
width: 0%;
|
||||
box-shadow: 0 0 10px var(--primary-glow);
|
||||
transition: width 0.2s linear;
|
||||
}
|
||||
|
||||
.playback-state-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -118,7 +118,9 @@ async def get_status():
|
||||
"current": {
|
||||
"title": current_track.title,
|
||||
"url": current_track.data.get("webpage_url") or current_track.url,
|
||||
"id": current_track.id
|
||||
"id": current_track.id,
|
||||
"duration": current_track.duration,
|
||||
"elapsed": round(current_track.elapsed, 1)
|
||||
} if current_track else None,
|
||||
"queue": [
|
||||
{
|
||||
|
||||
@@ -32,6 +32,14 @@ class YTDLSource(discord.PCMVolumeTransformer):
|
||||
self.title = data.get('title')
|
||||
self.url = data.get('url')
|
||||
self.id = uuid.uuid4().hex # Unique track identifier
|
||||
self.duration = data.get('duration') # Duration in seconds
|
||||
self.elapsed = 0.0 # Elapsed time in seconds
|
||||
|
||||
def read(self):
|
||||
data = super().read()
|
||||
if data:
|
||||
self.elapsed += 0.02 # 20ms frames read
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
async def from_url(cls, url, *, loop=None, stream=False):
|
||||
|
||||
Reference in new issue
Block a user