Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd89590a7d | ||
|
|
dc70f40eff | ||
|
|
825296c3e0 | ||
|
|
bf0e86a10d | ||
|
|
727295b52e | ||
|
|
6c23f7691d | ||
|
|
6248f31402 | ||
|
|
cb0b652703 | ||
|
|
9c4fa5fb27 | ||
|
|
759b4d0dc9 |
@@ -286,6 +286,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
|||||||
* `Negative Cond Placeholder` - Models like FLUX.1 do not use Negative Conditioning. This is a placeholder node for them. You can use FLUX.1 by replacing the Negative Conditioning used in Impact KSampler, KSampler (Inspire), and Detailer with this node.
|
* `Negative Cond Placeholder` - Models like FLUX.1 do not use Negative Conditioning. This is a placeholder node for them. You can use FLUX.1 by replacing the Negative Conditioning used in Impact KSampler, KSampler (Inspire), and Detailer with this node.
|
||||||
* `Execution Order Controller` - A helper node that can forcibly control the execution order of nodes.
|
* `Execution Order Controller` - A helper node that can forcibly control the execution order of nodes.
|
||||||
* Connect the output of the node that should be executed first to the signal, and make the input of the node that should be executed later pass through this node.
|
* Connect the output of the node that should be executed first to the signal, and make the input of the node that should be executed later pass through this node.
|
||||||
|
* `List Bridge` - When passing the list output through this node, it collects and organizes the data before forwarding it, which ensures that the previous stage's sub-workflow has been completed.
|
||||||
|
|
||||||
|
|
||||||
## MMDet nodes (DEPRECATED) - Don't use these nodes
|
## MMDet nodes (DEPRECATED) - Don't use these nodes
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"StringListToString": StringListToString,
|
"StringListToString": StringListToString,
|
||||||
"WildcardPromptFromString": WildcardPromptFromString,
|
"WildcardPromptFromString": WildcardPromptFromString,
|
||||||
"ImpactExecutionOrderController": ImpactExecutionOrderController,
|
"ImpactExecutionOrderController": ImpactExecutionOrderController,
|
||||||
|
"ImpactListBridge": ImpactListBridge,
|
||||||
|
|
||||||
"RemoveNoiseMask": RemoveNoiseMask,
|
"RemoveNoiseMask": RemoveNoiseMask,
|
||||||
|
|
||||||
@@ -393,6 +394,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ImpactSwitch": "Switch (Any)",
|
"ImpactSwitch": "Switch (Any)",
|
||||||
"ImpactInversedSwitch": "Inversed Switch (Any)",
|
"ImpactInversedSwitch": "Inversed Switch (Any)",
|
||||||
"ImpactExecutionOrderController": "Execution Order Controller",
|
"ImpactExecutionOrderController": "Execution Order Controller",
|
||||||
|
"ImpactListBridge": "List Bridge",
|
||||||
|
|
||||||
"MasksToMaskList": "Mask Batch to Mask List",
|
"MasksToMaskList": "Mask Batch to Mask List",
|
||||||
"MaskListToMaskBatch": "Mask List to Mask Batch",
|
"MaskListToMaskBatch": "Mask List to Mask Batch",
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
import locale
|
import locale
|
||||||
import traceback
|
import traceback
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
|
|
||||||
if sys.argv[0] == 'install.py':
|
if sys.argv[0] == 'install.py':
|
||||||
@@ -66,6 +69,42 @@ def process_wrap(cmd_str, cwd=None, handler=None, env=None):
|
|||||||
stderr_thread.join()
|
stderr_thread.join()
|
||||||
|
|
||||||
return process.wait()
|
return process.wait()
|
||||||
|
|
||||||
|
|
||||||
|
@functools.cache
|
||||||
|
def get_installed_packages() -> Set[str]:
|
||||||
|
try:
|
||||||
|
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
|
||||||
|
pip_list = set([line.split()[0].lower() for line in result.split('\n') if line.strip()])
|
||||||
|
return pip_list
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise Exception(f"[ComfyUI-Impact-Pack] Failed to retrieve the information of installed pip packages.")
|
||||||
|
|
||||||
|
|
||||||
|
def is_package_installed(name: str) -> bool:
|
||||||
|
name = name.strip()
|
||||||
|
pattern = r'([^<>!=]+)([<>!=]=?)'
|
||||||
|
match = re.search(pattern, name)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
name = match.group(1)
|
||||||
|
|
||||||
|
result = name.lower() in get_installed_packages()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def is_requirements_installed(file_path: str) -> bool:
|
||||||
|
print(f"Requirements file: {file_path}")
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if not is_package_installed(line):
|
||||||
|
return False
|
||||||
|
|
||||||
|
pip_install = [sys.executable, "-m", "pip", "install", "-U"]
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -353,7 +353,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.outputs[0].type = origin_type;
|
this.outputs[0].type = origin_type;
|
||||||
this.outputs[0].name = origin_type;
|
this.outputs[0].name = 'output1';
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -483,8 +483,12 @@ app.registerExtension({
|
|||||||
|
|
||||||
if(this.inputs[0].type == '*'){
|
if(this.inputs[0].type == '*'){
|
||||||
const node = app.graph.getNodeById(link_info.origin_id);
|
const node = app.graph.getNodeById(link_info.origin_id);
|
||||||
let origin_type = node.outputs[link_info.origin_slot].type;
|
let origin_type = node.outputs[link_info.origin_slot]?.type;
|
||||||
|
if(link_info.target_slot == 0 && this.inputs.length > 1) {
|
||||||
|
origin_type = this.inputs[1].type;
|
||||||
|
node.connect(link_info.origin_slot, node.id, 'input1');
|
||||||
|
}
|
||||||
|
|
||||||
if(origin_type == '*') {
|
if(origin_type == '*') {
|
||||||
this.disconnectInput(link_info.target_slot);
|
this.disconnectInput(link_info.target_slot);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class PreviewBridge:
|
|||||||
|
|
||||||
is_empty_mask = torch.all(mask == 0)
|
is_empty_mask = torch.all(mask == 0)
|
||||||
|
|
||||||
if block and is_empty_mask and core.is_execution_model_version_supported:
|
if block and is_empty_mask and core.is_execution_model_version_supported():
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
||||||
elif block and is_empty_mask:
|
elif block and is_empty_mask:
|
||||||
@@ -362,7 +362,7 @@ class PreviewBridgeLatent:
|
|||||||
|
|
||||||
res_latent = latent
|
res_latent = latent
|
||||||
|
|
||||||
if block and is_empty_mask and core.is_execution_model_version_supported:
|
if block and is_empty_mask and core.is_execution_model_version_supported():
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
||||||
elif block and is_empty_mask:
|
elif block and is_empty_mask:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
version_code = [7, 10]
|
version_code = [7, 11]
|
||||||
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||||
|
|
||||||
dependency_version = 23
|
dependency_version = 23
|
||||||
|
|||||||
@@ -691,7 +691,7 @@ class ImpactControlBridge:
|
|||||||
def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
|
def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
|
||||||
global error_skip_flag
|
global error_skip_flag
|
||||||
|
|
||||||
if core.is_execution_model_version_supported:
|
if core.is_execution_model_version_supported():
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
else:
|
else:
|
||||||
print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.")
|
print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.")
|
||||||
@@ -766,6 +766,29 @@ class ImpactExecutionOrderController:
|
|||||||
return signal, value
|
return signal, value
|
||||||
|
|
||||||
|
|
||||||
|
class ImpactListBridge:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {"required": {
|
||||||
|
"list_input": (any_typ,),
|
||||||
|
}}
|
||||||
|
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
DESCRIPTION = "When passing the list output through this node, it collects and organizes the data before forwarding it, which ensures that the previous stage's sub-workflow has been completed."
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
RETURN_TYPES = (any_typ, )
|
||||||
|
RETURN_NAMES = ("list_output", )
|
||||||
|
|
||||||
|
INPUT_IS_LIST = True
|
||||||
|
OUTPUT_IS_LIST = (True, )
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def doit(list_input):
|
||||||
|
return (list_input,)
|
||||||
|
|
||||||
|
|
||||||
original_handle_execution = execution.PromptExecutor.handle_execution_error
|
original_handle_execution = execution.PromptExecutor.handle_execution_error
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,16 @@ class GeneralSwitch:
|
|||||||
dyn_inputs = {"input1": (any_typ, {"lazy": True, "tooltip": "Any input. When connected, one more input slot is added."}), }
|
dyn_inputs = {"input1": (any_typ, {"lazy": True, "tooltip": "Any input. When connected, one more input slot is added."}), }
|
||||||
if core.is_execution_model_version_supported():
|
if core.is_execution_model_version_supported():
|
||||||
stack = inspect.stack()
|
stack = inspect.stack()
|
||||||
if stack[2].function == 'get_input_info' and stack[3].function == 'add_node':
|
if stack[2].function == 'get_input_info':
|
||||||
for x in range(2, 200):
|
# bypass validation
|
||||||
dyn_inputs[f"input{x}"] = (any_typ, {"lazy": True})
|
class AllContainer:
|
||||||
|
def __contains__(self, item):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return any_typ, {"lazy": True}
|
||||||
|
|
||||||
|
dyn_inputs = AllContainer()
|
||||||
|
|
||||||
inputs = {"required": {
|
inputs = {"required": {
|
||||||
"select": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1, "tooltip": "The input number you want to output among the inputs"}),
|
"select": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1, "tooltip": "The input number you want to output among the inputs"}),
|
||||||
@@ -163,7 +170,7 @@ class GeneralInversedSwitch:
|
|||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
def doit(self, select, prompt, unique_id, input, **kwargs):
|
def doit(self, select, prompt, unique_id, input, **kwargs):
|
||||||
if core.is_execution_model_version_supported:
|
if core.is_execution_model_version_supported():
|
||||||
from comfy_execution.graph import ExecutionBlocker
|
from comfy_execution.graph import ExecutionBlocker
|
||||||
else:
|
else:
|
||||||
print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.")
|
print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.")
|
||||||
@@ -181,7 +188,7 @@ class GeneralInversedSwitch:
|
|||||||
for i in range(0, cnt + 1):
|
for i in range(0, cnt + 1):
|
||||||
if select == i+1:
|
if select == i+1:
|
||||||
res.append(input)
|
res.append(input)
|
||||||
elif core.is_execution_model_version_supported:
|
elif core.is_execution_model_version_supported():
|
||||||
res.append(ExecutionBlocker(None))
|
res.append(ExecutionBlocker(None))
|
||||||
else:
|
else:
|
||||||
res.append(None)
|
res.append(None)
|
||||||
@@ -366,7 +373,7 @@ class ImageListToImageBatch:
|
|||||||
|
|
||||||
def doit(self, images):
|
def doit(self, images):
|
||||||
if len(images) <= 1:
|
if len(images) <= 1:
|
||||||
return (images,)
|
return (images[0],)
|
||||||
else:
|
else:
|
||||||
image1 = images[0]
|
image1 = images[0]
|
||||||
for image2 in images[1:]:
|
for image2 in images[1:]:
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ def read_wildcard_dict(wildcard_path):
|
|||||||
try:
|
try:
|
||||||
with open(file_path, 'r', encoding="ISO-8859-1") as f:
|
with open(file_path, 'r', encoding="ISO-8859-1") as f:
|
||||||
lines = f.read().splitlines()
|
lines = f.read().splitlines()
|
||||||
wildcard_dict[key] = lines
|
wildcard_dict[key] = [x for x in lines if not x.strip().startswith('#')]
|
||||||
except yaml.reader.ReaderError:
|
except yaml.reader.ReaderError:
|
||||||
with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
lines = f.read().splitlines()
|
lines = f.read().splitlines()
|
||||||
wildcard_dict[key] = lines
|
wildcard_dict[key] = [x for x in lines if not x.strip().startswith('#')]
|
||||||
elif file.endswith('.yaml'):
|
elif file.endswith('.yaml'):
|
||||||
file_path = os.path.join(root, file)
|
file_path = os.path.join(root, file)
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ def process(text, seed=None):
|
|||||||
matches = re.findall(wildcard_pattern, multi_select_pattern[1])
|
matches = re.findall(wildcard_pattern, multi_select_pattern[1])
|
||||||
if len(options) == 1 and matches:
|
if len(options) == 1 and matches:
|
||||||
# count$$<single wildcard>
|
# count$$<single wildcard>
|
||||||
options = local_wildcard_dict.get(matches[0])
|
options = get_wildcard_options(multi_select_pattern[1])
|
||||||
else:
|
else:
|
||||||
# count$$opt1|opt2|...
|
# count$$opt1|opt2|...
|
||||||
options[0] = multi_select_pattern[1]
|
options[0] = multi_select_pattern[1]
|
||||||
@@ -199,6 +199,34 @@ def process(text, seed=None):
|
|||||||
|
|
||||||
return replaced_string, replacements_found
|
return replaced_string, replacements_found
|
||||||
|
|
||||||
|
def get_wildcard_options(string):
|
||||||
|
pattern = r"__([\w.\-+/*\\]+?)__"
|
||||||
|
matches = re.findall(pattern, string)
|
||||||
|
|
||||||
|
options = []
|
||||||
|
|
||||||
|
for match in matches:
|
||||||
|
keyword = match.lower()
|
||||||
|
keyword = wildcard_normalize(keyword)
|
||||||
|
if keyword in local_wildcard_dict:
|
||||||
|
options.extend(local_wildcard_dict[keyword])
|
||||||
|
elif '*' in keyword:
|
||||||
|
subpattern = keyword.replace('*', '.*').replace('+', '\\+')
|
||||||
|
total_patterns = []
|
||||||
|
found = False
|
||||||
|
for k, v in local_wildcard_dict.items():
|
||||||
|
if re.match(subpattern, k) is not None or re.match(subpattern, k+'/') is not None:
|
||||||
|
total_patterns += v
|
||||||
|
found = True
|
||||||
|
|
||||||
|
if found:
|
||||||
|
options.extend(total_patterns)
|
||||||
|
elif '/' not in keyword:
|
||||||
|
string_fallback = string.replace(f"__{match}__", f"__*/{match}__", 1)
|
||||||
|
options.extend(get_wildcard_options(string_fallback))
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
def replace_wildcard(string):
|
def replace_wildcard(string):
|
||||||
pattern = r"__([\w.\-+/*\\]+?)__"
|
pattern = r"__([\w.\-+/*\\]+?)__"
|
||||||
matches = re.findall(pattern, string)
|
matches = re.findall(pattern, string)
|
||||||
@@ -259,7 +287,7 @@ def process(text, seed=None):
|
|||||||
|
|
||||||
|
|
||||||
def is_numeric_string(input_str):
|
def is_numeric_string(input_str):
|
||||||
return re.match(r'^-?\d+(\.\d+)?$', input_str) is not None
|
return re.match(r'^-?(\d*\.?\d+|\d+\.?\d*)$', input_str) is not None
|
||||||
|
|
||||||
|
|
||||||
def safe_float(x):
|
def safe_float(x):
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-impact-pack"
|
name = "comfyui-impact-pack"
|
||||||
description = "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
|
description = "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
|
||||||
version = "7.10"
|
version = "7.11"
|
||||||
license = { file = "LICENSE.txt" }
|
license = { file = "LICENSE.txt" }
|
||||||
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user