Add live update workflow node from sub workflow inputs/outputs
This commit is contained in:
+5
-5
@@ -1,10 +1,10 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { setupWorkflowNode } from './workflow.js';
|
||||
import { setupInputNode } from "./input.js";
|
||||
import { setupContinueNode } from "./continue.js";
|
||||
import { setupOutputNode } from "./output.js";
|
||||
import { setupLipSyncNode } from "./lipSync.js";
|
||||
import { setupWorkflowNode } from './nodetype_workflow.js';
|
||||
import { setupInputNode } from "./nodetype_input.js";
|
||||
import { setupContinueNode } from "./nodetype_continue.js";
|
||||
import { setupOutputNode } from "./nodetype_output.js";
|
||||
import { setupLipSyncNode } from "./nodetype_lipSync.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "FlowChain.jsnodes",
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import {chainCallback} from "./utils.js";
|
||||
import {clearInputs} from "./inputs.js";
|
||||
import {addWidgets} from "./widgets.js";
|
||||
import {colors, bg_colors, node_type_list} from "./constants.js";
|
||||
|
||||
function initialisation(node) {
|
||||
node.widgets[0].callback = ( value ) => {
|
||||
clearInputs(node);
|
||||
node.addOutput("output", value);
|
||||
node.addInput("input", value);
|
||||
node.color = colors[node_type_list.indexOf(value)];
|
||||
node.bgcolor = bg_colors[node_type_list.indexOf(value)];
|
||||
};
|
||||
node.color = colors[node_type_list.indexOf("none")];
|
||||
node.bgcolor = bg_colors[node_type_list.indexOf("none")];
|
||||
}
|
||||
|
||||
function configure(info) {
|
||||
let widgetDict = info.widgets_values
|
||||
if (info.widgets_values.length == undefined) {
|
||||
|
||||
for (let w of this.widgets) {
|
||||
if (w.name in widgetDict) {
|
||||
w.value = widgetDict[w.name].value;
|
||||
}
|
||||
}
|
||||
// check if widgetDict in this.widgets
|
||||
for (let [key, value] of Object.entries(widgetDict)) {
|
||||
let widget = this.widgets.find(w => w.name === key);
|
||||
let type = this.widgets.find(w => w.name === "type");
|
||||
if(!widget){
|
||||
addWidgets(this, key, value, app);
|
||||
widget = this.widgets.find(w => w.name === key);
|
||||
}
|
||||
//this.widgets.push(value);
|
||||
widget.options = info.widgets_values[key].options;
|
||||
widget.value = info.widgets_values[key].value;
|
||||
//if value exists in inputs
|
||||
|
||||
for (let input of this.inputs)
|
||||
if (input.name == key){
|
||||
//find if key exists in inputs array in inputs.Name
|
||||
if (info.widgets_values[key].type != "converted-widget"){
|
||||
this.removeInput(this.inputs.indexOf(input));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.outputs_values != undefined){
|
||||
// deep copy outputs
|
||||
if(this.id == -1){
|
||||
this.outputs[0] = {links: null, name: info.outputs_values.name, type: info.outputs_values.type};
|
||||
}else{
|
||||
this.outputs[0] = {...info.outputs_values};
|
||||
}
|
||||
}
|
||||
this.setSize(info.size);
|
||||
};
|
||||
|
||||
|
||||
function serialize(info) {
|
||||
info.widgets_values = {};
|
||||
if (!this.widgets) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let w of this.widgets) {
|
||||
info.widgets_values[w.name] = {name: w.name, options : w.options, value: w.value, type: w.type, origType: w.origType, last_y: w.last_y};
|
||||
}
|
||||
for (let w of this.inputs){
|
||||
// if w.name exists in info.widgets_values
|
||||
if (info.widgets_values[w.name]){
|
||||
if(info.widgets_values[w.name].type == "converted-widget"){
|
||||
if(info.widgets_values[w.name].origType == "toggle"){
|
||||
w.type = "BOOLEAN";
|
||||
}else if(info.widgets_values[w.name].origType == "combo"){
|
||||
w.type = "COMBO";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let w of this.inputs){
|
||||
if (w.name=="input"){
|
||||
w.type = info.widgets_values.type.value;
|
||||
}
|
||||
}
|
||||
if (this.outputs.length > 0){
|
||||
if (this.outputs[0].links == null){
|
||||
info.outputs_values = {links: null, name: this.outputs[0].name, type: this.outputs[0].type};
|
||||
}else{
|
||||
info.outputs_values = {links: [...this.outputs[0].links], name: this.outputs[0].name, slot_index: this.outputs[0].slot_index, type: this.outputs[0].type};
|
||||
}
|
||||
}
|
||||
this.setSize(info.size);
|
||||
}
|
||||
|
||||
|
||||
export function setupContinueNode(nodeType, nodeData, app) {
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
chainCallback(this, "onConfigure", configure);
|
||||
chainCallback(this, "onSerialize", serialize);
|
||||
initialisation(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { chainCallback } from "./utils.js";
|
||||
import { addInputs, cleanInputs, clearInputs } from "./inputs.js";
|
||||
import { colors, bg_colors, node_type_list } from "./constants.js";
|
||||
|
||||
export function setupInputNode(nodeType, nodeData, app) {
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
chainCallback(this, "onConfigure", function(info) {
|
||||
let widgetDict = info.widgets_values;
|
||||
const inputs = {};
|
||||
|
||||
inputs["default"] = {
|
||||
inputs: ["default", info.widgets_values[1], info.widgets_values[2]]
|
||||
};
|
||||
|
||||
addInputs(this, inputs, info.widgets_values);
|
||||
});
|
||||
chainCallback(this, "onSerialize", function(info) {
|
||||
let widgetDict = info.widgets_values;
|
||||
|
||||
for (let inp of this.inputs){
|
||||
// if w.name exists in info.widgets_values
|
||||
if (inp.widget){
|
||||
if (inp.type != this.local_input_defs.required[inp.name][0]){
|
||||
inp.type = this.local_input_defs.required[inp.name][0];
|
||||
const wid = this.widgets.find(w => w.name == inp.name);
|
||||
if (wid && wid.origType != this.local_input_defs.required[inp.name][0])
|
||||
wid.origType = this.local_input_defs.required[inp.name][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.widgets[1].callback = ( value ) => {
|
||||
cleanInputs(this);
|
||||
|
||||
switch(value){
|
||||
case "STRING":
|
||||
this.addOutput("output", "STRING");
|
||||
ComfyWidgets.STRING(
|
||||
this,
|
||||
"default",
|
||||
["STRING",{default: "",},],
|
||||
app,
|
||||
)
|
||||
|
||||
break;
|
||||
case "INT":
|
||||
this.addOutput("output", "INT");
|
||||
ComfyWidgets.INT(
|
||||
this,
|
||||
"default",
|
||||
//['',{default: 0, "min": 0, "max": 18446744073709551616, "step": 1},],
|
||||
['',{default: 0},],
|
||||
app,
|
||||
)
|
||||
break;
|
||||
case "FLOAT":
|
||||
this.addOutput("output", "FLOAT");
|
||||
ComfyWidgets.FLOAT(
|
||||
this,
|
||||
"default",
|
||||
['',{default: 0, "min": 0.00, "max": 2048.00, "step": 0.01},],
|
||||
app,
|
||||
)
|
||||
break;
|
||||
case "BOOLEAN":
|
||||
this.addOutput("output", "BOOLEAN");
|
||||
this.addWidget("toggle", "default", false, ()=>{});
|
||||
break;
|
||||
case "none":
|
||||
break;
|
||||
default:
|
||||
this.addOutput("output", value);
|
||||
this.addInput("default", value);
|
||||
break;
|
||||
}
|
||||
this.color = colors[node_type_list.indexOf(value)];
|
||||
this.bgcolor = bg_colors[node_type_list.indexOf(value)];
|
||||
};
|
||||
if (this.widgets[1].value == "none")
|
||||
clearInputs(this);
|
||||
this.color = colors[node_type_list.indexOf("none")];
|
||||
this.bgcolor = bg_colors[node_type_list.indexOf("none")];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useKVState } from "./utils.js";
|
||||
import { chainCallback } from "./utils.js";
|
||||
import { addLoadVideoCommon } from "./videoPreview.js";
|
||||
|
||||
export function setupLipSyncNode(nodeType, nodeData, app) {
|
||||
useKVState(nodeType);
|
||||
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
||||
let new_widgets = []
|
||||
if (this.widgets) {
|
||||
for (let w of this.widgets) {
|
||||
let input = this.constructor.nodeData.input
|
||||
let config = input?.required[w.name] ?? input.optional[w.name]
|
||||
if (!config) {
|
||||
continue
|
||||
}
|
||||
if (w?.type == "text" && config[1].vhs_path_extensions) {
|
||||
new_widgets.push(app.widgets.VHSPATH({}, w.name, ["VHSPATH", config[1]]));
|
||||
} else {
|
||||
new_widgets.push(w)
|
||||
}
|
||||
}
|
||||
this.widgets = new_widgets;
|
||||
}
|
||||
});
|
||||
addLoadVideoCommon(nodeType, nodeData);
|
||||
const onGetImageSizeExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function(message) {
|
||||
const r = onGetImageSizeExecuted? onGetImageSizeExecuted.apply(this,arguments): undefined
|
||||
let video = message["video_path"][0];
|
||||
if(video){
|
||||
this.updateParameters({format: "video/mp4", filename: message["video_path"][0], subfolder: message["video_path"][1], "type": "output"});
|
||||
}
|
||||
return r
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import { chainCallback } from "./utils.js";
|
||||
import { clearInputs } from "./inputs.js";
|
||||
import { addWidgets } from "./widgets.js";
|
||||
import { colors, bg_colors, node_type_list } from "./constants.js";
|
||||
|
||||
export function setupOutputNode(nodeType, nodeData, app) {
|
||||
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
chainCallback(this, "onConfigure", function(info) {
|
||||
let widgetDict = info.widgets_values
|
||||
if (info.widgets_values.length == undefined) {
|
||||
|
||||
for (let w of this.widgets) {
|
||||
if (w.name in widgetDict) {
|
||||
w.value = widgetDict[w.name].value;
|
||||
}
|
||||
}
|
||||
// check if widgetDict in this.widgets
|
||||
for (let [key, value] of Object.entries(widgetDict)) {
|
||||
let widget = this.widgets.find(w => w.name === key);
|
||||
let type = this.widgets.find(w => w.name === "type");
|
||||
if(!widget){
|
||||
addWidgets(this, key, value, app);
|
||||
widget = this.widgets.find(w => w.name === key);
|
||||
}
|
||||
//this.widgets.push(value);
|
||||
widget.options = info.widgets_values[key].options;
|
||||
widget.value = info.widgets_values[key].value;
|
||||
//if value exists in inputs
|
||||
|
||||
for (let input of this.inputs){
|
||||
if (input.name == key){
|
||||
//find if key exists in inputs array in inputs.Name
|
||||
if (info.widgets_values[key].type != "converted-widget"){
|
||||
this.removeInput(this.inputs.indexOf(input));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (let w of this.inputs){
|
||||
if (w.name=="default"){
|
||||
w.type = info.widgets_values.type.value;
|
||||
}
|
||||
}
|
||||
if (info.outputs_values != undefined){
|
||||
// deep copy outputs
|
||||
if(this.id == -1){
|
||||
this.outputs[0] = {links: null, name: info.outputs_values.name, type: info.outputs_values.type};
|
||||
}else{
|
||||
this.outputs[0] = {...info.outputs_values};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
chainCallback(this, "onSerialize", function(info) {
|
||||
info.widgets_values = {};
|
||||
if (!this.widgets) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let w of this.widgets) {
|
||||
info.widgets_values[w.name] = {name: w.name, options : w.options, value: w.value, type: w.type, origType: w.origType, last_y: w.last_y};
|
||||
}
|
||||
|
||||
|
||||
for (let w of this.inputs){
|
||||
// if w.name exists in info.widgets_values
|
||||
if (info.widgets_values[w.name]){
|
||||
if(info.widgets_values[w.name].type == "converted-widget"){
|
||||
if(info.widgets_values[w.name].origType == "toggle"){
|
||||
w.type = "BOOLEAN";
|
||||
}else if(info.widgets_values[w.name].origType == "text"){
|
||||
w.type = "STRING";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.outputs.length > 0){
|
||||
if (this.outputs[0].links == null){
|
||||
info.outputs_values = {links: null, name: this.outputs[0].name, type: this.outputs[0].type};
|
||||
}else{
|
||||
info.outputs_values = {links: [...this.outputs[0].links], name: this.outputs[0].name, slot_index: this.outputs[0].slot_index, type: this.outputs[0].type};
|
||||
}
|
||||
}
|
||||
this.setSize(info.size);
|
||||
});
|
||||
this.widgets[1].callback = ( value ) => {
|
||||
// D'abord, déconnecter tous les liens existants
|
||||
for (let i = 0; i < this.outputs.length; i++) {
|
||||
const output = this.outputs[i];
|
||||
if (output.links && output.links.length) {
|
||||
const links = output.links.slice();
|
||||
for (const linkId of links) {
|
||||
this.graph.removeLink(linkId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.inputs.length; i++) {
|
||||
const input = this.inputs[i];
|
||||
if (input.link) {
|
||||
this.graph.removeLink(input.link);
|
||||
}
|
||||
}
|
||||
clearInputs(this);
|
||||
switch(value){
|
||||
case "none":
|
||||
break;
|
||||
default:
|
||||
this.addOutput("output", value);
|
||||
this.addInput("default", value);
|
||||
break;
|
||||
}
|
||||
this.color = colors[node_type_list.indexOf(value)];
|
||||
this.bgcolor = bg_colors[node_type_list.indexOf(value)];
|
||||
};
|
||||
if (this.widgets[1].value == "none") clearInputs(this);
|
||||
this.color = colors[node_type_list.indexOf("none")];
|
||||
this.bgcolor = bg_colors[node_type_list.indexOf("none")];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { chainCallback, fitHeight } from "./utils.js";
|
||||
import { addInputs, cleanInputs, removeInputs } from "./inputs.js";
|
||||
import { importWorkflow } from "./workflows.js";
|
||||
import { addOutputs } from "./inputs.js";
|
||||
import { hideWidget } from "./widgets.js";
|
||||
|
||||
export function setupWorkflowNode(nodeType, nodeData, app) {
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
chainCallback(this, "onConfigure", function(info) {
|
||||
if (info.widgets_values[0] != "None"){
|
||||
const inputs = app.lipsync_studio[info.widgets_values[0]].inputs;
|
||||
|
||||
addInputs(this, inputs, info.widgets_values);
|
||||
addOutputs(this, info.widgets_values[0]);
|
||||
removeInputs(this, inputs, info.widgets_values);
|
||||
fitHeight(this);
|
||||
importWorkflow(this, info.widgets_values[0], app, nodeData)
|
||||
.then(data => {
|
||||
if (data){
|
||||
this.widgets[1].value = data;
|
||||
const inputs = app.lipsync_studio[info.widgets_values[0]].inputs;
|
||||
|
||||
addInputs(this, inputs, info.widgets_values);
|
||||
addOutputs(this, info.widgets_values[0]);
|
||||
removeInputs(this, inputs, info.widgets_values);
|
||||
fitHeight(this);
|
||||
//importWorkflow(this, info.widgets_values[0], app, nodeData)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur lors de l\'importation:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
chainCallback(this, "onSerialize", function(info) {
|
||||
for (let inp of this.inputs){
|
||||
if (inp.widget){
|
||||
if (inp.type != this.local_input_defs.required[inp.name][0])
|
||||
inp.type = this.local_input_defs.required[inp.name][0];
|
||||
}
|
||||
}
|
||||
if(this.widgets[0].options.values == "COMBO"){
|
||||
this.widgets[0].options.values = ["None", ...Object.keys(app.lipsync_studio)];
|
||||
}
|
||||
});
|
||||
const workflow_reload = this.title.startsWith("Workflow: ")?true:false;
|
||||
this.widgets[0].options.values = ["None", ...Object.keys(app.lipsync_studio)]
|
||||
this.widgets[0].callback = ( value ) => {
|
||||
cleanInputs(this);
|
||||
if (value == "None"){
|
||||
this.title = "Workflow (FlowChain ⛓️)";
|
||||
}else{
|
||||
this.widgets[1].value = importWorkflow(this, value, app, nodeData);
|
||||
const inputs = app.lipsync_studio[value].inputs;
|
||||
addInputs(this, inputs, {}, true);
|
||||
addOutputs(this, value);
|
||||
fitHeight(this);
|
||||
}
|
||||
};
|
||||
if (!workflow_reload){
|
||||
this.widgets[0].value = "None";
|
||||
this.widgets[1].value = "";
|
||||
}
|
||||
hideWidget(this, this.widgets[1], { holdSpace: false })
|
||||
cleanInputs(this);
|
||||
this.color = "#004670";
|
||||
this.bgcolor = "#002942";
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user