review all process
This commit is contained in:
@@ -37,8 +37,22 @@ The idea came from the frustration users experienced with the many limitations a
|
||||
By solving these problems, my tool makes node management more intuitive, stable, and efficient, allowing you to focus on what matters: creating.
|
||||
|
||||
## 🚀 Updates
|
||||
**2025.03.23**
|
||||
First of all, I apologize for not maintaining the code regularly. ComfyUI has evolved significantly since my last version, so I've completely revised the code to ensure better integration with ComfyUI and avoid future issues.
|
||||
|
||||
Features in this version:
|
||||
- 💪 No need to convert your workflows to API format anymore - you can use original workflows directly.
|
||||
- 💪 Better cache management
|
||||
- 🗑️ remove "import workflow", no longer usefull since working on standart workflow.
|
||||
-
|
||||
|
||||
Warning and know issue:
|
||||
- 🛑 old version will not be compatible, sorry about that
|
||||
- ⚠️ The word "seed" in input as "INT" will break the node so if you wan't use "seed" as input prefer to give a name like "seed_input" or whatever...
|
||||
|
||||
|
||||
**2024.11.01 Initial version features :**
|
||||
- 💪 Convert your workflows into nodes
|
||||
1 Convert your workflows into nodes
|
||||
- ⛓️ Chain your workflow
|
||||
- 👄 Extra Node that use [LipSync Studio v0.6](https://www.patreon.com/Wav2LipStudio)
|
||||
|
||||
@@ -93,19 +107,14 @@ After this command be created folder Comfyui-FlowChain
|
||||
## ⛓️ Workflow Node
|
||||

|
||||
|
||||
- Load a workflow in **workflows** list. This field will show all workflows saved in the comfyui user folder: **ComfyUI\user\default\workflows\api**, if you add a new workflow in this folder you have to refresh UI (F5 to refresh web page) to see it in the **workflows** list.
|
||||
- Workflows have to be saved as **API format** of comfyui, but save it also in normal format because "api formal file" can't be loaded in comfyui as usually.
|
||||
Select a workflow from the **workflows** dropdown menu. This list displays all compatible workflows saved in your ComfyUI user directory:
|
||||
|
||||
<img src="docs/assets/save_as_api.png">
|
||||
`ComfyUI\user\default\workflows`
|
||||
|
||||
If you don't see **"Export (API format)"** options in Comfyui do this :
|
||||
- go to Settings
|
||||
- Activate the **Dev Mode** options
|
||||
|
||||

|
||||
|
||||
-You can also Import the file by "copy/paste" your workflow path in "workflow_api_path" and click import, that will add your workflow in the comfyui api path.
|
||||
For a workflow to be considered **compatible**, it must contain at least one "Workflow Input" or "Workflow Output" node. This ensures the workflow can properly interface with the FlowChain system.
|
||||
|
||||
**Note:** After adding new workflows to this directory, you'll need to refresh the ComfyUI interface (press F5) to see them appear in the dropdown list. But once loaded, it will refresh the parent node automatically
|
||||
|
||||
## ⛓️ Input Node
|
||||

|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 82 KiB |
+33
-223
@@ -83,7 +83,7 @@ async def workflows(request):
|
||||
@server.PromptServer.instance.routes.get("/flowchain/workflow")
|
||||
async def workflow(request):
|
||||
user = UserManager().get_request_user_id(request)
|
||||
|
||||
result = {}
|
||||
original_path = request.query.get("workflow_path")
|
||||
unversal_path = original_path.replace("\\", "/")
|
||||
json_path = unversal_path.split("/")
|
||||
@@ -94,231 +94,41 @@ async def workflow(request):
|
||||
if os.path.exists(json_path):
|
||||
with open(json_path, "r", encoding="utf-8") as f:
|
||||
json_content = json.load(f)
|
||||
err = "none"
|
||||
"""
|
||||
|
||||
nodes_input = {}
|
||||
nodes_output = {}
|
||||
|
||||
# Vérifier le format du workflow (API ou standard)
|
||||
if "nodes" in json_content:
|
||||
try:
|
||||
api_workflow = convert_standard_to_api_format(json_content)
|
||||
if api_workflow:
|
||||
json_content = api_workflow
|
||||
# Conservons la référence au workflow original si nécessaire
|
||||
# json_content["_original_standard_format"] = True
|
||||
else:
|
||||
err = "Échec de la conversion du workflow standard en format API"
|
||||
except Exception as e:
|
||||
err = f"Erreur lors de la conversion: {str(e)}"
|
||||
"""
|
||||
result = {"error": err, "workflow": json_content, "file_name": file_name}
|
||||
# Extraire les nœuds WorkflowInput et WorkflowOutput
|
||||
for node in json_content["nodes"]:
|
||||
if node.get("type") == "WorkflowInput":
|
||||
# Convertir au format compatible pour le client
|
||||
node_id = str(node.get("id", "unknown"))
|
||||
nodes_input[node_id] = {
|
||||
"class_type": "WorkflowInput",
|
||||
"inputs": node.get("widgets_values", {})
|
||||
}
|
||||
elif node.get("type") == "WorkflowOutput":
|
||||
node_id = str(node.get("id", "unknown"))
|
||||
nodes_output[node_id] = {
|
||||
"class_type": "WorkflowOutput",
|
||||
"inputs": node.get("widgets_values", {})
|
||||
}
|
||||
else:
|
||||
# Format API
|
||||
nodes_input = {k: v for k, v in json_content.items() if
|
||||
v.get("class_type") == "WorkflowInput"}
|
||||
nodes_output = {k: v for k, v in json_content.items() if
|
||||
v.get("class_type") == "WorkflowOutput"}
|
||||
|
||||
# Ajouter au résultat seulement si le fichier contient des nœuds WorkflowInput ou WorkflowOutput
|
||||
if nodes_input or nodes_output:
|
||||
result = {"inputs": nodes_input,
|
||||
"outputs": nodes_output,
|
||||
'workflow': json_content}
|
||||
else:
|
||||
result = {"error": "File not found"}
|
||||
|
||||
return web.json_response(result, content_type='application/json')
|
||||
|
||||
|
||||
def convert_standard_to_api_format(standard_workflow):
|
||||
"""
|
||||
Convertit un workflow au format standard en format API de manière similaire à graphToPrompt
|
||||
"""
|
||||
# Vérifier si c'est un workflow valide
|
||||
if not standard_workflow or "nodes" not in standard_workflow or not isinstance(standard_workflow["nodes"], list):
|
||||
print("Format de workflow invalide")
|
||||
return None
|
||||
|
||||
# Créer une structure pour retrouver les nœuds par ID
|
||||
nodes_by_id = {node["id"]: node for node in standard_workflow["nodes"]}
|
||||
|
||||
# Créer une map des liens
|
||||
links_by_id = {}
|
||||
if "links" in standard_workflow and isinstance(standard_workflow["links"], list):
|
||||
for link in standard_workflow["links"]:
|
||||
# Format du lien: [id, origin_node, origin_slot, target_node, target_slot, type]
|
||||
if len(link) >= 5:
|
||||
links_by_id[link[0]] = {
|
||||
"id": link[0],
|
||||
"origin_id": link[1],
|
||||
"origin_slot": link[2],
|
||||
"target_id": link[3],
|
||||
"target_slot": link[4],
|
||||
"type": link[5] if len(link) > 5 else None
|
||||
}
|
||||
|
||||
# Map des liens par cible (nœud + slot)
|
||||
links_by_target = {}
|
||||
for link_id, link in links_by_id.items():
|
||||
key = f"{link['target_id']}-{link['target_slot']}"
|
||||
links_by_target[key] = link
|
||||
|
||||
# Résultat au format API
|
||||
api_format = {}
|
||||
|
||||
# Obtenir un ordre d'exécution approximatif (simplifié)
|
||||
# Remarque: c'est une simplification, le vrai ordre d'exécution est plus complexe
|
||||
execution_order = simulate_execution_order(standard_workflow)
|
||||
|
||||
# Traiter les nœuds selon l'ordre d'exécution simulé
|
||||
for node_id in execution_order:
|
||||
node = nodes_by_id.get(node_id)
|
||||
if not node:
|
||||
continue
|
||||
|
||||
# Ignorer les nœuds désactivés (mode NEVER ou BYPASS) - similaire à LGraphEventMode
|
||||
# Le mode 2 correspond généralement à NEVER, le mode 3 à BYPASS dans LiteGraph
|
||||
if node.get("mode") in [2, 4]:
|
||||
continue
|
||||
|
||||
# Initialiser le nœud au format API
|
||||
api_node = {
|
||||
"inputs": {},
|
||||
"class_type": node.get("type", ""),
|
||||
"_meta": {
|
||||
"title": node.get("title", node.get("type", ""))
|
||||
}
|
||||
}
|
||||
|
||||
# Traiter les widgets (valeurs des paramètres)
|
||||
if "widgets_values" in node:
|
||||
# Si c'est un tableau (ancien format)
|
||||
if isinstance(node["widgets_values"], list):
|
||||
# Associer chaque valeur à son nom si disponible
|
||||
if "widgets" in node and isinstance(node["widgets"], list):
|
||||
for i, widget in enumerate(node["widgets"]):
|
||||
if i < len(node["widgets_values"]) and "name" in widget:
|
||||
api_node["inputs"][widget["name"]] = node["widgets_values"][i]
|
||||
# Si c'est un objet (format plus récent)
|
||||
elif isinstance(node["widgets_values"], dict):
|
||||
for name, value in node["widgets_values"].items():
|
||||
# Si la valeur a une structure spécifique avec une propriété "value"
|
||||
if isinstance(value, dict) and "value" in value:
|
||||
api_node["inputs"][name] = value["value"]
|
||||
else:
|
||||
api_node["inputs"][name] = value
|
||||
|
||||
# Traiter les connexions d'entrée
|
||||
if "inputs" in node and isinstance(node["inputs"], list):
|
||||
for slot_index, input_data in enumerate(node["inputs"]):
|
||||
if not input_data:
|
||||
continue
|
||||
|
||||
input_name = input_data.get("name", f"input_{slot_index}")
|
||||
|
||||
# Vérifier si cette entrée a un lien
|
||||
if "link" in input_data and input_data["link"] is not None:
|
||||
link = links_by_id.get(input_data["link"])
|
||||
if link:
|
||||
# Suivre les redirections (nœuds en bypass)
|
||||
origin_id, origin_slot = trace_link_through_bypasses(
|
||||
link["origin_id"],
|
||||
link["origin_slot"],
|
||||
nodes_by_id,
|
||||
links_by_id
|
||||
)
|
||||
|
||||
if origin_id is not None:
|
||||
# Format API pour une connexion: [node_id, output_slot]
|
||||
api_node["inputs"][input_name] = [str(origin_id), origin_slot]
|
||||
|
||||
# Ajouter le nœud au format API
|
||||
api_format[str(node_id)] = api_node
|
||||
|
||||
# Nettoyer les liens qui pointent vers des nœuds inexistants
|
||||
for node_id, node_data in api_format.items():
|
||||
inputs_to_remove = []
|
||||
|
||||
for input_name, input_value in node_data["inputs"].items():
|
||||
if isinstance(input_value, list) and len(input_value) == 2:
|
||||
source_node_id = input_value[0]
|
||||
if source_node_id not in api_format:
|
||||
inputs_to_remove.append(input_name)
|
||||
|
||||
for input_name in inputs_to_remove:
|
||||
del node_data["inputs"][input_name]
|
||||
|
||||
return api_format
|
||||
|
||||
|
||||
def simulate_execution_order(workflow):
|
||||
"""
|
||||
Simule un ordre d'exécution approximatif des nœuds.
|
||||
C'est une simplification de graph.computeExecutionOrder().
|
||||
"""
|
||||
# Fonction simplifiée - dans un vrai graphe, cela serait plus complexe
|
||||
# en impliquant une analyse topologique du graphe de dépendances
|
||||
|
||||
# On construit un graphe de dépendances
|
||||
nodes_by_id = {node["id"]: node for node in workflow["nodes"]}
|
||||
dependencies = {node["id"]: set() for node in workflow["nodes"]}
|
||||
|
||||
# Trouver les dépendances de chaque nœud
|
||||
if "links" in workflow and isinstance(workflow["links"], list):
|
||||
for link in workflow["links"]:
|
||||
if len(link) >= 5:
|
||||
target_id = link[3] # Le nœud qui reçoit la connexion
|
||||
source_id = link[1] # Le nœud d'origine
|
||||
if target_id in dependencies:
|
||||
dependencies[target_id].add(source_id)
|
||||
|
||||
# Tri topologique simplifié
|
||||
visited = set()
|
||||
result = []
|
||||
|
||||
def visit(node_id):
|
||||
if node_id in visited:
|
||||
return
|
||||
visited.add(node_id)
|
||||
for dep_id in dependencies[node_id]:
|
||||
if dep_id in dependencies: # S'assurer que le nœud existe
|
||||
visit(dep_id)
|
||||
result.append(node_id)
|
||||
|
||||
# Visiter tous les nœuds
|
||||
for node_id in dependencies:
|
||||
if node_id not in visited:
|
||||
visit(node_id)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def trace_link_through_bypasses(origin_id, origin_slot, nodes_by_id, links_by_id):
|
||||
"""
|
||||
Suit une chaîne de nœuds en mode BYPASS pour trouver la source réelle.
|
||||
Simule le comportement de la boucle while dans graphToPrompt.
|
||||
"""
|
||||
current_id = origin_id
|
||||
current_slot = origin_slot
|
||||
|
||||
# Maximum d'itérations pour éviter les boucles infinies
|
||||
max_iterations = 100
|
||||
iterations = 0
|
||||
|
||||
while iterations < max_iterations:
|
||||
iterations += 1
|
||||
|
||||
parent = nodes_by_id.get(current_id)
|
||||
if not parent:
|
||||
break
|
||||
|
||||
# Si le nœud parent n'est pas en mode BYPASS, on a trouvé la source
|
||||
if parent.get("mode") != 3: # 3 est généralement le mode BYPASS
|
||||
return current_id, current_slot
|
||||
|
||||
# Chercher le lien d'entrée correspondant pour continuer à remonter
|
||||
if "inputs" in parent and isinstance(parent["inputs"], list):
|
||||
for i, input_data in enumerate(parent["inputs"]):
|
||||
if not input_data or "link" not in input_data:
|
||||
continue
|
||||
|
||||
# Vérifier si le type correspond (simplifié)
|
||||
# Dans le vrai code, il y a une vérification plus complexe des types
|
||||
link = links_by_id.get(input_data["link"])
|
||||
if link:
|
||||
# Mise à jour pour la prochaine itération
|
||||
current_id = link["origin_id"]
|
||||
current_slot = link["origin_slot"]
|
||||
break
|
||||
else:
|
||||
# Aucun lien compatible trouvé
|
||||
break
|
||||
|
||||
# Si on arrive ici, soit on a atteint le maximum d'itérations,
|
||||
# soit on n'a pas trouvé de chemin valide
|
||||
return origin_id, origin_slot
|
||||
+271
-337
@@ -273,7 +273,7 @@ function addLoadVideoCommon(nodeType, nodeData) {
|
||||
addVideoPreview(nodeType);
|
||||
addPreviewOptions(nodeType);
|
||||
}
|
||||
function cleanInputs(root_obj, nodeData, reset_value=true) {
|
||||
function cleanInputs(root_obj, start_index = 2, reset_value=true) {
|
||||
//nodeData.input.required = {};
|
||||
/*
|
||||
if (!root_obj.inputs) root_obj.inputs = [];
|
||||
@@ -302,13 +302,15 @@ function cleanInputs(root_obj, nodeData, reset_value=true) {
|
||||
}
|
||||
|
||||
|
||||
root_obj.widgets = root_obj.widgets.splice(0,2)
|
||||
root_obj.widgets = root_obj.widgets.splice(0,start_index)
|
||||
if(reset_value){
|
||||
root_obj.widgets_values = [];
|
||||
/*
|
||||
for (let key in root_obj.widgets_values) {
|
||||
if (key != "workflows"){
|
||||
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)
|
||||
@@ -326,9 +328,7 @@ function cleanInputs(root_obj, nodeData, reset_value=true) {
|
||||
}
|
||||
function clearInputs(root_obj, reset_value=true) {
|
||||
//nodeData.input.required = {};
|
||||
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 = [];
|
||||
|
||||
// Déconnecter tous les liens d'abord pour les entrées
|
||||
@@ -375,6 +375,7 @@ function clearInputs(root_obj, reset_value=true) {
|
||||
}*/
|
||||
|
||||
}
|
||||
/*
|
||||
function addWidgetType(root_obj, value, nodeData){
|
||||
if (!root_obj.widgets) {
|
||||
root_obj.widgets = [];
|
||||
@@ -437,7 +438,7 @@ function addWidgetType(root_obj, value, nodeData){
|
||||
if (type == "VAE") root_obj.addInput(field_name, "VAE");
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
async function convertWorkflowToApiFormat(standardWorkflow) {
|
||||
try {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -528,117 +529,63 @@ async function importWorkflow(root_obj, workflow_path, app, nodeData, reset_valu
|
||||
const filename = workflow_path.replace(/\\/g, '/').split("/");
|
||||
root_obj.title = "Workflow: "+filename[filename.length-1].replace(".json", "").replace(/_/g, " ");
|
||||
|
||||
root_obj.local_input_defs = {
|
||||
required: {},
|
||||
optional: {}
|
||||
};
|
||||
/*
|
||||
|
||||
return api.fetchApi("/flowchain/workflow?workflow_path="+workflow_path)
|
||||
.then(response => response.json())
|
||||
.then(async data => {*/
|
||||
cleanInputs(root_obj,nodeData, reset_values);
|
||||
/*
|
||||
if (data.error != "none"){
|
||||
return false;
|
||||
}*/
|
||||
|
||||
//let workflow = data.workflow;
|
||||
let workflow = app.lipsync_studio[workflow_path].workflow;
|
||||
|
||||
// Si c'est un format standard, le convertir en format API
|
||||
if ("nodes" in workflow) {
|
||||
try {
|
||||
//app.loading_bling = true;
|
||||
workflow = await convertWorkflowToApiFormat(workflow);
|
||||
//app.loading_bling = false;
|
||||
} catch (error) {
|
||||
console.error("Échec de la conversion du workflow:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!workflow) {
|
||||
console.error('Workflow invalide ou échec de conversion');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Traiter le workflow API
|
||||
const nodes_input = Object.fromEntries(
|
||||
Object.entries(workflow).filter(([k, v]) => v.class_type == "WorkflowInput")
|
||||
);
|
||||
.then(async data => {
|
||||
//cleanInputs(root_obj,nodeData, reset_values);
|
||||
|
||||
let workflow = data.workflow;
|
||||
app.lipsync_studio[workflow_path] = data;
|
||||
|
||||
// Si c'est un format standard, le convertir en format API
|
||||
if ("nodes" in workflow) {
|
||||
try {
|
||||
workflow = await convertWorkflowToApiFormat(workflow);
|
||||
} catch (error) {
|
||||
console.error("Échec de la conversion du workflow:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!workflow) {
|
||||
console.error('Workflow invalide ou échec de conversion');
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
// Traiter le workflow API
|
||||
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")
|
||||
);
|
||||
root_obj.widgets[1].value = JSON.stringify(workflow);
|
||||
//ComfyWidgets.STRING(root_obj, "workflow", ['STRING',{default: JSON.stringify(workflow)},],app,)
|
||||
//app.hideWidget(root_obj, root_obj.widgets[root_obj.widgets.length - 1], {holdSpace: false});
|
||||
// root_obj.widgets[root_obj.widgets.length - 1].hidden = true;
|
||||
// Ajouter les widgets d'entrée
|
||||
|
||||
Object.entries(nodes_input).forEach(node => {
|
||||
addWidgetType(root_obj, node[1].inputs, nodeData);
|
||||
});
|
||||
const nodes_output = Object.fromEntries(
|
||||
Object.entries(workflow).filter(([k, v]) => v.class_type == "WorkflowOutput")
|
||||
);
|
||||
root_obj.widgets[1].value = JSON.stringify(workflow);
|
||||
|
||||
|
||||
Object.entries(nodes_input).forEach(node => {
|
||||
addWidgetType(root_obj, node[1].inputs, nodeData);
|
||||
});
|
||||
|
||||
// Ajouter les sorties
|
||||
Object.entries(nodes_output).forEach(node => {
|
||||
root_obj.addOutput(`${node[1].inputs.Name}`, node[1].inputs.type);
|
||||
});
|
||||
// Ajouter les sorties
|
||||
Object.entries(nodes_output).forEach(node => {
|
||||
root_obj.addOutput(`${node[1].inputs.Name}`, node[1].inputs.type);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
root_obj.size[0] = 400;
|
||||
return JSON.stringify(workflow);
|
||||
/*
|
||||
root_obj.size[0] = 400;*/
|
||||
return JSON.stringify(workflow);
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur lors de l\'importation:', error);
|
||||
return false;
|
||||
});*/
|
||||
}
|
||||
/*
|
||||
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{
|
||||
let 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, nodeData){
|
||||
|
||||
function addWidgets(root_obj, field_name, value, app){
|
||||
let type = value.type;
|
||||
if (type == "converted-widget"){
|
||||
type = value.origType;
|
||||
@@ -662,7 +609,7 @@ function addWidgetInfo(root_obj, field_name, value, app, nodeData){
|
||||
ComfyWidgets.INT(
|
||||
root_obj,
|
||||
field_name,
|
||||
['',{default: value.value, "min": 0, "max": 18446744073709551616, "step": 1},],
|
||||
['INT',{default: value.value, "min": 0, "max": 18446744073709551616, "step": 1},],
|
||||
app,
|
||||
)
|
||||
// Stocker la définition localement
|
||||
@@ -673,7 +620,7 @@ function addWidgetInfo(root_obj, field_name, value, app, nodeData){
|
||||
ComfyWidgets.FLOAT(
|
||||
root_obj,
|
||||
field_name,
|
||||
['',{default: value.value, callback: (val) => console.log('VALUE', val), "min": 0.00, "max": 2048.00, "step": 0.01},],
|
||||
['FLOAT',{default: value.value, "min": 0.00, "max": 2048.00, "step": 0.01},],
|
||||
app,
|
||||
)
|
||||
// Stocker la définition localement
|
||||
@@ -692,6 +639,22 @@ function addWidgetInfo(root_obj, field_name, value, app, nodeData){
|
||||
// Stocker la définition localement
|
||||
root_obj.local_input_defs.required[field_name] = ["STRING", {}];
|
||||
}
|
||||
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 addOutputs(root_obj, workflow_name){
|
||||
const outputs = app.lipsync_studio[workflow_name].outputs;
|
||||
for (let [key, value] of Object.entries(outputs)){
|
||||
const isoutput = root_obj.outputs.find(i => i.name == value.inputs.Name.value);
|
||||
if (!isoutput){
|
||||
root_obj.addOutput(value.inputs.Name.value, value.inputs.type.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change
|
||||
@@ -710,25 +673,32 @@ function hideWidget(
|
||||
// @ts-expect-error custom widget type
|
||||
widget.type = "converted-widget" + suffix
|
||||
if (holdSpace) {
|
||||
widget.computeSize = () => [0, LiteGraph.NODE_WIDGET_HEIGHT]
|
||||
widget.computeSize = () => [0, LiteGraph.NODE_WIDGET_HEIGHT]
|
||||
widget.serializeValue = (node, index) => {
|
||||
// Prevent serializing the widget if we have no input linked
|
||||
if (!node.inputs) {
|
||||
return undefined
|
||||
}
|
||||
let node_input = node.inputs.find((i) => i.widget?.name === widget.name)
|
||||
|
||||
if (!node_input || !node_input.link) {
|
||||
return undefined
|
||||
}
|
||||
return widget.origSerializeValue
|
||||
? widget.origSerializeValue(node, index)
|
||||
: widget.value
|
||||
}
|
||||
} else {
|
||||
// -4 is due to the gap litegraph adds between widgets automatically
|
||||
widget.computeSize = () => [0, -4]
|
||||
}
|
||||
widget.serializeValue = (node, index) => {
|
||||
// Prevent serializing the widget if we have no input linked
|
||||
if (!node.inputs) {
|
||||
return undefined
|
||||
}
|
||||
let node_input = node.inputs.find((i) => i.widget?.name === widget.name)
|
||||
|
||||
if (!node_input || !node_input.link) {
|
||||
return undefined
|
||||
}
|
||||
return widget.origSerializeValue
|
||||
? widget.origSerializeValue(node, index)
|
||||
: widget.value
|
||||
// -4 is due to the gap litegraph adds between widgets automatically
|
||||
widget.computeSize = () => [0, -4]
|
||||
widget.serializeValue = (node, index) => {
|
||||
return widget.origSerializeValue
|
||||
? widget.origSerializeValue(node, index)
|
||||
: widget.value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Hide any linked widgets, e.g. seed+seedControl
|
||||
if (widget.linkedWidgets) {
|
||||
@@ -738,23 +708,6 @@ function hideWidget(
|
||||
}
|
||||
}
|
||||
|
||||
function showWidget(widget) {
|
||||
// @ts-expect-error custom widget type
|
||||
widget.type = widget.origType
|
||||
widget.computeSize = widget.origComputeSize
|
||||
widget.serializeValue = widget.origSerializeValue
|
||||
|
||||
delete widget.origType
|
||||
delete widget.origComputeSize
|
||||
delete widget.origSerializeValue
|
||||
|
||||
// Hide any linked widgets, e.g. seed+seedControl
|
||||
if (widget.linkedWidgets) {
|
||||
for (const w of widget.linkedWidgets) {
|
||||
showWidget(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getWidgetType(config) {
|
||||
// Special handling for COMBO so we restrict links based on the entries
|
||||
@@ -778,14 +731,7 @@ function hideWidget(
|
||||
|
||||
// Add input and store widget config for creating on primitive node
|
||||
const [oldWidth, oldHeight] = node.size
|
||||
/*const inputIsOptional = !!widget.options?.inputIsOptional
|
||||
|
||||
const input = node.addInput(widget.name, type, {
|
||||
// @ts-expect-error [GET_CONFIG] is not a valid property of IWidget
|
||||
widget: { name: widget.name, [GET_CONFIG]: () => config },
|
||||
...(inputIsOptional ? { shape: LiteGraph.SlotShape.HollowCircle } : {})
|
||||
})
|
||||
*/
|
||||
|
||||
for (const widget of node.widgets) {
|
||||
widget.last_y += LiteGraph.NODE_SLOT_HEIGHT
|
||||
}
|
||||
@@ -798,21 +744,34 @@ function hideWidget(
|
||||
return node
|
||||
}
|
||||
|
||||
function convertToWidget(node, widget) {
|
||||
showWidget(widget)
|
||||
const [oldWidth, oldHeight] = node.size
|
||||
node.removeInput(node.inputs.findIndex((i) => i.widget?.name === widget.name))
|
||||
|
||||
for (const widget of node.widgets) {
|
||||
widget.last_y -= LiteGraph.NODE_SLOT_HEIGHT
|
||||
export function addInputs(root_obj, inputs, widgets_values){
|
||||
|
||||
|
||||
let start_index = 2;
|
||||
for (let [key, value] of Object.entries(inputs)){
|
||||
const isinput = root_obj.inputs.find(i => i.name == value.inputs[0]);
|
||||
const isWidget = root_obj.widgets.find(w => w.name == value.inputs[0]);
|
||||
|
||||
if (!isWidget && (!isinput||isinput.widget)){
|
||||
if (value.inputs[2]){
|
||||
const values = widgets_values[start_index] ?? value.inputs[2];
|
||||
const widget_param = {value: values, type: value.inputs[1]}
|
||||
//const widget_param = {value: widgets_values[start_index], type: value.inputs.default.type}
|
||||
addWidgets(root_obj, value.inputs[0], widget_param, app);
|
||||
if (isinput){
|
||||
//node.removeInput(node.inputs.findIndex((i) => i.widget?.name === widget.name))
|
||||
const config = [value.inputs[2]]
|
||||
convertToInput(root_obj, root_obj.widgets[start_index], config)
|
||||
}
|
||||
start_index += 1;
|
||||
}else{
|
||||
const input_param = {type: value.inputs[1]};
|
||||
addWidgets(root_obj, value.inputs[0], input_param, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original size but grow if needed
|
||||
node.setSize([
|
||||
Math.max(oldWidth, node.size[0]),
|
||||
Math.max(oldHeight, node.size[1])
|
||||
])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "FlowChain.jsnodes",
|
||||
@@ -823,19 +782,33 @@ app.registerExtension({
|
||||
|
||||
switch (nodeData.name) {
|
||||
case "Workflow":
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
chainCallback(this, "onConfigure", function(info) {
|
||||
//let widgetDict = info.widgets_values
|
||||
if (!info.widgets_values[0] != "None" && !app.loading_bling){
|
||||
|
||||
if (this.widgets.length==2){
|
||||
if (info.widgets_values[0] != "None"){
|
||||
//this.widgets[1].value = importWorkflow(this, info.widgets_values[0], app, nodeData)
|
||||
//if (this.widgets.length==2){
|
||||
const inputs = app.lipsync_studio[info.widgets_values[0]].inputs;
|
||||
addInputs(this, inputs, info.widgets_values);
|
||||
addOutputs(this, info.widgets_values[0]);
|
||||
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]);
|
||||
importWorkflow(this, info.widgets_values[0], app, nodeData)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur lors de l\'importation:', error);
|
||||
});
|
||||
|
||||
/*
|
||||
if (info.widgets_values[1] != this.widgets[1].value){
|
||||
this.widgets[1].value = info.widgets_values[1];
|
||||
//hideWidget(this, this.widgets[1])
|
||||
//ComfyWidgets.STRING(this, "workflow", ['STRING',{default: workflow},],app,);
|
||||
//this.widgets[1].hidden = true;
|
||||
}
|
||||
//this.widgets[0].value = info.widgets_values[0];
|
||||
const inputs = app.lipsync_studio[info.widgets_values[0]].inputs;
|
||||
|
||||
let start_index = 2;
|
||||
@@ -844,7 +817,7 @@ app.registerExtension({
|
||||
const isWidget = this.widgets.find(w => w.name == value.inputs.Name.value);
|
||||
if (!isWidget && (!isinput||isinput.widget)){
|
||||
const widget_param = {value: info.widgets_values[start_index], type: value.inputs.default.type}
|
||||
addWidgetInfo(this, value.inputs.Name.value, widget_param, app, nodeData);
|
||||
addWidgets(this, value.inputs.Name.value, widget_param, app, nodeData);
|
||||
if (isinput){
|
||||
//node.removeInput(node.inputs.findIndex((i) => i.widget?.name === widget.name))
|
||||
const config = [value.inputs.default.type]
|
||||
@@ -852,154 +825,105 @@ app.registerExtension({
|
||||
}
|
||||
start_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
//}
|
||||
/*
|
||||
if(info.widgets_values[0] != "None")
|
||||
api.fetchApi("/flowchain/workflow?workflow_path="+info.widgets_values[0])
|
||||
.then(response => response.json())
|
||||
.then(async data => {
|
||||
cleanInputs(this);
|
||||
app.lipsync_studio[info.widgets_values[0]] = data;
|
||||
addInputs(this, info.widgets_values[0], info.widgets_values);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Erreur lors de l\'importation:', error);
|
||||
});*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
if(info.widgets_values.workflows.value != "None"){
|
||||
const workflow_name = info.widgets_values.workflows.value;
|
||||
this.widgets[0].value = workflow_name;
|
||||
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, nodeData);
|
||||
widget = this.widgets.find(w => w.name === key);
|
||||
}
|
||||
|
||||
console.log(key)
|
||||
|
||||
if(widget){
|
||||
console.log(info.widgets_values[key])
|
||||
console.log(info.widgets_values[key].options)
|
||||
widget.options = info.widgets_values[key].options;
|
||||
widget.value = info.widgets_values[key].value;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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) {
|
||||
for (let w of this.inputs){
|
||||
// if w.name exists in info.widgets_values
|
||||
|
||||
if (w.widget){
|
||||
if (w.type != this.local_input_defs.required[w.name][0])
|
||||
w.type = this.local_input_defs.required[w.name][0];
|
||||
}
|
||||
}
|
||||
|
||||
// Créer la structure info.inputs si elle n'existe pas
|
||||
/*
|
||||
if (!info.inputs)
|
||||
info.inputs = {};
|
||||
|
||||
// Ajouter les valeurs des widgets dans info.inputs
|
||||
if (this.widgets) {
|
||||
for (let w of this.widgets) {
|
||||
// Stocker uniquement la valeur dans info.inputs
|
||||
info.inputs[w.name] = w.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Le reste de votre code existant pour info.widgets_values reste inchangé
|
||||
info.widgets_values = {};
|
||||
if (this.widgets) {
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
let inps = {};
|
||||
if (info.widgets_values[0] != "None"){
|
||||
const workflow_name = info.widgets_values[0];
|
||||
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];
|
||||
info.outputs_values = []
|
||||
for (let w of this.outputs){
|
||||
info.outputs_values.push({links: w.links, name: w.name, type: w.type});
|
||||
}
|
||||
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;
|
||||
if (!app.loading_bling){
|
||||
|
||||
|
||||
this.widgets[0].options.values = ["None", ...Object.keys(app.lipsync_studio)]
|
||||
this.widgets[0].callback = ( value ) => {
|
||||
if (value == "None" || value == ""){
|
||||
this.title = "Workflow (FlowChain ⛓️)";
|
||||
cleanInputs(this, nodeData);
|
||||
}else{
|
||||
this.widgets[1].value = importWorkflow(this, value, app, nodeData)
|
||||
}
|
||||
};
|
||||
if (!workflow_reload){
|
||||
this.widgets[0].value = "None";
|
||||
this.widgets[1].value = "";
|
||||
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, {});
|
||||
addOutputs(this, value);
|
||||
}
|
||||
//hideWidget(this, this.widgets[1], { holdSpace: false })
|
||||
cleanInputs(this, nodeData);
|
||||
this.color = "#004670";
|
||||
this.bgcolor = "#002942";
|
||||
};
|
||||
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";
|
||||
|
||||
}
|
||||
break;
|
||||
case "WorkflowInput":
|
||||
nodeType.prototype.onNodeCreated = function() {
|
||||
chainCallback(this, "onConfigure", function(info) {
|
||||
let widgetDict = info.widgets_values
|
||||
if (info.widgets_values.length == undefined) {
|
||||
let widgetDict = info.widgets_values;
|
||||
const inputs = {};
|
||||
|
||||
inputs["default"] = {
|
||||
inputs: ["default", info.widgets_values[1], info.widgets_values[2]]
|
||||
/*
|
||||
default:{value: info.widgets_values[2], type: info.widgets_values[1]},
|
||||
Name: {value: "default", type: "STRING"},
|
||||
type: {value: info.widgets_values[1], type: "COMBO"}*/
|
||||
};
|
||||
|
||||
addInputs(this, inputs, info.widgets_values);
|
||||
/*
|
||||
const inputs = {};
|
||||
for (let [k, value] of Object.entries(info.widgets_values)) {
|
||||
if (k == "default"){
|
||||
inputs[k] = {
|
||||
inputs: {
|
||||
default:{value: value.value, type: info.widgets_values.default.type},
|
||||
Name: {value: k, type: "STRING"},
|
||||
type: {value: info.widgets_values.default.type, type: "STRING"}
|
||||
}
|
||||
};
|
||||
}else{
|
||||
inputs[k] = {
|
||||
inputs: {
|
||||
default:{value: value.value, type: value.type},
|
||||
Name: {value: k, type: value.type},
|
||||
type: {value: value.type, type: value.type}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
let widgetDict = {};
|
||||
if(info.widgets_values.default)
|
||||
widgetDict = [info.widgets_values.Name.value, info.widgets_values.default.type, info.widgets_values.default.value];
|
||||
else
|
||||
widgetDict = [info.widgets_values.Name.value, info.widgets_values.type.value];
|
||||
|
||||
addInputs(this, inputs, widgetDict);*/
|
||||
/*if (info.widgets_values.length == undefined) {
|
||||
|
||||
|
||||
for (let w of this.widgets) {
|
||||
if (w.name in widgetDict) {
|
||||
w.value = widgetDict[w.name].value;
|
||||
@@ -1011,7 +935,14 @@ app.registerExtension({
|
||||
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, nodeData);
|
||||
let widget_param = {}
|
||||
if (value.name == "default"){
|
||||
widget_param = {value: value.value, type: type.value}
|
||||
}else{
|
||||
widget_param = value;
|
||||
}
|
||||
// addWidgets(this, key, widget_param, app);
|
||||
addInputs(this, {inputs: {Name: {value: key}, type: {value: type.value}}}, widgetDict);
|
||||
widget = this.widgets.find(w => w.name === key);
|
||||
}
|
||||
//this.widgets.push(value);
|
||||
@@ -1046,25 +977,45 @@ app.registerExtension({
|
||||
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);
|
||||
}
|
||||
}*/
|
||||
this.setSize(info.size);*/
|
||||
|
||||
});
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
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};
|
||||
}
|
||||
if (w.name == "default"){
|
||||
const default_type = this.inputs.find(i => i.name == "default");
|
||||
if (default_type)
|
||||
info.widgets_values[w.name] = {name: w.name, options : w.options, value: w.value, type: default_type.type};
|
||||
else
|
||||
info.widgets_values[w.name] = {name: w.name, options : w.options, value: w.value, type: info.widgets_values.type.value};
|
||||
}else{
|
||||
info.widgets_values[w.name] = {name: w.name, options : w.options, value: w.value, type: w.type};
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
for (let w of this.inputs){
|
||||
// if w.name exists in info.widgets_values
|
||||
if (info.widgets_values[w.name]){
|
||||
w.type = info.widgets_values[w.name].type;
|
||||
if(info.widgets_values[w.name].type == "converted-widget"){
|
||||
if(info.widgets_values[w.name].origType == "toggle"){
|
||||
w.type = "BOOLEAN";
|
||||
@@ -1082,12 +1033,13 @@ app.registerExtension({
|
||||
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.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++) {
|
||||
/*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();
|
||||
@@ -1103,14 +1055,16 @@ app.registerExtension({
|
||||
this.graph.removeLink(input.link);
|
||||
}
|
||||
}
|
||||
clearInputs(this);
|
||||
clearInputs(this);*/
|
||||
cleanInputs(this);
|
||||
|
||||
switch(value){
|
||||
case "STRING":
|
||||
this.addOutput("output", "STRING");
|
||||
ComfyWidgets.STRING(
|
||||
this,
|
||||
"default",
|
||||
["STRING",{default: "",callback: (val) => console.log('VALUE', val),},],
|
||||
["STRING",{default: "",},],
|
||||
app,
|
||||
)
|
||||
|
||||
@@ -1130,7 +1084,7 @@ app.registerExtension({
|
||||
ComfyWidgets.FLOAT(
|
||||
this,
|
||||
"default",
|
||||
['',{default: 0,callback: (val) => console.log('VALUE', val), "min": 0.00, "max": 2048.00, "step": 0.01},],
|
||||
['',{default: 0, "min": 0.00, "max": 2048.00, "step": 0.01},],
|
||||
app,
|
||||
)
|
||||
break;
|
||||
@@ -1170,7 +1124,7 @@ app.registerExtension({
|
||||
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, nodeData);
|
||||
addWidgets(this, key, value, app);
|
||||
widget = this.widgets.find(w => w.name === key);
|
||||
}
|
||||
//this.widgets.push(value);
|
||||
@@ -1183,13 +1137,6 @@ app.registerExtension({
|
||||
//find if key exists in inputs array in inputs.Name
|
||||
if (info.widgets_values[key].type != "converted-widget"){
|
||||
this.removeInput(this.inputs.indexOf(input));
|
||||
//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];*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1269,7 +1216,7 @@ app.registerExtension({
|
||||
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, nodeData);
|
||||
addWidgets(this, key, value, app);
|
||||
widget = this.widgets.find(w => w.name === key);
|
||||
}
|
||||
//this.widgets.push(value);
|
||||
@@ -1282,15 +1229,6 @@ app.registerExtension({
|
||||
//find if key exists in inputs array in inputs.Name
|
||||
if (info.widgets_values[key].type != "converted-widget"){
|
||||
this.removeInput(this.inputs.indexOf(input));
|
||||
//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];
|
||||
*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1312,11 +1250,7 @@ app.registerExtension({
|
||||
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 = {};
|
||||
|
||||
+9
-7
@@ -277,12 +277,13 @@ class Workflow(SaveImage):
|
||||
# get max workflow id
|
||||
# coinvert workflow_outputs to list
|
||||
workflow_outputs = list(workflow_outputs.values())
|
||||
workflow_node = {"node": {"id":k, **v} for k, v in workflow.items() if v["class_type"] == "Workflow" and v["inputs"]["workflows"] == workflow_name}
|
||||
# prendre le premier workflow
|
||||
workflow_node = [{"id":k, **v} for k, v in workflow.items() if v["class_type"] == "Workflow" and v["inputs"]["workflows"] == workflow_name][0]
|
||||
sub_input_nodes = {k: v for k, v in subworkflow.items() if v["class_type"] == "WorkflowInput"}
|
||||
do_not_delete = []
|
||||
for sub_id, sub_node in sub_input_nodes.items():
|
||||
if sub_node["inputs"]["Name"] in workflow_node["node"]["inputs"]:
|
||||
value = workflow_node["node"]["inputs"][sub_node["inputs"]["Name"]]
|
||||
if sub_node["inputs"]["Name"] in workflow_node["inputs"]:
|
||||
value = workflow_node["inputs"][sub_node["inputs"]["Name"]]
|
||||
if type(value) == list:
|
||||
subworkflow = change_subnode(subworkflow, sub_id, value)
|
||||
else:
|
||||
@@ -298,7 +299,7 @@ class Workflow(SaveImage):
|
||||
for input_name, input_value in node["inputs"].items():
|
||||
if type(input_value) == list:
|
||||
if len(input_value) > 0:
|
||||
if input_value[0] == workflow_node["node"]["id"]:
|
||||
if input_value[0] == workflow_node["id"]:
|
||||
for sub_output_id, sub_output_node in sub_output_nodes.items():
|
||||
if sub_output_node["inputs"]["Name"] == workflow_outputs[input_value[1]]["inputs"]["Name"]:
|
||||
workflow[node_id]["inputs"][input_name] = sub_output_node["inputs"]["default"]
|
||||
@@ -340,8 +341,7 @@ class Workflow(SaveImage):
|
||||
|
||||
workflow_outputs_sub = {k: v for k, v in subworkflow.items() if v["class_type"] == "WorkflowOutput"}
|
||||
workflow, subworkflow = merge_inputs_outputs(workflow, workflow_name, subworkflow, workflow_outputs_sub)
|
||||
workflow = {k: v for k, v in workflow.items() if
|
||||
not (v["class_type"] == "Workflow" and v["inputs"]["workflows"] == workflow_name)}
|
||||
workflow = {k: v for k, v in workflow.items() if k != key}
|
||||
# add subworkflow to workflow
|
||||
workflow.update(subworkflow)
|
||||
return workflow, max_id
|
||||
@@ -393,16 +393,18 @@ class Workflow(SaveImage):
|
||||
}
|
||||
|
||||
# Now you can access the original inputs as before
|
||||
queue_to_use = len(queue_info["queue_pending"]) > 0 and queue_info["queue_pending"] or queue_info["queue_running"]
|
||||
queue_to_use = queue_info["queue_running"]
|
||||
original_inputs = [v["inputs"] for k, v in queue_to_use[0][2].items() if
|
||||
"workflows" in v["inputs"] and v["inputs"]["workflows"] == workflows][0]
|
||||
|
||||
# find "workflow" node in queue_info["queue_pending"][-1][3]["extra_png_info"]["workflow"]["nodes"]
|
||||
"""
|
||||
for node in queue_to_use[-1][3]["extra_pnginfo"]["workflow"]["nodes"]:
|
||||
workflow_name = 'Workflow: ' + original_inputs["workflows"].replace(".json", "").replace("_", " ")
|
||||
if node["type"] == "Workflow" and node["title"] == workflow_name:
|
||||
workflow = node["widgets_values"][1]
|
||||
break
|
||||
"""
|
||||
|
||||
else:
|
||||
# Fallback to empty inputs if server instance not available
|
||||
|
||||
Reference in New Issue
Block a user