feat: Live-Download-Fortschritt + Jobs abbrechen
Job-Engine:
- Ausgabe wird binaer + byteweise gelesen und CRLF-bewusst verarbeitet:
einzelnes \r (tqdm/hf-Fortschritt) ueberschreibt die letzte Logzeile statt
sie zu fluten; \r\n bzw. \n = echte neue Zeile. So kommt der Download-
Fortschritt live an. (Text-Modus wuerde \r zu \n uebersetzen -> daher binaer.)
- cancel_job(): laufenden Prozess terminieren, Status "canceled", _PROCS-Registry
- POST /api/jobs/{id}/cancel
Frontend:
- Aktivitaet: Fortschrittsbalken + %-Anzeige aus der letzten Logzeile,
"Abbrechen"-Button je laufendem Job, Status "abgebrochen"
- Server/Aktueller Vorgang: Fortschrittsbalken + Abbrechen (mit Rueckfrage),
"abgebrochen"-Behandlung
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -39,8 +39,11 @@ function mount() {
|
||||
</div>
|
||||
|
||||
<div class="card" id="w-update-card" style="display:none">
|
||||
<div class="card-h"><h3>Aktueller Vorgang</h3><span id="w-update-badge" style="margin-left:auto"></span></div>
|
||||
<div class="card-h"><h3>Aktueller Vorgang</h3>
|
||||
<button class="ghost" id="w-update-cancel" style="margin-left:auto;display:none;padding:3px 12px">Abbrechen</button>
|
||||
<span id="w-update-badge" style="margin-left:10px"></span></div>
|
||||
<div class="card-sub" id="w-update-label">—</div>
|
||||
<div id="w-update-bar" class="bar" style="display:none;margin-bottom:10px"><i style="width:0%"></i></div>
|
||||
<div id="w-update-log" class="log" style="max-height:280px"></div>
|
||||
</div>
|
||||
|
||||
@@ -67,9 +70,17 @@ function mount() {
|
||||
$("#w-os-update").addEventListener("click", osUpdate);
|
||||
$("#w-reboot").addEventListener("click", rebootServer);
|
||||
$("#w-console-sel").addEventListener("change", connectConsole);
|
||||
$("#w-update-cancel").addEventListener("click", cancelActive);
|
||||
connectConsole();
|
||||
}
|
||||
|
||||
async function cancelActive() {
|
||||
if (!activeJob) return;
|
||||
if (!await confirmModal({ title: activeJob.label + " abbrechen?", body: "Der laufende Vorgang wird gestoppt. Bei einem Download bleibt eine unvollständige Datei zurück (kannst du später neu starten).", confirmLabel: "Abbrechen", danger: true })) return;
|
||||
try { await api(`/api/jobs/${activeJob.id}/cancel`, { method: "POST" }); toast("Abbruch angefordert…"); }
|
||||
catch (e) { toast(e.message, true); }
|
||||
}
|
||||
|
||||
let ws = null;
|
||||
function connectConsole() {
|
||||
if (ws) { ws.close(); ws = null; }
|
||||
@@ -122,16 +133,32 @@ function onJobs(jobs) {
|
||||
card.style.display = "";
|
||||
const logEl = $("#w-update-log");
|
||||
if (logEl) { logEl.textContent = (j.log || []).join("\n"); logEl.scrollTop = logEl.scrollHeight; }
|
||||
const done = j.state === "done", failed = j.state === "failed", canceled = j.state === "canceled";
|
||||
const terminal = done || failed || canceled;
|
||||
const badge = $("#w-update-badge");
|
||||
const done = j.state === "done", failed = j.state === "failed";
|
||||
if (badge) badge.innerHTML = done ? '<span class="badge b-run">✓ fertig</span>'
|
||||
: failed ? '<span class="badge b-err">✗ fehlgeschlagen</span>'
|
||||
: canceled ? '<span class="badge">abgebrochen</span>'
|
||||
: '<span class="badge b-load">läuft…</span>';
|
||||
if (done || failed) {
|
||||
$("#w-update-label").textContent = activeJob.label + (done ? " — erfolgreich abgeschlossen." : " — fehlgeschlagen (siehe Protokoll).");
|
||||
|
||||
// Fortschrittsbalken aus der letzten Log-Zeile (tqdm: " 45%|…")
|
||||
const last = (j.log || [])[j.log.length - 1] || "";
|
||||
const pm = !terminal && last.match(/(\d+(?:\.\d+)?)\s*%/);
|
||||
const bar = $("#w-update-bar");
|
||||
if (bar) {
|
||||
if (pm) { bar.style.display = ""; bar.querySelector("i").style.width = Math.min(100, parseFloat(pm[1])) + "%"; }
|
||||
else bar.style.display = "none";
|
||||
}
|
||||
$("#w-update-cancel").style.display = terminal ? "none" : "";
|
||||
|
||||
if (terminal) {
|
||||
$("#w-update-label").textContent = activeJob.label +
|
||||
(done ? " — erfolgreich abgeschlossen." : canceled ? " — abgebrochen." : " — fehlgeschlagen (siehe Protokoll).");
|
||||
if (!notified.has(j.id)) {
|
||||
notified.add(j.id);
|
||||
toast(done ? "✓ " + activeJob.label + " abgeschlossen." : "✗ " + activeJob.label + " fehlgeschlagen.", failed);
|
||||
toast(done ? "✓ " + activeJob.label + " abgeschlossen."
|
||||
: canceled ? activeJob.label + " abgebrochen."
|
||||
: "✗ " + activeJob.label + " fehlgeschlagen.", failed);
|
||||
if (done) setTimeout(refreshSoon, 800);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user