Keep pause across Coolify restarts, apply the chosen still from the image picker, and remux clips with Range/faststart so finished video can be scrubbed.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+34
-15
@@ -9,7 +9,7 @@ import { isLtxWorkflow, isTextToVideo, parseVideoWorkflow, workflowForExtension,
|
||||
import { clipVideoPath, clipTitle, deleteRetryDraft, extendTempDir, getClip, nextClipPartName, removeExtendTemp, stillPath } from '~/server/utils/library'
|
||||
import { extractLastFrame, probeHasAudio } from '~/server/utils/ffmpeg'
|
||||
import { ensureComfyReady } from '~/server/utils/comfyLifecycle'
|
||||
import { onLiveVideoSettled, onStudioQueueBurstStarted } from '~/server/utils/studioQueue'
|
||||
import { onLiveVideoSettled, onStudioQueueBurstStarted, isStudioQueuePaused } from '~/server/utils/studioQueue'
|
||||
import {
|
||||
finishQueueBurst,
|
||||
getShotQueue,
|
||||
@@ -238,6 +238,17 @@ function seedCurrentVideo(job: Job) {
|
||||
return { tmpDir, currentPath, part1Path: join(tmpDir, 'part1.mp4'), framePath: join(tmpDir, 'last_frame.png') }
|
||||
}
|
||||
|
||||
function chainShouldHold(job: Job) {
|
||||
if (!job.library) return false
|
||||
if (job.library.stopAfterCurrent) return true
|
||||
if (isStudioQueuePaused(job.library.ownerKey)) return true
|
||||
const queue = job.library.queueId
|
||||
? getShotQueue(job.library.ownerKey, job.library.queueId)
|
||||
: null
|
||||
if (queue?.stopAfterCurrent || queue?.status === 'paused') return true
|
||||
return false
|
||||
}
|
||||
|
||||
export async function continueQueuedExtensions(
|
||||
job: Job,
|
||||
params: VideoChainParams,
|
||||
@@ -269,14 +280,22 @@ export async function continueQueuedExtensions(
|
||||
for (let i = startFrom; i < extensions.length; i++) {
|
||||
if (shotsLeft <= 0) break
|
||||
assertJobActive(job)
|
||||
if (job.library.stopAfterCurrent) break
|
||||
if (chainShouldHold(job)) {
|
||||
job.library.stopAfterCurrent = true
|
||||
break
|
||||
}
|
||||
|
||||
const liveQueue = job.library.queueId
|
||||
? getShotQueue(job.library.ownerKey, job.library.queueId)
|
||||
: null
|
||||
if (liveQueue?.stopAfterCurrent) job.library.stopAfterCurrent = true
|
||||
if (liveQueue?.autoRun === false) job.library.queueAutoRun = false
|
||||
if (job.library.stopAfterCurrent) break
|
||||
if (liveQueue?.stopAfterCurrent || liveQueue?.autoRun === false) {
|
||||
if (liveQueue.stopAfterCurrent) job.library.stopAfterCurrent = true
|
||||
if (liveQueue.autoRun === false) job.library.queueAutoRun = false
|
||||
}
|
||||
if (chainShouldHold(job)) {
|
||||
job.library.stopAfterCurrent = true
|
||||
break
|
||||
}
|
||||
const live = liveQueue ? liveSegmentPrompt(liveQueue, i) : extensions[i]
|
||||
const ext = {
|
||||
prompt: live.prompt || extensions[i].prompt,
|
||||
@@ -289,10 +308,7 @@ export async function continueQueuedExtensions(
|
||||
emitChainJob(job, { type: 'status', message: 'Waiting 3s buffer...', progress: 1 })
|
||||
await sleep(3000)
|
||||
assertJobActive(job)
|
||||
const pausedQueue = job.library.queueId
|
||||
? getShotQueue(job.library.ownerKey, job.library.queueId)
|
||||
: null
|
||||
if (job.library.stopAfterCurrent || pausedQueue?.stopAfterCurrent) {
|
||||
if (chainShouldHold(job)) {
|
||||
job.library.stopAfterCurrent = true
|
||||
break
|
||||
}
|
||||
@@ -376,7 +392,10 @@ export async function continueQueuedExtensions(
|
||||
job.library.extendPart1Path = undefined
|
||||
}
|
||||
|
||||
if (shotsLeft <= 0 || job.library.stopAfterCurrent) break
|
||||
if (shotsLeft <= 0 || chainShouldHold(job)) {
|
||||
if (chainShouldHold(job)) job.library.stopAfterCurrent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (job.library) job.library.chainContinuing = false
|
||||
@@ -398,7 +417,10 @@ export async function continueQueuedExtensions(
|
||||
export async function continueQueuedExtensionsIfNeeded(job: Job) {
|
||||
if (!job.library) return
|
||||
if (job.status === 'error' || job.status === 'cancelled' || job.status === 'complete') return
|
||||
if (job.library.stopAfterCurrent) return
|
||||
if (chainShouldHold(job)) {
|
||||
job.library.stopAfterCurrent = true
|
||||
return
|
||||
}
|
||||
const remaining = remainingAfterCurrentShot(job.library)
|
||||
if (!remaining.length) return
|
||||
const queue = job.library.queueId
|
||||
@@ -440,10 +462,7 @@ export async function runGeneration(job: Job, params: VideoChainParams) {
|
||||
persist: extensions.length === 0 || !autoRun
|
||||
})
|
||||
|
||||
const stopNow = job.library?.stopAfterCurrent === true
|
||||
|| (job.library?.queueId
|
||||
? getShotQueue(job.library.ownerKey, job.library.queueId)?.stopAfterCurrent === true
|
||||
: false)
|
||||
const stopNow = chainShouldHold(job)
|
||||
const keepGoing = Boolean(autoRun && !stopNow && extensions.length && job.library)
|
||||
if (job.library?.queueId && !keepGoing) {
|
||||
await finishQueueBurst(job.library.ownerKey, job.library.queueId, true).catch(() => null)
|
||||
|
||||
Reference in New Issue
Block a user