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:
+89
-1
@@ -1109,6 +1109,41 @@
|
||||
<span class="mt-0.5 block text-xs text-zinc-500">Overnight set-and-forget. Leave this off to edit prompts on Queue (under this job) and process the next shot when you are ready.</span>
|
||||
</span>
|
||||
</label>
|
||||
<div v-if="studioMode === 'video'" class="space-y-3">
|
||||
<label
|
||||
class="flex cursor-pointer items-start gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3"
|
||||
title="Runs a low-denoise Bilateral + Flux cleanup pass on the handoff frame to strip compression artifacts."
|
||||
>
|
||||
<input v-model="refineExtensionFrame" type="checkbox" class="mt-1">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-zinc-200">Refine Extension Frame</span>
|
||||
<span class="mt-0.5 block text-xs text-zinc-500">Runs a low-denoise Bilateral + Flux cleanup pass on the handoff frame to strip compression artifacts.</span>
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
v-if="refineExtensionFrame"
|
||||
class="rounded-2xl border border-white/10 bg-zinc-950/40 p-3"
|
||||
>
|
||||
<Thumbwheel
|
||||
v-model="refinementDenoise"
|
||||
label="Refinement Denoise"
|
||||
:min="REFINEMENT_DENOISE_MIN"
|
||||
:max="REFINEMENT_DENOISE_MAX"
|
||||
:step="0.01"
|
||||
:digits="2"
|
||||
/>
|
||||
</div>
|
||||
<label
|
||||
class="flex cursor-pointer items-start gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3"
|
||||
title="Saves an uncompressed PNG of the final frame alongside the MP4 for cleaner future extensions."
|
||||
>
|
||||
<input v-model="saveLosslessAnchor" type="checkbox" class="mt-1">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-zinc-200">Save Lossless Anchor Frame</span>
|
||||
<span class="mt-0.5 block text-xs text-zinc-500">Saves an uncompressed PNG of the final frame alongside the MP4 for cleaner future extensions.</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<button
|
||||
type="button"
|
||||
@@ -1458,6 +1493,38 @@
|
||||
@insert-trigger="insertLoraTrigger($event, 'extend')"
|
||||
@update-trigger="saveLoraTrigger"
|
||||
/>
|
||||
<div class="space-y-3">
|
||||
<label
|
||||
class="flex cursor-pointer items-start gap-3 rounded-xl border border-white/10 bg-zinc-950/40 px-3 py-2.5"
|
||||
title="Runs a low-denoise Bilateral + Flux cleanup pass on the handoff frame to strip compression artifacts."
|
||||
>
|
||||
<input v-model="refineExtensionFrame" type="checkbox" class="mt-1">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-zinc-200">Refine Extension Frame</span>
|
||||
<span class="mt-0.5 block text-xs text-zinc-500">Bilateral + Flux cleanup on the handoff frame.</span>
|
||||
</span>
|
||||
</label>
|
||||
<div v-if="refineExtensionFrame" class="rounded-xl border border-white/10 bg-zinc-950/40 p-3">
|
||||
<Thumbwheel
|
||||
v-model="refinementDenoise"
|
||||
label="Refinement Denoise"
|
||||
:min="REFINEMENT_DENOISE_MIN"
|
||||
:max="REFINEMENT_DENOISE_MAX"
|
||||
:step="0.01"
|
||||
:digits="2"
|
||||
/>
|
||||
</div>
|
||||
<label
|
||||
class="flex cursor-pointer items-start gap-3 rounded-xl border border-white/10 bg-zinc-950/40 px-3 py-2.5"
|
||||
title="Saves an uncompressed PNG of the final frame alongside the MP4 for cleaner future extensions."
|
||||
>
|
||||
<input v-model="saveLosslessAnchor" type="checkbox" class="mt-1">
|
||||
<span>
|
||||
<span class="block text-sm font-medium text-zinc-200">Save Lossless Anchor Frame</span>
|
||||
<span class="mt-0.5 block text-xs text-zinc-500">Keep a PNG anchor with this extension for cleaner next hops.</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full rounded-2xl bg-amber-400 px-4 py-2.5 text-sm font-semibold text-zinc-950 transition hover:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
@@ -2324,6 +2391,12 @@ import {
|
||||
defaultVideoDurationPref,
|
||||
setDefaultVideoDurationPref
|
||||
} from '~/utils/videoDuration'
|
||||
import {
|
||||
clampRefinementDenoise,
|
||||
REFINEMENT_DENOISE_DEFAULT,
|
||||
REFINEMENT_DENOISE_MAX,
|
||||
REFINEMENT_DENOISE_MIN
|
||||
} from '~/utils/extensionFrame'
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const route = useRoute()
|
||||
@@ -2758,6 +2831,9 @@ const renameDraft = ref('')
|
||||
const renameInput = ref<HTMLInputElement | null>(null)
|
||||
const unlockPassword = ref('')
|
||||
const queueAutoRun = ref(false)
|
||||
const refineExtensionFrame = ref(true)
|
||||
const saveLosslessAnchor = ref(false)
|
||||
const refinementDenoise = ref(REFINEMENT_DENOISE_DEFAULT)
|
||||
const queuePendingCount = ref(0)
|
||||
const activeQueueId = ref('')
|
||||
const liveQueue = ref<{
|
||||
@@ -5452,6 +5528,9 @@ async function restoreStudioPayload(kind: string, payload: {
|
||||
referenceStillIds?: Array<string | null>
|
||||
extensions?: { prompt: string; promptPre?: string; promptPost?: string; duration: number; permanenceRefs?: PermanenceRef[]; loraName?: string; loraStack?: LoraStackItem[] }[]
|
||||
queueAutoRun?: boolean
|
||||
refineExtensionFrame?: boolean
|
||||
saveLosslessAnchor?: boolean
|
||||
refinementDenoise?: number
|
||||
promptPre?: string
|
||||
promptPost?: string
|
||||
globalLocks?: string
|
||||
@@ -5520,6 +5599,9 @@ async function restoreStudioPayload(kind: string, payload: {
|
||||
readLoraStack(item)
|
||||
))
|
||||
queueAutoRun.value = payload.queueAutoRun === true
|
||||
refineExtensionFrame.value = payload.refineExtensionFrame !== false
|
||||
saveLosslessAnchor.value = payload.saveLosslessAnchor === true
|
||||
refinementDenoise.value = clampRefinementDenoise(payload.refinementDenoise, REFINEMENT_DENOISE_DEFAULT)
|
||||
globalLocks.value = payload.globalLocks || ''
|
||||
familyPermanenceRefs.value = payload.permanenceRefs || []
|
||||
const shotRefs: Record<number, PermanenceRef[]> = {}
|
||||
@@ -6113,7 +6195,10 @@ async function generateExtension() {
|
||||
promptPre: extendPromptPre.value,
|
||||
promptPost: extendPromptPost.value,
|
||||
duration: extendDuration.value,
|
||||
loraStack: extendLoraStack.value
|
||||
loraStack: extendLoraStack.value,
|
||||
refineExtensionFrame: refineExtensionFrame.value,
|
||||
saveLosslessAnchor: saveLosslessAnchor.value,
|
||||
refinementDenoise: clampRefinementDenoise(refinementDenoise.value)
|
||||
}
|
||||
})
|
||||
if (queuedOffLiveOutput(started, 'video')) {
|
||||
@@ -7391,6 +7476,9 @@ async function generate() {
|
||||
}))))
|
||||
body.append('queueAutoRun', String(queueAutoRun.value))
|
||||
}
|
||||
body.append('refineExtensionFrame', String(refineExtensionFrame.value))
|
||||
body.append('saveLosslessAnchor', String(saveLosslessAnchor.value))
|
||||
body.append('refinementDenoise', String(clampRefinementDenoise(refinementDenoise.value)))
|
||||
const downloadName = clipFileName(clipName.value.trim() || autoOutputName.value)
|
||||
const chainPlan = plannedChain.value.map(part => ({ ...part }))
|
||||
console.info('[aigen generate payload]', {
|
||||
|
||||
Reference in New Issue
Block a user