Compare commits

..

5 Commits
3.15 ... 3.17

Author SHA1 Message Date
Dr.Lt.Data
92aba9565a version marker 2025-02-02 18:17:27 +09:00
HuangYongliang
6ea0aebb0b fix channel_url config (#1501)
* 1.fix channel_url not effecte for default_cache_update
2.support http channel url for airgap env

* fix pylint
2025-02-02 18:16:39 +09:00
Dr.Lt.Data
b5cdcb75b4 feat: add always_lazy_install config option. 2025-02-02 18:01:16 +09:00
Dr.Lt.Data
bd9aae40b8 update DB 2025-02-02 17:40:07 +09:00
Dr.Lt.Data
33f931c0a4 feat: Support for uv has been added.
Set `use_uv` in `config.ini`.
2025-02-02 17:26:29 +09:00
12 changed files with 105 additions and 54 deletions

View File

@@ -5,6 +5,7 @@
![menu](https://raw.githubusercontent.com/ltdrdata/ComfyUI-extension-tutorials/refs/heads/Main/ComfyUI-Manager/images/dialog.jpg)
## NOTICE
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
* V3.10: `double-click feature` is removed
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
* V3.3.2: Overhauled. Officially supports [https://comfyregistry.org/](https://comfyregistry.org/).
@@ -246,6 +247,25 @@ The following settings are applied based on the section marked as `is_default`.
![missing-list](https://raw.githubusercontent.com/ltdrdata/ComfyUI-extension-tutorials/Main/ComfyUI-Manager/images/missing-list.jpg)
# Config
* You can modify the `config.ini` file to apply the settings for ComfyUI-Manager.
* The path to the `config.ini` used by ComfyUI-Manager is displayed in the startup log messages.
* See also: [https://github.com/ltdrdata/ComfyUI-Manager#paths]
* Configuration options:
```
[default]
git_exe = <Manually specify the path to the git executable. If left empty, the default git executable path will be used.>
use_uv = <Use uv instead of pip for dependency installation.>
channel_url = https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main
bypass_ssl = <Set to True if SSL errors occur to disable SSL.>
file_logging = <Configure whether to create a log file used by ComfyUI-Manager.>
windows_selector_event_loop_policy = <If an event loop error occurs on Windows, set this to True.>
model_download_by_agent = <When downloading models, use an agent instead of torchvision_download_url.>
downgrade_blacklist = <Set a list of packages to prevent downgrades. List them separated by commas.>
security_level = <Set the security level.>
always_lazy_install = <Whether to perform dependency installation on restart even in environments other than Windows.>
```
## Additional Feature
* Logging to file feature
* This feature is enabled by default and can be disabled by setting `file_logging = False` in the `config.ini`.

View File

@@ -8478,6 +8478,7 @@
"Star Face Loader",
"StarFiveWildcards",
"StarImageSwitch",
"StarLatentSwitch",
"StarTextFilter",
"StarTextInput",
"Starupscale"
@@ -20790,6 +20791,7 @@
"Enhanced Random Light Source",
"Float Relay",
"HLFrequencyDetailRestore",
"Hex to Color",
"Image Add Alpha",
"Image Frequency Analyzer",
"Image Relay",

View File

@@ -7314,11 +7314,6 @@
"last_update": "2024-07-31 14:13:17",
"author_account_age_days": 1602
},
"https://github.com/myAiLemon/MagicGetPromptAutomatically": {
"stars": 0,
"last_update": "2025-02-01 06:37:46",
"author_account_age_days": 345
},
"https://github.com/myshell-ai/ComfyUI-ShellAgent-Plugin": {
"stars": 8,
"last_update": "2025-01-16 08:03:08",

View File

@@ -42,7 +42,7 @@ import manager_downloader
from node_package import InstalledNodePackage
version_code = [3, 15]
version_code = [3, 17]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
@@ -174,7 +174,7 @@ git_script_path = os.path.join(manager_util.comfyui_manager_path, "git_helper.py
manager_files_path = None
manager_config_path = None
manager_channel_list_path = None
manager_startup_script_path = None
manager_startup_script_path:str = None
manager_snapshot_path = None
manager_pip_overrides_path = None
manager_components_path = None
@@ -325,6 +325,8 @@ def normalize_channel(channel):
return None
elif channel.startswith('https://'):
return channel
elif channel.startswith('http://') and get_config()['http_channel_enabled'] == True:
return channel
tmp_dict = get_channel_dict()
channel_url = tmp_dict.get(channel)
@@ -809,7 +811,7 @@ class UnifiedManager:
package_name = remap_pip_package(line.strip())
if package_name and not package_name.startswith('#') and package_name not in self.processed_install:
self.processed_install.add(package_name)
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
install_cmd = manager_util.make_pip_cmd(["install", package_name])
if package_name.strip() != "" and not package_name.startswith('#'):
res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
@@ -819,7 +821,7 @@ class UnifiedManager:
if os.path.exists(install_script_path) and install_script_path not in self.processed_install:
self.processed_install.add(install_script_path)
print("Install: install script")
install_cmd = [sys.executable, "install.py"]
install_cmd = manager_util.make_pip_cmd(["install.py"])
return try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
return True
@@ -1545,7 +1547,8 @@ def write_config():
config = configparser.ConfigParser()
config['default'] = {
'preview_method': manager_funcs.get_current_preview_method(),
'git_exe': get_config()['git_exe'],
'git_exe': get_config()['git_exe'],
'use_uv': get_config()['use_uv'],
'channel_url': get_config()['channel_url'],
'share_option': get_config()['share_option'],
'bypass_ssl': get_config()['bypass_ssl'],
@@ -1556,6 +1559,7 @@ def write_config():
'downgrade_blacklist': get_config()['downgrade_blacklist'],
'security_level': get_config()['security_level'],
'skip_migration_check': get_config()['skip_migration_check'],
'always_lazy_install': get_config()['always_lazy_install']
}
directory = os.path.dirname(manager_config_path)
@@ -1581,9 +1585,13 @@ def read_config():
else:
security_level = default_conf['security_level'] if 'security_level' in default_conf else 'normal'
manager_util.use_uv = default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False
return {
'http_channel_enabled': default_conf['http_channel_enabled'].lower() == 'true' if 'http_channel_enabled' in default_conf else False,
'preview_method': default_conf['preview_method'] if 'preview_method' in default_conf else manager_funcs.get_current_preview_method(),
'git_exe': default_conf['git_exe'] if 'git_exe' in default_conf else '',
'use_uv': default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False,
'channel_url': default_conf['channel_url'] if 'channel_url' in default_conf else DEFAULT_CHANNEL,
'share_option': default_conf['share_option'] if 'share_option' in default_conf else 'all',
'bypass_ssl': default_conf['bypass_ssl'].lower() == 'true' if 'bypass_ssl' in default_conf else False,
@@ -1593,13 +1601,17 @@ def read_config():
'model_download_by_agent': default_conf['model_download_by_agent'].lower() == 'true' if 'model_download_by_agent' in default_conf else False,
'downgrade_blacklist': default_conf['downgrade_blacklist'] if 'downgrade_blacklist' in default_conf else '',
'skip_migration_check': default_conf['skip_migration_check'].lower() == 'true' if 'skip_migration_check' in default_conf else False,
'security_level': security_level
'always_lazy_install': default_conf['always_lazy_install'].lower() == 'true' if 'always_lazy_install' in default_conf else False,
'security_level': security_level,
}
except Exception:
manager_util.use_uv = False
return {
'http_channel_enabled': False,
'preview_method': manager_funcs.get_current_preview_method(),
'git_exe': '',
'use_uv': False,
'channel_url': DEFAULT_CHANNEL,
'share_option': 'all',
'bypass_ssl': False,
@@ -1609,6 +1621,7 @@ def read_config():
'model_download_by_agent': False,
'downgrade_blacklist': '',
'skip_migration_check': False,
'always_lazy_install': False,
'security_level': 'normal',
}
@@ -1618,6 +1631,8 @@ def get_config():
if cached_config is None:
cached_config = read_config()
if cached_config['http_channel_enabled']:
print("[ComfyUI-Manager] Warning: http channel enabled, make sure server in secure env")
return cached_config
@@ -1666,7 +1681,9 @@ def switch_to_default_branch(repo):
def try_install_script(url, repo_path, install_cmd, instant_execution=False):
if not instant_execution and ((len(install_cmd) > 0 and install_cmd[0].startswith('#')) or (platform.system() == "Windows" and comfy_ui_commit_datetime.date() >= comfy_ui_required_commit_datetime.date())):
if not instant_execution and (
(len(install_cmd) > 0 and install_cmd[0].startswith('#')) or platform.system() == "Windows" or get_config()['always_lazy_install']
):
if not os.path.exists(manager_startup_script_path):
os.makedirs(manager_startup_script_path)
@@ -1681,6 +1698,10 @@ def try_install_script(url, repo_path, install_cmd, instant_execution=False):
if is_blacklisted(install_cmd[4]):
print(f"[ComfyUI-Manager] skip black listed pip installation: '{install_cmd[4]}'")
return True
elif len(install_cmd) == 6 and install_cmd[3:5] == ['pip', 'install']: # uv mode
if is_blacklisted(install_cmd[5]):
print(f"[ComfyUI-Manager] skip black listed pip installation: '{install_cmd[5]}'")
return True
print(f"\n## ComfyUI-Manager: EXECUTE => {install_cmd}")
code = manager_funcs.run_script(install_cmd, cwd=repo_path)
@@ -1797,9 +1818,9 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
if package_name and not package_name.startswith('#'):
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()]
install_cmd = manager_util.make_pip_cmd(["install", s[0].strip(), '--index-url', s[1].strip()])
else:
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
install_cmd = manager_util.make_pip_cmd(["install", package_name])
if package_name.strip() != "" and not package_name.startswith('#'):
try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
@@ -2071,14 +2092,8 @@ async def get_data_by_mode(mode, filename, channel_url=None):
cache_uri = str(manager_util.simple_hash(uri))+'_'+filename
cache_uri = os.path.join(manager_util.cache_dir, cache_uri)
if mode == "cache":
if manager_util.is_file_created_within_one_day(cache_uri):
if mode == "cache" and manager_util.is_file_created_within_one_day(cache_uri):
json_obj = await manager_util.get_data(cache_uri)
else:
json_obj = await manager_util.get_data(uri)
with manager_util.cache_lock:
with open(cache_uri, "w", encoding='utf-8') as file:
json.dump(json_obj, file, indent=4, sort_keys=True)
else:
json_obj = await manager_util.get_data(uri)
with manager_util.cache_lock:
@@ -2120,7 +2135,7 @@ def gitclone_fix(files, instant_execution=False, no_deps=False):
def pip_install(packages):
install_cmd = ['#FORCE', sys.executable, "-m", "pip", "install", '-U'] + packages
install_cmd = ['#FORCE'] + manager_util.make_pip_cmd(["install", '-U']) + packages
try_install_script('pip install via manager', '..', install_cmd)
@@ -2417,7 +2432,8 @@ def check_state_of_git_node_pack_single(item, do_fetch=False, do_update_check=Tr
def get_installed_pip_packages():
# extract pip package infos
pips = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'], text=True).split('\n')
cmd = manager_util.make_pip_cmd(['pip', 'freeze'])
pips = subprocess.check_output(cmd, text=True).split('\n')
res = {}
for x in pips:

View File

@@ -1576,8 +1576,9 @@ cm_global.register_api('cm.try-install-custom-node', confirm_try_install)
async def default_cache_update():
channel_url = core.get_config()['channel_url']
async def get_cache(filename):
uri = f"{core.DEFAULT_CHANNEL}/{filename}"
uri = f"{channel_url}/{filename}"
cache_uri = str(manager_util.simple_hash(uri)) + '_' + filename
cache_uri = os.path.join(manager_util.cache_dir, cache_uri)
@@ -1598,7 +1599,7 @@ async def default_cache_update():
# load at least once
await core.unified_manager.reload('remote', dont_wait=False)
await core.unified_manager.get_custom_nodes('default', 'remote')
await core.unified_manager.get_custom_nodes(channel_url, 'remote')
logging.info("[ComfyUI-Manager] All startup tasks have been completed.")

View File

@@ -19,6 +19,14 @@ cache_lock = threading.Lock()
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
use_uv = False
def make_pip_cmd(cmd):
if use_uv:
return [sys.executable, '-m', 'uv', 'pip'] + cmd
else:
return [sys.executable, '-m', 'pip'] + cmd
# DON'T USE StrictVersion - cannot handle pre_release version
# try:
@@ -209,7 +217,7 @@ def get_installed_packages(renew=False):
if renew or pip_map is None:
try:
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
result = subprocess.check_output(make_pip_cmd(['list']), universal_newlines=True)
pip_map = {}
for line in result.split('\n'):
@@ -260,7 +268,7 @@ class PIPFixer:
if len(spec) > 0:
platform = spec[1]
else:
cmd = [sys.executable, '-m', 'pip', 'install', '--force', 'torch', 'torchvision', 'torchaudio']
cmd = make_pip_cmd(['install', '--force', 'torch', 'torchvision', 'torchaudio'])
subprocess.check_output(cmd, universal_newlines=True)
logging.error(cmd)
return
@@ -270,15 +278,13 @@ class PIPFixer:
torch_torchvision_torchaudio_ver = torch_torchvision_torchaudio_version_map.get(torch_ver)
if torch_torchvision_torchaudio_ver is None:
cmd = [sys.executable, '-m', 'pip', 'install', '--pre',
'torch', 'torchvision', 'torchaudio',
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"]
cmd = make_pip_cmd(['install', '--pre', 'torch', 'torchvision', 'torchaudio',
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"])
logging.info("[ComfyUI-Manager] restore PyTorch to nightly version")
else:
torchvision_ver, torchaudio_ver = torch_torchvision_torchaudio_ver
cmd = [sys.executable, '-m', 'pip', 'install',
f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torchaudio_ver}",
'--index-url', f"https://download.pytorch.org/whl/{platform}"]
cmd = make_pip_cmd(['install', f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torchaudio_ver}",
'--index-url', f"https://download.pytorch.org/whl/{platform}"])
logging.info(f"[ComfyUI-Manager] restore PyTorch to {torch_ver}+{platform}")
subprocess.check_output(cmd, universal_newlines=True)
@@ -289,7 +295,7 @@ class PIPFixer:
# remove `comfy` python package
try:
if 'comfy' in new_pip_versions:
cmd = [sys.executable, '-m', 'pip', 'uninstall', 'comfy']
cmd = make_pip_cmd(['uninstall', 'comfy'])
subprocess.check_output(cmd, universal_newlines=True)
logging.warning("[ComfyUI-Manager] '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.")
@@ -335,7 +341,7 @@ class PIPFixer:
if len(targets) > 0:
for x in targets:
cmd = [sys.executable, '-m', 'pip', 'install', f"{x}=={versions[0].version_string}"]
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}"])
subprocess.check_output(cmd, universal_newlines=True)
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
@@ -348,7 +354,8 @@ class PIPFixer:
np = new_pip_versions.get('numpy')
if np is not None:
if StrictVersion(np) >= StrictVersion('2'):
subprocess.check_output([sys.executable, '-m', 'pip', 'install', "numpy<2"], universal_newlines=True)
cmd = make_pip_cmd(['install', "numpy<2"])
subprocess.check_output(cmd , universal_newlines=True)
except Exception as e:
logging.error("[ComfyUI-Manager] Failed to restore numpy")
logging.error(e)

View File

@@ -1446,7 +1446,7 @@ export class CustomNodesManager {
let v = result[hash];
if(v != 'success')
errorMsg += v;
errorMsg += v+'\n';
}
for(let k in self.install_context.targets) {

View File

@@ -738,7 +738,7 @@ export class ModelManager {
let v = result[hash];
if(v != 'success')
errorMsg += v;
errorMsg += v + '\n';
}
for(let k in self.install_context.targets) {

View File

@@ -5003,6 +5003,7 @@
"FixUTF8StringNode",
"ImageResizeNode",
"ImagesToVideoNode",
"LoadImageFormURLNode",
"LoadImageFromFolderNode",
"SaveImageToFolderNode",
"SaveImagesToFolderNode",

View File

@@ -8478,6 +8478,7 @@
"Star Face Loader",
"StarFiveWildcards",
"StarImageSwitch",
"StarLatentSwitch",
"StarTextFilter",
"StarTextInput",
"Starupscale"
@@ -20790,6 +20791,7 @@
"Enhanced Random Light Source",
"Float Relay",
"HLFrequencyDetailRestore",
"Hex to Color",
"Image Add Alpha",
"Image Frequency Analyzer",
"Image Relay",

View File

@@ -103,6 +103,24 @@ manager_config_path = os.path.join(manager_files_path, 'config.ini')
cm_cli_path = os.path.join(comfyui_manager_path, "cm-cli.py")
default_conf = {}
def read_config():
global default_conf
import configparser
config = configparser.ConfigParser()
config.read(manager_config_path)
default_conf = config['default']
def read_uv_mode():
if 'use_uv' in default_conf:
manager_util.use_uv = default_conf['use_uv']
read_config()
read_uv_mode()
cm_global.pip_overrides = {'numpy': 'numpy<2', 'ultralytics': 'ultralytics==8.3.40'}
if os.path.exists(manager_pip_overrides_path):
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
@@ -419,11 +437,11 @@ except ModuleNotFoundError:
print("## ComfyUI-Manager: installing dependencies. (GitPython)")
try:
result = subprocess.check_output([sys.executable, '-s', '-m', 'pip', 'install', '-r', requirements_path])
result = subprocess.check_output(manager_util.make_pip_cmd(['install', '-r', requirements_path]))
except subprocess.CalledProcessError:
print("## [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])
result = subprocess.check_output(manager_util.make_pip_cmd(['install', '--user', '-r', requirements_path]))
except subprocess.CalledProcessError:
print("## [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)")
@@ -452,11 +470,6 @@ else:
def read_downgrade_blacklist():
try:
import configparser
config = configparser.ConfigParser()
config.read(manager_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 != '']
@@ -471,19 +484,13 @@ read_downgrade_blacklist()
def check_bypass_ssl():
try:
import configparser
import ssl
config = configparser.ConfigParser()
config.read(manager_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 {manager_config_path})")
ssl._create_default_https_context = ssl._create_unverified_context # SSL certificate error fix.
except Exception:
pass
check_bypass_ssl()
@@ -603,9 +610,9 @@ def execute_lazy_install_script(repo_path, executable):
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()]
install_cmd = manager_util.make_pip_cmd(["install", s[0].strip(), '--index-url', s[1].strip()])
else:
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
install_cmd = manager_util.make_pip_cmd(["install", package_name])
process_wrap(install_cmd, repo_path)

View File

@@ -1,7 +1,7 @@
[project]
name = "comfyui-manager"
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
version = "3.15"
version = "3.17"
license = { file = "LICENSE.txt" }
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]