Add VACE Source Prep node for trimming long source clips
New node that handles frame selection/trimming before the mask generator, with input_left/input_right controls for per-mode windowing. Adds B > target_frames validation to the mask generator with a helpful error message. Includes JS extension for smart widget visibility per mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
78
web/js/vace_widgets.js
Normal file
78
web/js/vace_widgets.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "VACE.SourcePrep.SmartDisplay",
|
||||
nodeCreated(node) {
|
||||
if (node.comfyClass !== "VACESourcePrep") return;
|
||||
|
||||
const modeWidget = node.widgets.find(w => w.name === "mode");
|
||||
if (!modeWidget) return;
|
||||
|
||||
const VISIBILITY = {
|
||||
"End Extend": { split_index: false, input_left: true, input_right: false, edge_frames: false },
|
||||
"Pre Extend": { split_index: false, input_left: false, input_right: true, edge_frames: false },
|
||||
"Middle Extend": { split_index: true, input_left: true, input_right: true, edge_frames: false },
|
||||
"Edge Extend": { split_index: false, input_left: true, input_right: true, edge_frames: true },
|
||||
"Join Extend": { split_index: false, input_left: true, input_right: true, edge_frames: true },
|
||||
"Bidirectional Extend": { split_index: true, input_left: true, input_right: false, edge_frames: false },
|
||||
"Frame Interpolation": { split_index: true, input_left: false, input_right: false, edge_frames: false },
|
||||
"Replace/Inpaint": { split_index: true, input_left: true, input_right: true, edge_frames: true },
|
||||
"Video Inpaint": { split_index: false, input_left: false, input_right: false, edge_frames: false },
|
||||
"Keyframe": { split_index: false, input_left: false, input_right: false, edge_frames: false },
|
||||
};
|
||||
|
||||
function toggleWidget(widget, show) {
|
||||
if (!widget) return;
|
||||
if (!widget._origType) widget._origType = widget.type;
|
||||
widget.type = show ? widget._origType : "hidden";
|
||||
}
|
||||
|
||||
function updateVisibility(mode) {
|
||||
const vis = VISIBILITY[mode];
|
||||
if (!vis) return;
|
||||
for (const [name, show] of Object.entries(vis)) {
|
||||
toggleWidget(node.widgets.find(w => w.name === name), show);
|
||||
}
|
||||
node.setSize(node.computeSize());
|
||||
app.graph.setDirtyCanvas(true);
|
||||
}
|
||||
|
||||
// Hook mode widget value setter to catch both UI and programmatic changes
|
||||
const descriptor = Object.getOwnPropertyDescriptor(modeWidget, "value") ||
|
||||
{ configurable: true };
|
||||
const hasCustomAccessor = !!descriptor.get;
|
||||
|
||||
if (!hasCustomAccessor) {
|
||||
let _value = modeWidget.value;
|
||||
Object.defineProperty(modeWidget, "value", {
|
||||
get() { return _value; },
|
||||
set(v) {
|
||||
_value = v;
|
||||
updateVisibility(v);
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
} else {
|
||||
const origGet = descriptor.get;
|
||||
const origSet = descriptor.set;
|
||||
Object.defineProperty(modeWidget, "value", {
|
||||
get() { return origGet.call(this); },
|
||||
set(v) {
|
||||
origSet.call(this, v);
|
||||
updateVisibility(v);
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Also hook callback for dropdown selection events
|
||||
const origCallback = modeWidget.callback;
|
||||
modeWidget.callback = function(value) {
|
||||
updateVisibility(value);
|
||||
if (origCallback) origCallback.call(this, value);
|
||||
};
|
||||
|
||||
// Initial update
|
||||
updateVisibility(modeWidget.value);
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user