Merge branch 'main' into feat/cnr

This commit is contained in:
Dr.Lt.Data
2024-12-18 09:08:15 +09:00
22 changed files with 11534 additions and 5273 deletions

View File

@@ -40,6 +40,36 @@ DEFAULT_CHANNEL = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/ma
custom_nodes_path = os.path.abspath(os.path.join(comfyui_manager_path, '..'))
default_custom_nodes_path = None
def get_default_custom_nodes_path():
global default_custom_nodes_path
if default_custom_nodes_path is None:
try:
import folder_paths
default_custom_nodes_path = folder_paths.get_folder_paths("custom_nodes")[0]
except:
default_custom_nodes_path = custom_nodes_path
return default_custom_nodes_path
def get_custom_nodes_paths():
try:
import folder_paths
return folder_paths.get_folder_paths("custom_nodes")
except:
return [custom_nodes_path]
def get_comfyui_tag():
repo = git.Repo(comfy_path)
try:
return repo.git.describe('--tags')
except:
return None
invalid_nodes = {}
@@ -92,7 +122,11 @@ def check_invalid_nodes():
comfy_path = os.environ.get('COMFYUI_PATH')
if comfy_path is None:
comfy_path = os.path.abspath(os.path.join(custom_nodes_path, '..'))
try:
import folder_paths
comfy_path = os.path.join(os.path.dirname(folder_paths.__file__))
except:
comfy_path = os.path.abspath(os.path.join(custom_nodes_path, '..'))
channel_list_path = os.path.join(comfyui_manager_path, 'channels.list')
config_path = os.path.join(comfyui_manager_path, "config.ini")
@@ -1548,7 +1582,7 @@ def __win_check_git_update(path, do_fetch=False, do_update=False):
new_env = os.environ.copy()
new_env["COMFYUI_PATH"] = comfy_path
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=custom_nodes_path)
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=get_default_custom_nodes_path())
output, _ = process.communicate()
output = output.decode('utf-8').strip()
@@ -1602,7 +1636,7 @@ def __win_check_git_pull(path):
new_env = os.environ.copy()
new_env["COMFYUI_PATH"] = comfy_path
command = [sys.executable, git_script_path, "--pull", path]
process = subprocess.Popen(command, env=new_env, cwd=custom_nodes_path)
process = subprocess.Popen(command, env=new_env, cwd=get_default_custom_nodes_path())
process.wait()
@@ -1617,6 +1651,7 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
else:
if os.path.exists(requirements_path) and not no_deps:
print("Install: pip packages")
pip_fixer = PIPFixer(get_installed_packages())
with open(requirements_path, "r") as requirements_file:
for line in requirements_file:
#handle comments
@@ -1639,6 +1674,8 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
if package_name.strip() != "" and not package_name.startswith('#'):
try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
pip_fixer.fix_broken()
if os.path.exists(install_script_path):
print(f"Install: install script")
install_cmd = [sys.executable, "install.py"]
@@ -1809,9 +1846,10 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
# node_dir = f"{repo_name}@unknown"
node_dir = repo_name
repo_path = os.path.join(custom_nodes_path, node_dir)
disabled_repo_path1 = os.path.join(custom_nodes_path, '.disabled', node_dir)
disabled_repo_path2 = os.path.join(custom_nodes_path, repo_name+'.disabled') # old style
custom_nodes_base = get_default_custom_nodes_path()
repo_path = os.path.join(custom_nodes_base, node_dir)
disabled_repo_path1 = os.path.join(custom_nodes_base, '.disabled', node_dir)
disabled_repo_path2 = os.path.join(custom_nodes_base, repo_name+'.disabled') # old style
if os.path.exists(repo_path):
return result.fail(f"Already exists: '{repo_path}'")
@@ -1826,7 +1864,7 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
# Clone the repository from the remote URL
if not instant_execution and platform.system() == 'Windows':
res = manager_funcs.run_script([sys.executable, git_script_path, "--clone", custom_nodes_path, url, repo_path], cwd=custom_nodes_path)
res = manager_funcs.run_script([sys.executable, git_script_path, "--clone", custom_nodes_base, url, repo_path], cwd=custom_nodes_base)
if res != 0:
return result.fail(f"Failed to clone '{url}' into '{repo_path}'")
else:
@@ -1912,6 +1950,23 @@ async def get_data_by_mode(mode, filename, channel_url=None):
return json_obj
def lookup_installed_custom_nodes(repo_name):
try:
import folder_paths
base_paths = folder_paths.get_folder_paths("custom_nodes")
except:
base_paths = [custom_nodes_path]
for base_path in base_paths:
repo_path = os.path.join(base_path, repo_name)
if os.path.exists(repo_path):
return True, repo_path
elif os.path.exists(repo_path+'.disabled'):
return False, repo_path
return None
def gitclone_fix(files, instant_execution=False, no_deps=False):
print(f"Try fixing: {files}")
for url in files:
@@ -1923,13 +1978,15 @@ def gitclone_fix(files, instant_execution=False, no_deps=False):
url = url[:-1]
try:
repo_name = os.path.splitext(os.path.basename(url))[0]
repo_path = os.path.join(custom_nodes_path, repo_name)
repo_path = lookup_installed_custom_nodes(repo_name)
if os.path.exists(repo_path+'.disabled'):
repo_path = repo_path+'.disabled'
if repo_path is not None:
repo_path = repo_path[1]
if not execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps):
return False
if not execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps):
return False
else:
print(f"Custom node not found: {repo_name}")
except Exception as e:
print(f"Install(git-clone) error: {url} / {e}", file=sys.stderr)
@@ -1976,12 +2033,12 @@ def gitclone_uninstall(files):
url = url[:-1]
try:
dir_name = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
dir_path = os.path.join(custom_nodes_path, dir_name)
repo_path = lookup_installed_custom_nodes(dir_name)
# safety check
if dir_path == '/' or dir_path[1:] == ":/" or dir_path == '':
print(f"Uninstall(git-clone) error: invalid path '{dir_path}' for '{url}'")
return False
if repo_path is None:
continue
dir_path = repo_path[1]
install_script_path = os.path.join(dir_path, "uninstall.py")
disable_script_path = os.path.join(dir_path, "disable.py")
@@ -1997,10 +2054,7 @@ def gitclone_uninstall(files):
if code != 0:
print(f"An error occurred during the execution of the disable.py script. Only the '{dir_path}' will be deleted.")
if os.path.exists(dir_path):
rmtree(dir_path)
elif os.path.exists(dir_path + ".disabled"):
rmtree(dir_path + ".disabled")
rmtree(dir_path)
except Exception as e:
print(f"Uninstall(git-clone) error: {url} / {e}", file=sys.stderr)
return False
@@ -2023,12 +2077,12 @@ def gitclone_set_active(files, is_disable):
url = url[:-1]
try:
dir_name = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
dir_path = os.path.join(custom_nodes_path, dir_name)
repo_path = lookup_installed_custom_nodes(dir_name)
# safety check
if dir_path == '/' or dir_path[1:] == ":/" or dir_path == '':
print(f"{action_name}(git-clone) error: invalid path '{dir_path}' for '{url}'")
return False
if repo_path is None:
continue
dir_path = repo_path[1]
if is_disable:
current_path = dir_path
@@ -2065,10 +2119,12 @@ def gitclone_update(files, instant_execution=False, skip_script=False, msg_prefi
url = url[:-1]
try:
repo_name = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
repo_path = os.path.join(custom_nodes_path, repo_name)
repo_path = lookup_installed_custom_nodes(repo_name)
if os.path.exists(repo_path+'.disabled'):
repo_path = repo_path+'.disabled'
if repo_path is None:
continue
repo_path = repo_path[1]
git_pull(repo_path)
@@ -2139,10 +2195,14 @@ def lookup_customnode_by_url(data, target):
for x in data['custom_nodes']:
if target in x['files']:
dir_name = os.path.splitext(os.path.basename(target))[0].replace(".git", "")
dir_path = os.path.join(custom_nodes_path, dir_name)
if os.path.exists(dir_path):
repo_path = lookup_installed_custom_nodes(dir_name)
if repo_path is None:
continue
if repo_path[0]:
x['installed'] = 'True'
elif os.path.exists(dir_path + ".disabled"):
else:
x['installed'] = 'Disabled'
return x
@@ -2151,13 +2211,15 @@ def lookup_customnode_by_url(data, target):
def simple_check_custom_node(url):
dir_name = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
dir_path = os.path.join(custom_nodes_path, dir_name)
if os.path.exists(dir_path):
return 'installed'
elif os.path.exists(dir_path+'.disabled'):
return 'disabled'
repo_path = lookup_installed_custom_nodes(dir_name)
return 'not-installed'
if repo_path is None:
return 'not-installed'
if repo_path[0]:
return 'installed'
else:
return 'disabled'
def check_state_of_git_node_pack_single(item, do_fetch=False, do_update_check=True, do_update=False):

View File

@@ -16,6 +16,7 @@ from server import PromptServer
import manager_core as core
import manager_util
import cm_global
from datetime import datetime
print(f"### Loading: ComfyUI-Manager ({core.version_str})")
@@ -192,7 +193,7 @@ def print_comfyui_version():
try:
if core.comfy_ui_commit_datetime.date() < core.comfy_ui_required_commit_datetime.date():
print(f"\n\n## [WARN] ComfyUI-Manager: Your ComfyUI version ({core.comfy_ui_revision})[{core.comfy_ui_commit_datetime.date()}] is too old. Please update to the latest version. ##\n\n")
print(f"\n\n## [WARN] ComfyUI-Manager: Your ComfyUI version ({core.get_comfyui_tag()})[{core.comfy_ui_commit_datetime.date()}] is too old. Please update to the latest version. ##\n\n")
except:
pass
@@ -211,10 +212,11 @@ def print_comfyui_version():
# <--
if current_branch == "master":
if comfyui_tag:
print(f"### ComfyUI Version: {comfyui_tag} | Released on '{core.comfy_ui_commit_datetime.date()}'")
else:
version_tag = core.get_comfyui_tag()
if version_tag is None:
print(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'")
else:
print(f"### ComfyUI Version: {core.get_comfyui_tag()} | Released on '{core.comfy_ui_commit_datetime.date()}'")
else:
print(f"### ComfyUI Revision: {core.comfy_ui_revision} on '{current_branch}' [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'")
except:
@@ -248,23 +250,45 @@ import urllib.request
def get_model_dir(data):
if 'download_model_base' in folder_paths.folder_names_and_paths:
models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0]
else:
models_base = folder_paths.models_dir
def resolve_custom_node(save_path):
save_path = save_path[13:] # remove 'custom_nodes/'
repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name
repo_path = core.lookup_installed_custom_nodes(repo_name)
if repo_path is not None and repo_path[0]:
# Returns the retargeted path based on the actually installed repository
return os.path.join(os.path.dirname(repo_path[1]), save_path)
else:
return None
if data['save_path'] != 'default':
if '..' in data['save_path'] or data['save_path'].startswith('/'):
print(f"[WARN] '{data['save_path']}' is not allowed path. So it will be saved into 'models/etc'.")
base_model = os.path.join(folder_paths.models_dir, "etc")
base_model = os.path.join(models_base, "etc")
else:
if data['save_path'].startswith("custom_nodes"):
base_model = os.path.join(core.comfy_path, data['save_path'])
base_model = resolve_custom_node(data['save_path'])
if base_model is None:
print(f"[ComfyUI-Manager] The target custom node for model download is not installed: {data['save_path']}")
return None
else:
base_model = os.path.join(folder_paths.models_dir, data['save_path'])
base_model = os.path.join(models_base, data['save_path'])
else:
model_type = data['type']
if model_type == "checkpoints" or model_type == "checkpoint":
base_model = folder_paths.folder_names_and_paths["checkpoints"][0][0]
elif model_type == "unclip":
base_model = folder_paths.folder_names_and_paths["checkpoints"][0][0]
elif model_type == "clip":
base_model = folder_paths.folder_names_and_paths["clip"][0][0]
elif model_type == "clip" or model_type == "text_encoders":
if folder_paths.folder_names_and_paths.get("text_encoders"):
base_model = folder_paths.folder_names_and_paths["text_encoders"][0][0]
else:
print(f"[ComfyUI-Manager] Your ComfyUI is outdated version.")
base_model = folder_paths.folder_names_and_paths["clip"][0][0] # outdated version
elif model_type == "VAE":
base_model = folder_paths.folder_names_and_paths["vae"][0][0]
elif model_type == "lora":
@@ -290,14 +314,17 @@ def get_model_dir(data):
print(f"[ComfyUI-Manager] Your ComfyUI is outdated version.")
base_model = folder_paths.folder_names_and_paths["unet"][0][0] # outdated version
else:
base_model = os.path.join(folder_paths.models_dir, "etc")
base_model = os.path.join(models_base, "etc")
return base_model
def get_model_path(data):
base_model = get_model_dir(data)
return os.path.join(base_model, data['filename'])
if base_model is None:
return None
else:
return os.path.join(base_model, data['filename'])
def check_state_of_git_node_pack(node_packs, do_fetch=False, do_update_check=True, do_update=False):
@@ -1228,17 +1255,21 @@ async def get_notice(request):
if match:
markdown_content = match.group(1)
if comfyui_tag:
markdown_content += f"<HR>ComfyUI: {comfyui_tag}<BR>Commit Date: {core.comfy_ui_commit_datetime.date()}"
else:
version_tag = core.get_comfyui_tag()
if version_tag is None:
markdown_content += f"<HR>ComfyUI: {core.comfy_ui_revision}[{comfy_ui_hash[:6]}]({core.comfy_ui_commit_datetime.date()})"
else:
markdown_content += (f"<HR>ComfyUI: {version_tag}<BR>"
f"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;({core.comfy_ui_commit_datetime.date()})")
# markdown_content += f"<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;()"
markdown_content += f"<BR>Manager: {core.version_str}"
markdown_content = add_target_blank(markdown_content)
try:
if core.comfy_ui_required_commit_datetime.date() > core.comfy_ui_commit_datetime.date():
if core.comfy_ui_commit_datetime == datetime(1900, 1, 1, 0, 0, 0):
markdown_content = f'<P style="text-align: center; color:red; background-color:white; font-weight:bold">Your ComfyUI isn\'t git repo.</P>' + markdown_content
elif core.comfy_ui_required_commit_datetime.date() > core.comfy_ui_commit_datetime.date():
markdown_content = f'<P style="text-align: center; color:red; background-color:white; font-weight:bold">Your ComfyUI is too OUTDATED!!!</P>' + markdown_content
except:
pass
@@ -1269,6 +1300,11 @@ def restart(self):
exit(0)
print(f"\nRestarting... [Legacy Mode]\n\n")
sys_argv = sys.argv.copy()
if '--windows-standalone-build' in sys_argv:
sys_argv.remove('--windows-standalone-build')
if sys.platform.startswith('win32'):
return os.execv(sys.executable, ['"' + sys.executable + '"', '"' + sys.argv[0] + '"'] + sys.argv[1:])
else:
@@ -1332,13 +1368,6 @@ async def load_components(request):
return web.Response(status=400)
args.enable_cors_header = "*"
if hasattr(PromptServer.instance, "app"):
app = PromptServer.instance.app
cors_middleware = server.create_cors_middleware(args.enable_cors_header)
app.middlewares.append(cors_middleware)
def sanitize(data):
return data.replace("<", "&lt;").replace(">", "&gt;")

View File

@@ -5,6 +5,8 @@ import json
import threading
import os
from datetime import datetime
import subprocess
import sys
cache_lock = threading.Lock()
@@ -171,4 +173,150 @@ def extract_package_as_zip(file_path, extract_path):
return extracted_files
except zipfile.BadZipFile:
print(f"File '{file_path}' is not a zip or is corrupted.")
return None
return None
pip_map = None
def get_installed_packages(renew=False):
global pip_map
if renew or pip_map is None:
try:
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
pip_map = {}
for line in result.split('\n'):
x = line.strip()
if x:
y = line.split()
if y[0] == 'Package' or y[0].startswith('-'):
continue
pip_map[y[0]] = y[1]
except subprocess.CalledProcessError as e:
print(f"[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
return set()
return pip_map
def clear_pip_cache():
global pip_map
pip_map = None
torch_torchvision_version_map = {
'2.5.1': '0.20.1',
'2.5.0': '0.20.0',
'2.4.1': '0.19.1',
'2.4.0': '0.19.0',
'2.3.1': '0.18.1',
'2.3.0': '0.18.0',
'2.2.2': '0.17.2',
'2.2.1': '0.17.1',
'2.2.0': '0.17.0',
'2.1.2': '0.16.2',
'2.1.1': '0.16.1',
'2.1.0': '0.16.0',
'2.0.1': '0.15.2',
'2.0.0': '0.15.1',
}
class PIPFixer:
def __init__(self, prev_pip_versions):
self.prev_pip_versions = { **prev_pip_versions }
def torch_rollback(self):
spec = self.prev_pip_versions['torch'].split('+')
if len(spec) > 0:
platform = spec[1]
else:
cmd = [sys.executable, '-m', 'pip', 'install', '--force', 'torch', 'torchvision', 'torchaudio']
subprocess.check_output(cmd, universal_newlines=True)
print(cmd)
return
torch_ver = StrictVersion(spec[0])
torch_ver = f"{torch_ver.major}.{torch_ver.minor}.{torch_ver.patch}"
torchvision_ver = torch_torchvision_version_map.get(torch_ver)
if torchvision_ver is None:
cmd = [sys.executable, '-m', 'pip', 'install', '--pre',
'torch', 'torchvision', 'torchaudio',
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"]
print("[manager-core] restore PyTorch to nightly version")
else:
cmd = [sys.executable, '-m', 'pip', 'install',
f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torch_ver}",
'--index-url', f"https://download.pytorch.org/whl/{platform}"]
print(f"[manager-core] restore PyTorch to {torch_ver}+{platform}")
subprocess.check_output(cmd, universal_newlines=True)
def fix_broken(self):
new_pip_versions = get_installed_packages(True)
# remove `comfy` python package
try:
if 'comfy' in new_pip_versions:
cmd = [sys.executable, '-m', 'pip', 'uninstall', 'comfy']
subprocess.check_output(cmd, universal_newlines=True)
print(f"[manager-core] 'comfy' python package is uninstalled.\nWARN: The 'comfy' package is completely unrelated to ComfyUI and should never be installed as it causes conflicts with ComfyUI.")
except Exception as e:
print(f"[manager-core] Failed to uninstall `comfy` python package")
print(e)
# fix torch - reinstall torch packages if version is changed
try:
if self.prev_pip_versions['torch'] != new_pip_versions['torch'] \
or self.prev_pip_versions['torchvision'] != new_pip_versions['torchvision'] \
or self.prev_pip_versions['torchaudio'] != new_pip_versions['torchaudio']:
self.torch_rollback()
except Exception as e:
print(f"[manager-core] Failed to restore PyTorch")
print(e)
# fix opencv
try:
ocp = new_pip_versions.get('opencv-contrib-python')
ocph = new_pip_versions.get('opencv-contrib-python-headless')
op = new_pip_versions.get('opencv-python')
oph = new_pip_versions.get('opencv-python-headless')
versions = [ocp, ocph, op, oph]
versions = [StrictVersion(x) for x in versions if x is not None]
versions.sort(reverse=True)
if len(versions) > 0:
# upgrade to maximum version
targets = []
cur = versions[0]
if ocp is not None and StrictVersion(ocp) != cur:
targets.append('opencv-contrib-python')
if ocph is not None and StrictVersion(ocph) != cur:
targets.append('opencv-contrib-python-headless')
if op is not None and StrictVersion(op) != cur:
targets.append('opencv-python')
if oph is not None and StrictVersion(oph) != cur:
targets.append('opencv-python-headless')
if len(targets) > 0:
for x in targets:
cmd = [sys.executable, '-m', 'pip', 'install', f"{x}=={versions[0].version_string}"]
subprocess.check_output(cmd, universal_newlines=True)
print(f"[manager-core] 'opencv' dependencies were fixed: {targets}")
except Exception as e:
print(f"[manager-core] Failed to restore opencv")
print(e)
# fix numpy
try:
np = new_pip_versions.get('numpy')
if np is not None:
if StrictVersion(np) >= StrictVersion('2'):
subprocess.check_output([sys.executable, '-m', 'pip', 'install', f"numpy<2"], universal_newlines=True)
except Exception as e:
print(f"[manager-core] Failed to restore numpy")
print(e)

View File

@@ -29,12 +29,37 @@ Detailed information: https://old.reddit.com/r/comfyui/comments/1dbls5n/psa_if_y
2. Remove files: lolMiner*, 4G_Ethash_Linux_Readme.txt, mine* in ComfyUI dir.
(Reinstall ComfyUI is recommended.)
""",
"ultralytics==8.3.41": f"""
Execute following commands:
{sys.executable} -m pip uninstall ultralytics
{sys.executable} -m pip install ultralytics==8.3.40
And kill and remove /tmp/ultralytics_runner
The version 8.3.41 to 8.3.42 of the Ultralytics package you installed is compromised. Please uninstall that version and reinstall the latest version.
https://blog.comfy.org/comfyui-statement-on-the-ultralytics-crypto-miner-situation/
""",
"ultralytics==8.3.42": f"""
Execute following commands:
{sys.executable} -m pip uninstall ultralytics
{sys.executable} -m pip install ultralytics==8.3.40
And kill and remove /tmp/ultralytics_runner
The version 8.3.41 to 8.3.42 of the Ultralytics package you installed is compromised. Please uninstall that version and reinstall the latest version.
https://blog.comfy.org/comfyui-statement-on-the-ultralytics-crypto-miner-situation/
"""
}
node_blacklist = {"ComfyUI_LLMVISION": "ComfyUI_LLMVISION"}
pip_blacklist = {"AppleBotzz": "ComfyUI_LLMVISION"}
pip_blacklist = {
"AppleBotzz": "ComfyUI_LLMVISION",
"ultralytics==8.3.41": "ultralytics==8.3.41"
}
file_blacklist = {
"ComfyUI_LLMVISION": ["%LocalAppData%\\rundll64.exe"],