Add extension-frame refine and lossless anchor options.

Prefer clip anchor PNGs for handoffs, optionally run Bilateral+Flux cleanup before I2V, and save decoded last-frame anchors so sequential extensions accumulate less compression damage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-09-04 07:55:46 -05:00
co-authored by Cursor
parent 5917d5bb37
commit f41ac7a826
14 changed files with 674 additions and 14 deletions
+21 -1
View File
@@ -6,9 +6,26 @@ import { clipPartDuration, clampVideoDuration } from '~/utils/videoDuration'
import { defaultVideoSteps, isLtxWorkflow, LTX_DISABLED_MESSAGE, ltxWorkflowEnabled, parseVideoWorkflow } from '~/utils/videoModels'
import { composePromptParts } from '~/utils/promptParts'
import { nextFamilyPartName } from '~/server/utils/library'
import {
clampRefinementDenoise,
parseRefineExtensionFrame,
parseSaveLosslessAnchor,
REFINEMENT_DENOISE_DEFAULT
} from '~/utils/extensionFrame'
export default defineEventHandler(async (event) => {
const body = await readBody<{ clipId?: string; prompt?: string; promptPre?: string; promptPost?: string; duration?: number; lora?: unknown; loraStack?: unknown }>(event).catch(() => ({}))
const body = await readBody<{
clipId?: string
prompt?: string
promptPre?: string
promptPost?: string
duration?: number
lora?: unknown
loraStack?: unknown
refineExtensionFrame?: boolean | string
saveLosslessAnchor?: boolean | string
refinementDenoise?: number | string
}>(event).catch(() => ({}))
const clipId = String(body?.clipId || '').trim()
const prompt = String(body?.prompt || '').trim()
if (!clipId) {
@@ -87,6 +104,9 @@ export default defineEventHandler(async (event) => {
extensions: [],
queueAutoRun: false,
extendFromClipId: clipId,
refineExtensionFrame: parseRefineExtensionFrame(body?.refineExtensionFrame, true),
saveLosslessAnchor: parseSaveLosslessAnchor(body?.saveLosslessAnchor, false),
refinementDenoise: clampRefinementDenoise(body?.refinementDenoise, REFINEMENT_DENOISE_DEFAULT),
globalLocks: source.globalLocks,
permanenceRefs: source.permanenceRefs,
...persistLoraFields(loraStack)
+12
View File
@@ -6,6 +6,12 @@ import { defaultVideoSteps, isLtxWorkflow, isTextToVideo, LTX_DISABLED_MESSAGE,
import { allowIdentityRefs, normalizePermanenceRefs, resolveGlobalLocks, type PermanenceRef } from '~/utils/globalLocks'
import { composePromptParts, ingestPromptParts, persistPromptWrappers } from '~/utils/promptParts'
import type { QueuedExtension } from '~/server/utils/library'
import {
clampRefinementDenoise,
parseRefineExtensionFrame,
parseSaveLosslessAnchor,
REFINEMENT_DENOISE_DEFAULT
} from '~/utils/extensionFrame'
function parseDuration(raw: unknown) {
const seconds = Number(raw)
@@ -250,6 +256,9 @@ export default defineEventHandler(async (event) => {
const chainTotal = 1 + extensions.length
const familyId = crypto.randomUUID()
const queueAutoRun = fields.queueAutoRun === 'true'
const refineExtensionFrame = parseRefineExtensionFrame(fields.refineExtensionFrame, true)
const saveLosslessAnchor = parseSaveLosslessAnchor(fields.saveLosslessAnchor, false)
const refinementDenoise = clampRefinementDenoise(fields.refinementDenoise, REFINEMENT_DENOISE_DEFAULT)
const studio = await addStudioJob({
ownerKey,
familyId,
@@ -282,6 +291,9 @@ export default defineEventHandler(async (event) => {
referenceStillIds,
extensions,
queueAutoRun,
refineExtensionFrame,
saveLosslessAnchor,
refinementDenoise,
globalLocks: globalLocks || undefined,
permanenceRefs: permanenceRefs.length ? permanenceRefs : undefined,
shotPermanenceRefs: shotPermanenceRefs.some(item => item.length) ? shotPermanenceRefs : undefined,