Files
RiceRound d62754850e fix bug
Added serial number validity period and days feature.
Fixed the bug where the serial number could only be used for 7 days.
Code optimization.
2025-01-19 07:26:32 +08:00

349 lines
14 KiB
Python

from collections import defaultdict
import copy
import os
import random
from server import PromptServer
from .updown_workflow import DownloadWorkflow
import folder_paths
import json
from .file_compressor import FileCompressor
from typing import Dict, Any
from .auth_unit import AuthUnit
class CryptoWorkflow:
def __init__(self, workflow, prompt, template_id):
self.workflow = copy.deepcopy(workflow)
self.prompt = copy.deepcopy(prompt)
self.template_id = template_id
self.workflow_nodes_dict = {}
self.link_owner_map = defaultdict(dict)
self.node_prompt_map = {}
self.last_node_id = 0
self.last_link_id = 0
self.save_crypto_node_id = 0
self.crypto_bridge_node_id = 0
self.input_nodes_ids = set()
self.output_nodes_ids = set()
self.crypto_nodes_ids = set()
self.crypto_result = {}
def invalid_workflow(self):
for node in self.workflow["nodes"]:
node_type = node.get("type", "")
if node_type == "SaveCryptoBridgeNode":
if self.crypto_bridge_node_id == 0:
self.crypto_bridge_node_id = int(node["id"])
else:
raise ValueError(
"Error: Multiple 'SaveCryptoBridgeNode' instances found."
)
continue
elif node_type == "SaveCryptoNode":
if self.save_crypto_node_id == 0:
self.save_crypto_node_id = int(node["id"])
else:
raise ValueError(
"Error: Multiple 'SaveCryptoNode' instances found."
)
continue
if self.save_crypto_node_id == 0:
raise ValueError("Error: No 'SaveCryptoNode' instances found.")
if self.crypto_bridge_node_id == 0:
raise ValueError("Error: No 'SaveCryptoBridgeNode' instances found.")
def load_workflow(self):
simplify_workflow = self.workflow
self.workflow_nodes_dict = {
int(node["id"]): node for node in simplify_workflow["nodes"]
}
for node in simplify_workflow["nodes"]:
output_nodes = node.get("outputs", [])
if not output_nodes:
continue
for output in output_nodes:
links = output.get("links", [])
if not links:
continue
for link in links:
link = int(link)
self.link_owner_map[link]["links"] = copy.deepcopy(links)
self.link_owner_map[link]["slot_index"] = output.get(
"slot_index", 0
)
self.link_owner_map[link]["owner_id"] = int(node["id"])
self.link_owner_map[link]["type"] = output.get("type", "")
self.last_node_id = int(simplify_workflow.get("last_node_id", 0))
self.last_link_id = int(simplify_workflow.get("last_link_id", 0))
def load_prompt(self):
simplify_prompt = self.prompt
self.node_prompt_map = {
int(node_id): node for (node_id, node) in simplify_prompt.items()
}
def analysis_node(self):
self.input_nodes_ids.clear()
self.output_nodes_ids.clear()
self.crypto_nodes_ids.clear()
def find_input_nodes(node_id, visited=None):
if visited is None:
visited = set()
if node_id in visited:
return
visited.add(node_id)
self.input_nodes_ids.add(node_id)
node = self.workflow_nodes_dict.get(node_id)
if not node:
return
for input_node in node.get("inputs", []):
input_link = input_node.get("link")
if input_link is not None and input_link in self.link_owner_map:
owner_id = self.link_owner_map[input_link]["owner_id"]
find_input_nodes(owner_id, visited)
for input_node in self.workflow_nodes_dict[self.save_crypto_node_id].get(
"inputs", []
):
if input_node["name"] and input_node["name"].startswith("input_anything"):
input_link = input_node["link"]
if input_link is not None and input_link in self.link_owner_map:
owner_id = self.link_owner_map[input_link]["owner_id"]
find_input_nodes(owner_id)
def find_output_nodes(node_id, visited=None):
if visited is None:
visited = set()
if node_id in visited:
return
visited.add(node_id)
self.output_nodes_ids.add(node_id)
node = self.workflow_nodes_dict.get(node_id)
if not node:
return
for output in node.get("outputs", []):
if not output or not output.get("links"):
continue
for link in output.get("links", []):
if not link:
continue
if link is not None:
for workflow_link in self.workflow.get("links", []):
if workflow_link[0] == link:
target_node_id = workflow_link[3]
find_output_nodes(target_node_id, visited)
output_links = set()
for output_node in self.workflow_nodes_dict[self.crypto_bridge_node_id].get(
"outputs", []
):
_links = output_node["links"]
output_links.update(_links)
for link in self.workflow.get("links", []):
if len(link) > 3 and link[0] in output_links:
find_output_nodes(link[3])
self.crypto_nodes_ids = (
self.workflow_nodes_dict.keys()
- self.input_nodes_ids
- self.output_nodes_ids
)
self.crypto_nodes_ids = self.crypto_nodes_ids - {
self.save_crypto_node_id,
self.crypto_bridge_node_id,
}
def calculate_crypto_result(self, crypto_file_name):
self.crypto_result = {"prompt": {}, "workflow": {}, "outputs": []}
for node_id in self.crypto_nodes_ids:
if node_id in self.node_prompt_map:
self.crypto_result["prompt"][node_id] = self.node_prompt_map[node_id]
if node_id in self.workflow_nodes_dict:
self.crypto_result["workflow"][node_id] = self.workflow_nodes_dict[
node_id
]
crypto_bridge_node = self.node_prompt_map[self.crypto_bridge_node_id]
for input_name, input_value in crypto_bridge_node.get("inputs", {}).items():
if isinstance(input_value, list) and len(input_value) == 2:
self.crypto_result["outputs"] = input_value[0], input_value[1]
json_result = json.dumps(self.crypto_result, indent=4, ensure_ascii=False)
save_dir = folder_paths.temp_directory
with open(os.path.join(save_dir, crypto_file_name), "w", encoding="utf-8") as f:
f.write(json_result)
return save_dir
def output_workflow_simple_shell(self, output_workflow_name):
simplify_workflow = copy.deepcopy(self.workflow)
save_crypto_node = None
crypto_bridge_node = None
for node in simplify_workflow["nodes"]:
if node["id"] == self.save_crypto_node_id:
save_crypto_node = node
if node["id"] == self.crypto_bridge_node_id:
crypto_bridge_node = node
except_nodes_ids = self.crypto_nodes_ids
except_nodes_ids.add(self.crypto_bridge_node_id)
simplify_workflow["nodes"] = [
node
for node in simplify_workflow["nodes"]
if int(node["id"]) not in except_nodes_ids
]
if save_crypto_node is None:
raise ValueError("SaveCryptoNode not found in workflow")
if crypto_bridge_node is None:
raise ValueError("CryptoBridgeNode not found in workflow")
save_crypto_node["type"] = "DecodeCryptoNode"
save_crypto_node["widgets_values"] = (
[save_crypto_node.get("widgets_values", [None])[0]]
if "widgets_values" in save_crypto_node
else [None]
)
save_crypto_node["widgets_values"].append("")
save_crypto_node["properties"] = {"Node name for S&R": "DecodeCryptoNode"}
if "outputs" not in crypto_bridge_node:
save_crypto_node["outputs"] = []
else:
save_crypto_node["outputs"] = copy.deepcopy(crypto_bridge_node["outputs"])
output_nodes_ids = [int(node["id"]) for node in simplify_workflow["nodes"]]
filtered_links = []
for link in simplify_workflow["links"]:
if len(link) < 5:
continue
if link[1] == self.crypto_bridge_node_id:
link[1] = self.save_crypto_node_id
if link[1] in output_nodes_ids and link[3] in output_nodes_ids:
filtered_links.append(link)
else:
pass
simplify_workflow["links"] = filtered_links
simplify_workflow.pop("groups", None)
save_dir = folder_paths.output_directory
with open(
os.path.join(folder_paths.output_directory, output_workflow_name),
"w",
encoding="utf-8",
) as f:
json.dump(simplify_workflow, f, ensure_ascii=False, indent=4)
return save_dir
def save_original_workflow(self, output_workflow_name, save_dir):
with open(
os.path.join(save_dir, output_workflow_name), "w", encoding="utf-8"
) as f:
json.dump(self.workflow, f, ensure_ascii=False, indent=4)
return save_dir
def save_original_prompt(self, output_prompt_name, save_dir):
with open(
os.path.join(save_dir, output_prompt_name), "w", encoding="utf-8"
) as f:
json.dump(self.prompt, f, ensure_ascii=False, indent=4)
return save_dir
class DecodeCryptoWorkflow:
def __init__(self, prompt, workflow, template_id):
self.prompt = prompt
self.workflow = workflow
self.template_id = template_id
self.crypto_result = {}
self.input_anything_map = {}
def calculate_input_anything_map(self):
self.input_anything_map.clear()
for node_id, node in self.prompt.items():
if node.get("class_type") == "DecodeCryptoNode":
for input_name, input_value in node.get("inputs", {}).items():
if input_name.startswith("input_anything"):
if isinstance(input_value, list) and len(input_value) == 2:
input_link_key = f"{input_value[0]}_{input_value[1]}"
self.input_anything_map[input_link_key] = input_name
return self.input_anything_map
def load_crypto_prompt(self, serial_number_token, user_token=None):
if not user_token:
user_token = AuthUnit().read_user_token()
content = DownloadWorkflow().download_workflow(
self.template_id, serial_number_token, user_token
)
if not isinstance(content, str) or content == "":
raise ValueError("failed to download workflow")
self.crypto_result = json.loads(content)
return self.crypto_result["prompt"]
def get_hidden_input(self, input_value):
if isinstance(input_value, list) and len(input_value) == 2:
key = f"{input_value[0]}_{input_value[1]}"
return self.input_anything_map.get(key, None)
def get_outputs(self):
return self.crypto_result["outputs"]
class WorkflowTrimHandler:
@staticmethod
def onprompt_handler(json_data: Dict[str, Any]) -> Dict[str, Any]:
prompt = json_data["prompt"]
has_new_component = False
has_old_component = False
for node in prompt.values():
class_type = node.get("class_type")
if class_type == "SaveCryptoNode":
has_new_component = True
break
if class_type == "ExcuteCryptoNode":
has_old_component = True
break
if has_new_component:
user_token, error_msg, error_code = AuthUnit().get_user_token()
print(
f"user_token: {user_token}, error_msg: {error_msg}, error_code: {error_code}"
)
if not user_token:
if error_code == 401 or error_code == -3:
AuthUnit().login_dialog("输出CryptoCat加密工作流,请先完成登录")
json_data["prompt"] = {}
else:
PromptServer.instance.send_sync(
"cryptocat_toast",
{"content": f"无法完成鉴权登录,{error_msg}", "type": "error"},
)
elif has_old_component:
prompt = WorkflowTrimHandler.replace_prompt(prompt)
json_data["prompt"] = prompt
return json_data
@staticmethod
def replace_prompt(prompt: Dict[str, Any]) -> Dict[str, Any]:
if not prompt:
raise ValueError("Invalid JSON format.")
crypto_file_path = ""
excute_crypto_id = None
for node_id, node in list(prompt.items()):
if node.get("class_type") == "ExcuteCryptoNode":
crypto_file_path = node["inputs"].get("crypto_file_path")
excute_crypto_id = node_id
del prompt[node_id]
break
if not crypto_file_path:
print("No 'ExcuteCryptoNode' found in prompt.")
return prompt
inject_json = FileCompressor.decompress_from_json(crypto_file_path, "19040822")
output_images_ids = inject_json.pop("output_images_ids")
for node in prompt.values():
if node.get("class_type") == "CryptoCatImage":
if excute_crypto_id in node["inputs"]["images"]:
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)
prompt.update(inject_json)
return prompt