feat: dodanie historii kolejki z możliwością ponownego odtwarzania w dashboardzie oraz komendy /history i /replay w Discord

This commit is contained in:
mmacierzanka committed 2026-07-04 15:35:53 +02:00
1 parent dfd681be04
commit 65d797a1a6
4 files changed
+248 -4

No files matched your search

+76
View File
@@ -50,6 +50,9 @@ const uploadStatusText = document.getElementById('upload-status-text');
const uploadsList = document.getElementById('uploads-list');
const emptyUploadsMsg = document.getElementById('empty-uploads-msg');
const uploadsCountBadge = document.getElementById('uploads-count');
const historyList = document.getElementById('history-list');
const emptyHistoryMsg = document.getElementById('empty-history-msg');
const historyCountBadge = document.getElementById('history-count');
// Progress Bar DOM Elements
const progressContainer = document.getElementById('progress-container');
@@ -390,6 +393,7 @@ function updateUI(status) {
// 6. Queue render (if not currently dragging)
if (!isDragging) {
renderQueue(status.queue);
renderHistory(status.history || []);
}
// Re-trigger lucide icons update
@@ -862,3 +866,75 @@ async function deleteUploadedFile(filename) {
}
}
// Render History
function renderHistory(history) {
if (!historyList) return;
const items = historyList.querySelectorAll('.queue-item');
items.forEach(el => el.remove());
if (!history || history.length === 0) {
if (emptyHistoryMsg) emptyHistoryMsg.style.display = 'flex';
if (historyCountBadge) historyCountBadge.textContent = '0 utworów';
return;
}
if (emptyHistoryMsg) emptyHistoryMsg.style.display = 'none';
if (historyCountBadge) historyCountBadge.textContent = `${history.length} ${history.length === 1 ? 'utwór' : history.length < 5 ? 'utwory' : 'utworów'}`;
history.forEach((item, index) => {
const li = document.createElement('li');
li.className = 'queue-item';
let durationStr = '--:--';
if (item.duration) {
const mins = Math.floor(item.duration / 60);
const secs = Math.floor(item.duration % 60);
durationStr = `${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
const safeTitle = (item.title || 'Nieznany tytuł').replace(/'/g, "\\'");
const safeQuery = (item.is_local ? (item.filepath || item.title) : item.url).replace(/'/g, "\\'");
li.innerHTML = `
<div class="queue-index"><i data-lucide="rotate-ccw"></i></div>
<div class="queue-track-info" style="flex: 1; min-width: 0;">
<div class="queue-track-title" title="${item.title}" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${item.title}</div>
<div class="queue-track-artist">${durationStr} ${item.is_local ? '• plik lokalny' : ''}</div>
</div>
<div class="queue-item-actions">
<button class="btn btn-secondary" style="padding: 4px 10px; font-size: 0.8rem; display: flex; align-items: center; gap: 4px;" onclick="replayFromHistory('${safeQuery}', ${item.is_local})" title="Dodaj ponownie do kolejki">
<i data-lucide="plus"></i> Dodaj
</button>
</div>
`;
historyList.appendChild(li);
});
lucide.createIcons();
}
// Replay from History
async function replayFromHistory(queryOrFile, isLocal) {
if (!currentStatus || !currentStatus.active_voice_channel) {
showToast('Bot musi być połączony z kanałem głosowym!', 'error');
return;
}
if (isLocal) {
const filename = queryOrFile.split('/').pop().split('\\').pop();
playUploadedFile(filename);
} else {
try {
const res = await fetch('/api/play', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: queryOrFile })
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail || 'Błąd odtwarzania');
showToast('Dodano z historii do kolejki!', 'success');
} catch (err) {
showToast(`Błąd: ${err.message}`, 'error');
}
}
}
+19 -3
View File
@@ -181,15 +181,31 @@
<!-- Queue List Panel -->
<div class="glass-card queue-panel">
<div class="queue-header">
<h2 class="card-title"><i data-lucide="list-music"></i> Kolejka utworów</h2>
<h2 class="card-title"><i data-lucide="list-music"></i> Kolejka odtwarzania</h2>
<span class="queue-count" id="queue-count">0 utworów</span>
</div>
<div class="queue-list-container">
<ul class="queue-list" id="queue-list">
<!-- Items dynamically injected here -->
<!-- Populated via JS -->
<div class="empty-queue-message" id="empty-queue-msg">
<i data-lucide="disc"></i>
<p>Kolejka jest pusta. Dodaj utwór powyżej!</p>
<p>Kolejka jest pusta. Dodaj coś!</p>
</div>
</ul>
</div>
</div>
<!-- History List Panel -->
<div class="glass-card history-panel">
<div class="queue-header">
<h2 class="card-title"><i data-lucide="history"></i> Historia odtwarzania</h2>
<span class="queue-count" id="history-count">0 utworów</span>
</div>
<div class="queue-list-container" style="max-height: 250px;">
<ul class="queue-list" id="history-list">
<div class="empty-queue-message" id="empty-history-msg">
<i data-lucide="clock"></i>
<p>Historia odtwarzania jest pusta.</p>
</div>
</ul>
</div>