init project
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
|
||||
# CryptoCat: ComfyUI压缩加密节点
|
||||
|
||||
## 简介
|
||||
CryptoCat是一个小巧的ComfyUI开源节点,它的作用在于简化工作流,同时给工作流提供加密保护。
|
||||

|
||||
|
||||
## 应用场景
|
||||
- 流程简化:可以大幅度简化工作流。
|
||||
- 加密授权:可以保护工作流里的一些核心思路。
|
||||
|
||||
## 快速开始
|
||||
1. 打开ComfyUI\custom_ Nodes\目录,克隆仓库到本地。
|
||||
2. 启动ComfyUI,菜单“高级”(advance)中找到CryptoCat目录。
|
||||
3. InputCrypto节点用于压缩工作流,连线运行之后会在目录下得到压缩后的工作流
|
||||
4. OutputCrypto节点不用管,它只是用于自动解压工作流
|
||||
5. RandomSeedNode会在服务端生成随机数,用于修补工作流封装后随机数不起效的情况
|
||||
|
||||
|
||||
## 贡献指南
|
||||
欢迎对CryptoCat项目做出贡献!你可以通过提交Pull Request或开设Issue来提出新功能建议或报告问题。
|
||||
|
||||
## 许可证
|
||||
本项目遵循MIT许可证。有关详细信息,请参阅LICENSE文件。
|
||||
|
||||
---
|
||||
CryptoCat © 2024. All Rights Reserved.
|
||||
@@ -1,2 +1,25 @@
|
||||
# ComfyUI_CryptoCat
|
||||
designed to simplify workflows while providing encryption protection for them.
|
||||
# CryptoCat: ComfyUI Compression and Encryption Node
|
||||
|
||||
## Introduction
|
||||
CryptoCat is a lightweight open-source node for ComfyUI, designed to simplify workflows while providing encryption protection for them.
|
||||

|
||||
|
||||
## Use Cases
|
||||
- Workflow Simplification: It can greatly simplify workflows.
|
||||
- Encryption Authorization: It can protect core ideas within the workflow.
|
||||
|
||||
## Quick Start
|
||||
1. Open the ComfyUI\custom_Nodes\ directory and clone the repository locally.
|
||||
2. Start ComfyUI, and find the CryptoCat directory in the "Advanced" menu.
|
||||
3. The InputCrypto node is used to compress the workflow, and after connecting and running it, you will get the compressed workflow in the directory.
|
||||
4. You don't need to worry about the OutputCrypto node, it just automatically decompresses the workflow.
|
||||
5. The RandomSeedNode generates random numbers on the server side to fix issues where the random numbers don't work after the workflow is packaged.
|
||||
|
||||
## Contribution Guide
|
||||
Contributions to the CryptoCat project are welcome! You can submit Pull Requests or open Issues to propose new features or report bugs.
|
||||
|
||||
## License
|
||||
This project is licensed under the MIT License. For more details, please refer to the LICENSE file.
|
||||
|
||||
---
|
||||
CryptoCat © 2024. All Rights Reserved.
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
from functools import wraps
|
||||
import json
|
||||
from .trim_workflow import PromptTrim
|
||||
from .crypto_node import SaveCryptoNode, ExcuteCryptoNode, RandomSeedNode
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SaveCryptoNode": SaveCryptoNode,
|
||||
"ExcuteCryptoNode": ExcuteCryptoNode,
|
||||
"RandomSeedNode": RandomSeedNode
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SaveCryptoNode": "InputCrypto",
|
||||
"ExcuteCryptoNode": "OutputCrypto",
|
||||
"RandomSeedNode": "RandomSeedNode"
|
||||
}
|
||||
|
||||
WEB_DIRECTORY = "./js"
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAMES_MAPPINGS', 'WEB_DIRECTORY']
|
||||
|
||||
|
||||
from server import PromptServer
|
||||
|
||||
|
||||
method_name = "trigger_on_prompt"
|
||||
original_method = getattr(PromptServer, method_name)
|
||||
|
||||
# 创建 hook
|
||||
@wraps(original_method)
|
||||
def new_trigger_on_prompt(self, json_data):
|
||||
prompt = json_data["prompt"]
|
||||
pr = PromptTrim(prompt)
|
||||
if pr.has_crypto_node():
|
||||
print("has crypto node")
|
||||
json_data["prompt"] = pr.replace_prompt()
|
||||
# 删除 extra_data 字段
|
||||
if "extra_data" in json_data:
|
||||
json_data["extra_data"] = {}
|
||||
print("hook new_trigger_on_prompt delete extra_data")
|
||||
result = original_method(self, json_data)
|
||||
return result
|
||||
|
||||
setattr(PromptServer, method_name, new_trigger_on_prompt)
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import torch
|
||||
from .trim_workflow import PromptTrim, WorkflowTrim
|
||||
from.file_compressor import FileCompressor
|
||||
import folder_paths
|
||||
|
||||
|
||||
class SaveCryptoNode():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"crypto_folder": ("STRING", {"default": folder_paths.output_directory}),
|
||||
"crypto_name": ("STRING", {"default": "my_cat.json"}),
|
||||
"output_images" : ("IMAGE",),
|
||||
},
|
||||
"optional": {
|
||||
"input_anything" : ("*",),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
}
|
||||
}
|
||||
|
||||
# @classmethod
|
||||
# def VALIDATE_INPUTS(s, input_types):
|
||||
# return True
|
||||
|
||||
|
||||
RETURN_TYPES = () #"STRING",
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "crypto" #这个节点中的主函数
|
||||
CATEGORY = "advanced/CryptoCat"
|
||||
|
||||
def crypto(self, crypto_folder, crypto_name, output_images, **kwargs):
|
||||
if not crypto_name or len(crypto_folder) < 2 or len(crypto_name) == 0:
|
||||
raise Exception("CryptoCat folder and filename must be at least two characters long")
|
||||
|
||||
# 从 kwargs 中提取特定参数
|
||||
unique_id = kwargs.pop('unique_id', None)
|
||||
prompt = kwargs.pop('prompt', None)
|
||||
extra_pnginfo = kwargs.pop('extra_pnginfo', None)
|
||||
|
||||
# 检查必需参数是否存在
|
||||
if unique_id is None:
|
||||
raise Exception("Warning: 'unique_id' is missing.")
|
||||
if prompt is None:
|
||||
raise Exception("Warning: 'prompt' is missing.")
|
||||
|
||||
# 将剩余的 kwargs 存储在 inputs 列表中
|
||||
inputs = list(kwargs.values())
|
||||
|
||||
temp_dir = os.environ.get('TEMP') or os.environ.get('TMP') or '/tmp'
|
||||
project_temp_folder = os.path.join(temp_dir, crypto_name)
|
||||
if not os.path.exists(project_temp_folder):
|
||||
os.makedirs(project_temp_folder)
|
||||
|
||||
|
||||
# 保存源文件,用于调试
|
||||
with open(os.path.join(project_temp_folder, "prompt.json"), "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(prompt, indent=4, ensure_ascii=False))
|
||||
with open(os.path.join(project_temp_folder, "workflow.json"), "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(extra_pnginfo, indent=4, ensure_ascii=False))
|
||||
|
||||
# 保存需要的输出文件
|
||||
project_folder = os.path.join(crypto_folder, crypto_name)
|
||||
if not os.path.exists(project_folder):
|
||||
os.makedirs(project_folder)
|
||||
|
||||
hide_prompt_path = os.path.join(project_folder, "prompt.dat")
|
||||
# 解析workflow
|
||||
wt = WorkflowTrim(extra_pnginfo)
|
||||
wt.trim_workflow()
|
||||
show_workflow = wt.replace_workflow(hide_prompt_path)
|
||||
with open(os.path.join(project_folder, "workflow.json"), "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(show_workflow, indent=4, ensure_ascii=False))
|
||||
|
||||
# 拆分prompt
|
||||
pr = PromptTrim(prompt)
|
||||
show_part_prompt, hide_part_prompt = pr.split_prompt(wt.get_remaining_node_ids())
|
||||
with open(os.path.join(project_temp_folder, "prompt_show.json"), "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(show_part_prompt, indent=4, ensure_ascii=False))
|
||||
|
||||
FileCompressor.compress_to_json(hide_part_prompt, hide_prompt_path, "19040822")
|
||||
return (hide_part_prompt,)
|
||||
|
||||
|
||||
|
||||
class ExcuteCryptoNode():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"crypto_file_path": ("STRING", {"default": folder_paths.output_directory}),
|
||||
},
|
||||
"optional": {
|
||||
"input_anything" : ("*",),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "excute" #这个节点中的主函数
|
||||
CATEGORY = "advanced/CryptoCat"
|
||||
|
||||
def excute(self, **kwargs):
|
||||
batch_size = 1
|
||||
height = 1024
|
||||
width = 1024
|
||||
color = 0xFF0000
|
||||
r = torch.full([batch_size, height, width, 1], ((color >> 16) & 0xFF) / 0xFF)
|
||||
g = torch.full([batch_size, height, width, 1], ((color >> 8) & 0xFF) / 0xFF)
|
||||
b = torch.full([batch_size, height, width, 1], ((color) & 0xFF) / 0xFF)
|
||||
return (torch.cat((r, g, b), dim=-1), )
|
||||
|
||||
|
||||
# 产生随机数
|
||||
class RandomSeedNode():
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
},
|
||||
"optional": {
|
||||
},
|
||||
"hidden": {
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
FUNCTION = "random" #这个节点中的主函数
|
||||
CATEGORY = "advanced/CryptoCat"
|
||||
|
||||
def IS_CHANGED():
|
||||
return float("NaN")
|
||||
|
||||
def random(self):
|
||||
# 产生随机数
|
||||
return (random.randint(0, 999999), )
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 563 KiB |
@@ -0,0 +1,41 @@
|
||||
import json
|
||||
import zlib
|
||||
import os
|
||||
|
||||
class FileCompressor:
|
||||
|
||||
@staticmethod
|
||||
def obfuscate(data, password):
|
||||
return bytes([b ^ ord(password[i % len(password)]) for i, b in enumerate(data)])
|
||||
|
||||
@staticmethod
|
||||
def compress_string(string_data, zip_path, password):
|
||||
compressed_data = zlib.compress(string_data.encode('utf-8'))
|
||||
obfuscated_data = FileCompressor.obfuscate(compressed_data, password)
|
||||
with open(zip_path, 'wb') as zipf:
|
||||
zipf.write(obfuscated_data)
|
||||
|
||||
@staticmethod
|
||||
def decompress_to_string(zip_path, password):
|
||||
with open(zip_path, 'rb') as zipf:
|
||||
obfuscated_data = zipf.read()
|
||||
compressed_data = FileCompressor.obfuscate(obfuscated_data, password) # 反混淆
|
||||
return zlib.decompress(compressed_data).decode('utf-8')
|
||||
|
||||
@staticmethod
|
||||
def compress_to_json(data, zip_path, password):
|
||||
try:
|
||||
json_string = json.dumps(data, indent=4, ensure_ascii=False) # Convert data to JSON string
|
||||
FileCompressor.compress_string(json_string, zip_path, password) # Call compress_string
|
||||
except Exception as e:
|
||||
print(f"Compression failed: {e}")
|
||||
|
||||
@staticmethod
|
||||
def decompress_from_json(zip_path, password):
|
||||
try:
|
||||
if not os.path.exists(zip_path):
|
||||
raise FileNotFoundError(f"{zip_path} does not exist")
|
||||
json_string = FileCompressor.decompress_to_string(zip_path, password) # Call decompress_to_string
|
||||
return json.loads(json_string) # Convert back to dictionary
|
||||
except Exception as e:
|
||||
print(f"Decompression failed: {e}")
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { ComfyApp, app } from "../../../scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "cryptocat.mycat",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
|
||||
if (nodeData.name === 'SaveCryptoNode') {
|
||||
const input_name = "input_anything";
|
||||
|
||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
|
||||
if (!link_info) return;
|
||||
// 更新输入名称
|
||||
let slot_i = 1;
|
||||
for (let i = 1; i < this.inputs.length; i++) {
|
||||
let input_i = this.inputs[i];
|
||||
if (input_i.name !== 'select' && input_i.name !== 'sel_mode') {
|
||||
input_i.name = `${input_name}${slot_i}`;
|
||||
slot_i++;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理断开连接后的输入移除逻辑
|
||||
if (!connected && (this.inputs.length > 2)) {
|
||||
const stackTrace = new Error().stack;
|
||||
|
||||
if (
|
||||
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device
|
||||
!stackTrace.includes('LGraphNode.connect') && // for mouse device
|
||||
!stackTrace.includes('loadGraphData') &&
|
||||
this.inputs[index].name != 'select') {
|
||||
this.removeInput(index);
|
||||
}
|
||||
|
||||
let slot_i = 1;
|
||||
for (let i = 1; i < this.inputs.length; i++) {
|
||||
let input_i = this.inputs[i];
|
||||
if (input_i.name != 'select' && input_i.name != 'sel_mode') {
|
||||
input_i.name = `${input_name}${slot_i}`
|
||||
slot_i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 动态添加输入
|
||||
let last_slot = this.inputs[this.inputs.length - 1];
|
||||
if (
|
||||
(last_slot.name == 'select' && last_slot.name != 'sel_mode' && this.inputs[this.inputs.length - 2].link != undefined)
|
||||
|| (last_slot.name != 'select' && last_slot.name != 'sel_mode' && last_slot.link != undefined)) {
|
||||
this.addInput(`${input_name}${slot_i}`, "*");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
nodeCreated(node, app) {
|
||||
if (node.comfyClass == "SaveCryptoNode") {
|
||||
if (node.widgets && node.widgets.length > 1 && app.workflowManager) {
|
||||
node.widgets[1].value = app.workflowManager.activeWorkflow.name;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// async setup(app) {
|
||||
// console.log(app.workflowManager.activeWorkflow.name)
|
||||
// if (this.widgets) {
|
||||
// this.widgets[1].value = app.workflowManager.activeWorkflow.name;
|
||||
// }
|
||||
// },
|
||||
|
||||
// },
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
torch
|
||||
aiohttp
|
||||
@@ -0,0 +1,352 @@
|
||||
import copy
|
||||
import os
|
||||
import random
|
||||
import sys
|
||||
import folder_paths
|
||||
import json
|
||||
from.file_compressor import FileCompressor
|
||||
|
||||
class WorkflowTrim():
|
||||
def __init__(self, workflow):
|
||||
if 'workflow' in workflow :
|
||||
self.workflow = copy.deepcopy(workflow['workflow'])
|
||||
else:
|
||||
self.workflow = copy.deepcopy(workflow)
|
||||
|
||||
self.output_images_link = 0
|
||||
|
||||
nodes_dict = {node['id']: node for node in self.workflow['nodes']}
|
||||
self.max_id_index = max(nodes_dict.keys()) + 1
|
||||
|
||||
|
||||
@staticmethod
|
||||
def find_workflow_related_nodes(nodes, input_ids):
|
||||
found_ids = set()
|
||||
stack = list(input_ids)
|
||||
while stack:
|
||||
link_id = stack.pop()
|
||||
for node_id, node in nodes.items():
|
||||
outputs = node.get('outputs', None)
|
||||
if not outputs:
|
||||
continue
|
||||
for output in outputs:
|
||||
links = output.get('links', None)
|
||||
if not links:
|
||||
continue
|
||||
if link_id in links:
|
||||
if node_id not in found_ids:
|
||||
found_ids.add(node_id)
|
||||
inputs = node.get('inputs', [])
|
||||
for input_node in inputs:
|
||||
link_id = input_node.get('link')
|
||||
if link_id is not None:
|
||||
stack.append(link_id)
|
||||
break
|
||||
return found_ids
|
||||
|
||||
def trim_workflow(self):
|
||||
if not self.workflow:
|
||||
raise ValueError("Invalid JSON format.")
|
||||
|
||||
nodes_dict = {node['id']: node for node in self.workflow['nodes']}
|
||||
save_crypto_node_ids = [node_id for node_id, details in nodes_dict.items() if details.get('type') == 'SaveCryptoNode']
|
||||
if len(save_crypto_node_ids) == 1:
|
||||
save_crypto_node_id = save_crypto_node_ids[0]
|
||||
|
||||
# 获取所有以 'input_anything' 开头的输入链接
|
||||
input_ids = set()
|
||||
for input_node in nodes_dict[save_crypto_node_id].get('inputs', []):
|
||||
if input_node['name'] and input_node['name'].startswith('input_anything'):
|
||||
input_ids.add(input_node['link'])
|
||||
if input_node['name'] and input_node['name'] == "output_images":
|
||||
self.output_images_link = input_node['link']
|
||||
|
||||
# 只处理有效的链接 ID
|
||||
input_ids = {link_id for link_id in input_ids if link_id is not None}
|
||||
|
||||
# 找到与输入链接相关的节点
|
||||
related_node_ids = self.find_workflow_related_nodes(nodes_dict, input_ids)
|
||||
|
||||
# 添加 SaveCryptoNode 本身
|
||||
related_node_ids.add(save_crypto_node_id)
|
||||
|
||||
# # 打印和删除不相关的节点
|
||||
# for node_id in nodes_dict.keys():
|
||||
# if node_id not in related_node_ids:
|
||||
# print(f"Removing node ID: {node_id} - {nodes_dict[node_id]}")
|
||||
|
||||
# 更新节点和链接
|
||||
self.workflow['nodes'] = [details for node_id, details in nodes_dict.items() if node_id in related_node_ids]
|
||||
remaining_node_ids = {node['id'] for node in self.workflow['nodes']}
|
||||
self.workflow.pop('groups', None)
|
||||
self.workflow['output_images_id'] = self.output_images_link
|
||||
|
||||
|
||||
# 保留包含任一节点 ID 的链接
|
||||
self.workflow['links'] = [
|
||||
link for link in self.workflow['links']
|
||||
if link[1] in remaining_node_ids and link[3] in remaining_node_ids
|
||||
]
|
||||
|
||||
return self.workflow
|
||||
|
||||
elif len(save_crypto_node_ids) > 1:
|
||||
raise ValueError("Error: Multiple 'SaveCryptoNode' instances found.")
|
||||
else:
|
||||
raise ValueError("Error: No 'SaveCryptoNode' instances found.")
|
||||
|
||||
|
||||
|
||||
|
||||
def replace_workflow(self,hide_prompt_path):
|
||||
if not self.workflow:
|
||||
raise ValueError("Invalid JSON format.")
|
||||
|
||||
nodes_dict = {node['id']: node for node in self.workflow['nodes']}
|
||||
output_images_link = None
|
||||
|
||||
# 替换 SaveCryptoNode 为 ExcuteCryptoNode
|
||||
for node in self.workflow['nodes']:
|
||||
if node.get('type') == 'SaveCryptoNode':
|
||||
node['type'] = 'ExcuteCryptoNode'
|
||||
node['properties']['Node name for S&R'] = 'ExcuteCryptoNode'
|
||||
|
||||
# 移除 output_images 输入并获取 link
|
||||
for inp in node['inputs']:
|
||||
if inp['name'] == 'output_images':
|
||||
output_images_link = inp.get('link')
|
||||
node['inputs'] = [inp for inp in node['inputs'] if inp['name'] != 'output_images']
|
||||
node['widgets_values'] = [hide_prompt_path]
|
||||
|
||||
# 找到 ExcuteCryptoNode 的位置
|
||||
execute_node = next((node for node in self.workflow['nodes'] if node.get('type') == 'ExcuteCryptoNode'), None)
|
||||
|
||||
if not execute_node:
|
||||
raise ValueError("No 'ExcuteCryptoNode' instance found.")
|
||||
|
||||
# 创建新的 SaveImage 节点
|
||||
execute_node_pos = execute_node['pos']
|
||||
self.max_id_index = max(max(nodes_dict.keys()) + 1, self.max_id_index)
|
||||
new_save_image_node = {
|
||||
"id": self.max_id_index, # 新节点 ID,确保唯一
|
||||
"type": "SaveImage",
|
||||
"pos": {
|
||||
"0": execute_node_pos['0'] + 200, # 设置位置在 ExcuteCryptoNode 右边 20 像素
|
||||
"1": execute_node_pos['1']
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 162
|
||||
},
|
||||
"flags": {},
|
||||
"order": 50,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": output_images_link, # 使用获取的 link
|
||||
"label": "images"
|
||||
}
|
||||
],
|
||||
"outputs": [], # 新节点没有输出
|
||||
"properties": {
|
||||
"Node name for S&R": "SaveImage"
|
||||
}
|
||||
}
|
||||
# 将新节点添加到节点列表
|
||||
self.workflow['nodes'].append(new_save_image_node)
|
||||
|
||||
# 删除原有的链接以避免冲突
|
||||
self.workflow['links'] = [
|
||||
link for link in self.workflow['links'] if link[0] != output_images_link
|
||||
]
|
||||
|
||||
# 用新的 outputs 替换 ExcuteCryptoNode 的 outputs
|
||||
execute_node['outputs'] = [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [output_images_link], # 连接到新节点
|
||||
"shape": 3,
|
||||
"label": "IMAGE",
|
||||
"slot_index": 0
|
||||
}
|
||||
]
|
||||
|
||||
# 添加链接信息
|
||||
self.workflow['links'].append([
|
||||
output_images_link, # ExcuteCryptoNode 的输出 ID
|
||||
execute_node['id'], # 新节点 ID
|
||||
0, # 新节点的输入索引
|
||||
new_save_image_node['id'], # ExcuteCryptoNode ID(旧节点 ID)
|
||||
0, # 旧节点的输出索引
|
||||
"IMAGE" # 数据类型
|
||||
])
|
||||
|
||||
return self.workflow
|
||||
|
||||
|
||||
def set_excute_crypto_node_path(self, path):
|
||||
if not self.workflow:
|
||||
raise ValueError("Invalid JSON format.")
|
||||
for node in self.workflow['nodes']:
|
||||
if node.get('type') == 'ExcuteCryptoNode':
|
||||
node['properties']['Path to executable file'] = path
|
||||
|
||||
|
||||
def get_remaining_node_ids(self):
|
||||
if not self.workflow:
|
||||
raise ValueError("Invalid JSON format.")
|
||||
remaining_node_ids = {node['id'] for node in self.workflow['nodes']}
|
||||
return remaining_node_ids
|
||||
|
||||
|
||||
|
||||
class PromptTrim():
|
||||
def __init__(self, prompt):
|
||||
self.prompt = prompt
|
||||
self.debug = True
|
||||
self.hide_part_prompt = {}
|
||||
self.show_part_prompt = {}
|
||||
|
||||
def split_prompt(self, related_node_ids):
|
||||
if not self.prompt:
|
||||
raise ValueError("Invalid JSON format.")
|
||||
|
||||
# 查找第一个 'SaveCryptoNode' 实例并转换为 int
|
||||
# save_crypto_node_id = next((int(node_id) for node_id, details in self.prompt.items() if details['class_type'] == 'SaveCryptoNode'), None)
|
||||
save_crypto_node_id = None
|
||||
output_images_ids = []
|
||||
for node_id, details in self.prompt.items():
|
||||
if details['class_type'] == 'SaveCryptoNode':
|
||||
output_images_ids = details['inputs'].get('output_images')
|
||||
save_crypto_node_id = int(node_id)
|
||||
break
|
||||
|
||||
if save_crypto_node_id is None:
|
||||
raise ValueError("Error: No 'SaveCryptoNode' instances found.")
|
||||
|
||||
if save_crypto_node_id not in related_node_ids:
|
||||
raise AssertionError("SaveCryptoNode not found in related node list.")
|
||||
|
||||
self.hide_part_prompt = {}
|
||||
self.show_part_prompt = {}
|
||||
|
||||
# 输出不在链中的节点
|
||||
for node_id in self.prompt.keys():
|
||||
node_id_int = int(node_id) # 转换为 int 类型
|
||||
if node_id_int not in related_node_ids:
|
||||
self.hide_part_prompt[node_id_int] = self.prompt[node_id]
|
||||
else:
|
||||
self.show_part_prompt[node_id_int] = self.prompt[node_id]
|
||||
|
||||
self.hide_part_prompt['output_images_ids'] = output_images_ids
|
||||
return self.show_part_prompt, self.hide_part_prompt
|
||||
|
||||
|
||||
def replace_prompt(self):
|
||||
if not self.prompt:
|
||||
raise ValueError("Invalid JSON format.")
|
||||
|
||||
crypto_file_path = ''
|
||||
excute_crypto_id = None
|
||||
|
||||
# 查找 ExcuteCryptoNode 并获取路径,同时构建需要删除的节点
|
||||
for node_id, node in list(self.prompt.items()):
|
||||
if node.get('class_type') == 'ExcuteCryptoNode':
|
||||
crypto_file_path = node['inputs'].get('crypto_file_path')
|
||||
excute_crypto_id = node_id
|
||||
# 删除当前节点
|
||||
del self.prompt[node_id]
|
||||
break
|
||||
|
||||
if not crypto_file_path:
|
||||
print("No 'ExcuteCryptoNode' found in prompt.")
|
||||
return self.prompt
|
||||
|
||||
# print(f"{crypto_file_path=}")
|
||||
|
||||
inject_json = FileCompressor.decompress_from_json(crypto_file_path, "19040822")
|
||||
output_images_ids = inject_json['output_images_ids']
|
||||
inject_json.pop('output_images_ids', None)
|
||||
for node_id, node in list(self.prompt.items()):
|
||||
if node.get('class_type') == 'SaveImage':
|
||||
ids = node['inputs']['images'] #input_images
|
||||
if excute_crypto_id in ids:
|
||||
node['inputs']['images'] = output_images_ids
|
||||
|
||||
# 随机生成一个种子,避免缓存问题
|
||||
random_seed_node = next((node for node in inject_json.values() if node.get('class_type') == 'RandomSeedNode'), None)
|
||||
if random_seed_node:
|
||||
random_seed_node["inputs"]["is_changed"] = random.randint(0, 999999)
|
||||
|
||||
|
||||
# 将 inject_json 的节点合并到当前 prompt
|
||||
# for node in inject_json.get('nodes', []):
|
||||
# self.prompt[node['id']] = node # 使用 ID 添加或更新节点
|
||||
self.prompt.update(inject_json)
|
||||
|
||||
if self.debug == True:
|
||||
temp_dir = os.environ.get('TEMP') or os.environ.get('TMP') or '/tmp'
|
||||
filename = os.path.basename(crypto_file_path) + "_prompt.json"
|
||||
with open(os.path.join(temp_dir, filename), "w", encoding="utf-8") as f:
|
||||
json.dump(self.prompt, f, indent=4)
|
||||
print(f"prompt len = {len(self.prompt)}")
|
||||
|
||||
return self.prompt
|
||||
|
||||
def has_crypto_node(self):
|
||||
if not self.prompt:
|
||||
return False
|
||||
return any(node.get('class_type') == 'ExcuteCryptoNode' for node in self.prompt.values())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_dir = os.path.dirname(os.path.dirname(current_dir))
|
||||
sys.path.append(parent_dir)
|
||||
import folder_paths
|
||||
|
||||
with open("D:\\work\\ComfyUI\\temp\\flux+lora简单版_original_workflow.json", "r", encoding="utf-8") as f:
|
||||
workflow = json.load(f)
|
||||
|
||||
wt = WorkflowTrim(workflow)
|
||||
wt.trim_workflow()
|
||||
replace_json = wt.replace_workflow('')
|
||||
|
||||
with open("D:\\work\\ComfyUI\\temp\\flux+lora简单版_trim.json", "w", encoding="utf-8") as f:
|
||||
json.dump(replace_json, f, indent=4)
|
||||
|
||||
|
||||
######################################################
|
||||
with open("D:\\work\\ComfyUI\\temp\\flux+lora简单版.txt", "r", encoding="utf-8") as f:
|
||||
prompt = json.load(f)
|
||||
|
||||
pt = PromptTrim(prompt)
|
||||
show_part_prompt, hide_part_prompt = pt.split_prompt(wt.get_remaining_node_ids())
|
||||
|
||||
with open("D:\\work\\ComfyUI\\temp\\flux+lora简单版_show_part_prompt.json", "w", encoding="utf-8") as f:
|
||||
json.dump(show_part_prompt, f, indent=4)
|
||||
|
||||
with open("D:\\work\\ComfyUI\\temp\\flux+lora简单版_hide_part_prompt.json", "w", encoding="utf-8") as f:
|
||||
json.dump(hide_part_prompt, f, indent=4)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user