/**
* WebSocket-Client fuer Echtzeit-Updates
* Verbindet sich mit dem Server und aktualisiert Dashboard dynamisch
*/
let ws = null;
let videoActive = {};
let videoQueue = {};
let reconnectTimer = null;
let queuePaused = false;
// WebSocket verbinden
function connectWebSocket() {
if (!window.WS_URL) return;
ws = new WebSocket(WS_URL);
ws.onopen = function () {
console.log("WebSocket verbunden:", WS_URL);
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
};
ws.onmessage = function (event) {
try {
const packet = JSON.parse(event.data);
if (packet.data_flow !== undefined) {
updateProgress(packet.data_flow);
} else if (packet.data_convert !== undefined) {
updateActiveConversions(packet.data_convert);
} else if (packet.data_queue !== undefined) {
updateQueue(packet.data_queue);
if (packet.queue_paused !== undefined) {
updatePauseState(packet.queue_paused);
}
} else if (packet.data_log !== undefined) {
// Log-Nachrichten ans Benachrichtigungs-System weiterleiten
if (typeof addNotification === "function") {
addNotification(packet.data_log.message, packet.data_log.level);
}
} else if (packet.data_import !== undefined) {
// Import-Fortschritt an Library weiterleiten
if (typeof handleImportWS === "function") {
handleImportWS(packet.data_import);
}
} else if (packet.data_library_scan !== undefined) {
// Scan-Fortschritt an Library weiterleiten
if (typeof handleScanWS === "function") {
handleScanWS(packet.data_library_scan);
}
}
// Globaler Progress-Balken aktualisieren
if (typeof _updateGlobalProgress === "function") {
_updateGlobalProgress(packet);
}
} catch (e) {
console.error("WebSocket Nachricht parsen fehlgeschlagen:", e);
}
};
ws.onclose = function () {
console.log("WebSocket getrennt, Reconnect in 3s...");
reconnectTimer = setTimeout(connectWebSocket, 3000);
};
ws.onerror = function (err) {
console.error("WebSocket Fehler:", err);
};
}
// === Aktive Konvertierungen ===
function updateActiveConversions(data) {
const container = document.getElementById("active-conversions");
if (!container) return;
// Entfernte Jobs loeschen
for (const key in videoActive) {
if (!(key in data)) {
const elem = document.getElementById("convert_" + key);
if (elem) elem.remove();
delete videoActive[key];
}
}
// Neue Jobs hinzufuegen
for (const [key, video] of Object.entries(data)) {
if (!videoActive[key]) {
const card = document.createElement("div");
card.className = "video-card";
card.id = "convert_" + key;
card.innerHTML = `
${video.source_file_name} → ${video.target_file_name}
0%
Frames
0
FPS
0
Speed
0x
Groesse
0 KiB
Bitrate
0 kbits/s
Zeit
0 Min
Verbleibend
-
`;
container.appendChild(card);
videoActive[key] = video;
}
}
}
function updateProgress(flow) {
const container = document.getElementById("convert_" + flow.id);
if (!container) return;
container.querySelector(".frames").textContent = flow.frames || 0;
container.querySelector(".fps").textContent = flow.fps || 0;
container.querySelector(".speed").textContent = flow.speed || 0;
container.querySelector(".size").textContent = flow.size ? flow.size[0] : 0;
container.querySelector(".size_unit").textContent = flow.size ? flow.size[1] : "KiB";
container.querySelector(".bitrate").textContent = flow.bitrate ? flow.bitrate[0] : 0;
container.querySelector(".bitrate_unit").textContent = flow.bitrate ? flow.bitrate[1] : "kbits/s";
container.querySelector(".time").textContent = flow.time || "0 Min";
container.querySelector(".eta").textContent = flow.time_remaining || "-";
container.querySelector(".loading-pct").textContent = (flow.loading || 0).toFixed(1);
const bar = container.querySelector(".progress-bar");
bar.style.width = (flow.loading || 0) + "%";
}
// === Warteschlange ===
function updateQueue(data) {
const container = document.getElementById("queue");
if (!container) return;
// Entfernte/geaenderte Jobs loeschen
for (const key in videoQueue) {
if (!(key in data) || videoQueue[key]?.status !== data[key]?.status) {
const elem = document.getElementById("queue_" + key);
if (elem) elem.remove();
delete videoQueue[key];
}
}
// Neue Jobs hinzufuegen
for (const [key, video] of Object.entries(data)) {
if (!videoQueue[key]) {
const card = document.createElement("div");
card.className = "queue-card";
card.id = "queue_" + key;
let statusHtml;
if (video.status === 1) {
statusHtml = 'Aktiv';
} else if (video.status === 3) {
statusHtml = 'Fehler';
} else if (video.status === 4) {
statusHtml = 'Abgebrochen';
} else {
statusHtml = 'Wartend';
}
card.innerHTML = `
${video.source_file_name}
`;
container.appendChild(card);
videoQueue[key] = video;
}
}
}
// === Befehle senden ===
function sendCommand(command, id) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
data_command: { cmd: command, id: id }
}));
} else {
console.warn("WebSocket nicht verbunden");
}
}
// === Queue Pause/Resume ===
function updatePauseState(paused) {
queuePaused = paused;
const btn = document.getElementById("btn-queue-pause");
const hint = document.getElementById("queue-paused-hint");
if (btn) {
btn.innerHTML = paused ? "▶ Weiter" : "⏸ Pause";
btn.className = paused ? "btn-primary btn-small" : "btn-secondary btn-small";
}
if (hint) {
hint.style.display = paused ? "" : "none";
}
}
function toggleQueuePause() {
const url = queuePaused ? "/api/queue/resume" : "/api/queue/pause";
fetch(url, {method: "POST"})
.then(r => r.json())
.then(data => updatePauseState(data.paused))
.catch(e => console.error("Pause/Resume fehlgeschlagen:", e));
}
// Verbindung herstellen
connectWebSocket();