diff --git a/cm-cli.py b/cm-cli.py index 4ae8ee8d..d7111716 100644 --- a/cm-cli.py +++ b/cm-cli.py @@ -13,13 +13,30 @@ from typing_extensions import List, Annotated import re import git + sys.path.append(os.path.dirname(__file__)) sys.path.append(os.path.join(os.path.dirname(__file__), "glob")) + +import manager_util + +comfy_path = os.environ.get('COMFYUI_PATH') +if comfy_path is None: + try: + import folder_paths + comfy_path = os.path.join(os.path.dirname(folder_paths.__file__)) + except: + comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..')) + +sys.path.append(comfy_path) + +import utils.extra_config import cm_global import manager_core as core from manager_core import unified_manager import cnr_utils + + comfyui_manager_path = os.path.abspath(os.path.dirname(__file__)) comfy_path = os.environ.get('COMFYUI_PATH') @@ -27,21 +44,14 @@ if comfy_path is None: print("\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(comfyui_manager_path, '..', '..')) -startup_script_path = os.path.join(comfyui_manager_path, "startup-scripts") -custom_nodes_path = os.path.join(comfy_path, 'custom_nodes') - -script_path = os.path.join(startup_script_path, "install-scripts.txt") -restore_snapshot_path = os.path.join(startup_script_path, "restore-snapshot.json") -pip_overrides_path = os.path.join(comfyui_manager_path, "pip_overrides.json") -git_script_path = os.path.join(comfyui_manager_path, "git_helper.py") cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision'] cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia'] -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 = {'numpy': 'numpy<2'} + +if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")): + with open(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json"), '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 check_comfyui_hash(): @@ -82,6 +92,8 @@ class Ctx: self.channel = 'default' self.no_deps = False self.mode = 'cache' + self.user_directory = None + self.custom_nodes_paths = [os.path.join(core.comfy_path, 'custom_nodes')] def set_channel_mode(self, channel, mode): if mode is not None: @@ -104,14 +116,45 @@ class Ctx: def set_no_deps(self, no_deps): self.no_deps = no_deps + def set_user_directory(self, user_directory): + if user_directory is None: + return -channel_ctx = Ctx() + extra_model_paths_yaml = os.path.join(user_directory, 'extra_model_paths.yaml') + if os.path.exists(extra_model_paths_yaml): + utils.extra_config.load_extra_path_config(extra_model_paths_yaml) + + core.update_user_directory(user_directory) + + if os.path.exists(core.manager_pip_overrides_path): + cm_global.pip_overrides = {'numpy': 'numpy<2'} + with open(core.manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file: + cm_global.pip_overrides = json.load(json_file) + + @staticmethod + def get_startup_scripts_path(): + return os.path.join(core.manager_startup_script_path, "install-scripts.txt") + + @staticmethod + def get_restore_snapshot_path(): + return os.path.join(core.manager_startup_script_path, "restore-snapshot.json") + + @staticmethod + def get_snapshot_path(): + return core.manager_snapshot_path + + @staticmethod + def get_custom_nodes_paths(): + return folder_paths.get_folder_paths('custom_nodes') + + +cmd_ctx = Ctx() def install_node(node_spec_str, is_all=False, cnt_msg=''): if core.is_valid_url(node_spec_str): # install via urls - res = asyncio.run(core.gitclone_install(node_spec_str, no_deps=channel_ctx.no_deps)) + res = asyncio.run(core.gitclone_install(node_spec_str, no_deps=cmd_ctx.no_deps)) if not res.result: print(res.msg) print(f"[bold red]ERROR: An error occurred while installing '{node_spec_str}'.[/bold red]") @@ -129,7 +172,7 @@ def install_node(node_spec_str, is_all=False, cnt_msg=''): if not is_specified: version_spec = None - res = asyncio.run(unified_manager.install_by_id(node_name, version_spec, channel_ctx.channel, channel_ctx.mode, instant_execution=True, no_deps=channel_ctx.no_deps)) + res = asyncio.run(unified_manager.install_by_id(node_name, version_spec, cmd_ctx.channel, cmd_ctx.mode, instant_execution=True, no_deps=cmd_ctx.no_deps)) if res.action == 'skip': print(f"{cnt_msg} [ SKIP ] {node_name:50} => Already installed") @@ -175,7 +218,7 @@ def fix_node(node_spec_str, is_all=False, cnt_msg=''): node_name, version_spec, _ = node_spec print(f"{cnt_msg} [ FIXING ]: {node_name:50}[{version_spec}]") - res = unified_manager.unified_fix(node_name, version_spec, no_deps=channel_ctx.no_deps) + res = unified_manager.unified_fix(node_name, version_spec, no_deps=cmd_ctx.no_deps) if not res.result: print(f"ERROR: f{res.msg}") @@ -215,7 +258,7 @@ def update_node(node_spec_str, is_all=False, cnt_msg=''): node_name, version_spec, _ = node_spec - res = unified_manager.unified_update(node_name, version_spec, no_deps=channel_ctx.no_deps, return_postinstall=True) + res = unified_manager.unified_update(node_name, version_spec, no_deps=cmd_ctx.no_deps, return_postinstall=True) if not res.result: print(f"ERROR: An error occurred while updating '{node_name}'.") @@ -333,7 +376,7 @@ def disable_node(node_spec_str: str, is_all=False, cnt_msg=''): def show_list(kind, simple=False): - custom_nodes = asyncio.run(unified_manager.get_custom_nodes(channel=channel_ctx.channel, mode=channel_ctx.mode)) + custom_nodes = asyncio.run(unified_manager.get_custom_nodes(channel=cmd_ctx.channel, mode=cmd_ctx.mode)) # collect not-installed unknown nodes not_installed_unknown_nodes = [] @@ -454,7 +497,7 @@ def show_snapshot(simple_mode=False): def show_snapshot_list(simple_mode=False): - snapshot_path = os.path.join(comfyui_manager_path, 'snapshots') + snapshot_path = cmd_ctx.get_snapshot_path() files = os.listdir(snapshot_path) json_files = [x for x in files if x.endswith('.json')] @@ -463,11 +506,11 @@ def show_snapshot_list(simple_mode=False): def cancel(): - if os.path.exists(script_path): - os.remove(script_path) + if os.path.exists(cmd_ctx.get_startup_scripts_path()): + os.remove(cmd_ctx.get_startup_scripts_path()) - if os.path.exists(restore_snapshot_path): - os.remove(restore_snapshot_path) + if os.path.exists(cmd_ctx.get_restore_snapshot_path()): + os.remove(cmd_ctx.get_restore_snapshot_path()) def auto_save_snapshot(): @@ -562,9 +605,14 @@ def install( help="Skip installing any Python dependencies", ), ] = False, + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) - channel_ctx.set_no_deps(no_deps) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_no_deps(no_deps) for_each_nodes(nodes, act=install_node) @@ -592,9 +640,14 @@ def reinstall( help="Skip installing any Python dependencies", ), ] = False, + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) - channel_ctx.set_no_deps(no_deps) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_no_deps(no_deps) for_each_nodes(nodes, act=reinstall_node) @@ -615,7 +668,7 @@ def uninstall( help="[remote|local|cache]" ), ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_channel_mode(channel, mode) for_each_nodes(nodes, act=uninstall_node) @@ -636,8 +689,13 @@ def update( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) if 'all' in nodes: auto_save_snapshot() @@ -667,8 +725,13 @@ def disable( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) if 'all' in nodes: auto_save_snapshot() @@ -693,8 +756,13 @@ def enable( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) if 'all' in nodes: auto_save_snapshot() @@ -719,8 +787,13 @@ def fix( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) if 'all' in nodes: auto_save_snapshot() @@ -754,6 +827,10 @@ def show( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): valid_commands = [ "installed", @@ -769,7 +846,8 @@ def show( typer.echo(f"Invalid command: `show {arg}`", err=True) exit(1) - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) if arg == 'snapshot': show_snapshot() elif arg == 'snapshot-list': @@ -794,6 +872,10 @@ def simple_show( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): valid_commands = [ "installed", @@ -808,7 +890,9 @@ def simple_show( typer.echo(f"[bold red]Invalid command: `show {arg}`[/bold red]", err=True) exit(1) - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) + if arg == 'snapshot': show_snapshot(True) elif arg == 'snapshot-list': @@ -821,8 +905,15 @@ def simple_show( def cli_only_mode( mode: str = typer.Argument( ..., help="[enable|disable]" - )): - cli_mode_flag = os.path.join(os.path.dirname(__file__), '.enable-cli-only-mode') + ), + user_directory: str = typer.Option( + None, + help="user directory" + ) +): + cmd_ctx.set_user_directory(user_directory) + cli_mode_flag = os.path.join(cmd_ctx.manager_files_directory, '.enable-cli-only-mode') + if mode.lower() == 'enable': with open(cli_mode_flag, 'w'): pass @@ -857,8 +948,13 @@ def deps_in_workflow( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ) ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) input_path = workflow output_path = output @@ -867,7 +963,7 @@ def deps_in_workflow( print(f"[bold red]File not found: {input_path}[/bold red]") exit(1) - used_exts, unknown_nodes = asyncio.run(core.extract_nodes_from_workflow(input_path, mode=channel_ctx.mode, channel_url=channel_ctx.channel)) + used_exts, unknown_nodes = asyncio.run(core.extract_nodes_from_workflow(input_path, mode=cmd_ctx.mode, channel_url=cmd_ctx.channel)) custom_nodes = {} for x in used_exts: @@ -894,7 +990,13 @@ def save_snapshot( show_default=False, help="Specify the output file path. (.json/.yaml)" ), ] = None, + user_directory: str = typer.Option( + None, + help="user directory" + ) ): + cmd_ctx.set_user_directory(user_directory) + path = core.save_snapshot_with_postfix('snapshot', output) print(f"Current snapshot is saved as `{path}`") @@ -920,7 +1022,13 @@ def restore_snapshot( is_flag=True, help="Restore for pip packages specified by local paths.", ), + user_directory: str = typer.Option( + None, + help="user directory" + ) ): + cmd_ctx.set_user_directory(user_directory) + extras = [] if pip_non_url: extras.append('--pip-non-url') @@ -936,7 +1044,7 @@ def restore_snapshot( if os.path.exists(snapshot_name): snapshot_path = os.path.abspath(snapshot_name) else: - snapshot_path = os.path.join(core.comfyui_manager_path, 'snapshots', snapshot_name) + snapshot_path = os.path.join(cmd_ctx.get_snapshot_path(), snapshot_name) if not os.path.exists(snapshot_path): print(f"[bold red]ERROR: `{snapshot_path}` is not exists.[/bold red]") exit(1) @@ -952,9 +1060,21 @@ def restore_snapshot( @app.command( "restore-dependencies", help="Restore dependencies from whole installed custom nodes." ) -def restore_dependencies(): - node_paths = [os.path.join(custom_nodes_path, name) for name in os.listdir(custom_nodes_path) - if os.path.isdir(os.path.join(custom_nodes_path, name)) and not name.endswith('.disabled')] +def restore_dependencies( + user_directory: str = typer.Option( + None, + help="user directory" + ) +): + cmd_ctx.set_user_directory(user_directory) + + node_paths = [] + + for base_path in cmd_ctx.get_custom_nodes_paths(): + for name in os.listdir(base_path): + target = os.path.join(base_path, name) + if os.path.isdir(target) and not name.endswith('.disabled'): + node_paths.append(target) total = len(node_paths) i = 1 @@ -971,7 +1091,8 @@ def restore_dependencies(): def post_install( path: str = typer.Argument( help="path to custom node", - )): + ) +): path = os.path.expanduser(path) unified_manager.execute_install_script('', path, instant_execution=True) @@ -995,8 +1116,13 @@ def install_deps( None, help="[remote|local|cache]" ), + user_directory: str = typer.Option( + None, + help="user directory" + ), ): - channel_ctx.set_channel_mode(channel, mode) + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) auto_save_snapshot() if not os.path.exists(deps): @@ -1040,14 +1166,20 @@ def export_custom_node_ids( mode: str = typer.Option( None, help="[remote|local|cache]" - )): - channel_ctx.set_channel_mode(channel, mode) + ), + user_directory: str = typer.Option( + None, + help="user directory" + ), +): + cmd_ctx.set_user_directory(user_directory) + cmd_ctx.set_channel_mode(channel, mode) with open(path, "w", encoding='utf-8') as output_file: for x in unified_manager.cnr_map.keys(): print(x, file=output_file) - custom_nodes = asyncio.run(unified_manager.get_custom_nodes(channel=channel_ctx.channel, mode=channel_ctx.mode)) + custom_nodes = asyncio.run(unified_manager.get_custom_nodes(channel=cmd_ctx.channel, mode=cmd_ctx.mode)) for x in custom_nodes.values(): if 'cnr_latest' not in x: if len(x['files']) == 1: @@ -1063,7 +1195,13 @@ def export_custom_node_ids( "migrate", help="Migrate legacy node system to new node system", ) -def migrate(): +def migrate( + user_directory: str = typer.Option( + None, + help="user directory" + ) +): + cmd_ctx.set_user_directory(user_directory) asyncio.run(unified_manager.migrate_unmanaged_nodes()) diff --git a/git_helper.py b/git_helper.py index 2ffe87c6..a709811a 100644 --- a/git_helper.py +++ b/git_helper.py @@ -326,20 +326,18 @@ def invalidate_custom_node_file(file_custom_node_infos): download_url(url, working_directory) -def apply_snapshot(target): +def apply_snapshot(path): 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'): + if not path.endswith('.json') and not path.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'): + if path.endswith('.json'): info = json.load(snapshot_file) - elif target.endswith('.yaml'): + elif path.endswith('.yaml'): info = yaml.load(snapshot_file, Loader=yaml.SafeLoader) info = info['custom_nodes'] else: diff --git a/glob/manager_core.py b/glob/manager_core.py index 811589ed..67c64769 100644 --- a/glob/manager_core.py +++ b/glob/manager_core.py @@ -34,7 +34,7 @@ import manager_util import manager_downloader -version_code = [3, 0] +version_code = [3, 1] version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') @@ -89,7 +89,6 @@ def check_invalid_nodes(): try: import folder_paths - node_paths = folder_paths.get_folder_paths("custom_nodes") except: try: sys.path.append(comfy_path) @@ -140,19 +139,50 @@ if comfy_path is None: comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..')) +channel_list_template_path = os.path.join(manager_util.comfyui_manager_path, 'channels.list.template') +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_snapshot_path = None +manager_pip_overrides_path = None + +def update_user_directory(user_dir): + global manager_files_path + global manager_config_path + global manager_channel_list_path + global manager_startup_script_path + global manager_snapshot_path + global manager_pip_overrides_path + + manager_files_path = os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager')) + if not os.path.exists(manager_files_path): + os.makedirs(manager_files_path) + + manager_snapshot_path = os.path.join(manager_files_path, "snapshots") + if not os.path.exists(manager_snapshot_path): + os.makedirs(manager_snapshot_path) + + manager_startup_script_path = os.path.join(manager_files_path, "startup-scripts") + if not os.path.exists(manager_startup_script_path): + os.makedirs(manager_startup_script_path) + + manager_config_path = os.path.join(manager_files_path, 'config.ini') + manager_channel_list_path = os.path.join(manager_files_path, 'channels.list') + manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json") + try: import folder_paths - manager_core_config_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'manager-core.ini')) + update_user_directory(folder_paths.get_user_directory()) + except Exception: # fallback: # This case is only possible when running with cm-cli, and in practice, this case is not actually used. - manager_core_config_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, 'manager-core.ini')) + update_user_directory(os.path.abspath(manager_util.comfyui_manager_path)) -channel_list_path = os.path.join(manager_util.comfyui_manager_path, 'channels.list') -config_path = os.path.join(manager_util.comfyui_manager_path, "config.ini") -startup_script_path = os.path.join(manager_util.comfyui_manager_path, "startup-scripts") -git_script_path = os.path.join(manager_util.comfyui_manager_path, "git_helper.py") cached_config = None js_path = None @@ -785,7 +815,7 @@ class UnifiedManager: return True def reserve_cnr_switch(self, target, zip_url, from_path, to_path, no_deps): - script_path = os.path.join(startup_script_path, "install-scripts.txt") + script_path = os.path.join(manager_startup_script_path, "install-scripts.txt") with open(script_path, "a") as file: obj = [target, "#LAZY-CNR-SWITCH-SCRIPT", zip_url, from_path, to_path, no_deps, get_default_custom_nodes_path(), sys.executable] file.write(f"{obj}\n") @@ -795,7 +825,7 @@ class UnifiedManager: return True def reserve_migration(self, moves): - script_path = os.path.join(startup_script_path, "install-scripts.txt") + script_path = os.path.join(manager_startup_script_path, "install-scripts.txt") with open(script_path, "a") as file: obj = ["", "#LAZY-MIGRATION", moves] file.write(f"{obj}\n") @@ -1402,10 +1432,10 @@ def get_channel_dict(): if channel_dict is None: channel_dict = {} - if not os.path.exists(channel_list_path): - shutil.copy(channel_list_path+'.template', channel_list_path) + if not os.path.exists(manager_channel_list_path): + shutil.copy(channel_list_template_path, manager_channel_list_path) - with open(os.path.join(manager_util.comfyui_manager_path, 'channels.list'), 'r') as file: + with open(manager_channel_list_path, 'r') as file: channels = file.read() for x in channels.split('\n'): channel_info = x.split("::") @@ -1468,18 +1498,18 @@ def write_config(): 'skip_migration_check': get_config()['skip_migration_check'], } - directory = os.path.dirname(manager_core_config_path) + directory = os.path.dirname(manager_config_path) if not os.path.exists(directory): os.makedirs(directory) - with open(manager_core_config_path, 'w') as configfile: + with open(manager_config_path, 'w') as configfile: config.write(configfile) def read_config(): try: config = configparser.ConfigParser() - config.read(config_path) + config.read(manager_config_path) default_conf = config['default'] # policy migration: disable_unsecure_features -> security_level @@ -1545,10 +1575,10 @@ 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 os.path.exists(startup_script_path): - os.makedirs(startup_script_path) + if not os.path.exists(manager_startup_script_path): + os.makedirs(manager_startup_script_path) - script_path = os.path.join(startup_script_path, "install-scripts.txt") + script_path = os.path.join(manager_startup_script_path, "install-scripts.txt") with open(script_path, "a") as file: obj = [repo_path] + install_cmd file.write(f"{obj}\n") @@ -2377,7 +2407,7 @@ def save_snapshot_with_postfix(postfix, path=None): date_time_format = now.strftime("%Y-%m-%d_%H-%M-%S") file_name = f"{date_time_format}_{postfix}" - path = os.path.join(manager_util.comfyui_manager_path, 'snapshots', f"{file_name}.json") + path = os.path.join(manager_snapshot_path, f"{file_name}.json") else: file_name = path.replace('\\', '/').split('/')[-1] file_name = file_name.split('.')[-2] diff --git a/glob/manager_server.py b/glob/manager_server.py index 90335440..2bd85218 100644 --- a/glob/manager_server.py +++ b/glob/manager_server.py @@ -634,8 +634,7 @@ async def fetch_externalmodel_list(request): @PromptServer.instance.routes.get("/snapshot/getlist") async def get_snapshot_list(request): - snapshots_directory = os.path.join(manager_util.comfyui_manager_path, 'snapshots') - items = [f[:-5] for f in os.listdir(snapshots_directory) if f.endswith('.json')] + items = [f[:-5] for f in os.listdir(core.manager_snapshot_path) if f.endswith('.json')] items.sort(reverse=True) return web.json_response({'items': items}, content_type='application/json') @@ -649,7 +648,7 @@ async def remove_snapshot(request): try: target = request.rel_url.query["target"] - path = os.path.join(manager_util.comfyui_manager_path, 'snapshots', f"{target}.json") + path = os.path.join(core.manager_snapshot_path, f"{target}.json") if os.path.exists(path): os.remove(path) @@ -667,12 +666,12 @@ async def restore_snapshot(request): try: target = request.rel_url.query["target"] - path = os.path.join(manager_util.comfyui_manager_path, 'snapshots', f"{target}.json") + path = os.path.join(core.manager_snapshot_path, f"{target}.json") if os.path.exists(path): - if not os.path.exists(core.startup_script_path): - os.makedirs(core.startup_script_path) + if not os.path.exists(core.manager_startup_script_path): + os.makedirs(core.manager_startup_script_path) - target_path = os.path.join(core.startup_script_path, "restore-snapshot.json") + target_path = os.path.join(core.manager_startup_script_path, "restore-snapshot.json") shutil.copy(path, target_path) print(f"Snapshot restore scheduled: `{target}`") @@ -1399,7 +1398,7 @@ async def default_cache_update(): threading.Thread(target=lambda: asyncio.run(default_cache_update())).start() -if not os.path.exists(core.config_path): +if not os.path.exists(core.manager_config_path): core.get_config() core.write_config() @@ -1408,5 +1407,5 @@ cm_global.register_extension('ComfyUI-Manager', {'version': core.version, 'name': 'ComfyUI Manager', 'nodes': {}, - 'description': 'It provides the ability to manage custom nodes in ComfyUI.', }) + 'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', }) diff --git a/glob/share_3rdparty.py b/glob/share_3rdparty.py index ce639f5f..8dc72333 100644 --- a/glob/share_3rdparty.py +++ b/glob/share_3rdparty.py @@ -65,10 +65,10 @@ async def share_option(request): def get_openart_auth(): - if not os.path.exists(os.path.join(core.comfyui_manager_path, ".openart_key")): + if not os.path.exists(os.path.join(core.manager_files_path, ".openart_key")): return None try: - with open(os.path.join(core.comfyui_manager_path, ".openart_key"), "r") as f: + with open(os.path.join(core.manager_files_path, ".openart_key"), "r") as f: openart_key = f.read().strip() return openart_key if openart_key else None except: @@ -76,10 +76,10 @@ def get_openart_auth(): def get_matrix_auth(): - if not os.path.exists(os.path.join(core.comfyui_manager_path, "matrix_auth")): + if not os.path.exists(os.path.join(core.manager_files_path, "matrix_auth")): return None try: - with open(os.path.join(core.comfyui_manager_path, "matrix_auth"), "r") as f: + with open(os.path.join(core.manager_files_path, "matrix_auth"), "r") as f: matrix_auth = f.read() homeserver, username, password = matrix_auth.strip().split("\n") if not homeserver or not username or not password: @@ -94,10 +94,10 @@ def get_matrix_auth(): def get_comfyworkflows_auth(): - if not os.path.exists(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey")): + if not os.path.exists(os.path.join(core.manager_files_path, "comfyworkflows_sharekey")): return None try: - with open(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey"), "r") as f: + with open(os.path.join(core.manager_files_path, "comfyworkflows_sharekey"), "r") as f: share_key = f.read() if not share_key.strip(): return None @@ -107,10 +107,10 @@ def get_comfyworkflows_auth(): def get_youml_settings(): - if not os.path.exists(os.path.join(core.comfyui_manager_path, ".youml")): + if not os.path.exists(os.path.join(core.manager_files_path, ".youml")): return None try: - with open(os.path.join(core.comfyui_manager_path, ".youml"), "r") as f: + with open(os.path.join(core.manager_files_path, ".youml"), "r") as f: youml_settings = f.read().strip() return youml_settings if youml_settings else None except: @@ -118,7 +118,7 @@ def get_youml_settings(): def set_youml_settings(settings): - with open(os.path.join(core.comfyui_manager_path, ".youml"), "w") as f: + with open(os.path.join(core.manager_files_path, ".youml"), "w") as f: f.write(settings) @@ -135,7 +135,7 @@ async def api_get_openart_auth(request): async def api_set_openart_auth(request): json_data = await request.json() openart_key = json_data['openart_key'] - with open(os.path.join(core.comfyui_manager_path, ".openart_key"), "w") as f: + with open(os.path.join(core.manager_files_path, ".openart_key"), "w") as f: f.write(openart_key) return web.Response(status=200) @@ -178,14 +178,14 @@ async def api_get_comfyworkflows_auth(request): @PromptServer.instance.routes.post("/manager/set_esheep_workflow_and_images") async def set_esheep_workflow_and_images(request): json_data = await request.json() - with open(os.path.join(core.comfyui_manager_path, "esheep_share_message.json"), "w", encoding='utf-8') as file: + with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), "w", encoding='utf-8') as file: json.dump(json_data, file, indent=4) return web.Response(status=200) @PromptServer.instance.routes.get("/manager/get_esheep_workflow_and_images") async def get_esheep_workflow_and_images(request): - with open(os.path.join(core.comfyui_manager_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file: + with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file: data = json.load(file) return web.Response(status=200, text=json.dumps(data)) @@ -194,12 +194,12 @@ def set_matrix_auth(json_data): homeserver = json_data['homeserver'] username = json_data['username'] password = json_data['password'] - with open(os.path.join(core.comfyui_manager_path, "matrix_auth"), "w") as f: + with open(os.path.join(core.manager_files_path, "matrix_auth"), "w") as f: f.write("\n".join([homeserver, username, password])) def set_comfyworkflows_auth(comfyworkflows_sharekey): - with open(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey"), "w") as f: + with open(os.path.join(core.manager_files_path, "comfyworkflows_sharekey"), "w") as f: f.write(comfyworkflows_sharekey) diff --git a/prestartup_script.py b/prestartup_script.py index 6d86c056..576aadcb 100644 --- a/prestartup_script.py +++ b/prestartup_script.py @@ -18,6 +18,7 @@ import manager_util import cm_global import manager_downloader from datetime import datetime +import folder_paths security_check.security_check() @@ -73,17 +74,19 @@ cm_global.register_api('cm.is_import_failed_extension', is_import_failed_extensi 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") + +custom_nodes_base_path = folder_paths.get_folder_paths('custom_nodes')[0] +manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'ComfyUI-Manager')) +manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json") +restore_snapshot_path = os.path.join(manager_files_path, "startup-scripts", "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: +if os.path.exists(manager_pip_overrides_path): + with open(manager_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' cm_global.pip_overrides['ultralytics'] = 'ultralytics==8.3.40' # for security @@ -146,15 +149,18 @@ try: postfix = "" # Logger setup + log_path_base = None 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_path_base = os.path.join(folder_paths.user_directory, 'comfyui') - log_file = open(f"comfyui{postfix}.log", "w", encoding="utf-8", errors="ignore") + if os.path.exists(f"{log_path_base}{postfix}.log"): + if os.path.exists(f"{log_path_base}{postfix}.prev.log"): + if os.path.exists(f"{log_path_base}{postfix}.prev2.log"): + os.remove(f"{log_path_base}{postfix}.prev2.log") + os.rename(f"{log_path_base}{postfix}.prev.log", f"{log_path_base}{postfix}.prev2.log") + os.rename(f"{log_path_base}{postfix}.log", f"{log_path_base}{postfix}.prev.log") + + log_file = open(f"{log_path_base}{postfix}.log", "w", encoding="utf-8", errors="ignore") log_lock = threading.Lock() @@ -339,8 +345,8 @@ 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')) +if log_path_base is not None: + print("** Log path:", os.path.abspath(f'{log_path_base}.log')) else: print("** Log path: file logging is disabled") @@ -386,7 +392,7 @@ 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") +script_list_path = os.path.join(folder_paths.user_directory, "default", "ComfyUI-Manager", "startup-scripts", "install-scripts.txt") pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages()) @@ -463,7 +469,7 @@ if os.path.exists(restore_snapshot_path): 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) + exit_code = process_wrap(cmd_str, custom_nodes_base_path, handler=msg_capture, env=new_env) if exit_code != 0: print("[ComfyUI-Manager] Restore snapshot failed.") diff --git a/pyproject.toml b/pyproject.toml index 6cb9eac2..693590c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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.0" +version = "3.1" license = { file = "LICENSE.txt" } dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]