Add Force clear for stuck queues including running YuE jobs.

Queue page and Music get a nuclear clear that cancels studio/live work, wipes shot batches, and force-stops Comfy so images/video/other music can run again.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-03 23:03:05 -05:00
co-authored by Cursor
parent 7da9e66f43
commit 780b407b3c
7 changed files with 105 additions and 10 deletions
+40 -4
View File
@@ -4,6 +4,7 @@
subtitle="Queue"
current="queue"
:queue-count="waitingCount || pendingCount"
@gpu="loadQueues"
/>
<main class="mx-auto min-w-0 max-w-7xl space-y-6 px-4 py-6 sm:px-6">
@@ -31,15 +32,25 @@
<button
type="button"
class="min-h-10 rounded-full border border-red-400/40 px-3 py-1.5 text-xs text-red-200 hover:border-red-300 hover:text-red-100 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="!clearableCount || Boolean(busyId)"
:disabled="!clearableCount || Boolean(busyId) || forceClearing"
@click="clearAll"
>
Clear queue
Clear waiting
</button>
<button
type="button"
class="min-h-10 rounded-full bg-red-500/90 px-3 py-1.5 text-xs font-semibold text-white hover:bg-red-400 disabled:cursor-not-allowed disabled:opacity-50"
:disabled="forceClearing || Boolean(busyId)"
title="Cancel running and waiting jobs, wipe shot batches, and kill Comfy if it is wedged"
@click="forceClearAll"
>
{{ forceClearing ? 'Force clearing…' : 'Force clear' }}
</button>
</div>
</div>
<p v-if="error" class="rounded-2xl border border-red-400/30 bg-red-400/10 px-4 py-3 text-sm text-red-200">{{ error }}</p>
<p v-else-if="forceClearNote" class="rounded-2xl border border-emerald-400/30 bg-emerald-400/10 px-4 py-3 text-sm text-emerald-100">{{ forceClearNote }}</p>
<p v-else-if="willPauseLatch" class="rounded-2xl border border-amber-300/30 bg-amber-400/10 px-4 py-3 text-sm text-amber-100">
Will pause after this video. The current clip keeps generating; this batch will stop after it finishes.
</p>
@@ -557,6 +568,8 @@ const pendingCount = ref(0)
const queuePaused = ref(false)
const error = ref('')
const busyId = ref('')
const forceClearing = ref(false)
const forceClearNote = ref('')
const liveMessage = ref<Record<string, string>>({})
const dirty = new Set<string>()
const sources = new Map<string, EventSource>()
@@ -1194,11 +1207,11 @@ async function remove(queue: Queue) {
async function clearAll() {
const n = clearableCount.value
if (!n) {
error.value = 'Pause the job that’s generating before clearing the queue.'
error.value = 'Nothing waiting to clear. Use Force clear if a running job is stuck.'
return
}
const running = jobs.value.some(job => job.status === 'running') || orphans.value.some(queue => queue.status === 'running')
const extra = running ? ' The job that’s generating will be left until you stop it.' : ''
const extra = running ? ' The job that’s generating will be left until you Force clear or cancel it.' : ''
if (!confirm(`Clear waiting and paused jobs from the queue? Saved clips stay in the library.${extra}`)) return
error.value = ''
try {
@@ -1212,6 +1225,29 @@ async function clearAll() {
}
}
async function forceClearAll() {
if (forceClearing.value) return
if (!confirm(
'Force clear cancels every running and waiting job (including stuck YuE/music), wipes shot batches, and kills Comfy if it is wedged. Saved library clips stay. Continue?'
)) return
forceClearing.value = true
error.value = ''
forceClearNote.value = ''
try {
const result = await $fetch<{
message?: string
cleared?: { cancelledStudio?: number; clearedPending?: number; cancelledLive?: number; clearedShotQueues?: number }
}>('/api/comfy/force-reset', { method: 'POST' })
const c = result.cleared || {}
await loadQueues()
forceClearNote.value = `Force cleared · studio ${c.cancelledStudio || 0}, live ${c.cancelledLive || 0}, shots ${c.clearedShotQueues || 0}. Comfy is stopped — Poke when ready.`
} catch (err: any) {
error.value = err?.data?.statusMessage || err?.statusMessage || err?.message || 'Could not force-clear the queue'
} finally {
forceClearing.value = false
}
}
let saveTimer: ReturnType<typeof setTimeout> | null = null
function saveSegment(queue: Queue, segment: Segment) {
markDirty(queue.id, segment.index)