102 lines
3.2 KiB
JavaScript
102 lines
3.2 KiB
JavaScript
|
|
function formatTime(seconds) {
|
|
const m = Math.floor(seconds / 60);
|
|
const s = Math.floor(seconds % 60).toString().padStart(2, '0');
|
|
return `${m}:${s}`;
|
|
}
|
|
|
|
const removeFromQueue = async (trackId) => {
|
|
await api(`/api/queue/${trackId}`, { method: "DELETE" });
|
|
updateQueue(); // Refresh queue after deletion
|
|
};
|
|
|
|
|
|
function updateProgress(position, duration) {
|
|
const progressBar = document.getElementById("progressBar");
|
|
const elapsedTime = document.getElementById("elapsedTime");
|
|
const totalTime = document.getElementById("totalTime");
|
|
|
|
progressBar.max = duration;
|
|
progressBar.value = position;
|
|
elapsedTime.textContent = formatTime(position);
|
|
totalTime.textContent = formatTime(duration);
|
|
}
|
|
|
|
|
|
const api = (endpoint, options = {}) =>
|
|
fetch(endpoint, options).then(res => res.json()).catch(console.error);
|
|
|
|
const setVolume = async (val) => {
|
|
await api(`/api/player/volume?volume=${val}`, { method: "PUT" });
|
|
document.getElementById("volumeValue").textContent = val;
|
|
};
|
|
|
|
const addToQueue = async () => {
|
|
const url = document.getElementById("trackUrl").value;
|
|
await api(`/api/queue?url=${encodeURIComponent(url)}`, { method: "POST" });
|
|
updateQueue();
|
|
};
|
|
|
|
const play = async () => { await api("/api/player/play", { method: "POST" });};
|
|
const stop = async () => { await api("/api/player/stop", { method: "POST" });};
|
|
const skip = async () => { await api("/api/player/skip", { method: "POST" });};
|
|
|
|
// WebSocket connections
|
|
let playerSocket, queueSocket;
|
|
|
|
function connectWebSockets() {
|
|
const proto = location.protocol === "https:" ? "wss" : "ws";
|
|
const base = `${proto}://${location.host}/api`;
|
|
|
|
playerSocket = new WebSocket(`${base}/player`);
|
|
queueSocket = new WebSocket(`${base}/queue`);
|
|
|
|
playerSocket.onopen = () => playerSocket.send("ping");
|
|
queueSocket.onopen = () => queueSocket.send("ping");
|
|
|
|
playerSocket.onmessage = (event) => {
|
|
const state = JSON.parse(event.data);
|
|
|
|
const { playback_state, track, position, volume } = state;
|
|
|
|
document.getElementById("trackArtist").textContent = track?.artist || "-";
|
|
document.getElementById("trackTitle").textContent = track?.title || "-";
|
|
document.getElementById("playbackState").textContent = playback_state;
|
|
|
|
document.getElementById("volumeSlider").value = volume;
|
|
document.getElementById("volumeValue").textContent = volume;
|
|
|
|
if (track) {
|
|
updateProgress(position, track.duration);
|
|
}
|
|
};
|
|
|
|
queueSocket.onmessage = (event) => {
|
|
const queue = JSON.parse(event.data);
|
|
const queueBody = document.getElementById("queueBody");
|
|
queueBody.innerHTML = "";
|
|
|
|
(queue.items || queue).forEach((track, index) => {
|
|
const row = document.createElement("tr");
|
|
|
|
row.innerHTML = `
|
|
<td style="padding: 8px;">${index + 1}</td>
|
|
<td style="padding: 8px;">${track.artist}</td>
|
|
<td style="padding: 8px;">${track.title}</td>
|
|
<td style="padding: 8px; text-align: right;">${formatTime(track.duration)}</td>
|
|
<td style="padding: 8px; text-align: center;">
|
|
<button onclick="removeFromQueue('${track.id}')">🗑️</button>
|
|
</td>
|
|
`;
|
|
|
|
queueBody.appendChild(row);
|
|
});
|
|
};
|
|
|
|
playerSocket.onerror = queueSocket.onerror = console.error;
|
|
playerSocket.onclose = () => setTimeout(connectWebSockets, 1000);
|
|
queueSocket.onclose = () => setTimeout(connectWebSockets, 1000);
|
|
}
|
|
|
|
connectWebSockets();
|
|
|