Name outputs from the input, engine, and LoRA keys instead of aigen_0001. Image-edit presets now store prompts, and aspect fill pads stills without stretching.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-30 16:24:49 -05:00
co-authored by Cursor
parent 4d411f83d4
commit 0e1222d035
23 changed files with 668 additions and 93 deletions
+21 -5
View File
@@ -1,5 +1,6 @@
import { addStudioJob, kickStudioQueue, listStudioJobs, videoJobsBusy } from '~/server/utils/studioQueue'
import { comfyConfigured } from '~/server/utils/comfy'
import { parseImageAspect, generateSizeForAspect } from '~/utils/imageAspect'
import { imageDimensions } from '~/server/utils/resolution'
import { clampImageCfg, clampImageScaleMegapixels, clampImageSteps } from '~/utils/generationPresets'
import {
@@ -63,7 +64,7 @@ function fileFromStill(ownerKey: string, id: string): ImageFile {
throw createError({ statusCode: 400, statusMessage: 'That still is missing from the library' })
}
return {
filename: still.filename || `${still.id}.png`,
filename: still.name ? `${still.name}.png` : (still.filename || `${still.id}.png`),
data: readFileSync(path),
type: 'image/png'
}
@@ -123,7 +124,8 @@ export default defineEventHandler(async (event) => {
mode === 'generate' ? 't2i' : mode === 'refine' ? 'refine' : 'scene'
)
const prompt = String(fields.prompt || '').trim()
if (!prompt) {
const aspect = parseImageAspect(fields.aspect)
if (!prompt && (mode === 'generate' || aspect === 'auto')) {
throw createError({ statusCode: 400, statusMessage: 'A prompt is required' })
}
if (!comfyConfigured()) {
@@ -193,11 +195,12 @@ export default defineEventHandler(async (event) => {
const seed = fields.seed && String(fields.seed) !== 'random'
? Number(fields.seed)
: Math.floor(Math.random() * 2_147_483_647)
const generateSize = clampImageV2Size(fields.width, fields.height, fields.aspect)
const generateSize = mode === 'generate'
? generateSizeForAspect(aspect)
: clampImageV2Size(fields.width, fields.height, fields.aspect)
const size = mode === 'generate'
? { width: generateSize.width, height: generateSize.height }
: imageDimensions(imageA!.data)
const clipName = String(fields.name || '').trim().slice(0, 80)
const v2Mode = mode as ImageV2Mode
const v2Task = (
mode === 'generate' ? 't2i' : mode === 'refine' ? 'refine' : mode === 'edit' ? 'scene' : task
@@ -223,6 +226,19 @@ export default defineEventHandler(async (event) => {
const generateSnofsClipFallback = 0
await listStudioLoras()
const loraStack = assertImageV2LoraStack(parsePostedLoraStack(fields.loraStack || fields.lora, 'image'), engine)
let inputFilename = imageA?.filename || ''
const postedStillId = String(fields.stillId || '').trim()
if (postedStillId) {
try {
const existing = getStill(ownerKey, postedStillId)
inputFilename = existing.name || existing.filename || inputFilename
} catch { /* use the uploaded filename */ }
}
const clipName = allocateOutputName(ownerKey, folderId, String(fields.name || '').trim(), {
inputFilename,
engine,
loraStack
})
const still = imageA
? await rememberInputStill({
@@ -262,7 +278,7 @@ export default defineEventHandler(async (event) => {
prompt,
name: clipName,
folderId,
aspect: mode === 'generate' ? (generateSize.aspect || '1:1') : 'auto',
aspect: mode === 'generate' ? generateSize.aspect : aspect,
width: size?.width || 0,
height: size?.height || 0,
steps,