discord-jukebox-bot/pkg/web/static/app.js

125 lines
No EOL
12 KiB
JavaScript

// DOM Elements
const statusIndicator = document.getElementById('status-indicator');
const statusDot = document.querySelector('.status-dot');
const statusText = document.getElementById('status-text');
const nowPlaying = document.getElementById('now-playing');
const notPlaying = document.getElementById('not-playing');
const lastUpdate = document.getElementById('last-update');
// Song info elements
const coverArt = document.getElementById('cover-art');
const songTitle = document.getElementById('song-title');
const songArtist = document.getElementById('song-artist');
const songAlbum = document.getElementById('song-album');
const songDuration = document.getElementById('song-duration');
const songGenre = document.getElementById('song-genre');
const songYear = document.getElementById('song-year');
// Base64 encoded default cover art (simple music note icon)
const DEFAULT_COVER_ART = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF+mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAyMy0wNC0wMVQxNTozODoxOSswMjowMCIgeG1wOk1vZGlmeURhdGU9IjIwMjMtMDQtMDFUMTU6NDE6MDcrMDI6MDAiIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMDQtMDFUMTU6NDE6MDcrMDI6MDAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MjZiMGE4MDQtZWYxZS1hMDQ0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';
// Initial status load
fetchStatus();
// Set up Server-Sent Events for live updates
setupEventSource();
// Functions
function setupEventSource() {
const eventSource = new EventSource('/events');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
updateUI(data);
};
eventSource.onerror = function() {
// If connection fails, close it and retry after a delay
eventSource.close();
statusDot.className = 'status-dot offline';
statusText.textContent = 'Connection lost. Retrying...';
setTimeout(setupEventSource, 5000);
};
}
function fetchStatus() {
fetch('/status')
.then(response => response.json())
.then(data => {
updateUI(data);
})
.catch(error => {
console.error('Error fetching status:', error);
statusDot.className = 'status-dot offline';
statusText.textContent = 'Error connecting to server';
});
}
function updateUI(data) {
// Update status indicator
if (data.isPlaying) {
statusDot.className = 'status-dot online';
statusText.textContent = 'Playing music';
nowPlaying.classList.remove('hidden');
notPlaying.classList.add('hidden');
} else {
statusDot.className = 'status-dot offline';
statusText.textContent = 'Not playing';
nowPlaying.classList.add('hidden');
notPlaying.classList.remove('hidden');
}
// Update song information if we have a current song
if (data.currentSong) {
updateSongInfo(data.currentSong);
}
// Update last update time
if (data.updateTime) {
const updateDate = new Date(data.updateTime);
lastUpdate.textContent = formatDate(updateDate);
}
}
function updateSongInfo(song) {
// Update the song title
songTitle.textContent = song.title || 'Unknown Title';
// Update the artist
songArtist.textContent = song.artist || 'Unknown Artist';
// Update the album
songAlbum.textContent = song.album || 'Unknown Album';
// Update the duration if available
if (song.duration) {
const minutes = Math.floor(song.duration / 60);
const seconds = song.duration % 60;
songDuration.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
} else {
songDuration.textContent = '--';
}
// Update the genre if available
songGenre.textContent = song.genre || '--';
// Update the year if available
songYear.textContent = song.year ? song.year.toString() : '--';
// Update the cover art if available
if (song.coverArt) {
// This assumes your backend has a route to get cover art
// You might need to adjust this to match your API
const coverArtUrl = `/cover/${song.coverArt}`;
coverArt.src = coverArtUrl;
coverArt.alt = `${song.album} cover`;
} else {
coverArt.src = DEFAULT_COVER_ART;
coverArt.alt = 'Default cover art';
}
}
function formatDate(date) {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}