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