Fix shot auto-run: do not settle or honor stale paused mid-chain, and keep Run remaining from dying after one shot.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-05 19:52:29 -05:00
co-authored by Cursor
parent 8ebbc0fc6e
commit 4dccaac6d3
3 changed files with 33 additions and 8 deletions
+7 -1
View File
@@ -1869,8 +1869,9 @@ export async function toggleStudioQueuePause(owner: string, options: {
}
export async function onStudioQueueBurstStarted(owner: string, queueId: string, liveJobId: string) {
const { isShotQueueCancelled } = await import('~/server/utils/shotQueue')
const { isShotQueueCancelled, getShotQueue } = await import('~/server/utils/shotQueue')
if (isShotQueueCancelled(queueId)) return
const queue = getShotQueue(owner, queueId)
await mutateStore(owner, (store) => {
const row = store.jobs.find(job => job.shotQueueId === queueId)
if (!row || row.status === 'cancelled') {
@@ -1882,6 +1883,11 @@ export async function onStudioQueueBurstStarted(owner: string, queueId: string,
row.pauseAfterCurrent = false
row.pausedByUser = false
row.holdForCutIn = false
// Run remaining / auto-run bursts must resume the rest without another click.
if (queue?.autoRun === true) {
row.resumeAutoRun = true
row.payload.queueAutoRun = true
}
row.updatedAt = Date.now()
syncPausedFlag(store)
})
+23 -4
View File
@@ -20,6 +20,7 @@ import {
isShotQueueCancelled,
lastCompletedIndex,
liveSegmentPrompt,
pendingSegmentCount,
prepareQueueBurst,
remainingFromQueue,
setQueueJob,
@@ -276,7 +277,10 @@ function chainShouldHold(job: Job) {
? getShotQueue(job.library.ownerKey, job.library.queueId)
: null
if (job.library.queueId && !queue) return true
if (queue?.stopAfterCurrent || queue?.status === 'paused') return true
if (queue?.stopAfterCurrent) return true
// A stale "paused" flag used to abort auto-run mid-batch after finishQueueBurst.
// Only honor paused when this live job is not explicitly auto-running.
if (queue?.status === 'paused' && job.library.queueAutoRun !== true) return true
return false
}
@@ -319,9 +323,12 @@ export async function continueQueuedExtensions(
const liveQueue = job.library.queueId
? getShotQueue(job.library.ownerKey, job.library.queueId)
: null
if (liveQueue?.stopAfterCurrent || liveQueue?.autoRun === false) {
if (liveQueue.stopAfterCurrent) job.library.stopAfterCurrent = true
if (liveQueue.autoRun === false) job.library.queueAutoRun = false
// User pause / cut-in only. Do not let a stale autoRun=false on disk cancel an
// in-flight "run remaining" / checkbox auto-run batch (options.autoRun).
if (liveQueue?.stopAfterCurrent) {
job.library.stopAfterCurrent = true
} else if (liveQueue?.autoRun === false && options.autoRun !== true) {
job.library.queueAutoRun = false
}
if (chainShouldHold(job)) {
job.library.stopAfterCurrent = true
@@ -494,12 +501,24 @@ export async function continueQueuedExtensions(
const paused = job.status !== 'error' && job.status !== 'cancelled'
const hold = job.library.stopAfterCurrent === true && paused
if (hold && job.status === 'running') job.status = 'complete'
// Keep autoRun on the shot queue when this burst was an explicit run-all and more
// shots remain — finishQueueBurst used to clear it and force one-click-per-shot.
const keepAuto = autoRun && !hold && job.status !== 'error' && job.status !== 'cancelled'
if (keepAuto && job.library) job.library.queueAutoRun = true
await finishQueueBurst(
job.library.ownerKey,
job.library.queueId,
hold || (paused && !job.library.queueAutoRun),
job.status === 'error' || job.status === 'cancelled' ? (job.error || 'Stopped') : undefined
).catch(() => null)
if (keepAuto) {
await updateShotQueue(job.library.ownerKey, job.library.queueId, (queue) => {
if (pendingSegmentCount(queue) > 0 && queue.status !== 'complete' && queue.status !== 'error') {
queue.autoRun = true
if (queue.status === 'paused') queue.status = 'idle'
}
}).catch(() => null)
}
setQueueJob(job.library.queueId, null)
}
}
+3 -3
View File
@@ -363,9 +363,9 @@ export function watchComfyJob(job: Job, options: { persist?: boolean } = {}): Pr
})
deletePendingJob(job.id)
job.saving = false
// Persist path: clear the studio "Generating" row as soon as the library save
// finishes. Waiting for runGeneration's finally used to leave jobs stuck in the queue.
if (persist && job.library?.ownerKey) {
// Only settle the studio row when this live job is done with the chain.
// Settling while chainContinuing killed overnight auto-run after every shot.
if (persist && job.library?.ownerKey && !job.library.chainContinuing) {
const { onLiveVideoSettled } = await import('~/server/utils/studioQueue')
await onLiveVideoSettled(job).catch(() => null)
}