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
+23 -2
View File
@@ -20,6 +20,8 @@ export interface PendingJob {
startedAt: number
imageName?: string
imageSubfolder?: string
extendTmpDir?: string
extendPart1Path?: string
}
function pendingRoot() {
@@ -68,7 +70,24 @@ export async function completePendingIfReady(pending: PendingJob) {
const history = await fetchHistory(pending.promptId)
const video = extractVideo(history, pending.promptId)
if (!video) return null
const buffer = await downloadComfyVideo(video)
let buffer = await downloadComfyVideo(video)
if (pending.extendPart1Path && pending.extendTmpDir) {
if (!existsSync(pending.extendPart1Path)) {
removeExtendTemp(pending.extendTmpDir)
throw createError({ statusCode: 500, statusMessage: 'Extension source clip was missing during stitch' })
}
try {
buffer = await stitchExtension({
part1Path: pending.extendPart1Path,
part2: buffer,
tmpDir: pending.extendTmpDir
})
} catch (error) {
removeExtendTemp(pending.extendTmpDir)
throw error
}
removeExtendTemp(pending.extendTmpDir)
}
const clip = await saveClip({
ownerKey: pending.ownerKey,
folderId: pending.folderId,
@@ -90,7 +109,9 @@ export async function completePendingIfReady(pending: PendingJob) {
return {
type: 'complete' as const,
status: 'complete' as const,
message: pending.folderLocked ? 'Saved to the locked folder. Unlock it to view.' : 'Video ready',
message: pending.folderLocked
? 'Saved to the locked folder. Unlock it to view.'
: (pending.extendPart1Path ? 'Extended video ready' : 'Video ready'),
progress: 100,
jobId: pending.jobId,
clipId: clip.id,