Stop ghost extension clips and recover last-frame extract after an interrupt.

Only one Comfy finish path can save a clip, hide catalog entries whose MP4 is missing, and retry last-frame extract so a truncated file does not dump FFmpeg noise into the UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-26 11:34:44 -05:00
co-authored by Cursor
parent db8c9aabba
commit 551c29a62a
8 changed files with 201 additions and 125 deletions
+23 -11
View File
@@ -63,18 +63,30 @@ export async function extractLastFrame(inputPath: string, outputPath: string) {
if (!existsSync(inputPath)) {
throw new Error('Source video is missing')
}
await runFfmpeg([
'-y',
'-sseof', '-1',
'-i', inputPath,
'-update', '1',
'-frames:v', '1',
'-q:v', '1',
outputPath
], 30000)
if (!existsSync(outputPath)) {
throw new Error('FFmpeg did not write the last frame')
const attempts: string[][] = []
try {
const duration = await probeDuration(inputPath)
const seek = Math.max(0, duration - 0.08)
attempts.push(['-y', '-ss', seek.toFixed(3), '-i', inputPath, '-frames:v', '1', '-an', '-q:v', '1', outputPath])
} catch {
// Truncated files often have no duration index; fall through to decode attempts.
}
attempts.push(
['-y', '-sseof', '-0.04', '-i', inputPath, '-update', '1', '-frames:v', '1', '-an', '-q:v', '1', outputPath],
['-y', '-i', inputPath, '-update', '1', '-an', '-q:v', '1', outputPath]
)
for (const args of attempts) {
try {
await runFfmpeg(args, 60000)
if (existsSync(outputPath)) return
} catch {
// Try the next seek strategy.
}
}
throw new Error('Could not extract the last frame. The video looks incomplete — interrupting Comfy mid-encode usually causes this. Earlier saved parts are still in the library.')
}
export async function probeHasAudio(filePath: string) {