Add generation quality controls and bind them to the MiniMax workflow.
Expose aspect, CFG, FPS, sampler, and scheduler in Generation settings, persist them on clips and held jobs, and inject those values into the Comfy graph. Keep the image picker stills-only and allow renaming library clips. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -11,9 +11,32 @@ function parseDuration(raw: string) {
|
||||
return Math.min(120, Math.max(0.5, seconds))
|
||||
}
|
||||
|
||||
function frameLength(seconds: number) {
|
||||
const raw = Math.max(5, Math.round(seconds * 24))
|
||||
return raw + (5 - (raw % 17) + 17) % 17
|
||||
const SAMPLERS = new Set(['res_multistep', 'euler', 'dpmpp_2m'])
|
||||
const SCHEDULERS = new Set(['simple', 'ddim_uniform', 'sgm_uniform'])
|
||||
|
||||
function frameLength(seconds: number, fps: number) {
|
||||
return Math.max(5, Math.floor(seconds * fps))
|
||||
}
|
||||
|
||||
function parseFps(raw: string | undefined) {
|
||||
const fps = Number(raw)
|
||||
return fps === 12 || fps === 30 || fps === 24 ? fps : 24
|
||||
}
|
||||
|
||||
function parseCfg(raw: string | undefined, turbo: boolean) {
|
||||
const fallback = turbo ? 1.5 : 4
|
||||
const value = Number(raw)
|
||||
if (!Number.isFinite(value)) return fallback
|
||||
const snapped = Math.round(value * 2) / 2
|
||||
return Math.min(10, Math.max(1, snapped))
|
||||
}
|
||||
|
||||
function parseSampler(raw: string | undefined) {
|
||||
return SAMPLERS.has(raw || '') ? raw! : 'res_multistep'
|
||||
}
|
||||
|
||||
function parseScheduler(raw: string | undefined) {
|
||||
return SCHEDULERS.has(raw || '') ? raw! : 'simple'
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
@@ -47,7 +70,11 @@ export default defineEventHandler(async (event) => {
|
||||
? Number(fields.seed)
|
||||
: Math.floor(Math.random() * 2_147_483_647)
|
||||
const durationSeconds = parseDuration(fields.duration || '5')
|
||||
const length = frameLength(durationSeconds)
|
||||
const fps = parseFps(fields.fps)
|
||||
const cfg = parseCfg(fields.cfg, turbo)
|
||||
const samplerName = parseSampler(fields.sampler_name)
|
||||
const scheduler = parseScheduler(fields.scheduler)
|
||||
const length = frameLength(durationSeconds, fps)
|
||||
const hideThumbnail = fields.hideThumbnail === 'true'
|
||||
const hideInput = fields.hideInput === 'true'
|
||||
const sound = fields.sound !== 'false'
|
||||
@@ -84,12 +111,16 @@ export default defineEventHandler(async (event) => {
|
||||
folderLocked,
|
||||
name: clipName,
|
||||
prompt,
|
||||
aspect: fields.aspect || 'auto',
|
||||
aspect: fields.aspect || '16:9',
|
||||
width,
|
||||
height,
|
||||
steps,
|
||||
turbo,
|
||||
seed,
|
||||
cfg,
|
||||
fps,
|
||||
samplerName,
|
||||
scheduler,
|
||||
thumb: image.data,
|
||||
stillId: still.id,
|
||||
stillFilename: still.filename,
|
||||
@@ -99,7 +130,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
emitJob(job, { type: 'status', message: 'Checking ComfyUI...', progress: 1 })
|
||||
|
||||
void runGeneration(job, { prompt, image, width, height, steps, seed, turbo, length, sound }).catch(async (error) => {
|
||||
void runGeneration(job, { prompt, image, width, height, steps, seed, turbo, length, sound, cfg, fps, samplerName, scheduler }).catch(async (error) => {
|
||||
if (isComfyBusyTimeout(error) && job.library) {
|
||||
try {
|
||||
const draft = await saveRetryDraft({
|
||||
@@ -119,7 +150,11 @@ export default defineEventHandler(async (event) => {
|
||||
duration: job.library.duration || durationSeconds,
|
||||
hideThumbnail: job.library.hideThumbnail,
|
||||
hideInput: job.library.hideInput === true,
|
||||
sound: job.library.sound !== false
|
||||
sound: job.library.sound !== false,
|
||||
cfg: job.library.cfg,
|
||||
fps: job.library.fps,
|
||||
samplerName: job.library.samplerName,
|
||||
scheduler: job.library.scheduler
|
||||
})
|
||||
job.library.draftId = draft.id
|
||||
job.status = 'deferred'
|
||||
@@ -142,7 +177,11 @@ export default defineEventHandler(async (event) => {
|
||||
seed,
|
||||
width,
|
||||
height,
|
||||
aspect: fields.aspect || 'auto',
|
||||
aspect: fields.aspect || '16:9',
|
||||
cfg,
|
||||
fps,
|
||||
sampler_name: samplerName,
|
||||
scheduler,
|
||||
steps,
|
||||
turbo,
|
||||
folderId,
|
||||
@@ -163,6 +202,10 @@ async function runGeneration(
|
||||
turbo: boolean
|
||||
length: number
|
||||
sound: boolean
|
||||
cfg: number
|
||||
fps: number
|
||||
samplerName: string
|
||||
scheduler: string
|
||||
}
|
||||
) {
|
||||
await ensureComfyReady((status) => {
|
||||
@@ -196,6 +239,10 @@ async function runGeneration(
|
||||
seed: params.seed,
|
||||
turbo: params.turbo,
|
||||
length: params.length,
|
||||
cfg: params.cfg,
|
||||
fps: params.fps,
|
||||
samplerName: params.samplerName,
|
||||
scheduler: params.scheduler,
|
||||
filenamePrefix: comfyFilenamePrefix(),
|
||||
sound: params.sound
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user