Add queue download progress

This commit is contained in:
Codex
2026-08-09 12:55:06 +02:00
parent 499271c3b8
commit 11f43bba05
7 changed files with 271 additions and 14 deletions
+75
View File
@@ -269,6 +269,36 @@ QUEUE_HTML = r"""<!doctype html>
.status.done { color: #082312; background: var(--good); }
.status.failed { color: #2d0910; background: var(--bad); }
.status.canceled { color: #2d1c00; background: var(--warn); }
.progress-wrap {
display: grid;
gap: 8px;
min-width: 0;
}
.progress-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
color: var(--muted);
font-size: 12px;
}
.progress-label {
overflow-wrap: anywhere;
}
.progress-track {
height: 10px;
border-radius: 999px;
overflow: hidden;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.progress-fill {
width: 0%;
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, var(--accent), #75c9ff);
transition: width 0.28s ease;
}
pre {
margin: 0;
max-height: 220px;
@@ -408,6 +438,38 @@ QUEUE_HTML = r"""<!doctype html>
return [...(job.log || [])].slice(-28).reverse().join("\n");
}
function progressPercent(job) {
const progress = job.progress || {};
const value = Number(progress.percent || 0);
if (!Number.isFinite(value)) return job.status === "done" ? 100 : 0;
return Math.max(0, Math.min(100, Math.round(value)));
}
function progressLabel(job) {
const progress = job.progress || {};
if (progress.message) return progress.message;
if (job.status === "running") return "Download running.";
if (job.status === "pending") return "Waiting for worker.";
if (job.status === "done") return "Download completed.";
if (job.status === "failed") return "Download failed.";
if (job.status === "canceled") return "Download canceled.";
return "Waiting.";
}
function progressDetail(job) {
const progress = job.progress || {};
const parts = [];
if (progress.episode_index && progress.episode_total) {
parts.push(`episode ${progress.episode_index}/${progress.episode_total}`);
} else if (progress.episode) {
parts.push(`episode ${progress.episode}`);
}
if (progress.segment && progress.segment_total) {
parts.push(`segment ${progress.segment}/${progress.segment_total}`);
}
return parts.join(" · ");
}
function renderJobActions(job, actions) {
actions.innerHTML = "";
if (job.job_type === "jellyfin_handoff") {
@@ -436,6 +498,11 @@ QUEUE_HTML = r"""<!doctype html>
status.className = "status";
status.classList.add(job.status);
item.querySelector("pre").textContent = buildJobLog(job) || "Waiting...";
const percent = progressPercent(job);
item.querySelector(".progress-fill").style.width = `${percent}%`;
item.querySelector(".progress-percent").textContent = `${percent}%`;
item.querySelector(".progress-label").textContent = progressLabel(job);
item.querySelector(".progress-detail").textContent = progressDetail(job);
item.querySelector(".toolbar .muted").textContent = job.exit_code === null || job.exit_code === undefined ? "" : `exit ${job.exit_code}`;
renderJobActions(job, item.querySelector(".row"));
state.jobCards[job.id] = item;
@@ -456,6 +523,14 @@ QUEUE_HTML = r"""<!doctype html>
<span class="status"></span>
</div>
<div class="job-body">
<div class="progress-wrap">
<div class="progress-meta">
<span class="progress-label"></span>
<span class="progress-percent"></span>
</div>
<div class="progress-track" aria-hidden="true"><div class="progress-fill"></div></div>
<div class="progress-meta"><span class="progress-detail"></span></div>
</div>
<pre></pre>
<div class="toolbar">
<span class="muted"></span>