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:
Towsty
2026-08-28 15:24:44 -05:00
co-authored by Cursor
parent 0af3cddb14
commit f8db0d626a
36 changed files with 1767 additions and 246 deletions
+23 -3
View File
@@ -46,6 +46,8 @@ export interface LibraryClip {
hideInput?: boolean
globalLocks?: string
permanenceRefs?: PermanenceRef[]
loraName?: string
loraStack?: import('~/utils/loras').LoraStackItem[]
}
export type StillRole = 'input' | 'output'
@@ -82,6 +84,8 @@ export interface QueuedExtension {
prompt: string
duration: number
permanenceRefs?: PermanenceRef[]
loraName?: string
loraStack?: import('~/utils/loras').LoraStackItem[]
}
export interface RetryDraft {
@@ -111,6 +115,10 @@ export interface RetryDraft {
globalLocks?: string
permanenceRefs?: PermanenceRef[]
shotPermanenceRefs?: PermanenceRef[][]
loraName?: string
loraStack?: import('~/utils/loras').LoraStackItem[]
shotLoras?: string[]
shotLoraStacks?: import('~/utils/loras').LoraStackItem[][]
}
interface Catalog {
@@ -639,7 +647,7 @@ function writeFrameMeta(dest: string, video: string, mode: FrameSourceMeta['mode
const meta: FrameSourceMeta = { videoMtime: info.mtimeMs, videoSize: info.size, mode }
if (typeof seek === 'number') meta.seek = seek
writeFileSync(frameMetaPath(dest), JSON.stringify(meta))
} catch { /* thumbs still work without the sidecar */ }
} catch { /* thumbs still work without Comfy */ }
}
function frameCacheFresh(video: string, dest: string, mode: FrameSourceMeta['mode']) {
@@ -1209,6 +1217,10 @@ export async function saveRetryDraft(params: {
globalLocks?: string
permanenceRefs?: PermanenceRef[]
shotPermanenceRefs?: PermanenceRef[][]
loraName?: string
loraStack?: import('~/utils/loras').LoraStackItem[]
shotLoras?: string[]
shotLoraStacks?: import('~/utils/loras').LoraStackItem[][]
}) {
return mutate(params.ownerKey, (catalog) => {
const existing = params.id ? catalog.drafts.find(item => item.id === params.id) : undefined
@@ -1238,7 +1250,11 @@ export async function saveRetryDraft(params: {
workflow: params.workflow,
globalLocks: params.globalLocks,
permanenceRefs: params.permanenceRefs,
shotPermanenceRefs: params.shotPermanenceRefs
shotPermanenceRefs: params.shotPermanenceRefs,
loraName: params.loraName,
loraStack: params.loraStack,
shotLoras: params.shotLoras,
shotLoraStacks: params.shotLoraStacks
}
catalog.drafts = [draft, ...catalog.drafts.filter(item => item.id !== draft.id)].slice(0, 20)
return draft
@@ -1392,6 +1408,8 @@ export async function saveClip(params: {
sound?: boolean
globalLocks?: string
permanenceRefs?: PermanenceRef[]
loraName?: string
loraStack?: import('~/utils/loras').LoraStackItem[]
segmentFirstFrame?: Buffer
}) {
const catalog = readCatalog(params.ownerKey)
@@ -1423,7 +1441,9 @@ export async function saveClip(params: {
sound: params.sound,
hideInput: params.hideInput === true,
globalLocks: params.globalLocks,
permanenceRefs: params.permanenceRefs?.length ? normalizePermanenceRefs(params.permanenceRefs) : undefined
permanenceRefs: params.permanenceRefs?.length ? normalizePermanenceRefs(params.permanenceRefs) : undefined,
loraName: params.loraName || undefined,
loraStack: params.loraStack?.length ? params.loraStack : undefined
}
mkdirSync(clipDir(params.ownerKey, clip.id), { recursive: true })
const videoPath = clipVideoPath(params.ownerKey, clip.id)