mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2026-01-11 14:40:49 +08:00
wip: remove manager core code
This commit is contained in:
parent
527c994d43
commit
da42eca04a
15
__init__.py
15
__init__.py
@ -1,16 +1,7 @@
|
||||
import os
|
||||
from .modules import manager_ext_server
|
||||
from .modules import share_3rdparty
|
||||
|
||||
cli_mode_flag = os.path.join(os.path.dirname(__file__), '.enable-cli-only-mode')
|
||||
|
||||
if not os.path.exists(cli_mode_flag):
|
||||
from .glob import manager_server
|
||||
from .glob import share_3rdparty
|
||||
WEB_DIRECTORY = "js"
|
||||
else:
|
||||
print(f"\n[ComfyUI-Manager] !! cli-only-mode is enabled !!\n")
|
||||
WEB_DIRECTORY = "js"
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
__all__ = ['NODE_CLASS_MAPPINGS']
|
||||
|
||||
|
||||
|
||||
|
||||
473
git_helper.py
473
git_helper.py
@ -1,473 +0,0 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
|
||||
import git
|
||||
import configparser
|
||||
import json
|
||||
import yaml
|
||||
import requests
|
||||
from tqdm.auto import tqdm
|
||||
from git.remote import RemoteProgress
|
||||
|
||||
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
print(f"\n[bold yellow]WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
|
||||
|
||||
def download_url(url, dest_folder, filename=None):
|
||||
# Ensure the destination folder exists
|
||||
if not os.path.exists(dest_folder):
|
||||
os.makedirs(dest_folder)
|
||||
|
||||
# Extract filename from URL if not provided
|
||||
if filename is None:
|
||||
filename = os.path.basename(url)
|
||||
|
||||
# Full path to save the file
|
||||
dest_path = os.path.join(dest_folder, filename)
|
||||
|
||||
# Download the file
|
||||
response = requests.get(url, stream=True)
|
||||
if response.status_code == 200:
|
||||
with open(dest_path, 'wb') as file:
|
||||
for chunk in response.iter_content(chunk_size=1024):
|
||||
if chunk:
|
||||
file.write(chunk)
|
||||
else:
|
||||
print(f"Failed to download file from {url}")
|
||||
|
||||
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
nodelist_path = os.path.join(os.path.dirname(__file__), "custom-node-list.json")
|
||||
working_directory = os.getcwd()
|
||||
|
||||
if os.path.basename(working_directory) != 'custom_nodes':
|
||||
print(f"WARN: This script should be executed in custom_nodes dir")
|
||||
print(f"DBG: INFO {working_directory}")
|
||||
print(f"DBG: INFO {sys.argv}")
|
||||
# exit(-1)
|
||||
|
||||
|
||||
class GitProgress(RemoteProgress):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.pbar = tqdm(ascii=True)
|
||||
|
||||
def update(self, op_code, cur_count, max_count=None, message=''):
|
||||
self.pbar.total = max_count
|
||||
self.pbar.n = cur_count
|
||||
self.pbar.pos = 0
|
||||
self.pbar.refresh()
|
||||
|
||||
|
||||
def gitclone(custom_nodes_path, url, target_hash=None, repo_path=None):
|
||||
repo_name = os.path.splitext(os.path.basename(url))[0]
|
||||
|
||||
if repo_path is None:
|
||||
repo_path = os.path.join(custom_nodes_path, repo_name)
|
||||
|
||||
# Clone the repository from the remote URL
|
||||
repo = git.Repo.clone_from(url, repo_path, recursive=True, progress=GitProgress())
|
||||
|
||||
if target_hash is not None:
|
||||
print(f"CHECKOUT: {repo_name} [{target_hash}]")
|
||||
repo.git.checkout(target_hash)
|
||||
|
||||
repo.git.clear_cache()
|
||||
repo.close()
|
||||
|
||||
|
||||
def gitcheck(path, do_fetch=False):
|
||||
try:
|
||||
# Fetch the latest commits from the remote repository
|
||||
repo = git.Repo(path)
|
||||
|
||||
if repo.head.is_detached:
|
||||
print("CUSTOM NODE CHECK: True")
|
||||
return
|
||||
|
||||
current_branch = repo.active_branch
|
||||
branch_name = current_branch.name
|
||||
|
||||
remote_name = current_branch.tracking_branch().remote_name
|
||||
remote = repo.remote(name=remote_name)
|
||||
|
||||
if do_fetch:
|
||||
remote.fetch()
|
||||
|
||||
# Get the current commit hash and the commit hash of the remote branch
|
||||
commit_hash = repo.head.commit.hexsha
|
||||
|
||||
if f'{remote_name}/{branch_name}' in repo.refs:
|
||||
remote_commit_hash = repo.refs[f'{remote_name}/{branch_name}'].object.hexsha
|
||||
else:
|
||||
print("CUSTOM NODE CHECK: True") # non default branch is treated as updatable
|
||||
return
|
||||
|
||||
# Compare the commit hashes to determine if the local repository is behind the remote repository
|
||||
if commit_hash != remote_commit_hash:
|
||||
# Get the commit dates
|
||||
commit_date = repo.head.commit.committed_datetime
|
||||
remote_commit_date = repo.refs[f'{remote_name}/{branch_name}'].object.committed_datetime
|
||||
|
||||
# Compare the commit dates to determine if the local repository is behind the remote repository
|
||||
if commit_date < remote_commit_date:
|
||||
print("CUSTOM NODE CHECK: True")
|
||||
else:
|
||||
print("CUSTOM NODE CHECK: False")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("CUSTOM NODE CHECK: Error")
|
||||
|
||||
|
||||
def switch_to_default_branch(repo):
|
||||
default_branch = repo.git.symbolic_ref('refs/remotes/origin/HEAD').replace('refs/remotes/origin/', '')
|
||||
repo.git.checkout(default_branch)
|
||||
|
||||
|
||||
def gitpull(path):
|
||||
# Check if the path is a git repository
|
||||
if not os.path.exists(os.path.join(path, '.git')):
|
||||
raise ValueError('Not a git repository')
|
||||
|
||||
# Pull the latest changes from the remote repository
|
||||
repo = git.Repo(path)
|
||||
if repo.is_dirty():
|
||||
print(f"STASH: '{path}' is dirty.")
|
||||
repo.git.stash()
|
||||
|
||||
commit_hash = repo.head.commit.hexsha
|
||||
try:
|
||||
if repo.head.is_detached:
|
||||
switch_to_default_branch(repo)
|
||||
|
||||
current_branch = repo.active_branch
|
||||
branch_name = current_branch.name
|
||||
|
||||
remote_name = current_branch.tracking_branch().remote_name
|
||||
remote = repo.remote(name=remote_name)
|
||||
|
||||
if f'{remote_name}/{branch_name}' not in repo.refs:
|
||||
switch_to_default_branch(repo)
|
||||
current_branch = repo.active_branch
|
||||
branch_name = current_branch.name
|
||||
|
||||
remote.fetch()
|
||||
remote_commit_hash = repo.refs[f'{remote_name}/{branch_name}'].object.hexsha
|
||||
|
||||
if commit_hash == remote_commit_hash:
|
||||
print("CUSTOM NODE PULL: None") # there is no update
|
||||
repo.close()
|
||||
return
|
||||
|
||||
remote.pull()
|
||||
|
||||
repo.git.submodule('update', '--init', '--recursive')
|
||||
new_commit_hash = repo.head.commit.hexsha
|
||||
|
||||
if commit_hash != new_commit_hash:
|
||||
print("CUSTOM NODE PULL: Success") # update success
|
||||
else:
|
||||
print("CUSTOM NODE PULL: Fail") # update fail
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("CUSTOM NODE PULL: Fail") # unknown git error
|
||||
|
||||
repo.close()
|
||||
|
||||
|
||||
def checkout_comfyui_hash(target_hash):
|
||||
repo = git.Repo(comfy_path)
|
||||
commit_hash = repo.head.commit.hexsha
|
||||
|
||||
if commit_hash != target_hash:
|
||||
try:
|
||||
print(f"CHECKOUT: ComfyUI [{target_hash}]")
|
||||
repo.git.checkout(target_hash)
|
||||
except git.GitCommandError as e:
|
||||
print(f"Error checking out the ComfyUI: {str(e)}")
|
||||
|
||||
|
||||
def checkout_custom_node_hash(git_custom_node_infos):
|
||||
repo_name_to_url = {}
|
||||
|
||||
for url in git_custom_node_infos.keys():
|
||||
repo_name = url.split('/')[-1]
|
||||
|
||||
if repo_name.endswith('.git'):
|
||||
repo_name = repo_name[:-4]
|
||||
|
||||
repo_name_to_url[repo_name] = url
|
||||
|
||||
for path in os.listdir(working_directory):
|
||||
if '@' in path or path.endswith("ComfyUI-Manager"):
|
||||
continue
|
||||
|
||||
fullpath = os.path.join(working_directory, path)
|
||||
|
||||
if os.path.isdir(fullpath):
|
||||
is_disabled = path.endswith(".disabled")
|
||||
|
||||
try:
|
||||
git_dir = os.path.join(fullpath, '.git')
|
||||
if not os.path.exists(git_dir):
|
||||
continue
|
||||
|
||||
need_checkout = False
|
||||
repo_name = os.path.basename(fullpath)
|
||||
|
||||
if repo_name.endswith('.disabled'):
|
||||
repo_name = repo_name[:-9]
|
||||
|
||||
if repo_name not in repo_name_to_url:
|
||||
if not is_disabled:
|
||||
# should be disabled
|
||||
print(f"DISABLE: {repo_name}")
|
||||
new_path = fullpath + ".disabled"
|
||||
os.rename(fullpath, new_path)
|
||||
need_checkout = False
|
||||
else:
|
||||
item = git_custom_node_infos[repo_name_to_url[repo_name]]
|
||||
if item['disabled'] and is_disabled:
|
||||
pass
|
||||
elif item['disabled'] and not is_disabled:
|
||||
# disable
|
||||
print(f"DISABLE: {repo_name}")
|
||||
new_path = fullpath + ".disabled"
|
||||
os.rename(fullpath, new_path)
|
||||
|
||||
elif not item['disabled'] and is_disabled:
|
||||
# enable
|
||||
print(f"ENABLE: {repo_name}")
|
||||
new_path = fullpath[:-9]
|
||||
os.rename(fullpath, new_path)
|
||||
fullpath = new_path
|
||||
need_checkout = True
|
||||
else:
|
||||
need_checkout = True
|
||||
|
||||
if need_checkout:
|
||||
repo = git.Repo(fullpath)
|
||||
commit_hash = repo.head.commit.hexsha
|
||||
|
||||
if commit_hash != item['hash']:
|
||||
print(f"CHECKOUT: {repo_name} [{item['hash']}]")
|
||||
repo.git.checkout(item['hash'])
|
||||
|
||||
except Exception:
|
||||
print(f"Failed to restore snapshots for the custom node '{path}'")
|
||||
|
||||
# clone missing
|
||||
for k, v in git_custom_node_infos.items():
|
||||
if 'ComfyUI-Manager' in k:
|
||||
continue
|
||||
|
||||
if not v['disabled']:
|
||||
repo_name = k.split('/')[-1]
|
||||
if repo_name.endswith('.git'):
|
||||
repo_name = repo_name[:-4]
|
||||
|
||||
path = os.path.join(working_directory, repo_name)
|
||||
if not os.path.exists(path):
|
||||
print(f"CLONE: {path}")
|
||||
gitclone(working_directory, k, target_hash=v['hash'])
|
||||
|
||||
|
||||
def invalidate_custom_node_file(file_custom_node_infos):
|
||||
global nodelist_path
|
||||
|
||||
enabled_set = set()
|
||||
for item in file_custom_node_infos:
|
||||
if not item['disabled']:
|
||||
enabled_set.add(item['filename'])
|
||||
|
||||
for path in os.listdir(working_directory):
|
||||
fullpath = os.path.join(working_directory, path)
|
||||
|
||||
if not os.path.isdir(fullpath) and fullpath.endswith('.py'):
|
||||
if path not in enabled_set:
|
||||
print(f"DISABLE: {path}")
|
||||
new_path = fullpath+'.disabled'
|
||||
os.rename(fullpath, new_path)
|
||||
|
||||
elif not os.path.isdir(fullpath) and fullpath.endswith('.py.disabled'):
|
||||
path = path[:-9]
|
||||
if path in enabled_set:
|
||||
print(f"ENABLE: {path}")
|
||||
new_path = fullpath[:-9]
|
||||
os.rename(fullpath, new_path)
|
||||
|
||||
# download missing: just support for 'copy' style
|
||||
py_to_url = {}
|
||||
|
||||
with open(nodelist_path, 'r', encoding="UTF-8") as json_file:
|
||||
info = json.load(json_file)
|
||||
for item in info['custom_nodes']:
|
||||
if item['install_type'] == 'copy':
|
||||
for url in item['files']:
|
||||
if url.endswith('.py'):
|
||||
py = url.split('/')[-1]
|
||||
py_to_url[py] = url
|
||||
|
||||
for item in file_custom_node_infos:
|
||||
filename = item['filename']
|
||||
if not item['disabled']:
|
||||
target_path = os.path.join(working_directory, filename)
|
||||
|
||||
if not os.path.exists(target_path) and filename in py_to_url:
|
||||
url = py_to_url[filename]
|
||||
print(f"DOWNLOAD: {filename}")
|
||||
download_url(url, working_directory)
|
||||
|
||||
|
||||
def apply_snapshot(target):
|
||||
try:
|
||||
# todo: should be if target is not in snapshots dir
|
||||
path = os.path.join(os.path.dirname(__file__), 'snapshots', f"{target}")
|
||||
if os.path.exists(path):
|
||||
if not target.endswith('.json') and not target.endswith('.yaml'):
|
||||
print(f"Snapshot file not found: `{path}`")
|
||||
print("APPLY SNAPSHOT: False")
|
||||
return None
|
||||
|
||||
with open(path, 'r', encoding="UTF-8") as snapshot_file:
|
||||
if target.endswith('.json'):
|
||||
info = json.load(snapshot_file)
|
||||
elif target.endswith('.yaml'):
|
||||
info = yaml.load(snapshot_file, Loader=yaml.SafeLoader)
|
||||
info = info['custom_nodes']
|
||||
else:
|
||||
# impossible case
|
||||
print("APPLY SNAPSHOT: False")
|
||||
return None
|
||||
|
||||
comfyui_hash = info['comfyui']
|
||||
git_custom_node_infos = info['git_custom_nodes']
|
||||
file_custom_node_infos = info['file_custom_nodes']
|
||||
|
||||
checkout_comfyui_hash(comfyui_hash)
|
||||
checkout_custom_node_hash(git_custom_node_infos)
|
||||
invalidate_custom_node_file(file_custom_node_infos)
|
||||
|
||||
print("APPLY SNAPSHOT: True")
|
||||
if 'pips' in info:
|
||||
return info['pips']
|
||||
else:
|
||||
return None
|
||||
|
||||
print(f"Snapshot file not found: `{path}`")
|
||||
print("APPLY SNAPSHOT: False")
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_exc()
|
||||
print("APPLY SNAPSHOT: False")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def restore_pip_snapshot(pips, options):
|
||||
non_url = []
|
||||
local_url = []
|
||||
non_local_url = []
|
||||
for k, v in pips.items():
|
||||
if v == "":
|
||||
non_url.append(k)
|
||||
else:
|
||||
if v.startswith('file:'):
|
||||
local_url.append(v)
|
||||
else:
|
||||
non_local_url.append(v)
|
||||
|
||||
failed = []
|
||||
if '--pip-non-url' in options:
|
||||
# try all at once
|
||||
res = 1
|
||||
try:
|
||||
res = subprocess.check_call([sys.executable, '-m', 'pip', 'install'] + non_url)
|
||||
except:
|
||||
pass
|
||||
|
||||
# fallback
|
||||
if res != 0:
|
||||
for x in non_url:
|
||||
res = 1
|
||||
try:
|
||||
res = subprocess.check_call([sys.executable, '-m', 'pip', 'install', x])
|
||||
except:
|
||||
pass
|
||||
|
||||
if res != 0:
|
||||
failed.append(x)
|
||||
|
||||
if '--pip-non-local-url' in options:
|
||||
for x in non_local_url:
|
||||
res = 1
|
||||
try:
|
||||
res = subprocess.check_call([sys.executable, '-m', 'pip', 'install', x])
|
||||
except:
|
||||
pass
|
||||
|
||||
if res != 0:
|
||||
failed.append(x)
|
||||
|
||||
if '--pip-local-url' in options:
|
||||
for x in local_url:
|
||||
res = 1
|
||||
try:
|
||||
res = subprocess.check_call([sys.executable, '-m', 'pip', 'install', x])
|
||||
except:
|
||||
pass
|
||||
|
||||
if res != 0:
|
||||
failed.append(x)
|
||||
|
||||
print(f"Installation failed for pip packages: {failed}")
|
||||
|
||||
|
||||
def setup_environment():
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
if 'default' in config and 'git_exe' in config['default'] and config['default']['git_exe'] != '':
|
||||
git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=config['default']['git_exe'])
|
||||
|
||||
|
||||
setup_environment()
|
||||
|
||||
|
||||
try:
|
||||
if sys.argv[1] == "--clone":
|
||||
repo_path = None
|
||||
if len(sys.argv) > 4:
|
||||
repo_path = sys.argv[4]
|
||||
|
||||
gitclone(sys.argv[2], sys.argv[3], repo_path=repo_path)
|
||||
elif sys.argv[1] == "--check":
|
||||
gitcheck(sys.argv[2], False)
|
||||
elif sys.argv[1] == "--fetch":
|
||||
gitcheck(sys.argv[2], True)
|
||||
elif sys.argv[1] == "--pull":
|
||||
gitpull(sys.argv[2])
|
||||
elif sys.argv[1] == "--apply-snapshot":
|
||||
options = set()
|
||||
for x in sys.argv:
|
||||
if x in ['--pip-non-url', '--pip-local-url', '--pip-non-local-url']:
|
||||
options.add(x)
|
||||
|
||||
pips = apply_snapshot(sys.argv[2])
|
||||
|
||||
if pips and len(options) > 0:
|
||||
restore_pip_snapshot(pips, options)
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
@ -1,112 +0,0 @@
|
||||
import traceback
|
||||
|
||||
#
|
||||
# Global Var
|
||||
#
|
||||
# Usage:
|
||||
# import cm_global
|
||||
# cm_global.variables['comfyui.revision'] = 1832
|
||||
# print(f"log mode: {cm_global.variables['logger.enabled']}")
|
||||
#
|
||||
variables = {}
|
||||
|
||||
|
||||
#
|
||||
# Global API
|
||||
#
|
||||
# Usage:
|
||||
# [register API]
|
||||
# import cm_global
|
||||
#
|
||||
# def api_hello(msg):
|
||||
# print(f"hello: {msg}")
|
||||
# return msg
|
||||
#
|
||||
# cm_global.register_api('hello', api_hello)
|
||||
#
|
||||
# [use API]
|
||||
# import cm_global
|
||||
#
|
||||
# test = cm_global.try_call(api='hello', msg='an example')
|
||||
# print(f"'{test}' is returned")
|
||||
#
|
||||
|
||||
APIs = {}
|
||||
|
||||
|
||||
def register_api(k, f):
|
||||
global APIs
|
||||
APIs[k] = f
|
||||
|
||||
|
||||
def try_call(**kwargs):
|
||||
if 'api' in kwargs:
|
||||
api_name = kwargs['api']
|
||||
try:
|
||||
api = APIs.get(api_name)
|
||||
if api is not None:
|
||||
del kwargs['api']
|
||||
return api(**kwargs)
|
||||
else:
|
||||
print(f"WARN: The '{kwargs['api']}' API has not been registered.")
|
||||
except Exception as e:
|
||||
print(f"ERROR: An exception occurred while calling the '{api_name}' API.")
|
||||
raise e
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
#
|
||||
# Extension Info
|
||||
#
|
||||
# Usage:
|
||||
# import cm_global
|
||||
#
|
||||
# cm_global.extension_infos['my_extension'] = {'version': [0, 1], 'name': 'me', 'description': 'example extension', }
|
||||
#
|
||||
extension_infos = {}
|
||||
|
||||
on_extension_registered_handlers = {}
|
||||
|
||||
|
||||
def register_extension(extension_name, v):
|
||||
global extension_infos
|
||||
global on_extension_registered_handlers
|
||||
extension_infos[extension_name] = v
|
||||
|
||||
if extension_name in on_extension_registered_handlers:
|
||||
for k, f in on_extension_registered_handlers[extension_name]:
|
||||
try:
|
||||
f(extension_name, v)
|
||||
except Exception:
|
||||
print(f"[ERROR] '{k}' on_extension_registered_handlers")
|
||||
traceback.print_exc()
|
||||
|
||||
del on_extension_registered_handlers[extension_name]
|
||||
|
||||
|
||||
def add_on_extension_registered(k, extension_name, f):
|
||||
global on_extension_registered_handlers
|
||||
if extension_name in extension_infos:
|
||||
try:
|
||||
v = extension_infos[extension_name]
|
||||
f(extension_name, v)
|
||||
except Exception:
|
||||
print(f"[ERROR] '{k}' on_extension_registered_handler")
|
||||
traceback.print_exc()
|
||||
else:
|
||||
if extension_name not in on_extension_registered_handlers:
|
||||
on_extension_registered_handlers[extension_name] = []
|
||||
|
||||
on_extension_registered_handlers[extension_name].append((k, f))
|
||||
|
||||
|
||||
def add_on_revision_detected(k, f):
|
||||
if 'comfyui.revision' in variables:
|
||||
try:
|
||||
f(variables['comfyui.revision'])
|
||||
except Exception:
|
||||
print(f"[ERROR] '{k}' on_revision_detected_handler")
|
||||
traceback.print_exc()
|
||||
else:
|
||||
variables['cm.on_revision_detected_handler'].append((k, f))
|
||||
@ -1,101 +0,0 @@
|
||||
from manager_util import *
|
||||
import zipfile
|
||||
import requests
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
base_url = "https://api.comfy.org"
|
||||
|
||||
|
||||
async def get_cnr_data(page=1, limit=1000, cache_mode=True):
|
||||
try:
|
||||
uri = f'{base_url}/nodes?page={page}&limit={limit}'
|
||||
json_obj = await get_data_with_cache(uri, cache_mode=cache_mode)
|
||||
|
||||
for v in json_obj['nodes']:
|
||||
if 'latest_version' not in v:
|
||||
v['latest_version'] = dict(version='nightly')
|
||||
|
||||
return json_obj['nodes']
|
||||
except:
|
||||
res = {}
|
||||
print(f"Cannot connect to comfyregistry.")
|
||||
|
||||
return res
|
||||
|
||||
|
||||
@dataclass
|
||||
class NodeVersion:
|
||||
changelog: str
|
||||
dependencies: List[str]
|
||||
deprecated: bool
|
||||
id: str
|
||||
version: str
|
||||
download_url: str
|
||||
|
||||
|
||||
def map_node_version(api_node_version):
|
||||
"""
|
||||
Maps node version data from API response to NodeVersion dataclass.
|
||||
|
||||
Args:
|
||||
api_data (dict): The 'node_version' part of the API response.
|
||||
|
||||
Returns:
|
||||
NodeVersion: An instance of NodeVersion dataclass populated with data from the API.
|
||||
"""
|
||||
return NodeVersion(
|
||||
changelog=api_node_version.get(
|
||||
"changelog", ""
|
||||
), # Provide a default value if 'changelog' is missing
|
||||
dependencies=api_node_version.get(
|
||||
"dependencies", []
|
||||
), # Provide a default empty list if 'dependencies' is missing
|
||||
deprecated=api_node_version.get(
|
||||
"deprecated", False
|
||||
), # Assume False if 'deprecated' is not specified
|
||||
id=api_node_version[
|
||||
"id"
|
||||
], # 'id' should be mandatory; raise KeyError if missing
|
||||
version=api_node_version[
|
||||
"version"
|
||||
], # 'version' should be mandatory; raise KeyError if missing
|
||||
download_url=api_node_version.get(
|
||||
"downloadUrl", ""
|
||||
), # Provide a default value if 'downloadUrl' is missing
|
||||
)
|
||||
|
||||
|
||||
def install_node(node_id, version=None):
|
||||
"""
|
||||
Retrieves the node version for installation.
|
||||
|
||||
Args:
|
||||
node_id (str): The unique identifier of the node.
|
||||
version (str, optional): Specific version of the node to retrieve. If omitted, the latest version is returned.
|
||||
|
||||
Returns:
|
||||
NodeVersion: Node version data or error message.
|
||||
"""
|
||||
if version is None:
|
||||
url = f"{base_url}/nodes/{node_id}/install"
|
||||
else:
|
||||
url = f"{base_url}/nodes/{node_id}/install?version={version}"
|
||||
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
# Convert the API response to a NodeVersion object
|
||||
return map_node_version(response.json())
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def all_versions_of_node(node_id):
|
||||
url = f"https://api.comfy.org/nodes/{node_id}/versions"
|
||||
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
return None
|
||||
|
||||
2955
glob/manager_core.py
2955
glob/manager_core.py
File diff suppressed because it is too large
Load Diff
@ -1,70 +0,0 @@
|
||||
import os
|
||||
from urllib.parse import urlparse
|
||||
|
||||
aria2 = os.getenv('COMFYUI_MANAGER_ARIA2_SERVER')
|
||||
HF_ENDPOINT = os.getenv('HF_ENDPOINT')
|
||||
|
||||
if aria2 is not None:
|
||||
secret = os.getenv('COMFYUI_MANAGER_ARIA2_SECRET')
|
||||
url = urlparse(aria2)
|
||||
port = url.port
|
||||
host = url.scheme + '://' + url.hostname
|
||||
import aria2p
|
||||
|
||||
aria2 = aria2p.API(aria2p.Client(host=host, port=port, secret=secret))
|
||||
|
||||
|
||||
def download_url(model_url: str, model_dir: str, filename: str):
|
||||
if aria2:
|
||||
return aria2_download_url(model_url, model_dir, filename)
|
||||
else:
|
||||
from torchvision.datasets.utils import download_url as torchvision_download_url
|
||||
|
||||
return torchvision_download_url(model_url, model_dir, filename)
|
||||
|
||||
|
||||
def aria2_find_task(dir: str, filename: str):
|
||||
target = os.path.join(dir, filename)
|
||||
|
||||
downloads = aria2.get_downloads()
|
||||
|
||||
for download in downloads:
|
||||
for file in download.files:
|
||||
if file.is_metadata:
|
||||
continue
|
||||
if str(file.path) == target:
|
||||
return download
|
||||
|
||||
|
||||
def aria2_download_url(model_url: str, model_dir: str, filename: str):
|
||||
import manager_core as core
|
||||
import tqdm
|
||||
import time
|
||||
|
||||
if model_dir.startswith(core.comfy_path):
|
||||
model_dir = model_dir[len(core.comfy_path) :]
|
||||
|
||||
if HF_ENDPOINT:
|
||||
model_url = model_url.replace('https://huggingface.co', HF_ENDPOINT)
|
||||
|
||||
download_dir = model_dir if model_dir.startswith('/') else os.path.join('/models', model_dir)
|
||||
|
||||
download = aria2_find_task(download_dir, filename)
|
||||
if download is None:
|
||||
options = {'dir': download_dir, 'out': filename}
|
||||
download = aria2.add(model_url, options)[0]
|
||||
|
||||
if download.is_active:
|
||||
with tqdm.tqdm(
|
||||
total=download.total_length,
|
||||
bar_format='{l_bar}{bar}{r_bar}',
|
||||
desc=filename,
|
||||
unit='B',
|
||||
unit_scale=True,
|
||||
) as progress_bar:
|
||||
while download.is_active:
|
||||
if progress_bar.total == 0 and download.total_length != 0:
|
||||
progress_bar.reset(download.total_length)
|
||||
progress_bar.update(download.completed_length - progress_bar.n)
|
||||
time.sleep(1)
|
||||
download.update()
|
||||
@ -1,92 +0,0 @@
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
|
||||
def security_check():
|
||||
print("[START] Security scan")
|
||||
|
||||
custom_nodes_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
comfyui_path = os.path.abspath(os.path.join(custom_nodes_path, '..'))
|
||||
|
||||
guide = {
|
||||
"ComfyUI_LLMVISION": """
|
||||
0.Remove ComfyUI\\custom_nodes\\ComfyUI_LLMVISION.
|
||||
1.Remove pip packages: openai-1.16.3.dist-info, anthropic-0.21.4.dist-info, openai-1.30.2.dist-info, anthropic-0.21.5.dist-info, anthropic-0.26.1.dist-info, %LocalAppData%\\rundll64.exe
|
||||
(For portable versions, it is recommended to reinstall. If you are using a venv, it is advised to recreate the venv.)
|
||||
2.Remove these files in your system: lib/browser/admin.py, Cadmino.py, Fadmino.py, VISION-D.exe, BeamNG.UI.exe
|
||||
3.Check your Windows registry for the key listed above and remove it.
|
||||
(HKEY_CURRENT_USER\\Software\\OpenAICLI)
|
||||
4.Run a malware scanner.
|
||||
5.Change all of your passwords, everywhere.
|
||||
|
||||
(Reinstall OS is recommended.)
|
||||
\n
|
||||
Detailed information: https://old.reddit.com/r/comfyui/comments/1dbls5n/psa_if_youve_used_the_comfyui_llmvision_node_from/
|
||||
""",
|
||||
"lolMiner": """
|
||||
1. Remove pip packages: lolMiner*
|
||||
2. Remove files: lolMiner*, 4G_Ethash_Linux_Readme.txt, mine* in ComfyUI dir.
|
||||
|
||||
(Reinstall ComfyUI is recommended.)
|
||||
"""
|
||||
}
|
||||
|
||||
node_blacklist = {"ComfyUI_LLMVISION": "ComfyUI_LLMVISION"}
|
||||
|
||||
pip_blacklist = {"AppleBotzz": "ComfyUI_LLMVISION"}
|
||||
|
||||
file_blacklist = {
|
||||
"ComfyUI_LLMVISION": ["%LocalAppData%\\rundll64.exe"],
|
||||
"lolMiner": [os.path.join(comfyui_path, 'lolMiner')]
|
||||
}
|
||||
|
||||
installed_pips = subprocess.check_output([sys.executable, '-m', "pip", "freeze"], text=True)
|
||||
|
||||
detected = set()
|
||||
try:
|
||||
anthropic_info = subprocess.check_output([sys.executable, '-m', "pip", "show", "anthropic"], text=True, stderr=subprocess.DEVNULL)
|
||||
anthropic_reqs = [x for x in anthropic_info.split('\n') if x.startswith("Requires")][0].split(': ')[1]
|
||||
if "pycrypto" in anthropic_reqs:
|
||||
location = [x for x in anthropic_info.split('\n') if x.startswith("Location")][0].split(': ')[1]
|
||||
for fi in os.listdir(location):
|
||||
if fi.startswith("anthropic"):
|
||||
guide["ComfyUI_LLMVISION"] = f"\n0.Remove {os.path.join(location, fi)}" + guide["ComfyUI_LLMVISION"]
|
||||
detected.add("ComfyUI_LLMVISION")
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
for k, v in node_blacklist.items():
|
||||
if os.path.exists(os.path.join(custom_nodes_path, k)):
|
||||
print(f"[SECURITY ALERT] custom node '{k}' is dangerous.")
|
||||
detected.add(v)
|
||||
|
||||
for k, v in pip_blacklist.items():
|
||||
if k in installed_pips:
|
||||
detected.add(v)
|
||||
break
|
||||
|
||||
for k, v in file_blacklist.items():
|
||||
for x in v:
|
||||
if os.path.exists(os.path.expandvars(x)):
|
||||
detected.add(k)
|
||||
break
|
||||
|
||||
if len(detected) > 0:
|
||||
for line in installed_pips.split('\n'):
|
||||
for k, v in pip_blacklist.items():
|
||||
if k in line:
|
||||
print(f"[SECURITY ALERT] '{line}' is dangerous.")
|
||||
|
||||
print("\n########################################################################")
|
||||
print(" Malware has been detected, forcibly terminating ComfyUI execution.")
|
||||
print("########################################################################\n")
|
||||
|
||||
for x in detected:
|
||||
print(f"\n======== TARGET: {x} =========")
|
||||
print(f"\nTODO:")
|
||||
print(guide.get(x))
|
||||
|
||||
exit(-1)
|
||||
|
||||
print("[DONE] Security scan")
|
||||
@ -101,24 +101,6 @@ docStyle.innerHTML = `
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cm-channel-badge {
|
||||
color: white;
|
||||
background-color: #AA0000;
|
||||
width: 220px;
|
||||
height: 23px;
|
||||
font-size: 13px;
|
||||
border-radius: 5px;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
float: left;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#custom-nodes-grid a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
@ -242,7 +224,6 @@ var update_comfyui_button = null;
|
||||
var switch_comfyui_button = null;
|
||||
var fetch_updates_button = null;
|
||||
var update_all_button = null;
|
||||
var badge_mode = "none";
|
||||
let share_option = 'all';
|
||||
|
||||
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||
@ -409,10 +390,10 @@ const style = `
|
||||
.pysssss-workflow-arrow-2:after {
|
||||
content: "▼";
|
||||
}
|
||||
.pysssss-workflow-arrow-2:hover {
|
||||
.pysssss-workflow-arrow-2:hover {
|
||||
filter: brightness(1.6);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
}
|
||||
}
|
||||
.pysssss-workflow-popup-2 ~ .litecontextmenu {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
@ -425,13 +406,6 @@ const style = `
|
||||
`;
|
||||
|
||||
|
||||
|
||||
async function init_badge_mode() {
|
||||
api.fetchApi('/manager/badge_mode')
|
||||
.then(response => response.text())
|
||||
.then(data => { badge_mode = data; })
|
||||
}
|
||||
|
||||
async function init_share_option() {
|
||||
api.fetchApi('/manager/share_option')
|
||||
.then(response => response.text())
|
||||
@ -448,7 +422,6 @@ async function init_notice(notice) {
|
||||
})
|
||||
}
|
||||
|
||||
await init_badge_mode();
|
||||
await init_share_option();
|
||||
|
||||
async function fetchNicknames() {
|
||||
@ -1511,7 +1484,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.ManagerMenu",
|
||||
name: "Comfy.ManagerExtMenu",
|
||||
init() {
|
||||
$el("style", {
|
||||
textContent: style,
|
||||
@ -1538,30 +1511,30 @@ app.registerExtension({
|
||||
// new style Manager buttons
|
||||
// unload models button into new style Manager button
|
||||
let cmGroup = new (await import("../../scripts/ui/components/buttonGroup.js")).ComfyButtonGroup(
|
||||
new(await import("../../scripts/ui/components/button.js")).ComfyButton({
|
||||
icon: "puzzle",
|
||||
action: () => {
|
||||
if(!manager_instance)
|
||||
setManagerInstance(new ManagerMenuDialog());
|
||||
manager_instance.show();
|
||||
},
|
||||
tooltip: "ComfyUI Manager",
|
||||
content: "Manager",
|
||||
classList: "comfyui-button comfyui-menu-mobile-collapse primary"
|
||||
}).element,
|
||||
new(await import("../../scripts/ui/components/button.js")).ComfyButton({
|
||||
icon: "star",
|
||||
action: () => {
|
||||
if(!manager_instance)
|
||||
setManagerInstance(new ManagerMenuDialog());
|
||||
new(await import("../../scripts/ui/components/button.js")).ComfyButton({
|
||||
icon: "star",
|
||||
action: () => {
|
||||
if(!manager_instance)
|
||||
setManagerInstance(new ManagerMenuDialog());
|
||||
|
||||
if(!CustomNodesManager.instance) {
|
||||
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
||||
}
|
||||
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.FAVORITES);
|
||||
},
|
||||
tooltip: "Show favorite custom node list"
|
||||
}).element,
|
||||
},
|
||||
tooltip: "Show favorite custom node list",
|
||||
content: "Manager",
|
||||
classList: "comfyui-button comfyui-menu-mobile-collapse primary"
|
||||
}).element,
|
||||
new(await import("../../scripts/ui/components/button.js")).ComfyButton({
|
||||
icon: "puzzle",
|
||||
action: () => {
|
||||
if(!manager_instance)
|
||||
setManagerInstance(new ManagerMenuDialog());
|
||||
manager_instance.show();
|
||||
},
|
||||
tooltip: "ComfyUI Manager",
|
||||
}).element,
|
||||
new(await import("../../scripts/ui/components/button.js")).ComfyButton({
|
||||
icon: "vacuum-outline",
|
||||
action: () => {
|
||||
|
||||
72
modules/manager_ext_core.py
Normal file
72
modules/manager_ext_core.py
Normal file
@ -0,0 +1,72 @@
|
||||
import os
|
||||
import sys
|
||||
import configparser
|
||||
import manager_core as core
|
||||
import cm_global
|
||||
from manager_util import *
|
||||
|
||||
|
||||
version_code = [3, 0]
|
||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
DEFAULT_CHANNEL = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main"
|
||||
|
||||
config_path = os.path.join(comfyui_manager_path, "config.ini")
|
||||
cached_config = None
|
||||
|
||||
def write_config():
|
||||
config = configparser.ConfigParser()
|
||||
config['default'] = {
|
||||
'share_option': get_config()['share_option'],
|
||||
"file_logging": get_config()['file_logging'],
|
||||
'default_ui': get_config()['default_ui'],
|
||||
'component_policy': get_config()['component_policy'],
|
||||
'double_click_policy': get_config()['double_click_policy'],
|
||||
'model_download_by_agent': get_config()['model_download_by_agent'],
|
||||
'security_level': get_config()['security_level'],
|
||||
}
|
||||
with open(config_path, 'w') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
|
||||
def read_config():
|
||||
try:
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
# policy migration: disable_unsecure_features -> security_level
|
||||
security_level = default_conf['security_level'] if 'security_level' in default_conf else 'normal'
|
||||
|
||||
return {
|
||||
'share_option': default_conf['share_option'] if 'share_option' in default_conf else 'all',
|
||||
'default_ui': default_conf['default_ui'] if 'default_ui' in default_conf else 'none',
|
||||
'component_policy': default_conf['component_policy'] if 'component_policy' in default_conf else 'workflow',
|
||||
'double_click_policy': default_conf['double_click_policy'] if 'double_click_policy' in default_conf else 'copy-all',
|
||||
'model_download_by_agent': default_conf['model_download_by_agent'].lower() == 'true' if 'model_download_by_agent' in default_conf else False,
|
||||
'security_level': security_level
|
||||
}
|
||||
|
||||
except Exception:
|
||||
return {
|
||||
'share_option': 'all',
|
||||
'default_ui': 'none',
|
||||
'component_policy': 'workflow',
|
||||
'double_click_policy': 'copy-all',
|
||||
'model_download_by_agent': False,
|
||||
'security_level': 'normal',
|
||||
}
|
||||
|
||||
|
||||
def get_config():
|
||||
global cached_config
|
||||
|
||||
if cached_config is None:
|
||||
cached_config = read_config()
|
||||
|
||||
return cached_config
|
||||
|
||||
|
||||
def pip_install(packages):
|
||||
install_cmd = ['#FORCE', sys.executable, "-m", "pip", "install", '-U'] + packages
|
||||
core.try_install_script('pip install via manager', '..', install_cmd)
|
||||
@ -2,21 +2,20 @@ import traceback
|
||||
|
||||
import folder_paths
|
||||
import locale
|
||||
import subprocess # don't remove this
|
||||
import concurrent
|
||||
import nodes
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import re
|
||||
import shutil
|
||||
import git
|
||||
|
||||
from server import PromptServer
|
||||
import manager_core as core
|
||||
import manager_util
|
||||
import cm_global
|
||||
|
||||
from . import manager_ext_core as ext_core
|
||||
from . import manager_ext_util
|
||||
|
||||
print(f"### Loading: ComfyUI-Manager ({core.version_str})")
|
||||
|
||||
comfy_ui_hash = "-"
|
||||
@ -78,49 +77,20 @@ async def get_risky_level(files):
|
||||
return "middle"
|
||||
|
||||
|
||||
class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
||||
def get_current_preview_method(self):
|
||||
if args.preview_method == latent_preview.LatentPreviewMethod.Auto:
|
||||
return "auto"
|
||||
elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB:
|
||||
return "latent2rgb"
|
||||
elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD:
|
||||
return "taesd"
|
||||
else:
|
||||
return "none"
|
||||
def get_current_preview_method(self):
|
||||
if args.preview_method == latent_preview.LatentPreviewMethod.Auto:
|
||||
return "auto"
|
||||
elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB:
|
||||
return "latent2rgb"
|
||||
elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD:
|
||||
return "taesd"
|
||||
else:
|
||||
return "none"
|
||||
|
||||
def run_script(self, cmd, cwd='.'):
|
||||
if len(cmd) > 0 and cmd[0].startswith("#"):
|
||||
print(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")
|
||||
return 0
|
||||
|
||||
process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)
|
||||
|
||||
stdout_thread = threading.Thread(target=handle_stream, args=(process.stdout, ""))
|
||||
stderr_thread = threading.Thread(target=handle_stream, args=(process.stderr, "[!]"))
|
||||
|
||||
stdout_thread.start()
|
||||
stderr_thread.start()
|
||||
|
||||
stdout_thread.join()
|
||||
stderr_thread.join()
|
||||
|
||||
return process.wait()
|
||||
|
||||
|
||||
core.manager_funcs = ManagerFuncsInComfyUI()
|
||||
|
||||
sys.path.append('../..')
|
||||
|
||||
from manager_downloader import download_url
|
||||
|
||||
core.comfy_path = os.path.dirname(folder_paths.__file__)
|
||||
core.js_path = os.path.join(core.comfy_path, "web", "extensions")
|
||||
|
||||
local_db_model = os.path.join(core.comfyui_manager_path, "model-list.json")
|
||||
local_db_alter = os.path.join(core.comfyui_manager_path, "alter-list.json")
|
||||
local_db_custom_node_list = os.path.join(core.comfyui_manager_path, "custom-node-list.json")
|
||||
local_db_extension_node_mappings = os.path.join(core.comfyui_manager_path, "extension-node-map.json")
|
||||
components_path = os.path.join(core.comfyui_manager_path, 'components')
|
||||
|
||||
|
||||
@ -140,10 +110,6 @@ def set_preview_method(method):
|
||||
set_preview_method(core.get_config()['preview_method'])
|
||||
|
||||
|
||||
def set_badge_mode(mode):
|
||||
core.get_config()['badge_mode'] = mode
|
||||
|
||||
|
||||
def set_default_ui_mode(mode):
|
||||
core.get_config()['default_ui'] = mode
|
||||
|
||||
@ -156,67 +122,6 @@ def set_double_click_policy(mode):
|
||||
core.get_config()['double_click_policy'] = mode
|
||||
|
||||
|
||||
def print_comfyui_version():
|
||||
global comfy_ui_hash
|
||||
global comfyui_tag
|
||||
|
||||
is_detached = False
|
||||
try:
|
||||
repo = git.Repo(os.path.dirname(folder_paths.__file__))
|
||||
core.comfy_ui_revision = len(list(repo.iter_commits('HEAD')))
|
||||
|
||||
comfy_ui_hash = repo.head.commit.hexsha
|
||||
cm_global.variables['comfyui.revision'] = core.comfy_ui_revision
|
||||
|
||||
core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime
|
||||
cm_global.variables['comfyui.commit_datetime'] = core.comfy_ui_commit_datetime
|
||||
|
||||
is_detached = repo.head.is_detached
|
||||
current_branch = repo.active_branch.name
|
||||
|
||||
if current_branch == "master":
|
||||
comfyui_tag = repo.git.describe('--tags', repo.heads.main.commit.hexsha)
|
||||
if not comfyui_tag.startswith("v"):
|
||||
comfyui_tag = None
|
||||
|
||||
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")
|
||||
except:
|
||||
pass
|
||||
|
||||
# process on_revision_detected -->
|
||||
if 'cm.on_revision_detected_handler' in cm_global.variables:
|
||||
for k, f in cm_global.variables['cm.on_revision_detected_handler']:
|
||||
try:
|
||||
f(core.comfy_ui_revision)
|
||||
except Exception:
|
||||
print(f"[ERROR] '{k}' on_revision_detected_handler")
|
||||
traceback.print_exc()
|
||||
|
||||
del cm_global.variables['cm.on_revision_detected_handler']
|
||||
else:
|
||||
print(f"[ComfyUI-Manager] Some features are restricted due to your ComfyUI being outdated.")
|
||||
# <--
|
||||
|
||||
if current_branch == "master":
|
||||
if comfyui_tag:
|
||||
print(f"### ComfyUI Version: {comfyui_tag} | Released on '{core.comfy_ui_commit_datetime.date()}'")
|
||||
else:
|
||||
print(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] | 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:
|
||||
if is_detached:
|
||||
print(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] *DETACHED | Released on '{core.comfy_ui_commit_datetime.date()}'")
|
||||
else:
|
||||
print("### ComfyUI Revision: UNKNOWN (The currently installed ComfyUI is not a Git repository)")
|
||||
|
||||
|
||||
print_comfyui_version()
|
||||
core.check_invalid_nodes()
|
||||
|
||||
|
||||
def setup_environment():
|
||||
git_exe = core.get_config()['git_exe']
|
||||
|
||||
@ -488,13 +393,13 @@ def convert_markdown_to_html(input_text):
|
||||
|
||||
def populate_markdown(x):
|
||||
if 'description' in x:
|
||||
x['description'] = convert_markdown_to_html(manager_util.sanitize_tag(x['description']))
|
||||
x['description'] = convert_markdown_to_html(manager_ext_util.sanitize_tag(x['description']))
|
||||
|
||||
if 'name' in x:
|
||||
x['name'] = manager_util.sanitize_tag(x['name'])
|
||||
x['name'] = manager_ext_util.sanitize_tag(x['name'])
|
||||
|
||||
if 'title' in x:
|
||||
x['title'] = manager_util.sanitize_tag(x['title'])
|
||||
x['title'] = manager_ext_util.sanitize_tag(x['title'])
|
||||
|
||||
|
||||
@routes.get("/customnode/getlist")
|
||||
@ -613,32 +518,6 @@ async def remove_snapshot(request):
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/snapshot/restore")
|
||||
async def remove_snapshot(request):
|
||||
if not is_allowed_security_level('middle'):
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
try:
|
||||
target = request.rel_url.query["target"]
|
||||
|
||||
path = os.path.join(core.comfyui_manager_path, 'snapshots', f"{target}.json")
|
||||
if os.path.exists(path):
|
||||
if not os.path.exists(core.startup_script_path):
|
||||
os.makedirs(core.startup_script_path)
|
||||
|
||||
target_path = os.path.join(core.startup_script_path, "restore-snapshot.json")
|
||||
shutil.copy(path, target_path)
|
||||
|
||||
print(f"Snapshot restore scheduled: `{target}`")
|
||||
return web.Response(status=200)
|
||||
|
||||
print(f"Snapshot file not found: `{path}`")
|
||||
return web.Response(status=400)
|
||||
except:
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/snapshot/get_current")
|
||||
async def get_current_snapshot_api(request):
|
||||
try:
|
||||
@ -647,15 +526,6 @@ async def get_current_snapshot_api(request):
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/snapshot/save")
|
||||
async def save_snapshot(request):
|
||||
try:
|
||||
core.save_snapshot_with_postfix('snapshot')
|
||||
return web.Response(status=200)
|
||||
except:
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
def unzip_install(files):
|
||||
temp_filename = 'manager-temp.zip'
|
||||
for url in files:
|
||||
@ -707,188 +577,6 @@ def download_url_with_agent(url, save_path):
|
||||
return True
|
||||
|
||||
|
||||
def copy_install(files, js_path_name=None):
|
||||
for url in files:
|
||||
if url.endswith("/"):
|
||||
url = url[:-1]
|
||||
try:
|
||||
filename = os.path.basename(url)
|
||||
if url.endswith(".py"):
|
||||
download_url(url, core.custom_nodes_path, filename)
|
||||
else:
|
||||
path = os.path.join(core.js_path, js_path_name) if js_path_name is not None else core.js_path
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
download_url(url, path, filename)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Install(copy) error: {url} / {e}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
print("Installation was successful.")
|
||||
return True
|
||||
|
||||
|
||||
def copy_uninstall(files, js_path_name='.'):
|
||||
for url in files:
|
||||
if url.endswith("/"):
|
||||
url = url[:-1]
|
||||
dir_name = os.path.basename(url)
|
||||
base_path = core.custom_nodes_path if url.endswith('.py') else os.path.join(core.js_path, js_path_name)
|
||||
file_path = os.path.join(base_path, dir_name)
|
||||
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
elif os.path.exists(file_path + ".disabled"):
|
||||
os.remove(file_path + ".disabled")
|
||||
except Exception as e:
|
||||
print(f"Uninstall(copy) error: {url} / {e}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
print("Uninstallation was successful.")
|
||||
return True
|
||||
|
||||
|
||||
def copy_set_active(files, is_disable, js_path_name='.'):
|
||||
if is_disable:
|
||||
action_name = "Disable"
|
||||
else:
|
||||
action_name = "Enable"
|
||||
|
||||
for url in files:
|
||||
if url.endswith("/"):
|
||||
url = url[:-1]
|
||||
dir_name = os.path.basename(url)
|
||||
base_path = core.custom_nodes_path if url.endswith('.py') else os.path.join(core.js_path, js_path_name)
|
||||
file_path = os.path.join(base_path, dir_name)
|
||||
|
||||
try:
|
||||
if is_disable:
|
||||
current_name = file_path
|
||||
new_name = file_path + ".disabled"
|
||||
else:
|
||||
current_name = file_path + ".disabled"
|
||||
new_name = file_path
|
||||
|
||||
os.rename(current_name, new_name)
|
||||
|
||||
except Exception as e:
|
||||
print(f"{action_name}(copy) error: {url} / {e}", file=sys.stderr)
|
||||
|
||||
return False
|
||||
|
||||
print(f"{action_name} was successful.")
|
||||
return True
|
||||
|
||||
|
||||
@routes.get("/customnode/versions/{node_name}")
|
||||
async def get_cnr_versions(request):
|
||||
node_name = request.match_info.get("node_name", None)
|
||||
versions = core.cnr_utils.all_versions_of_node(node_name)
|
||||
|
||||
if versions:
|
||||
return web.json_response(versions, content_type='application/json')
|
||||
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/customnode/disabled_versions/{node_name}")
|
||||
async def get_disabled_versions(request):
|
||||
node_name = request.match_info.get("node_name", None)
|
||||
versions = []
|
||||
if node_name in core.unified_manager.nightly_inactive_nodes:
|
||||
versions.append(dict(version='nightly'))
|
||||
|
||||
for v in core.unified_manager.cnr_inactive_nodes.get(node_name, {}).keys():
|
||||
versions.append(dict(version=v))
|
||||
|
||||
if versions:
|
||||
return web.json_response(versions, content_type='application/json')
|
||||
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.post("/customnode/reinstall")
|
||||
async def reinstall_custom_node(request):
|
||||
await uninstall_custom_node(request)
|
||||
await install_custom_node(request)
|
||||
|
||||
|
||||
@routes.post("/customnode/install")
|
||||
async def install_custom_node(request):
|
||||
if not is_allowed_security_level('middle'):
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
|
||||
# non-nightly cnr is safe
|
||||
risky_level = None
|
||||
cnr_id = json_data.get('id')
|
||||
skip_post_install = json_data.get('skip_post_install')
|
||||
|
||||
if json_data['version'] != 'unknown':
|
||||
selected_version = json_data.get('selected_version', 'latest')
|
||||
if selected_version != 'nightly':
|
||||
risky_level = 'low'
|
||||
node_spec_str = f"{cnr_id}@{selected_version}"
|
||||
else:
|
||||
node_spec_str = f"{cnr_id}@nightly"
|
||||
else:
|
||||
# unknown
|
||||
unknown_name = os.path.basename(json_data['files'][0])
|
||||
node_spec_str = f"{unknown_name}@unknown"
|
||||
|
||||
# apply security policy if not cnr node (nightly isn't regarded as cnr node)
|
||||
if risky_level is None:
|
||||
risky_level = await get_risky_level(json_data['files'])
|
||||
|
||||
if not is_allowed_security_level(risky_level):
|
||||
print(SECURITY_MESSAGE_GENERAL)
|
||||
return web.Response(status=404)
|
||||
|
||||
node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
|
||||
|
||||
if node_spec is None:
|
||||
return
|
||||
|
||||
node_name, version_spec, is_specified = node_spec
|
||||
res = await core.unified_manager.install_by_id(node_name, version_spec, json_data['channel'], json_data['mode'], return_postinstall=skip_post_install)
|
||||
# discard post install if skip_post_install mode
|
||||
|
||||
if res not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:
|
||||
return web.Response(status=400)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@routes.post("/customnode/fix")
|
||||
async def fix_custom_node(request):
|
||||
if not is_allowed_security_level('middle'):
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
|
||||
node_id = json_data.get('id')
|
||||
node_ver = json_data['version']
|
||||
if node_ver != 'unknown':
|
||||
node_name = node_id
|
||||
else:
|
||||
# unknown
|
||||
node_name = os.path.basename(json_data['files'][0])
|
||||
|
||||
res = core.unified_manager.unified_fix(node_name, node_ver)
|
||||
|
||||
if res.result:
|
||||
print(f"After restarting ComfyUI, please refresh the browser.")
|
||||
return web.json_response({}, content_type='application/json')
|
||||
|
||||
print(f"ERROR: An error occurred while fixing '{node_name}@{node_ver}'.")
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.post("/customnode/install/git_url")
|
||||
async def install_custom_node_git_url(request):
|
||||
if not is_allowed_security_level('high'):
|
||||
@ -910,7 +598,7 @@ async def install_custom_node_git_url(request):
|
||||
|
||||
|
||||
@routes.post("/customnode/install/pip")
|
||||
async def install_custom_node_git_url(request):
|
||||
async def install_pip(request):
|
||||
if not is_allowed_security_level('high'):
|
||||
print(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||
return web.Response(status=403)
|
||||
@ -921,60 +609,6 @@ async def install_custom_node_git_url(request):
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@routes.post("/customnode/uninstall")
|
||||
async def uninstall_custom_node(request):
|
||||
if not is_allowed_security_level('middle'):
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
|
||||
node_id = json_data.get('id')
|
||||
if json_data['version'] != 'unknown':
|
||||
is_unknown = False
|
||||
node_name = node_id
|
||||
else:
|
||||
# unknown
|
||||
is_unknown = True
|
||||
node_name = os.path.basename(json_data['files'][0])
|
||||
|
||||
res = core.unified_manager.unified_uninstall(node_name, is_unknown)
|
||||
|
||||
if res.result:
|
||||
print(f"After restarting ComfyUI, please refresh the browser.")
|
||||
return web.json_response({}, content_type='application/json')
|
||||
|
||||
print(f"ERROR: An error occurred while uninstalling '{node_name}'.")
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.post("/customnode/update")
|
||||
async def update_custom_node(request):
|
||||
if not is_allowed_security_level('middle'):
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
|
||||
node_id = json_data.get('id')
|
||||
if json_data['version'] != 'unknown':
|
||||
node_name = node_id
|
||||
else:
|
||||
# unknown
|
||||
node_name = os.path.basename(json_data['files'][0])
|
||||
|
||||
res = core.unified_manager.unified_update(node_name, json_data['version'])
|
||||
|
||||
core.clear_pip_cache()
|
||||
|
||||
if res.result:
|
||||
print(f"After restarting ComfyUI, please refresh the browser.")
|
||||
return web.json_response({}, content_type='application/json')
|
||||
|
||||
print(f"ERROR: An error occurred while updating '{node_name}'.")
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/comfyui_manager/update_comfyui")
|
||||
async def update_comfyui(request):
|
||||
print(f"Update ComfyUI")
|
||||
@ -995,17 +629,6 @@ async def update_comfyui(request):
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/comfyui_manager/comfyui_versions")
|
||||
async def comfyui_versions(request):
|
||||
try:
|
||||
res, current = core.get_comfyui_versions()
|
||||
return web.json_response({'versions': res, 'current': current}, status=200, content_type='application/json')
|
||||
except Exception as e:
|
||||
print(f"ComfyUI update fail: {e}", file=sys.stderr)
|
||||
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/comfyui_manager/comfyui_switch_version")
|
||||
async def comfyui_switch_version(request):
|
||||
try:
|
||||
@ -1019,40 +642,6 @@ async def comfyui_switch_version(request):
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.post("/customnode/disable")
|
||||
async def disable_node(request):
|
||||
json_data = await request.json()
|
||||
|
||||
node_id = json_data.get('id')
|
||||
if json_data['version'] != 'unknown':
|
||||
is_unknown = False
|
||||
node_name = node_id
|
||||
else:
|
||||
# unknown
|
||||
is_unknown = True
|
||||
node_name = os.path.basename(json_data['files'][0])
|
||||
|
||||
res = core.unified_manager.unified_disable(node_name, is_unknown)
|
||||
|
||||
if res:
|
||||
return web.json_response({}, content_type='application/json')
|
||||
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.get("/manager/migrate_unmanaged_nodes")
|
||||
async def migrate_unmanaged_nodes(request):
|
||||
print(f"[ComfyUI-Manager] Migrating unmanaged nodes...")
|
||||
await core.unified_manager.migrate_unmanaged_nodes()
|
||||
print("Done.")
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@routes.get("/manager/need_to_migrate")
|
||||
async def need_to_migrate(request):
|
||||
return web.Response(text=str(core.need_to_migrate), status=200)
|
||||
|
||||
|
||||
@routes.post("/model/install")
|
||||
async def install_model(request):
|
||||
json_data = await request.json()
|
||||
@ -1146,17 +735,6 @@ async def preview_method(request):
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@routes.get("/manager/badge_mode")
|
||||
async def badge_mode(request):
|
||||
if "value" in request.rel_url.query:
|
||||
set_badge_mode(request.rel_url.query['value'])
|
||||
core.write_config()
|
||||
else:
|
||||
return web.Response(text=core.get_config()['badge_mode'], status=200)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@routes.get("/manager/default_ui")
|
||||
async def default_ui_mode(request):
|
||||
if "value" in request.rel_url.query:
|
||||
@ -1357,67 +935,14 @@ if hasattr(PromptServer.instance, "app"):
|
||||
def sanitize(data):
|
||||
return data.replace("<", "<").replace(">", ">")
|
||||
|
||||
|
||||
async def _confirm_try_install(sender, custom_node_url, msg):
|
||||
json_obj = await core.get_data_by_mode('default', 'custom-node-list.json')
|
||||
|
||||
sender = manager_util.sanitize_tag(sender)
|
||||
msg = manager_util.sanitize_tag(msg)
|
||||
target = core.lookup_customnode_by_url(json_obj, custom_node_url)
|
||||
|
||||
if target is not None:
|
||||
PromptServer.instance.send_sync("cm-api-try-install-customnode",
|
||||
{"sender": sender, "target": target, "msg": msg})
|
||||
else:
|
||||
print(f"[ComfyUI Manager API] Failed to try install - Unknown custom node url '{custom_node_url}'")
|
||||
|
||||
|
||||
def confirm_try_install(sender, custom_node_url, msg):
|
||||
asyncio.run(_confirm_try_install(sender, custom_node_url, msg))
|
||||
|
||||
|
||||
cm_global.register_api('cm.try-install-custom-node', confirm_try_install)
|
||||
|
||||
import asyncio
|
||||
|
||||
|
||||
async def default_cache_update():
|
||||
async def get_cache(filename):
|
||||
uri = f"{core.DEFAULT_CHANNEL}/{filename}"
|
||||
cache_uri = str(manager_util.simple_hash(uri)) + '_' + filename
|
||||
cache_uri = os.path.join(core.cache_dir, cache_uri)
|
||||
|
||||
json_obj = await manager_util.get_data(uri, True)
|
||||
|
||||
with core.cache_lock:
|
||||
with open(cache_uri, "w", encoding='utf-8') as file:
|
||||
json.dump(json_obj, file, indent=4, sort_keys=True)
|
||||
print(f"[ComfyUI-Manager] default cache updated: {uri}")
|
||||
|
||||
a = get_cache("custom-node-list.json")
|
||||
b = get_cache("extension-node-map.json")
|
||||
c = get_cache("model-list.json")
|
||||
d = get_cache("alter-list.json")
|
||||
e = get_cache("github-stats.json")
|
||||
|
||||
await asyncio.gather(a, b, c, d, e)
|
||||
|
||||
if not core.get_config()['skip_migration_check']:
|
||||
await core.check_need_to_migrate()
|
||||
else:
|
||||
print("[ComfyUI-Manager] Migration check is skipped...")
|
||||
|
||||
|
||||
threading.Thread(target=lambda: asyncio.run(default_cache_update())).start()
|
||||
|
||||
if not os.path.exists(core.config_path):
|
||||
core.get_config()
|
||||
core.write_config()
|
||||
|
||||
|
||||
cm_global.register_extension('ComfyUI-Manager',
|
||||
{'version': core.version,
|
||||
'name': 'ComfyUI Manager',
|
||||
'nodes': {'Terminal Log //CM'},
|
||||
'description': 'It provides the ability to manage custom nodes in ComfyUI.', })
|
||||
'name': 'ComfyUI Manager (Extension)',
|
||||
'nodes': {'Terminal Log //CM'},
|
||||
'description': 'ComfyUI-Manager (Extension)', })
|
||||
|
||||
cm_global.variables['manager-core.show_menu'] = False
|
||||
@ -1,704 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import atexit
|
||||
import threading
|
||||
import re
|
||||
import locale
|
||||
import platform
|
||||
import json
|
||||
import ast
|
||||
import logging
|
||||
|
||||
glob_path = os.path.join(os.path.dirname(__file__), "glob")
|
||||
sys.path.append(glob_path)
|
||||
|
||||
import security_check
|
||||
from manager_util import *
|
||||
import cm_global
|
||||
|
||||
security_check.security_check()
|
||||
|
||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
|
||||
|
||||
def skip_pip_spam(x):
|
||||
return ('Requirement already satisfied:' in x) or ("DEPRECATION: Loading egg at" in x)
|
||||
|
||||
|
||||
message_collapses = [skip_pip_spam]
|
||||
import_failed_extensions = set()
|
||||
cm_global.variables['cm.on_revision_detected_handler'] = []
|
||||
enable_file_logging = True
|
||||
|
||||
|
||||
def register_message_collapse(f):
|
||||
global message_collapses
|
||||
message_collapses.append(f)
|
||||
|
||||
|
||||
def is_import_failed_extension(name):
|
||||
global import_failed_extensions
|
||||
return name in import_failed_extensions
|
||||
|
||||
|
||||
def check_file_logging():
|
||||
global enable_file_logging
|
||||
try:
|
||||
import configparser
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'file_logging' in default_conf and default_conf['file_logging'].lower() == 'false':
|
||||
enable_file_logging = False
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
check_file_logging()
|
||||
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
if comfy_path is None:
|
||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||
|
||||
sys.__comfyui_manager_register_message_collapse = register_message_collapse
|
||||
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
|
||||
cm_global.register_api('cm.register_message_collapse', register_message_collapse)
|
||||
cm_global.register_api('cm.is_import_failed_extension', is_import_failed_extension)
|
||||
|
||||
|
||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||
custom_nodes_path = os.path.abspath(os.path.join(comfyui_manager_path, ".."))
|
||||
startup_script_path = os.path.join(comfyui_manager_path, "startup-scripts")
|
||||
restore_snapshot_path = os.path.join(startup_script_path, "restore-snapshot.json")
|
||||
git_script_path = os.path.join(comfyui_manager_path, "git_helper.py")
|
||||
cm_cli_path = os.path.join(comfyui_manager_path, "cm-cli.py")
|
||||
pip_overrides_path = os.path.join(comfyui_manager_path, "pip_overrides.json")
|
||||
|
||||
|
||||
cm_global.pip_overrides = {}
|
||||
if os.path.exists(pip_overrides_path):
|
||||
with open(pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||
cm_global.pip_overrides = json.load(json_file)
|
||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
||||
|
||||
|
||||
def remap_pip_package(pkg):
|
||||
if pkg in cm_global.pip_overrides:
|
||||
res = cm_global.pip_overrides[pkg]
|
||||
print(f"[ComfyUI-Manager] '{pkg}' is remapped to '{res}'")
|
||||
return res
|
||||
else:
|
||||
return pkg
|
||||
|
||||
|
||||
std_log_lock = threading.Lock()
|
||||
|
||||
|
||||
class TerminalHook:
|
||||
def __init__(self):
|
||||
self.hooks = {}
|
||||
|
||||
def add_hook(self, k, v):
|
||||
self.hooks[k] = v
|
||||
|
||||
def remove_hook(self, k):
|
||||
if k in self.hooks:
|
||||
del self.hooks[k]
|
||||
|
||||
def write_stderr(self, msg):
|
||||
for v in self.hooks.values():
|
||||
try:
|
||||
v.write_stderr(msg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def write_stdout(self, msg):
|
||||
for v in self.hooks.values():
|
||||
try:
|
||||
v.write_stdout(msg)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
terminal_hook = TerminalHook()
|
||||
sys.__comfyui_manager_terminal_hook = terminal_hook
|
||||
|
||||
|
||||
def handle_stream(stream, prefix):
|
||||
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
||||
for msg in stream:
|
||||
if prefix == '[!]' and ('it/s]' in msg or 's/it]' in msg) and ('%|' in msg or 'it [' in msg):
|
||||
if msg.startswith('100%'):
|
||||
print('\r' + msg, end="", file=sys.stderr),
|
||||
else:
|
||||
print('\r' + msg[:-1], end="", file=sys.stderr),
|
||||
else:
|
||||
if prefix == '[!]':
|
||||
print(prefix, msg, end="", file=sys.stderr)
|
||||
else:
|
||||
print(prefix, msg, end="")
|
||||
|
||||
|
||||
def process_wrap(cmd_str, cwd_path, handler=None, env=None):
|
||||
process = subprocess.Popen(cmd_str, cwd=cwd_path, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1)
|
||||
|
||||
if handler is None:
|
||||
handler = handle_stream
|
||||
|
||||
stdout_thread = threading.Thread(target=handler, args=(process.stdout, ""))
|
||||
stderr_thread = threading.Thread(target=handler, args=(process.stderr, "[!]"))
|
||||
|
||||
stdout_thread.start()
|
||||
stderr_thread.start()
|
||||
|
||||
stdout_thread.join()
|
||||
stderr_thread.join()
|
||||
|
||||
return process.wait()
|
||||
|
||||
|
||||
try:
|
||||
if '--port' in sys.argv:
|
||||
port_index = sys.argv.index('--port')
|
||||
if port_index + 1 < len(sys.argv):
|
||||
port = int(sys.argv[port_index + 1])
|
||||
postfix = f"_{port}"
|
||||
else:
|
||||
postfix = ""
|
||||
else:
|
||||
postfix = ""
|
||||
|
||||
# Logger setup
|
||||
if enable_file_logging:
|
||||
if os.path.exists(f"comfyui{postfix}.log"):
|
||||
if os.path.exists(f"comfyui{postfix}.prev.log"):
|
||||
if os.path.exists(f"comfyui{postfix}.prev2.log"):
|
||||
os.remove(f"comfyui{postfix}.prev2.log")
|
||||
os.rename(f"comfyui{postfix}.prev.log", f"comfyui{postfix}.prev2.log")
|
||||
os.rename(f"comfyui{postfix}.log", f"comfyui{postfix}.prev.log")
|
||||
|
||||
log_file = open(f"comfyui{postfix}.log", "w", encoding="utf-8", errors="ignore")
|
||||
|
||||
log_lock = threading.Lock()
|
||||
|
||||
original_stdout = sys.stdout
|
||||
original_stderr = sys.stderr
|
||||
|
||||
if original_stdout.encoding.lower() == 'utf-8':
|
||||
write_stdout = original_stdout.write
|
||||
write_stderr = original_stderr.write
|
||||
else:
|
||||
def wrapper_stdout(msg):
|
||||
original_stdout.write(msg.encode('utf-8').decode(original_stdout.encoding, errors="ignore"))
|
||||
|
||||
def wrapper_stderr(msg):
|
||||
original_stderr.write(msg.encode('utf-8').decode(original_stderr.encoding, errors="ignore"))
|
||||
|
||||
write_stdout = wrapper_stdout
|
||||
write_stderr = wrapper_stderr
|
||||
|
||||
pat_tqdm = r'\d+%.*\[(.*?)\]'
|
||||
pat_import_fail = r'seconds \(IMPORT FAILED\):(.*)$'
|
||||
|
||||
is_start_mode = True
|
||||
|
||||
|
||||
class ComfyUIManagerLogger:
|
||||
def __init__(self, is_stdout):
|
||||
self.is_stdout = is_stdout
|
||||
self.encoding = "utf-8"
|
||||
self.last_char = ''
|
||||
|
||||
def fileno(self):
|
||||
try:
|
||||
if self.is_stdout:
|
||||
return original_stdout.fileno()
|
||||
else:
|
||||
return original_stderr.fileno()
|
||||
except AttributeError:
|
||||
# Handle error
|
||||
raise ValueError("The object does not have a fileno method")
|
||||
|
||||
def isatty(self):
|
||||
return False
|
||||
|
||||
def write(self, message):
|
||||
global is_start_mode
|
||||
|
||||
if any(f(message) for f in message_collapses):
|
||||
return
|
||||
|
||||
if is_start_mode:
|
||||
match = re.search(pat_import_fail, message)
|
||||
if match:
|
||||
import_failed_extensions.add(match.group(1).strip())
|
||||
|
||||
if 'Starting server' in message:
|
||||
is_start_mode = False
|
||||
|
||||
if not self.is_stdout:
|
||||
match = re.search(pat_tqdm, message)
|
||||
if match:
|
||||
message = re.sub(r'([#|])\d', r'\1▌', message)
|
||||
message = re.sub('#', '█', message)
|
||||
if '100%' in message:
|
||||
self.sync_write(message)
|
||||
else:
|
||||
write_stderr(message)
|
||||
original_stderr.flush()
|
||||
else:
|
||||
self.sync_write(message)
|
||||
else:
|
||||
self.sync_write(message)
|
||||
|
||||
def sync_write(self, message, file_only=False):
|
||||
with log_lock:
|
||||
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')[:-3]
|
||||
if self.last_char != '\n':
|
||||
log_file.write(message)
|
||||
else:
|
||||
log_file.write(f"[{timestamp}] {message}")
|
||||
log_file.flush()
|
||||
self.last_char = message if message == '' else message[-1]
|
||||
|
||||
if not file_only:
|
||||
with std_log_lock:
|
||||
if self.is_stdout:
|
||||
write_stdout(message)
|
||||
original_stdout.flush()
|
||||
terminal_hook.write_stderr(message)
|
||||
else:
|
||||
write_stderr(message)
|
||||
original_stderr.flush()
|
||||
terminal_hook.write_stdout(message)
|
||||
|
||||
def flush(self):
|
||||
log_file.flush()
|
||||
|
||||
with std_log_lock:
|
||||
if self.is_stdout:
|
||||
original_stdout.flush()
|
||||
else:
|
||||
original_stderr.flush()
|
||||
|
||||
def close(self):
|
||||
self.flush()
|
||||
|
||||
def reconfigure(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
# You can close through sys.stderr.close_log()
|
||||
def close_log(self):
|
||||
sys.stderr = original_stderr
|
||||
sys.stdout = original_stdout
|
||||
log_file.close()
|
||||
|
||||
def close_log():
|
||||
sys.stderr = original_stderr
|
||||
sys.stdout = original_stdout
|
||||
log_file.close()
|
||||
|
||||
|
||||
if enable_file_logging:
|
||||
sys.stdout = ComfyUIManagerLogger(True)
|
||||
stderr_wrapper = ComfyUIManagerLogger(False)
|
||||
sys.stderr = stderr_wrapper
|
||||
|
||||
atexit.register(close_log)
|
||||
else:
|
||||
sys.stdout.close_log = lambda: None
|
||||
stderr_wrapper = None
|
||||
|
||||
|
||||
class LoggingHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
global is_start_mode
|
||||
|
||||
message = record.getMessage()
|
||||
|
||||
if is_start_mode:
|
||||
match = re.search(pat_import_fail, message)
|
||||
if match:
|
||||
import_failed_extensions.add(match.group(1).strip())
|
||||
|
||||
if 'Starting server' in message:
|
||||
is_start_mode = False
|
||||
|
||||
if stderr_wrapper:
|
||||
stderr_wrapper.sync_write(message+'\n', file_only=True)
|
||||
|
||||
|
||||
logging.getLogger().addHandler(LoggingHandler())
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ComfyUI-Manager] Logging failed: {e}")
|
||||
|
||||
|
||||
try:
|
||||
import git
|
||||
except ModuleNotFoundError:
|
||||
my_path = os.path.dirname(__file__)
|
||||
requirements_path = os.path.join(my_path, "requirements.txt")
|
||||
|
||||
print(f"## ComfyUI-Manager: installing dependencies. (GitPython)")
|
||||
try:
|
||||
result = subprocess.check_output([sys.executable, '-s', '-m', 'pip', 'install', '-r', requirements_path])
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"## [ERROR] ComfyUI-Manager: Attempting to reinstall dependencies using an alternative method.")
|
||||
try:
|
||||
result = subprocess.check_output([sys.executable, '-s', '-m', 'pip', 'install', '--user', '-r', requirements_path])
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"## [ERROR] ComfyUI-Manager: Failed to install the GitPython package in the correct Python environment. Please install it manually in the appropriate environment. (You can seek help at https://app.element.io/#/room/%23comfyui_space%3Amatrix.org)")
|
||||
|
||||
try:
|
||||
import git
|
||||
print(f"## ComfyUI-Manager: installing dependencies done.")
|
||||
except:
|
||||
# maybe we should sys.exit() here? there is at least two screens worth of error messages still being pumped after our error messages
|
||||
print(f"## [ERROR] ComfyUI-Manager: GitPython package seems to be installed, but failed to load somehow. Make sure you have a working git client installed")
|
||||
|
||||
|
||||
print("** ComfyUI startup time:", datetime.now())
|
||||
print("** Platform:", platform.system())
|
||||
print("** Python version:", sys.version)
|
||||
print("** Python executable:", sys.executable)
|
||||
print("** ComfyUI Path:", comfy_path)
|
||||
|
||||
if enable_file_logging:
|
||||
print("** Log path:", os.path.abspath('comfyui.log'))
|
||||
else:
|
||||
print("** Log path: file logging is disabled")
|
||||
|
||||
|
||||
def read_downgrade_blacklist():
|
||||
try:
|
||||
import configparser
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'downgrade_blacklist' in default_conf:
|
||||
items = default_conf['downgrade_blacklist'].split(',')
|
||||
items = [x.strip() for x in items if x != '']
|
||||
cm_global.pip_downgrade_blacklist += items
|
||||
cm_global.pip_downgrade_blacklist = list(set(cm_global.pip_downgrade_blacklist))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
read_downgrade_blacklist()
|
||||
|
||||
|
||||
def check_bypass_ssl():
|
||||
try:
|
||||
import configparser
|
||||
import ssl
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'bypass_ssl' in default_conf and default_conf['bypass_ssl'].lower() == 'true':
|
||||
print(f"[ComfyUI-Manager] WARN: Unsafe - SSL verification bypass option is Enabled. (see ComfyUI-Manager/config.ini)")
|
||||
ssl._create_default_https_context = ssl._create_unverified_context # SSL certificate error fix.
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
check_bypass_ssl()
|
||||
|
||||
|
||||
# Perform install
|
||||
processed_install = set()
|
||||
script_list_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "startup-scripts", "install-scripts.txt")
|
||||
pip_map = None
|
||||
|
||||
|
||||
def get_installed_packages():
|
||||
global pip_map
|
||||
|
||||
if 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 is_installed(name):
|
||||
name = name.strip()
|
||||
|
||||
if name.startswith('#'):
|
||||
return True
|
||||
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)([0-9.a-zA-Z]*)'
|
||||
match = re.search(pattern, name)
|
||||
|
||||
if match:
|
||||
name = match.group(1)
|
||||
|
||||
if name in cm_global.pip_blacklist:
|
||||
return True
|
||||
|
||||
if name in cm_global.pip_downgrade_blacklist:
|
||||
pips = get_installed_packages()
|
||||
|
||||
if match is None:
|
||||
if name in pips:
|
||||
return True
|
||||
elif match.group(2) in ['<=', '==', '<']:
|
||||
if name in pips:
|
||||
if StrictVersion(pips[name]) >= StrictVersion(match.group(3)):
|
||||
print(f"[ComfyUI-Manager] skip black listed pip installation: '{name}'")
|
||||
return True
|
||||
|
||||
pkg = get_installed_packages().get(name.lower())
|
||||
if pkg is None:
|
||||
return False # update if not installed
|
||||
|
||||
if match is None:
|
||||
return True # don't update if version is not specified
|
||||
|
||||
if match.group(2) in ['>', '>=']:
|
||||
if StrictVersion(pkg) < StrictVersion(match.group(3)):
|
||||
return False
|
||||
elif StrictVersion(pkg) > StrictVersion(match.group(3)):
|
||||
print(f"[SKIP] Downgrading pip package isn't allowed: {name.lower()} (cur={pkg})")
|
||||
|
||||
return True # prevent downgrade
|
||||
|
||||
|
||||
if os.path.exists(restore_snapshot_path):
|
||||
try:
|
||||
cloned_repos = []
|
||||
|
||||
def msg_capture(stream, prefix):
|
||||
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
||||
for msg in stream:
|
||||
if msg.startswith("CLONE: "):
|
||||
cloned_repos.append(msg[7:])
|
||||
if prefix == '[!]':
|
||||
print(prefix, msg, end="", file=sys.stderr)
|
||||
else:
|
||||
print(prefix, msg, end="")
|
||||
|
||||
elif prefix == '[!]' and ('it/s]' in msg or 's/it]' in msg) and ('%|' in msg or 'it [' in msg):
|
||||
if msg.startswith('100%'):
|
||||
print('\r' + msg, end="", file=sys.stderr),
|
||||
else:
|
||||
print('\r'+msg[:-1], end="", file=sys.stderr),
|
||||
else:
|
||||
if prefix == '[!]':
|
||||
print(prefix, msg, end="", file=sys.stderr)
|
||||
else:
|
||||
print(prefix, msg, end="")
|
||||
|
||||
print(f"[ComfyUI-Manager] Restore snapshot.")
|
||||
new_env = os.environ.copy()
|
||||
new_env["COMFYUI_PATH"] = comfy_path
|
||||
|
||||
cmd_str = [sys.executable, cm_cli_path, 'restore-snapshot', restore_snapshot_path]
|
||||
exit_code = process_wrap(cmd_str, custom_nodes_path, handler=msg_capture, env=new_env)
|
||||
|
||||
if exit_code != 0:
|
||||
print(f"[ComfyUI-Manager] Restore snapshot failed.")
|
||||
else:
|
||||
print(f"[ComfyUI-Manager] Restore snapshot done.")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(f"[ComfyUI-Manager] Restore snapshot failed.")
|
||||
|
||||
os.remove(restore_snapshot_path)
|
||||
|
||||
|
||||
def execute_lazy_install_script(repo_path, executable):
|
||||
global processed_install
|
||||
|
||||
install_script_path = os.path.join(repo_path, "install.py")
|
||||
requirements_path = os.path.join(repo_path, "requirements.txt")
|
||||
|
||||
if os.path.exists(requirements_path):
|
||||
print(f"Install: pip packages for '{repo_path}'")
|
||||
with open(requirements_path, "r") as requirements_file:
|
||||
for line in requirements_file:
|
||||
package_name = remap_pip_package(line.strip())
|
||||
if package_name and not is_installed(package_name):
|
||||
if '--index-url' in package_name:
|
||||
s = package_name.split('--index-url')
|
||||
install_cmd = [sys.executable, "-m", "pip", "install", s[0].strip(), '--index-url', s[1].strip()]
|
||||
else:
|
||||
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
|
||||
|
||||
process_wrap(install_cmd, repo_path)
|
||||
|
||||
if os.path.exists(install_script_path) and f'{repo_path}/install.py' not in processed_install:
|
||||
processed_install.add(f'{repo_path}/install.py')
|
||||
print(f"Install: install script for '{repo_path}'")
|
||||
install_cmd = [executable, "install.py"]
|
||||
|
||||
new_env = os.environ.copy()
|
||||
new_env["COMFYUI_PATH"] = comfy_path
|
||||
process_wrap(install_cmd, repo_path, env=new_env)
|
||||
|
||||
|
||||
def execute_lazy_cnr_switch(target, zip_url, from_path, to_path, no_deps, custom_nodes_path):
|
||||
import uuid
|
||||
import shutil
|
||||
|
||||
# 1. download
|
||||
archive_name = f"CNR_temp_{str(uuid.uuid4())}.zip" # should be unpredictable name - security precaution
|
||||
download_path = os.path.join(custom_nodes_path, archive_name)
|
||||
download_url(zip_url, custom_nodes_path, archive_name)
|
||||
|
||||
# 2. extract files into <node_id>@<cur_ver>
|
||||
extracted = extract_package_as_zip(download_path, from_path)
|
||||
os.remove(download_path)
|
||||
|
||||
if extracted is None:
|
||||
if len(os.listdir(from_path)) == 0:
|
||||
shutil.rmtree(from_path)
|
||||
|
||||
print(f'Empty archive file: {target}')
|
||||
return False
|
||||
|
||||
|
||||
# 3. calculate garbage files (.tracking - extracted)
|
||||
tracking_info_file = os.path.join(from_path, '.tracking')
|
||||
prev_files = set()
|
||||
with open(tracking_info_file, 'r') as f:
|
||||
for line in f:
|
||||
prev_files.add(line.strip())
|
||||
garbage = prev_files.difference(extracted)
|
||||
garbage = [os.path.join(custom_nodes_path, x) for x in garbage]
|
||||
|
||||
# 4-1. remove garbage files
|
||||
for x in garbage:
|
||||
if os.path.isfile(x):
|
||||
os.remove(x)
|
||||
|
||||
# 4-2. remove garbage dir if empty
|
||||
for x in garbage:
|
||||
if os.path.isdir(x):
|
||||
if not os.listdir(x):
|
||||
os.rmdir(x)
|
||||
|
||||
# 5. rename dir name <node_id>@<prev_ver> ==> <node_id>@<cur_ver>
|
||||
print(f"'{from_path}' is moved to '{to_path}'")
|
||||
shutil.move(from_path, to_path)
|
||||
|
||||
# 6. create .tracking file
|
||||
tracking_info_file = os.path.join(to_path, '.tracking')
|
||||
with open(tracking_info_file, "w", encoding='utf-8') as file:
|
||||
file.write('\n'.join(list(extracted)))
|
||||
|
||||
|
||||
def execute_migration(moves):
|
||||
import shutil
|
||||
for x in moves:
|
||||
if os.path.exists(x[0]) and not os.path.exists(x[1]):
|
||||
shutil.move(x[0], x[1])
|
||||
print(f"[ComfyUI-Manager] MIGRATION: '{x[0]}' -> '{x[1]}'")
|
||||
|
||||
|
||||
# Check if script_list_path exists
|
||||
if os.path.exists(script_list_path):
|
||||
print("\n#######################################################################")
|
||||
print("[ComfyUI-Manager] Starting dependency installation/(de)activation for the extension\n")
|
||||
|
||||
executed = set()
|
||||
# Read each line from the file and convert it to a list using eval
|
||||
with open(script_list_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
||||
for line in file:
|
||||
if line in executed:
|
||||
continue
|
||||
|
||||
executed.add(line)
|
||||
|
||||
try:
|
||||
script = ast.literal_eval(line)
|
||||
|
||||
if script[1].startswith('#') and script[1] != '#FORCE':
|
||||
if script[1] == "#LAZY-INSTALL-SCRIPT":
|
||||
execute_lazy_install_script(script[0], script[2])
|
||||
|
||||
elif script[1] == "#LAZY-CNR-SWITCH-SCRIPT":
|
||||
execute_lazy_cnr_switch(script[0], script[2], script[3], script[4], script[5], script[6])
|
||||
execute_lazy_install_script(script[3], script[7])
|
||||
|
||||
elif script[1] == "#LAZY-MIGRATION":
|
||||
execute_migration(script[2])
|
||||
|
||||
elif os.path.exists(script[0]):
|
||||
if script[1] == "#FORCE":
|
||||
del script[1]
|
||||
else:
|
||||
if 'pip' in script[1:] and 'install' in script[1:] and is_installed(script[-1]):
|
||||
continue
|
||||
|
||||
print(f"\n## ComfyUI-Manager: EXECUTE => {script[1:]}")
|
||||
print(f"\n## Execute install/(de)activation script for '{script[0]}'")
|
||||
|
||||
new_env = os.environ.copy()
|
||||
new_env["COMFYUI_PATH"] = comfy_path
|
||||
exit_code = process_wrap(script[1:], script[0], env=new_env)
|
||||
|
||||
if exit_code != 0:
|
||||
print(f"install/(de)activation script failed: {script[0]}")
|
||||
else:
|
||||
print(f"\n## ComfyUI-Manager: CANCELED => {script[1:]}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to execute install/(de)activation script: {line} / {e}")
|
||||
|
||||
# Remove the script_list_path file
|
||||
if os.path.exists(script_list_path):
|
||||
os.remove(script_list_path)
|
||||
|
||||
print("\n[ComfyUI-Manager] Startup script completed.")
|
||||
print("#######################################################################\n")
|
||||
|
||||
del processed_install
|
||||
del pip_map
|
||||
|
||||
|
||||
def check_windows_event_loop_policy():
|
||||
try:
|
||||
import configparser
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
config.read(config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'windows_selector_event_loop_policy' in default_conf and default_conf['windows_selector_event_loop_policy'].lower() == 'true':
|
||||
try:
|
||||
import asyncio
|
||||
import asyncio.windows_events
|
||||
asyncio.set_event_loop_policy(asyncio.windows_events.WindowsSelectorEventLoopPolicy())
|
||||
print(f"[ComfyUI-Manager] Windows event loop policy mode enabled")
|
||||
except Exception as e:
|
||||
print(f"[ComfyUI-Manager] WARN: Windows initialization fail: {e}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
check_windows_event_loop_policy()
|
||||
@ -1,39 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_enabled_subdirectories_with_files(base_directory):
|
||||
subdirs_with_files = []
|
||||
for subdir in os.listdir(base_directory):
|
||||
try:
|
||||
full_path = os.path.join(base_directory, subdir)
|
||||
if os.path.isdir(full_path) and not subdir.endswith(".disabled") and not subdir.startswith('.') and subdir != '__pycache__':
|
||||
print(f"## Install dependencies for '{subdir}'")
|
||||
requirements_file = os.path.join(full_path, "requirements.txt")
|
||||
install_script = os.path.join(full_path, "install.py")
|
||||
|
||||
if os.path.exists(requirements_file) or os.path.exists(install_script):
|
||||
subdirs_with_files.append((full_path, requirements_file, install_script))
|
||||
except Exception as e:
|
||||
print(f"EXCEPTION During Dependencies INSTALL on '{subdir}':\n{e}")
|
||||
|
||||
return subdirs_with_files
|
||||
|
||||
|
||||
def install_requirements(requirements_file_path):
|
||||
if os.path.exists(requirements_file_path):
|
||||
subprocess.run(["pip", "install", "-r", requirements_file_path])
|
||||
|
||||
|
||||
def run_install_script(install_script_path):
|
||||
if os.path.exists(install_script_path):
|
||||
subprocess.run(["python", install_script_path])
|
||||
|
||||
|
||||
custom_nodes_directory = "custom_nodes"
|
||||
subdirs_with_files = get_enabled_subdirectories_with_files(custom_nodes_directory)
|
||||
|
||||
|
||||
for subdir, requirements_file, install_script in subdirs_with_files:
|
||||
install_requirements(requirements_file)
|
||||
run_install_script(install_script)
|
||||
@ -1,21 +0,0 @@
|
||||
git clone https://github.com/comfyanonymous/ComfyUI
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/ltdrdata/ComfyUI-Manager
|
||||
cd ..
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pip install -r custom_nodes/ComfyUI-Manager/requirements.txt
|
||||
cd ..
|
||||
echo "#!/bin/bash" > run_gpu.sh
|
||||
echo "cd ComfyUI" >> run_gpu.sh
|
||||
echo "source venv/bin/activate" >> run_gpu.sh
|
||||
echo "python main.py --preview-method auto" >> run_gpu.sh
|
||||
chmod +x run_gpu.sh
|
||||
|
||||
echo "#!/bin/bash" > run_cpu.sh
|
||||
echo "cd ComfyUI" >> run_cpu.sh
|
||||
echo "source venv/bin/activate" >> run_cpu.sh
|
||||
echo "python main.py --preview-method auto --cpu" >> run_cpu.sh
|
||||
chmod +x run_cpu.sh
|
||||
@ -1,17 +0,0 @@
|
||||
git clone https://github.com/comfyanonymous/ComfyUI
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/ltdrdata/ComfyUI-Manager
|
||||
cd ..
|
||||
python -m venv venv
|
||||
call venv/Scripts/activate
|
||||
python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pip install -r custom_nodes/ComfyUI-Manager/requirements.txt
|
||||
cd ..
|
||||
echo "cd ComfyUI" >> run_gpu.bat
|
||||
echo "call venv/Scripts/activate" >> run_gpu.bat
|
||||
echo "python main.py" >> run_gpu.bat
|
||||
|
||||
echo "cd ComfyUI" >> run_cpu.bat
|
||||
echo "call venv/Scripts/activate" >> run_cpu.bat
|
||||
echo "python main.py --cpu" >> run_cpu.bat
|
||||
@ -1,2 +0,0 @@
|
||||
.\python_embeded\python.exe -s -m pip install gitpython
|
||||
.\python_embeded\python.exe -c "import git; git.Repo.clone_from('https://github.com/ltdrdata/ComfyUI-Manager', './ComfyUI/custom_nodes/ComfyUI-Manager')"
|
||||
@ -1,12 +0,0 @@
|
||||
import git
|
||||
|
||||
commit_hash = "a361cc1"
|
||||
|
||||
repo = git.Repo('.')
|
||||
|
||||
if repo.is_dirty():
|
||||
repo.git.stash()
|
||||
|
||||
repo.git.update_ref("refs/remotes/origin/main", commit_hash)
|
||||
repo.remotes.origin.fetch()
|
||||
repo.git.pull("origin", "main")
|
||||
Loading…
Reference in New Issue
Block a user