Add clip extension from the last frame with FFmpeg stitching.

Let a finished player continue into a new MiniMax segment, then losslessly concat the parts and discard the intermediate files so the library keeps one extended MP4.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-25 23:06:58 -05:00
co-authored by Cursor
parent dbe45fd2ff
commit e81f12f89a
10 changed files with 666 additions and 18 deletions
+25 -4
View File
@@ -1,4 +1,4 @@
import { NODE_LABELS, isEncodingNode } from '~/server/utils/workflow'
import { existsSync } from 'node:fs'
import type { Job } from '~/server/utils/jobs'
function classifyError(message: string) {
@@ -42,6 +42,7 @@ export function watchComfyJob(job: Job): Promise<void> {
job.error = classifyError(error)
emitJob(job, { type: 'error', error: job.error, message: job.error })
if (job.id) deletePendingJob(job.id)
removeExtendTemp(job.library?.extendTmpDir)
resolve()
}
@@ -54,9 +55,24 @@ export function watchComfyJob(job: Job): Promise<void> {
cleanup()
try {
job.video = video
emitJob(job, { type: 'status', message: 'Saving to library...', progress: 98 })
const stitching = Boolean(job.library?.extendPart1Path && job.library.extendTmpDir)
emitJob(job, {
type: 'status',
message: stitching ? 'Extracting last frame & stitching extension...' : 'Saving to library...',
progress: stitching ? 97 : 98
})
if (job.library) {
const buffer = await downloadComfyVideo(video)
let buffer = await downloadComfyVideo(video)
if (stitching) {
if (!existsSync(job.library.extendPart1Path!)) {
throw new Error('Extension source clip was missing during stitch')
}
buffer = await stitchExtension({
part1Path: job.library.extendPart1Path!,
part2: buffer,
tmpDir: job.library.extendTmpDir!
})
}
const clip = await saveClip({
ownerKey: job.library.ownerKey,
folderId: job.library.folderId,
@@ -87,11 +103,15 @@ export function watchComfyJob(job: Job): Promise<void> {
imageSubfolder: job.library.imageSubfolder,
promptId: job.promptId
})
removeExtendTemp(job.library.extendTmpDir)
job.library.extendTmpDir = undefined
}
job.status = 'complete'
emitJob(job, {
type: 'complete',
message: job.library?.folderLocked ? 'Saved to the locked folder. Unlock it to view.' : 'Video ready',
message: job.library?.folderLocked
? 'Saved to the locked folder. Unlock it to view.'
: (job.library?.extendSourceClipId ? 'Extended video ready' : 'Video ready'),
progress: 100,
filename: job.library?.folderLocked ? undefined : video.filename,
subfolder: job.library?.folderLocked ? undefined : video.subfolder,
@@ -102,6 +122,7 @@ export function watchComfyJob(job: Job): Promise<void> {
})
deletePendingJob(job.id)
} catch (saveError) {
removeExtendTemp(job.library?.extendTmpDir)
const message = saveError instanceof Error ? saveError.message : String(saveError)
job.status = 'error'
job.error = `Video generated but library save failed: ${message}`