Files
numz-Comfyui-FlowChain/web/js/jsnodes.js
T
2024-10-19 23:20:42 +02:00

1027 lines
48 KiB
JavaScript

import { app } from "../../../scripts/app.js";
import { api } from '../../../scripts/api.js'
import { ComfyWidgets } from '../../../scripts/widgets.js'
const client_id = '5b49a023-b05a-4c53-8dc9-addc3a749911'
const colors = ["#222222", "#5940bb", "#FFFFFF", "#7cbb1a", "#29699c", "#777788", "#268bd2", "#2ab7ca", "#d33682", "#dc322f", "#facfad","#77ff77", "#5940bb"]
const bg_colors = ["#000000", "#392978", "#89888d", "#496c12", "#19466a", "#4b4b56", "#165481", "#176974", "#851f50", "#911e1c", "#9f826b", "#499f49", "#392978"]
const node_type_list = ["none", "IMAGE", "MASK", "STRING", "INT", "FLOAT", "LATENT", "CLIP", "CONDITIONING", "MODEL", "VAE", "BOOLEAN", "SWITCH"]
function chainCallback(object, property, callback) {
if (object == undefined) {
console.error("Tried to add callback to non-existant object")
return;
}
if (property in object) {
const callback_orig = object[property]
object[property] = function () {
const r = callback_orig.apply(this, arguments);
callback.apply(this, arguments);
return r
};
} else {
object[property] = callback;
}
}
function useKVState(nodeType) {
chainCallback(nodeType.prototype, "onNodeCreated", function () {
chainCallback(this, "onConfigure", function(info) {
if (!this.widgets) {
//Node has no widgets, there is nothing to restore
return
}
if (typeof(info.widgets_values) != "object") {
//widgets_values is in some unknown inactionable format
return
}
let widgetDict = info.widgets_values
if (widgetDict.length == undefined) {
for (let w of this.widgets) {
if (w.name in widgetDict) {
w.value = widgetDict[w.name];
if (w.name == "videopreview") {
w.updateSource();
}
}
}
}
});
chainCallback(this, "onSerialize", function(info) {
info.widgets_values = {};
if (!this.widgets) {
//object has no widgets, there is nothing to store
return;
}
for (let w of this.widgets) {
info.widgets_values[w.name] = w.value;
}
});
})
}
function addVideoPreview(nodeType) {
chainCallback(nodeType.prototype, "onNodeCreated", function() {
var element = document.createElement("div");
const previewNode = this;
var previewWidget = this.addDOMWidget("videopreview", "preview", element, {
serialize: false,
hideOnZoom: false,
getValue() {
return element.value;
},
setValue(v) {
element.value = v;
},
});
previewWidget.computeSize = function(width) {
if (this.aspectRatio && !this.parentEl.hidden) {
let height = (previewNode.size[0]-20)/ this.aspectRatio + 10;
if (!(height > 0)) {
height = 0;
}
this.computedHeight = height + 10;
return [width, height];
}
return [width, -4];//no loaded src, widget should not display
}
element.addEventListener('contextmenu', (e) => {
e.preventDefault()
return app.canvas._mousedown_callback(e)
}, true);
element.addEventListener('pointerdown', (e) => {
e.preventDefault()
return app.canvas._mousedown_callback(e)
}, true);
element.addEventListener('mousewheel', (e) => {
e.preventDefault()
return app.canvas._mousewheel_callback(e)
}, true);
previewWidget.value = {hidden: false, paused: false, params: {}}
previewWidget.parentEl = document.createElement("div");
previewWidget.parentEl.className = "vhs_preview";
previewWidget.parentEl.style['width'] = "100%"
element.appendChild(previewWidget.parentEl);
previewWidget.videoEl = document.createElement("video");
previewWidget.videoEl.controls = false;
previewWidget.videoEl.loop = true;
previewWidget.videoEl.muted = true;
previewWidget.videoEl.style['width'] = "100%"
previewWidget.videoEl.addEventListener("loadedmetadata", () => {
previewWidget.aspectRatio = previewWidget.videoEl.videoWidth / previewWidget.videoEl.videoHeight;
fitHeight(this);
});
previewWidget.videoEl.addEventListener("error", () => {
//TODO: consider a way to properly notify the user why a preview isn't shown.
previewWidget.parentEl.hidden = true;
fitHeight(this);
});
previewWidget.videoEl.onmouseenter = () => {
previewWidget.videoEl.muted = false;
};
previewWidget.videoEl.onmouseleave = () => {
previewWidget.videoEl.muted = true;
};
previewWidget.imgEl = document.createElement("img");
previewWidget.imgEl.style['width'] = "100%"
previewWidget.imgEl.hidden = true;
previewWidget.imgEl.onload = () => {
previewWidget.aspectRatio = previewWidget.imgEl.naturalWidth / previewWidget.imgEl.naturalHeight;
fitHeight(this);
};
var timeout = null;
this.updateParameters = (params, force_update) => {
if (!previewWidget.value.params) {
if(typeof(previewWidget.value != 'object')) {
previewWidget.value = {hidden: false, paused: false}
}
previewWidget.value.params = {}
}
Object.assign(previewWidget.value.params, params)
timeout = setTimeout(() => previewWidget.updateSource(),100);
};
previewWidget.updateSource = function () {
if (this.value.params == undefined) {
return;
}
let params = {}
Object.assign(params, this.value.params);//shallow copy
this.parentEl.hidden = this.value.hidden;
if (params.format?.split('/')[0] == 'video' ||
app.ui.settings.getSettingValue("VHS.AdvancedPreviews", false) &&
(params.format?.split('/')[1] == 'gif') || params.format == 'folder') {
this.videoEl.autoplay = !this.value.paused && !this.value.hidden;
let target_width = 256
if (element.style?.width) {
//overscale to allow scrolling. Endpoint won't return higher than native
target_width = element.style.width.slice(0,-2)*2;
}
if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") {
params.force_size = target_width+"x?"
} else {
let size = params.force_size.split("x")
let ar = parseInt(size[0])/parseInt(size[1])
params.force_size = target_width+"x"+(target_width/ar)
}
if (app.ui.settings.getSettingValue("VHS.AdvancedPreviews", false)) {
this.videoEl.src = api.apiURL('/viewvideo?' + new URLSearchParams(params));
} else {
previewWidget.videoEl.src = api.apiURL('/view?' + new URLSearchParams(params));
}
this.videoEl.hidden = false;
this.imgEl.hidden = true;
} else if (params.format?.split('/')[0] == 'image'){
//Is animated image
this.imgEl.src = api.apiURL('/view?' + new URLSearchParams(params));
this.videoEl.hidden = true;
this.imgEl.hidden = false;
}
}
previewWidget.parentEl.appendChild(previewWidget.videoEl)
previewWidget.parentEl.appendChild(previewWidget.imgEl)
});
}
function fitHeight(node) {
node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]])
node?.graph?.setDirtyCanvas(true);
}
function addPreviewOptions(nodeType) {
chainCallback(nodeType.prototype, "getExtraMenuOptions", function(_, options) {
let optNew = []
const previewWidget = this.widgets.find((w) => w.name === "videopreview");
let url = null
if (previewWidget.videoEl?.hidden == false && previewWidget.videoEl.src) {
url = api.apiURL('/view?' + new URLSearchParams(previewWidget.value.params));
url = url.replace('%2503d', '001')
} else if (previewWidget.imgEl?.hidden == false && previewWidget.imgEl.src) {
url = previewWidget.imgEl.src;
url = new URL(url);
}
if (url) {
optNew.push(
{
content: "Open preview",
callback: () => {
window.open(url, "_blank")
},
},
{
content: "Save preview",
callback: () => {
const a = document.createElement("a");
a.href = url;
a.setAttribute("download", new URLSearchParams(previewWidget.value.params).get("filename"));
document.body.append(a);
a.click();
requestAnimationFrame(() => a.remove());
},
}
);
}
const PauseDesc = (previewWidget.value.paused ? "Resume" : "Pause") + " preview";
if(previewWidget.videoEl.hidden == false) {
optNew.push({content: PauseDesc, callback: () => {
if(previewWidget.value.paused) {
previewWidget.videoEl?.play();
} else {
previewWidget.videoEl?.pause();
}
previewWidget.value.paused = !previewWidget.value.paused;
}});
}
//TODO: Consider hiding elements if no video preview is available yet.
//It would reduce confusion at the cost of functionality
//(if a video preview lags the computer, the user should be able to hide in advance)
const visDesc = (previewWidget.value.hidden ? "Show" : "Hide") + " preview";
optNew.push({content: visDesc, callback: () => {
if (!previewWidget.videoEl.hidden && !previewWidget.value.hidden) {
previewWidget.videoEl.pause();
} else if (previewWidget.value.hidden && !previewWidget.videoEl.hidden && !previewWidget.value.paused) {
previewWidget.videoEl.play();
}
previewWidget.value.hidden = !previewWidget.value.hidden;
previewWidget.parentEl.hidden = previewWidget.value.hidden;
fitHeight(this);
}});
optNew.push({content: "Sync preview", callback: () => {
//TODO: address case where videos have varying length
//Consider a system of sync groups which are opt-in?
for (let p of document.getElementsByClassName("vhs_preview")) {
for (let child of p.children) {
if (child.tagName == "VIDEO") {
child.currentTime=0;
} else if (child.tagName == "IMG") {
child.src = child.src;
}
}
}
}});
if(options.length > 0 && options[0] != null && optNew.length > 0) {
optNew.push(null);
}
options.unshift(...optNew);
});
}
function addLoadVideoCommon(nodeType, nodeData) {
addVideoPreview(nodeType);
addPreviewOptions(nodeType);
}
function cleanInputs(root_obj, reset_value=true) {
if (!root_obj.inputs) {
root_obj.inputs = [];
}
if (!root_obj.outputs) {
root_obj.outputs = [];
}
if (!root_obj.widgets) {
root_obj.widgets = [];
//root_obj.widgets_values = [];
}
if (!root_obj.widgets_values) {
root_obj.widgets_values = [];
}
root_obj.widgets = root_obj.widgets.splice(0,3)
if(reset_value){
for (let key in root_obj.widgets_values) {
if (key != "workflows" && key != "workflow_api_path" && key != "Import Workflow"){
delete root_obj.widgets_values[key];
}
}
const max_node_output = root_obj.outputs.length;
for(let i = 0; i<max_node_output; i++)
root_obj.removeOutput(0)
}
const max_node_input = root_obj.inputs.length;
for(let i = 0; i<max_node_input; i++)
root_obj.removeInput(0)
}
function clearInputs(root_obj, reset_value=true) {
if (!root_obj.inputs) {
root_obj.inputs = [];
}
if (!root_obj.outputs) {
root_obj.outputs = [];
}
if (!root_obj.widgets) {
root_obj.widgets = [];
}
if (!root_obj.widgets_values) {
root_obj.widgets_values = [];
}
for(let i = 0; i<root_obj.inputs.length; i++){
if (root_obj.inputs[i].name == "default" || root_obj.inputs[i].name == "input"){
root_obj.removeInput(i);
}
}
root_obj.widgets = root_obj.widgets.splice(0,2)
if(reset_value){
for (let key in root_obj.widgets_values) {
if (key != "Name" && key != "type"){
delete root_obj.widgets_values[key];
}
}
}
const max_node_input = root_obj.outputs.length;
for(let i = 0; i<max_node_input; i++)
root_obj.removeOutput(0)
}
function addWidgetType(root_obj, value){
if (!root_obj.widgets) {
root_obj.widgets = [];
}
if (!root_obj.inputs) {
root_obj.inputs = [];
}
const field_name = value.Name
const type = value.type
if (type == "IMAGE" && root_obj.inputs.filter(i => i.name === field_name).length == 0){
root_obj.addInput(field_name, "IMAGE");
}
const input_value = root_obj.widgets.length + 1 < root_obj.widgets_values.length?root_obj.widgets_values[root_obj.widgets.length]:value.default;
if (type == "STRING" || type =="text"){
ComfyWidgets.STRING(root_obj, field_name, ['STRING',{default: value.default,callback: () => {},},],app,)
}
if (type == "INT"){
ComfyWidgets.INT(
root_obj,
field_name,
['',{default: input_value,callback: () => {},},], app,)
}
if (type == "FLOAT"){
ComfyWidgets.FLOAT(
root_obj,
field_name,
['',{default: input_value,callback: (val) => console.log('VALUE', val), "min": 0.00, "max": 1.00, "step": 0.01},],
app,
)
const widget = root_obj.inputs.filter(i => i.name === field_name);
if(widget.length > 0)
app.convertToWidget(root_obj, widget[0]);
}
if (type == "BOOLEAN"){
root_obj.addWidget("toggle",field_name, input_value, ()=>{});
const widget = root_obj.inputs.filter(i => i.name === field_name);
if(widget.length > 0)
app.convertToWidget(root_obj, widget[0]);
}
if (type == "LATENT"){
root_obj.addInput(field_name, "LATENT");
}
if (type == "MODEL"){
root_obj.addInput(field_name, "MODEL");
}
if (type == "CLIP"){
root_obj.addInput(field_name, "CLIP");
}
if (type == "MASK"){
root_obj.addInput(field_name, "MASK");
}
if (type == "CONDITIONING"){
root_obj.addInput(field_name, "CONDITIONING");
}
if (type == "VAE"){
root_obj.addInput(field_name, "VAE");
}
}
function importWorkflow(root_obj, workflow_path, app, reset_values=true){
const filename = workflow_path.replace(/\\/g, '/').split("/");
root_obj.title = "Workflow: "+filename[filename.length-1].replace(".json", "").replace(/_/g, " ");
api.fetchApi("/flowchain/workflow?workflow_path="+workflow_path)
.then(response => response.json())
.then(data => {
cleanInputs(root_obj, reset_values);
if (data.error != "none"){
return false
}else{
const workflow = data.workflow;
console.log('Workflow:', workflow);
const nodes_input = Object.fromEntries(
Object.entries(workflow).filter(([k, v]) => v.class_type == "WorkflowInput")
);
const nodes_output = Object.fromEntries(
Object.entries(workflow).filter(([k, v]) => v.class_type == "WorkflowOutput")
);
Object.fromEntries(
Object.entries(nodes_input).filter((node, idx) => addWidgetType(root_obj, node[1].inputs))
);
Object.fromEntries(
Object.entries(nodes_output).filter((node, idx) =>root_obj.addOutput(`${node[1].inputs.Name}`, node[1].inputs.type))
);
console.log('Nodes:', nodes_input);
root_obj.size[0] = 400;
return true
}
})
.catch(error => {
console.error('Error:', error);
throw error; // Rilancia l'errore per consentire al chiamante di gestirlo
});
}
function addWidgetInfo(root_obj, field_name, value, app){
let type = value.type;
if (type == "converted-widget"){
type = value.origType;
}
if (type == "STRING" || type =="text"){
ComfyWidgets.STRING(root_obj, field_name, ['STRING',{default: value.value,callback: () => {},},],app,)
}
if (type == "INT" || type == "number"){
ComfyWidgets.INT(
root_obj,
field_name,
['',{default: value.value, callback: () => {},},],
app,
)
}
if (type == "FLOAT"){
ComfyWidgets.FLOAT(
root_obj,
field_name,
['',{default: value.value, callback: (val) => console.log('VALUE', val),},],
app,
)
}
if (type == "BOOLEAN" || type == "toggle"){
root_obj.addWidget("toggle",field_name, value.value, ()=>{});
}
}
app.registerExtension({
name: "FlowChain.jsnodes",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if(!nodeData?.category?.startsWith("FlowChain")) {
return;
}
switch (nodeData.name) {
case "Workflow":
nodeType.prototype.onNodeCreated = function() {
chainCallback(this, "onConfigure", function(info) {
let widgetDict = info.widgets_values
if (info.widgets_values.length == undefined) {
if(info.widgets_values.workflows.value != "None"){
const workflow_name = info.widgets_values.workflows.value;
console.log("workflow_name", workflow_name)
console.log(app.lipsync_studio[workflow_name])
const inputs = app.lipsync_studio[workflow_name].inputs;
for (let w of this.widgets) {
if (w.name in widgetDict) {
w.value = widgetDict[w.name].value;
}
}
for (let [key, value] of Object.entries(widgetDict)) {
let widget = this.widgets.find(w => w.name === key);
if(!widget){
addWidgetInfo(this, key, value, app);
widget = this.widgets.find(w => w.name === key);
}
widget.options = info.widgets_values[key].options;
widget.value = info.widgets_values[key].value;
for (let input of this.inputs)
if (input.name == key){
for (let [key2, value2] of Object.entries(inputs)){
if (value2.inputs.Name == key){
input.type = value2.inputs.type;
widget.type = "converted-widget"
widget.origType = info.widgets_values[key].origType;
widget.origComputeSize = undefined;
widget.last_y = info.widgets_values[key].last_y;
widget.origSerializeValue = nodeType.prototype.serializeValue;
widget.value = info.widgets_values[key].value;
widget.computeSize = () => [0, -4]; // -4 is due to the gap litegraph adds between widgets automatically
break;
}
}
break;
}
}
}
}
if (this.id == -1){
for (let i = 0; i < this.outputs.length; i++) {
this.outputs[i] = {links: null, name: info.outputs_values[i].name, type: info.outputs_values[i].type};
}
}else{
if (info.outputs_values != undefined){
this.outputs = [...info.outputs_values];
}
}
this.setSize(info.size);
});
chainCallback(this, "onSerialize", function(info) {
let inps = {};
if (info.widgets_values[2] != "None"){
const workflow_name = info.widgets_values[2];
inps = app.lipsync_studio[workflow_name].inputs
}
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};
}
info.outputs_values = [...this.outputs];
for (let w of this.inputs){
for (let [key, value] of Object.entries(inps)){
if (value.inputs.Name == w.name){
w.type = value.inputs.type;
break;
}
}
}
this.setSize(info.size);
});
const workflow_reload = this.title.startsWith("Workflow: ")?true:false;
const filename = this.title.replace("Workflow: ", "");
this.addWidget("STRING", "workflow_api_path", "", ()=>{});
this.addWidget("button", "Import Workflow", null, () => {
const workflow_path = this.widgets.find(w => w.name === "workflow_api_path")["value"];
const filename = workflow_path.replace(/\\/g, '/').split("/");
this.title = "Workflow: "+filename[filename.length-1].replace(".json", "").replace(/_/g, " ");
cleanInputs(this);
if (workflow_path != "" && workflow_path != "None")
api.fetchApi("/flowchain/workflow?workflow_path="+workflow_path)
.then(response => response.json())
.then(data => {
// Eseguire l'elaborazione dei dati
const workflow = data.workflow;
//console.log('Workflow:', workflow);
if (data.error == "none"){
const combo = this.widgets.find(w => w.name === "workflows");
if (!combo.options.values.includes(data.file_name)){
combo.options.values.push(data.file_name);
}
const nodes_input = Object.fromEntries(
Object.entries(workflow).filter(([k, v]) => v.class_type == "WorkflowInput")
);
const nodes_output = Object.fromEntries(
Object.entries(workflow).filter(([k, v]) => v.class_type == "WorkflowOutput")
);
//remove a values from app.lipsync_studio
if (data.file_name in app.lipsync_studio){
delete app.lipsync_studio[data.file_name];
}
if ("No file in worflows/api folder" in app.lipsync_studio){
delete app.lipsync_studio["No file in worflows/api folder"];
}
app.lipsync_studio[data.file_name] = {inputs: nodes_input, outputs: nodes_output};
combo.value = data.file_name;
importWorkflow(this, data.file_name, app)
}else{
alert(data.error)
}
})
.catch(error => {
console.error('Error:', error);
throw error; // Rilancia l'errore per consentire al chiamante di gestirlo
});
});
this.addWidget("combo", "workflows", "None", (value) => {
if (value == "None" || value == ""){
this.title = "Workflow (FlowChain ⛓️)";
cleanInputs(this);
}else{
importWorkflow(this, value, app)
}
},{
values: ["None", ...Object.keys(app.lipsync_studio)]
});
if(!workflow_reload || !filename in app.lipsync_studio){
cleanInputs(this);
}
this.color = "#004670";
this.bgcolor = "#002942";
}
break;
case "WorkflowInput":
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){
addWidgetInfo(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"){
input.type = info.widgets_values.type.value;
widget.type = "converted-widget"
widget.origType = info.widgets_values[key].origType;
widget.origComputeSize = undefined;
widget.last_y = info.widgets_values[key].last_y;
widget.origSerializeValue = nodeType.prototype.serializeValue;
widget.computeSize = () => [0, -4];
}else{
this.removeInput(this.inputs.indexOf(input));
}
break;
}
}
}
}
// get inputs by name
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};
}
}
this.setSize(info.size);
/*for(let i = this.outputs.length - 1; i>0; i--){
if (this.outputs[i].name == "*"){
this.removeOutput(i);
}
}*/
});
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";
}
}
}
}
// deep copy outputs without reference
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 ) => {
clearInputs(this);
switch(value){
case "STRING":
this.addOutput("output", "STRING");
ComfyWidgets.STRING(
this,
"default",
["STRING",{default: "",callback: (val) => console.log('VALUE', val),},],
app,
)
break;
case "INT":
this.addOutput("output", "INT");
ComfyWidgets.INT(
this,
"default",
['',{default: 0,callback: (val) => console.log('VALUE', val),},],
app,
)
break;
case "FLOAT":
this.addOutput("output", "FLOAT");
ComfyWidgets.FLOAT(
this,
"default",
['',{default: 0,callback: (val) => console.log('VALUE', val), "min": 0.00, "max": 1.00, "step": 0.01},],
app,
)
break;
case "BOOLEAN":
this.addOutput("output", "BOOLEAN");
this.addWidget("toggle", "default", false, ()=>{});
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)];
};
this.color = colors[node_type_list.indexOf("none")];
this.bgcolor = bg_colors[node_type_list.indexOf("none")];
}
break;
case "WorkflowContinue":
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){
addWidgetInfo(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"){
input.type = info.widgets_values.type.value;
widget.type = "converted-widget"
widget.origType = info.widgets_values[key].origType;
widget.origComputeSize = undefined;
widget.last_y = info.widgets_values[key].last_y;
widget.origSerializeValue = nodeType.prototype.serializeValue;
widget.computeSize = () => [0, -4];
}else{
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);
});
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 == "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);
});
this.widgets[0].callback = ( value ) => {
clearInputs(this);
this.addOutput("output", value);
this.addInput("input", value);
this.color = colors[node_type_list.indexOf(value)];
this.bgcolor = bg_colors[node_type_list.indexOf(value)];
};
this.color = colors[node_type_list.indexOf("none")];
this.bgcolor = bg_colors[node_type_list.indexOf("none")];
}
break;
case "WorkflowOutput":
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){
addWidgetInfo(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"){
input.type = info.widgets_values.type.value;
widget.type = "converted-widget"
widget.origType = info.widgets_values[key].origType;
widget.origComputeSize = undefined;
widget.last_y = info.widgets_values[key].last_y;
widget.origSerializeValue = nodeType.prototype.serializeValue;
widget.computeSize = () => [0, -4];
}else{
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};
}
}
/*for(let i = this.outputs.length - 1; i>0; i--){
if (this.outputs[i].name == "*"){
this.removeOutput(i);
}
}*/
});
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 ) => {
clearInputs(this);
this.addOutput("output", value);
this.addInput("default", value);
this.color = colors[node_type_list.indexOf(value)];
this.bgcolor = bg_colors[node_type_list.indexOf(value)];
};
clearInputs(this);
this.color = colors[node_type_list.indexOf("none")];
this.bgcolor = bg_colors[node_type_list.indexOf("none")];
}
break;
case "WorkflowLipSync":
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
}
break;
}
},
async init(app) {
api.fetchApi("/flowchain/workflows")
.then(response => response.json())
.then(data => {
app.lipsync_studio = data
})
.catch(error => {
console.error('Error:', error);
throw error;
});
}
});