Add LoRA stacks with per-item strengths and named video/image generation presets so setups survive reload without mixing Klein onto MiniMax. Keep LTX Pink Cherry and xAIGen LoRAs on xAIGen, and run Recommend on Beast.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { addStudioJob, kickStudioQueue, listStudioJobs } from '~/server/utils/studioQueue'
|
||||
import { defaultVideoSteps, isLtxWorkflow, isTextToVideo, parseVideoWorkflow } from '~/utils/videoModels'
|
||||
import { listStudioLoras, parsePostedLoraStack, parseShotLoraStacks, persistLoraFields } from '~/server/utils/loras'
|
||||
import type { LoraStackItem } from '~/utils/loras'
|
||||
import { defaultVideoSteps, isLtxWorkflow, isTextToVideo, LTX_DISABLED_MESSAGE, ltxWorkflowEnabled, parseVideoWorkflow } from '~/utils/videoModels'
|
||||
import { allowIdentityRefs, normalizePermanenceRefs, resolveGlobalLocks, type PermanenceRef } from '~/utils/globalLocks'
|
||||
|
||||
function parseDuration(raw: unknown) {
|
||||
@@ -13,18 +15,23 @@ function parseExtendDuration(raw: unknown) {
|
||||
}
|
||||
|
||||
function parseExtensions(raw: string | undefined) {
|
||||
if (!raw) return [] as { prompt: string; duration: number; permanenceRefs?: PermanenceRef[] }[]
|
||||
if (!raw) return [] as { prompt: string; duration: number; permanenceRefs?: PermanenceRef[]; loraName?: string; loraStack?: LoraStackItem[] }[]
|
||||
try {
|
||||
const parsed = JSON.parse(raw)
|
||||
if (!Array.isArray(parsed)) return []
|
||||
return parsed
|
||||
.map((item: { prompt?: unknown; duration?: unknown; permanenceRefs?: unknown }) => ({
|
||||
prompt: String(item?.prompt || '').trim(),
|
||||
duration: parseExtendDuration(item?.duration),
|
||||
permanenceRefs: normalizePermanenceRefs(item?.permanenceRefs)
|
||||
}))
|
||||
.map((item: { prompt?: unknown; duration?: unknown; permanenceRefs?: unknown; loraName?: unknown; loraStack?: unknown }) => {
|
||||
const loraStack = parsePostedLoraStack(item?.loraStack ?? item?.loraName, 'video')
|
||||
return {
|
||||
prompt: String(item?.prompt || '').trim(),
|
||||
duration: parseExtendDuration(item?.duration),
|
||||
permanenceRefs: normalizePermanenceRefs(item?.permanenceRefs),
|
||||
...persistLoraFields(loraStack)
|
||||
}
|
||||
})
|
||||
.filter(item => item.prompt)
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (error && typeof error === 'object' && 'statusCode' in error) throw error
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -101,7 +108,11 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
|
||||
await listStudioLoras()
|
||||
const workflow = parseVideoWorkflow(fields.workflow)
|
||||
if (isLtxWorkflow(workflow) && !ltxWorkflowEnabled()) {
|
||||
throw createError({ statusCode: 400, statusMessage: LTX_DISABLED_MESSAGE })
|
||||
}
|
||||
const textToVideo = isTextToVideo(workflow)
|
||||
if (!image && !textToVideo) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'An initial image is required' })
|
||||
@@ -116,10 +127,16 @@ export default defineEventHandler(async (event) => {
|
||||
const extensions = parseExtensions(fields.extensions)
|
||||
const permanenceRefs = parseFamilyPermanenceRefs(fields.permanenceRefs)
|
||||
const shotPermanenceRefs = parseShotPermanenceRefs(fields.shotPermanenceRefs, 1 + extensions.length)
|
||||
const defaultStack = parsePostedLoraStack(fields.loraStack || fields.lora, 'video')
|
||||
const shotLoraStacks = parseShotLoraStacks(fields.shotLoraStacks || fields.shotLoras, 1 + extensions.length)
|
||||
if (defaultStack.length && !shotLoraStacks[0].length) shotLoraStacks[0] = defaultStack
|
||||
for (const [index, item] of extensions.entries()) {
|
||||
if (!shotPermanenceRefs[index + 1]?.length && item.permanenceRefs?.length) {
|
||||
shotPermanenceRefs[index + 1] = item.permanenceRefs
|
||||
}
|
||||
const shotStack = shotLoraStacks[index + 1]
|
||||
if (!shotStack.length && item.loraStack?.length) shotLoraStacks[index + 1] = item.loraStack
|
||||
else if (shotStack.length) Object.assign(item, persistLoraFields(shotStack))
|
||||
}
|
||||
const requestedIdentity = workflow === 'v2' && !textToVideo && !isLtxWorkflow(workflow) && fields.useIdentityRefs === 'true'
|
||||
const useIdentityRefs = allowIdentityRefs(requestedIdentity, permanenceRefs, globalLocks, shotPermanenceRefs)
|
||||
@@ -216,7 +233,10 @@ export default defineEventHandler(async (event) => {
|
||||
queueAutoRun,
|
||||
globalLocks: globalLocks || undefined,
|
||||
permanenceRefs: permanenceRefs.length ? permanenceRefs : undefined,
|
||||
shotPermanenceRefs: shotPermanenceRefs.some(item => item.length) ? shotPermanenceRefs : undefined
|
||||
shotPermanenceRefs: shotPermanenceRefs.some(item => item.length) ? shotPermanenceRefs : undefined,
|
||||
...persistLoraFields(defaultStack),
|
||||
shotLoras: shotLoraStacks.some(stack => stack.length) ? shotLoraStacks.map(stack => stack[0]?.name || '') : undefined,
|
||||
shotLoraStacks: shotLoraStacks.some(stack => stack.length) ? shotLoraStacks : undefined
|
||||
}
|
||||
})
|
||||
await kickStudioQueue()
|
||||
|
||||
Reference in New Issue
Block a user