Compare commits

..

1 Commits

Author SHA1 Message Date
Robin Huang
982a60e85f Add linux form factor. 2025-03-11 11:32:53 -07:00
6 changed files with 45 additions and 99 deletions

View File

@@ -18573,16 +18573,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "ComfyUI nodes for PhotoDoodle model." "description": "ComfyUI nodes for PhotoDoodle model."
}, },
{
"author": "Yuan-ManX",
"title": "ComfyUI-StyleStudio",
"reference": "https://github.com/Yuan-ManX/ComfyUI-StyleStudio",
"files": [
"https://github.com/Yuan-ManX/ComfyUI-StyleStudio"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for StyleStudio model."
},
{ {
"author": "Starnodes2024", "author": "Starnodes2024",
"title": "ComfyUI_StarNodes", "title": "ComfyUI_StarNodes",
@@ -21984,16 +21974,6 @@
"install_type": "git-clone", "install_type": "git-clone",
"description": "A ComfyUI custom node that provides dynamic text substitution using wildcards and CSV files. Perfect for creating varied prompts with consistent relationships between terms." "description": "A ComfyUI custom node that provides dynamic text substitution using wildcards and CSV files. Perfect for creating varied prompts with consistent relationships between terms."
}, },
{
"author": "finegrain",
"title": "comfyui-finegrain",
"reference": "https://github.com/finegrain-ai/comfyui-finegrain",
"files": [
"https://github.com/finegrain-ai/comfyui-finegrain"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes to interact with the Finegrain API."
},

View File

@@ -68,7 +68,6 @@ async def _get_cnr_data(cache_mode=True, dont_wait=True):
else: else:
form_factor = 'other' form_factor = 'other'
print(f"form_factor: {form_factor}")
while remained: while remained:
# Add comfyui_version and form_factor to the API request # Add comfyui_version and form_factor to the API request
sub_uri = f'{base_url}/nodes?page={page}&limit=30&comfyui_version={comfyui_ver}&form_factor={form_factor}' sub_uri = f'{base_url}/nodes?page={page}&limit=30&comfyui_version={comfyui_ver}&form_factor={form_factor}'

View File

@@ -43,7 +43,7 @@ import manager_downloader
from node_package import InstalledNodePackage from node_package import InstalledNodePackage
version_code = [3, 30, 9] version_code = [3, 30, 3]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
@@ -53,11 +53,6 @@ DEFAULT_CHANNEL = "https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/ma
default_custom_nodes_path = None default_custom_nodes_path = None
class InvalidChannel(Exception):
def __init__(self, channel):
self.channel = channel
super().__init__(channel)
def get_default_custom_nodes_path(): def get_default_custom_nodes_path():
global default_custom_nodes_path global default_custom_nodes_path
if default_custom_nodes_path is None: if default_custom_nodes_path is None:
@@ -256,7 +251,6 @@ comfy_ui_revision = "Unknown"
comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0) comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0)
channel_dict = None channel_dict = None
valid_channels = set()
channel_list = None channel_list = None
@@ -361,7 +355,7 @@ def normalize_channel(channel):
if channel_url: if channel_url:
return channel_url return channel_url
raise InvalidChannel(channel) raise Exception(f"Invalid channel name '{channel}'")
class ManagedResult: class ManagedResult:
@@ -776,11 +770,6 @@ class UnifiedManager:
print(f"[bold red]ERROR: Invalid mode is specified `--mode {mode}`[/bold red]", file=sys.stderr) print(f"[bold red]ERROR: Invalid mode is specified `--mode {mode}`[/bold red]", file=sys.stderr)
return {} return {}
# validate channel - only the channel set by the user is allowed.
if channel_url not in valid_channels:
logging.error(f'[ComfyUI-Manager] An invalid channel was used: {channel_url}')
raise InvalidChannel(channel_url)
json_obj = await get_data_by_mode(mode, 'custom-node-list.json', channel_url=channel_url) json_obj = await get_data_by_mode(mode, 'custom-node-list.json', channel_url=channel_url)
for x in json_obj['custom_nodes']: for x in json_obj['custom_nodes']:
try: try:
@@ -852,7 +841,6 @@ class UnifiedManager:
install_script_path = os.path.join(repo_path, "install.py") install_script_path = os.path.join(repo_path, "install.py")
requirements_path = os.path.join(repo_path, "requirements.txt") requirements_path = os.path.join(repo_path, "requirements.txt")
res = True
if lazy_mode: if lazy_mode:
install_cmd = ["#LAZY-INSTALL-SCRIPT", sys.executable] install_cmd = ["#LAZY-INSTALL-SCRIPT", sys.executable]
return try_install_script(url, repo_path, install_cmd) return try_install_script(url, repo_path, install_cmd)
@@ -860,6 +848,7 @@ class UnifiedManager:
if os.path.exists(requirements_path) and not no_deps: if os.path.exists(requirements_path) and not no_deps:
print("Install: pip packages") print("Install: pip packages")
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path) pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
res = True
lines = manager_util.robust_readlines(requirements_path) lines = manager_util.robust_readlines(requirements_path)
for line in lines: for line in lines:
package_name = remap_pip_package(line.strip()) package_name = remap_pip_package(line.strip())
@@ -870,14 +859,15 @@ class UnifiedManager:
res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution) res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
pip_fixer.fix_broken() pip_fixer.fix_broken()
return res
if os.path.exists(install_script_path) and install_script_path not in self.processed_install: if os.path.exists(install_script_path) and install_script_path not in self.processed_install:
self.processed_install.add(install_script_path) self.processed_install.add(install_script_path)
print("Install: install script") print("Install: install script")
install_cmd = [sys.executable, "install.py"] install_cmd = [sys.executable, "install.py"]
return res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution) return try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
return res return True
def reserve_cnr_switch(self, target, zip_url, from_path, to_path, no_deps): def reserve_cnr_switch(self, target, zip_url, from_path, to_path, no_deps):
script_path = os.path.join(manager_startup_script_path, "install-scripts.txt") script_path = os.path.join(manager_startup_script_path, "install-scripts.txt")
@@ -1425,11 +1415,7 @@ class UnifiedManager:
version_spec = self.resolve_unspecified_version(node_id) version_spec = self.resolve_unspecified_version(node_id)
if version_spec == 'unknown' or version_spec == 'nightly': if version_spec == 'unknown' or version_spec == 'nightly':
try: custom_nodes = await self.get_custom_nodes(channel, mode)
custom_nodes = await self.get_custom_nodes(channel, mode)
except InvalidChannel as e:
return ManagedResult('fail').fail(f'Invalid channel is used: {e.channel}')
the_node = custom_nodes.get(node_id) the_node = custom_nodes.get(node_id)
if the_node is not None: if the_node is not None:
if version_spec == 'unknown': if version_spec == 'unknown':
@@ -1487,6 +1473,28 @@ class UnifiedManager:
return res return res
async def migrate_unmanaged_nodes(self):
"""
fix path for nightly and unknown nodes of unmanaged nodes
"""
await self.reload('cache')
await self.get_custom_nodes('default', 'cache')
print("Migration: STAGE 1")
moves = []
# migrate nightly inactive
for x, v in self.nightly_inactive_nodes.items():
if v.endswith('@nightly'):
continue
new_path = os.path.join(get_default_custom_nodes_path(), '.disabled', f"{x}@nightly")
moves.append((v, new_path))
self.reserve_migration(moves)
print("DONE (Migration reserved)")
unified_manager = UnifiedManager() unified_manager = UnifiedManager()
@@ -1556,14 +1564,8 @@ def get_installed_node_packs():
return res return res
def refresh_channel_dict():
if channel_dict is None:
get_channel_dict()
def get_channel_dict(): def get_channel_dict():
global channel_dict global channel_dict
global valid_channels
if channel_dict is None: if channel_dict is None:
channel_dict = {} channel_dict = {}
@@ -1577,7 +1579,6 @@ def get_channel_dict():
channel_info = x.split("::") channel_info = x.split("::")
if len(channel_info) == 2: if len(channel_info) == 2:
channel_dict[channel_info[0]] = channel_info[1] channel_dict[channel_info[0]] = channel_info[1]
valid_channels.add(channel_info[1])
return channel_dict return channel_dict

View File

@@ -273,23 +273,14 @@ import zipfile
import urllib.request import urllib.request
def get_model_dir(data, show_log=False) -> str | None: def get_model_dir(data, show_log=False):
if 'download_model_base' in folder_paths.folder_names_and_paths: if 'download_model_base' in folder_paths.folder_names_and_paths:
models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0] models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0]
else: else:
models_base = folder_paths.models_dir models_base = folder_paths.models_dir
# NOTE: Validate to prevent path traversal.
if any(char in data['filename'] for char in {'/', '\\', ':'}):
return None
def resolve_custom_node(save_path): def resolve_custom_node(save_path):
save_path = save_path[13:] # remove 'custom_nodes/' save_path = save_path[13:] # remove 'custom_nodes/'
# NOTE: Validate to prevent path traversal.
if save_path.startswith(os.path.sep) or ':' in save_path:
return None
repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name
# NOTE: The creation of files within the custom node path should be removed in the future. # NOTE: The creation of files within the custom node path should be removed in the future.
@@ -408,6 +399,7 @@ async def task_worker():
try: try:
node_spec = core.unified_manager.resolve_node_spec(node_spec_str) node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
if node_spec is None: if node_spec is None:
logging.error(f"Cannot resolve install target: '{node_spec_str}'") logging.error(f"Cannot resolve install target: '{node_spec_str}'")
return f"Cannot resolve install target: '{node_spec_str}'" return f"Cannot resolve install target: '{node_spec_str}'"
@@ -937,7 +929,6 @@ def check_model_installed(json_obj):
@routes.get("/externalmodel/getlist") @routes.get("/externalmodel/getlist")
async def fetch_externalmodel_list(request): async def fetch_externalmodel_list(request):
# The model list is only allowed in the default channel, yet.
json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json') json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json')
check_model_installed(json_obj) check_model_installed(json_obj)
@@ -1206,8 +1197,9 @@ async def install_custom_node(request):
git_url = None git_url = None
selected_version = json_data.get('selected_version') if json_data['version'] != 'unknown':
if json_data['version'] != 'unknown' and selected_version != 'unknown': selected_version = json_data.get('selected_version')
if skip_post_install: if skip_post_install:
if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes: if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes:
core.unified_manager.unified_enable(cnr_id) core.unified_manager.unified_enable(cnr_id)
@@ -1224,9 +1216,6 @@ async def install_custom_node(request):
if git_url is None: if git_url is None:
logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}") logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}")
return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}") return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}")
elif json_data['version'] != 'unknown' and selected_version == 'unknown':
logging.error(f"[ComfyUI-Manager] Invalid installation request: {json_data}")
return web.Response(status=400, text="Invalid installation request")
else: else:
# unknown # unknown
unknown_name = os.path.basename(json_data['files'][0]) unknown_name = os.path.basename(json_data['files'][0])
@@ -1418,14 +1407,17 @@ async def disable_node(request):
return web.Response(status=200) return web.Response(status=200)
async def check_whitelist_for_model(item): @routes.get("/manager/migrate_unmanaged_nodes")
json_obj = await core.get_data_by_mode('cache', 'model-list.json') async def migrate_unmanaged_nodes(request):
logging.info("[ComfyUI-Manager] Migrating unmanaged nodes...")
await core.unified_manager.migrate_unmanaged_nodes()
logging.info("Done.")
return web.Response(status=200)
for x in json_obj.get('models', []):
if x['save_path'] == item['save_path'] and x['base'] == item['base'] and x['filename'] == item['filename']: @routes.get("/manager/need_to_migrate")
return True async def need_to_migrate(request):
return web.Response(text=str(core.need_to_migrate), status=200)
return False
@routes.post("/manager/queue/install_model") @routes.post("/manager/queue/install_model")
@@ -1436,11 +1428,6 @@ async def install_model(request):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
# validate request
if not await check_whitelist_for_model(json_data):
logging.error(f"[ComfyUI-Manager] Invalid model install request is detected: {json_data}")
return web.Response(status=400, text="Invalid model install request is detected")
if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'): if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'):
models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default') models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default')
@@ -1708,7 +1695,6 @@ cm_global.register_api('cm.try-install-custom-node', confirm_try_install)
async def default_cache_update(): async def default_cache_update():
core.refresh_channel_dict()
channel_url = core.get_config()['channel_url'] channel_url = core.get_config()['channel_url']
async def get_cache(filename): async def get_cache(filename):
try: try:

View File

@@ -9,26 +9,6 @@
}, },
{
"author": "finegrain",
"title": "comfyui-finegrain",
"reference": "https://github.com/finegrain-ai/comfyui-finegrain",
"files": [
"https://github.com/finegrain-ai/comfyui-finegrain"
],
"install_type": "git-clone",
"description": "ComfyUI custom nodes to interact with the Finegrain API."
},
{
"author": "Yuan-ManX",
"title": "ComfyUI-StyleStudio",
"reference": "https://github.com/Yuan-ManX/ComfyUI-StyleStudio",
"files": [
"https://github.com/Yuan-ManX/ComfyUI-StyleStudio"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for StyleStudio model."
},
{ {
"author": "Jerome Bacquet", "author": "Jerome Bacquet",
"title": "ComfyUI XenoFlow", "title": "ComfyUI XenoFlow",

View File

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