Keep extension chains alive and save each finished segment.

The overall bar made a long queue look stuck at ~9% during the first clip, and nothing was written until the last step. Save checkpoints, use per-step progress, and stop treating a short Comfy history lag as a dropped job.
This commit is contained in:
2026-08-26 02:21:55 -05:00
parent f6feb41d62
commit d78c3fee7d
6 changed files with 78 additions and 30 deletions
+16
View File
@@ -320,6 +320,7 @@
</div>
<p v-if="chainTotal > 1" class="mb-2 text-xs text-amber-200/80">
Step {{ chainStep }}/{{ chainTotal }}{{ chainLabel ? `: ${chainLabel}` : '' }}
<template v-if="overallProgress"> · {{ overallProgress }}% overall</template>
</p>
<div class="h-2 overflow-hidden rounded-full bg-zinc-800">
<div class="h-full rounded-full bg-amber-400 progress-glow transition-all duration-300" :style="{ width: progress + '%' }" />
@@ -696,6 +697,7 @@ const maxStep = ref(8)
const chainStep = ref(1)
const chainTotal = ref(1)
const chainLabel = ref('')
const overallProgress = ref(0)
const elapsedMs = ref(0)
const videoUrl = ref('')
const jobId = ref('')
@@ -1297,6 +1299,7 @@ async function generateExtension() {
chainStep.value = 1
chainTotal.value = 1
chainLabel.value = ''
overallProgress.value = 0
statusMessage.value = 'Extracting last frame & stitching extension...'
startTimer()
try {
@@ -1473,6 +1476,7 @@ async function generate() {
chainStep.value = 1
chainTotal.value = 1 + extensionQueue.value.length
chainLabel.value = extensionQueue.value.length ? 'Initial' : ''
overallProgress.value = 0
startTimer()
try {
const body = new FormData()
@@ -1545,12 +1549,24 @@ function applyJobEvent(payload: Record<string, any>, hidden: boolean, folderLock
if (typeof payload.chainStep === 'number') chainStep.value = payload.chainStep
if (typeof payload.chainTotal === 'number') chainTotal.value = payload.chainTotal
if (payload.chainLabel) chainLabel.value = String(payload.chainLabel)
if (typeof payload.overallProgress === 'number') overallProgress.value = payload.overallProgress
const failed = payload.type === 'error' || payload.status === 'error' || payload.status === 'cancelled'
const complete = payload.type === 'complete' || payload.status === 'complete'
const deferred = payload.type === 'deferred' || payload.status === 'deferred'
if (settledUi) return
if (payload.type === 'checkpoint' && payload.clipId) {
loadLibrary().catch(() => null)
const locked = folderLocked || payload.folderLocked === true
if (!watchingLibrary.value && !locked) {
currentClipId.value = payload.clipId
videoUrl.value = clipVideoUrl(payload.clipId)
concealOutput.value = hidden || payload.hideThumbnail === true
}
return
}
if (deferred) {
settledUi = true
busy.value = false