Tighten needsFit to exact canvas size, guard against empty renders

needsFit only compared aspect ratio, so a same-aspect-but-larger still
(e.g. a 4032x2268 phone photo against a 1344x768 canvas) would skip
fitting and hand MiniMaxH3AddGuide a still that does not match the
job's own latent size. Require an exact width+height match instead;
anything else (including unreadable dimensions) still fits.

Also guard the video save path: an empty Comfy download, an empty
stitched assembly, or a stitched result with no readable duration now
fails the job with a clear error instead of silently saving/attaching
a broken zero-length clip.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-11 23:11:02 -05:00
co-authored by Cursor
parent 65c69b779a
commit d8f42ecc68
4 changed files with 58 additions and 10 deletions
+12 -2
View File
@@ -21,8 +21,8 @@ function fixture(mode='video') {
prepareGraph:async r=>{queued.push({hero:r.request.identityStillId,start:r.request.startClipId,action:r.request.promptSections.action});return {}},queuePrompt:async()=>({prompt_id:`p${++id}`}),
fetchHistory:async p=>({[p]:{status:{status_str:'success'},outputs:{}}}),comfyFetch:()=>{throw new Error('Unexpected network call')},
extractVideo:()=>({filename:'video.mp4',subfolder:'preview/studio2/r',type:'output'}),extractEditedImage:()=>({filename:'image.png',subfolder:'preview/studio2/r',type:'output'}),
downloadComfyVideo:async()=>Buffer.from('video'),downloadComfyImage:async()=>Buffer.from('image'),imageDimensions:()=>({width:960,height:960}),
videoSourcePaths:()=>['source'],clipVideoPath:()=>'/fake/source',studio2Root:()=>'/fake',join,mkdirSync(){},stitchExtension:async()=>Buffer.from('stitched'),
downloadComfyVideo:async()=>Buffer.alloc(128,'v'),downloadComfyImage:async()=>Buffer.from('image'),imageDimensions:()=>({width:960,height:960}),
videoSourcePaths:()=>['source'],clipVideoPath:()=>'/fake/source',studio2Root:()=>'/fake',join,mkdirSync(){},writeFileSync(){},unlinkSync(){},probeDuration:async()=>5,stitchExtension:async()=>Buffer.alloc(128,'s'),
saveClip:async p=>{const a={id:`asset${saved.length}`,...p};saved.push(a);return a},saveStill:async p=>{const a={id:`asset${saved.length}`,...p};saved.push(a);return a},
attachStudio2Metadata:async(owner,id,data)=>{assert.ok(saved.some(a=>a.id===id));assert.equal(data.identityStillId,'hero')},
purge:async r=>{assert.ok(saved.length>purged.length);purged.push(r.index);r.purgeResult='Cleared input + output'},
@@ -40,6 +40,16 @@ for(const mode of ['video','iterate']) test(`${mode} runner saves each output be
test('failed library save never purges host media',async()=>{
const f=fixture();f.scope.saveClip=async()=>{throw new Error('Disk full')};await runner(f.scope)(f.record,f.job);assert.equal(f.record.state,'failed');assert.equal(f.purged.length,0)
})
test('an empty Comfy video output fails the job with a real error and never saves or attaches a clip',async()=>{
const f=fixture();f.scope.downloadComfyVideo=async()=>Buffer.alloc(0)
await runner(f.scope)(f.record,f.job)
assert.equal(f.record.state,'failed');assert.match(f.record.error,/empty video file/);assert.equal(f.saved.length,0);assert.equal(f.record.outputs.length,0)
})
test('a stitched extension with no readable duration fails the job and never saves or attaches a clip',async()=>{
const f=fixture('video');f.scope.probeDuration=async()=>NaN
await runner(f.scope)(f.record,f.job)
assert.equal(f.record.state,'failed');assert.match(f.record.error,/no readable duration/);assert.equal(f.saved.length,0);assert.equal(f.record.outputs.length,0)
})
test('resume uses persisted Comfy prompt and never submits it again',async()=>{
const f=fixture();f.record.request.batch=[];f.record.promptId='existing';f.record.files=[];f.record.index=0
f.scope.queuePrompt=async()=>{throw new Error('Duplicate submit')};await runner(f.scope)(f.record,f.job)