diff --git a/cm-cli.py b/cm-cli.py index 93aaa553..cb0e7c98 100644 --- a/cm-cli.py +++ b/cm-cli.py @@ -54,7 +54,7 @@ def check_comfyui_hash(): core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime -# check_comfyui_hash() # This is a preparation step for manager_core +check_comfyui_hash() # This is a preparation step for manager_core def read_downgrade_blacklist(): @@ -83,10 +83,6 @@ class Ctx: self.mode = 'remote' self.processed_install = set() self.custom_node_map_cache = None - self.no_deps = False - - def set_no_deps(self, no_deps): - self.no_deps = no_deps def set_channel_mode(self, channel, mode): if mode is not None: @@ -206,11 +202,10 @@ class Ctx: cm_ctx = Ctx() -def install_node(node_name, is_all=False, cnt_msg='', install_path=None, no_deps=False): +def install_node(node_name, is_all=False, cnt_msg=''): if core.is_valid_url(node_name): # install via urls - print(f"Installing {node_name} to {install_path}") - res = core.gitclone_install([node_name], install_path=install_path) + res = core.gitclone_install([node_name]) if not res: print(f"[bold red]ERROR: An error occurred while installing '{node_name}'.[/bold red]") else: @@ -224,7 +219,7 @@ def install_node(node_name, is_all=False, cnt_msg='', install_path=None, no_deps elif os.path.exists(node_path + '.disabled'): enable_node(node_name) else: - res = core.gitclone_install(node_item['files'], instant_execution=True, msg_prefix=f"[{cnt_msg}] ", install_path=install_path, no_deps=no_deps) + res = core.gitclone_install(node_item['files'], instant_execution=True, msg_prefix=f"[{cnt_msg}] ") if not res: print(f"[bold red]ERROR: An error occurred while installing '{node_name}'.[/bold red]") else: @@ -474,7 +469,7 @@ def auto_save_snapshot(): print(f"Current snapshot is saved as `{path}`") -def for_each_nodes(nodes, act, allow_all=True, install_path=None, no_deps=False): +def for_each_nodes(nodes, act, allow_all=True): is_all = False if allow_all and 'all' in nodes: is_all = True @@ -486,7 +481,7 @@ def for_each_nodes(nodes, act, allow_all=True, install_path=None, no_deps=False) i = 1 for x in nodes: try: - act(x, is_all=is_all, cnt_msg=f'{i}/{total}', install_path=install_path, no_deps=no_deps) + act(x, is_all=is_all, cnt_msg=f'{i}/{total}') except Exception as e: print(f"ERROR: {e}") traceback.print_exc() @@ -518,22 +513,9 @@ def install( None, help="[remote|local|cache]" ), - install_path: str = typer.Option( - None, - help="Specify the installation path" - ), - no_deps: Annotated[ - Optional[bool], - typer.Option( - "--no-deps", - show_default=False, - help="Skip installing any Python dependencies", - ), - ] = False, ): cm_ctx.set_channel_mode(channel, mode) - cm_ctx.set_no_deps(no_deps) - for_each_nodes(nodes, act=install_node, install_path=install_path, no_deps=no_deps) + for_each_nodes(nodes, act=install_node) @app.command(help="Reinstall custom nodes") diff --git a/glob/manager_core.py b/glob/manager_core.py index 959e2d36..a61d19be 100644 --- a/glob/manager_core.py +++ b/glob/manager_core.py @@ -53,8 +53,8 @@ def get_custom_nodes_paths(): def get_comfyui_tag(): + repo = git.Repo(comfy_path) try: - repo = git.Repo(comfy_path) return repo.git.describe('--tags') except: return None @@ -82,7 +82,6 @@ comfy_ui_required_commit_datetime = datetime(2024, 1, 24, 0, 0, 0) comfy_ui_revision = "Unknown" comfy_ui_commit_datetime = datetime(1900, 1, 1, 0, 0, 0) -is_electron = os.environ.get("ORIGINAL_XDG_CURRENT_DESKTOP") != None cache_lock = threading.Lock() @@ -433,7 +432,7 @@ def __win_check_git_pull(path): process.wait() -def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=False, no_deps=False): +def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=False): install_script_path = os.path.join(repo_path, "install.py") requirements_path = os.path.join(repo_path, "requirements.txt") @@ -441,7 +440,7 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa install_cmd = ["#LAZY-INSTALL-SCRIPT", sys.executable] try_install_script(url, repo_path, install_cmd) else: - if os.path.exists(requirements_path) and not no_deps: + if os.path.exists(requirements_path): print("Install: pip packages") pip_fixer = PIPFixer(get_installed_packages()) with open(requirements_path, "r") as requirements_file: @@ -585,7 +584,7 @@ def is_valid_url(url): return False -def gitclone_install(files, instant_execution=False, msg_prefix='', install_path=None, no_deps=False): +def gitclone_install(files, instant_execution=False, msg_prefix=''): print(f"{msg_prefix}Install: {files}") for url in files: if not is_valid_url(url): @@ -595,9 +594,9 @@ def gitclone_install(files, instant_execution=False, msg_prefix='', install_path if url.endswith("/"): url = url[:-1] try: - print(f"Download: git clone '{url}' to {install_path}") + print(f"Download: git clone '{url}'") repo_name = os.path.splitext(os.path.basename(url))[0] - repo_path = os.path.join(install_path or get_default_custom_nodes_path(), repo_name) + repo_path = os.path.join(get_default_custom_nodes_path(), repo_name) # Clone the repository from the remote URL if not instant_execution and platform.system() == 'Windows': @@ -609,7 +608,7 @@ def gitclone_install(files, instant_execution=False, msg_prefix='', install_path repo.git.clear_cache() repo.close() - if not execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps): + if not execute_install_script(url, repo_path, instant_execution=instant_execution): return False except Exception as e: diff --git a/glob/manager_server.py b/glob/manager_server.py index f14a0de0..2449c04a 100644 --- a/glob/manager_server.py +++ b/glob/manager_server.py @@ -11,8 +11,6 @@ import threading import re import shutil import git -import datetime -import logging from server import PromptServer import manager_core as core @@ -1221,14 +1219,12 @@ async def get_notice(request): markdown_content = add_target_blank(markdown_content) try: - if core.is_electron: - pass - elif core.comfy_ui_commit_datetime == datetime.datetime(1900, 1, 1, 0, 0, 0): + if core.comfy_ui_commit_datetime == datetime(1900, 1, 1, 0, 0, 0): markdown_content = f'

Your ComfyUI isn\'t git repo.

' + markdown_content elif core.comfy_ui_required_commit_datetime.date() > core.comfy_ui_commit_datetime.date(): markdown_content = f'

Your ComfyUI is too OUTDATED!!!

' + markdown_content - except Exception as error: - logging.warning("Unexpected error when checking ComfyUI version via git.") + except: + pass return web.Response(text=markdown_content, status=200) else: diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js index 8f17f800..8d2b12bf 100644 --- a/js/comfyui-manager.js +++ b/js/comfyui-manager.js @@ -238,6 +238,7 @@ function is_legacy_front() { document.head.appendChild(docStyle); +var update_comfyui_button = null; var fetch_updates_button = null; var update_all_button = null; var badge_mode = "none"; @@ -560,6 +561,40 @@ function drawBadge(node, orig, restArgs) { } +async function updateComfyUI() { + let prev_text = update_comfyui_button.innerText; + update_comfyui_button.innerText = "Updating ComfyUI..."; + update_comfyui_button.disabled = true; + update_comfyui_button.style.backgroundColor = "gray"; + + try { + const response = await api.fetchApi('/comfyui_manager/update_comfyui'); + + if (response.status == 400) { + show_message('Failed to update ComfyUI.'); + return false; + } + + if (response.status == 201) { + show_message('ComfyUI has been successfully updated.'); + } + else { + show_message('ComfyUI is already up to date with the latest version.'); + } + + return true; + } + catch (exception) { + show_message(`Failed to update ComfyUI / ${exception}`); + return false; + } + finally { + update_comfyui_button.disabled = false; + update_comfyui_button.innerText = prev_text; + update_comfyui_button.style.backgroundColor = ""; + } +} + async function fetchUpdates(update_check_checkbox) { let prev_text = fetch_updates_button.innerText; fetch_updates_button.innerText = "Fetching updates..."; @@ -612,13 +647,15 @@ async function fetchUpdates(update_check_checkbox) { async function updateAll(update_check_checkbox, manager_dialog) { let prev_text = update_all_button.innerText; - update_all_button.innerText = "Updating all..."; + update_all_button.innerText = "Updating all...(ComfyUI)"; update_all_button.disabled = true; update_all_button.style.backgroundColor = "gray"; try { var mode = manager_instance.datasrc_combo.value; + update_all_button.innerText = "Updating all..."; + const response1 = await api.fetchApi('/comfyui_manager/update_comfyui'); const response2 = await api.fetchApi(`/customnode/update_all?mode=${mode}`); if (response2.status == 403) { @@ -626,12 +663,12 @@ async function updateAll(update_check_checkbox, manager_dialog) { return false; } - if (response2.status == 400) { - show_message('Failed to update several extensions.

See terminal log.
'); + if (response1.status == 400 || response2.status == 400) { + show_message('Failed to update ComfyUI or several extensions.

See terminal log.
'); return false; } - if(response2.status == 201) { + if(response1.status == 201 || response2.status == 201) { const update_info = await response2.json(); let failed_list = ""; @@ -645,7 +682,7 @@ async function updateAll(update_check_checkbox, manager_dialog) { } show_message( - "All extensions have been updated to the latest version.
To apply the updated custom node, please ComfyUI. And refresh browser.
" + "ComfyUI and all extensions have been updated to the latest version.
To apply the updated custom node, please ComfyUI. And refresh browser.
" +failed_list +updated_list ); @@ -659,13 +696,13 @@ async function updateAll(update_check_checkbox, manager_dialog) { }); } else { - show_message('All extensions are already up-to-date with the latest versions.'); + show_message('ComfyUI and all extensions are already up-to-date with the latest versions.'); } return true; } catch (exception) { - show_message(`Failed to update several extensions / ${exception}`); + show_message(`Failed to update ComfyUI or several extensions / ${exception}`); return false; } finally { @@ -700,6 +737,14 @@ class ManagerMenuDialog extends ComfyDialog { createControlsMid() { let self = this; + update_comfyui_button = + $el("button.cm-button", { + type: "button", + textContent: "Update ComfyUI", + onclick: + () => updateComfyUI() + }); + fetch_updates_button = $el("button.cm-button", { type: "button", @@ -711,7 +756,7 @@ class ManagerMenuDialog extends ComfyDialog { update_all_button = $el("button.cm-button", { type: "button", - textContent: "Update All Nodes", + textContent: "Update All", onclick: () => updateAll(this.update_check_checkbox, self) }); @@ -769,8 +814,22 @@ class ManagerMenuDialog extends ComfyDialog { $el("br", {}, []), update_all_button, + update_comfyui_button, fetch_updates_button, + $el("br", {}, []), + $el("button.cm-button", { + type: "button", + textContent: "Alternatives of A1111", + onclick: + () => { + if(!CustomNodesManager.instance) { + CustomNodesManager.instance = new CustomNodesManager(app, self); + } + CustomNodesManager.instance.show(CustomNodesManager.ShowMode.ALTERNATIVES); + } + }), + $el("br", {}, []), $el("button.cm-button-red", { type: "button", @@ -975,6 +1034,28 @@ class ManagerMenuDialog extends ComfyDialog { $el("br", {}, []), $el("filedset.cm-experimental", {}, [ $el("legend.cm-experimental-legend", {}, ["EXPERIMENTAL"]), + $el("button.cm-experimental-button", { + type: "button", + textContent: "Snapshot Manager", + onclick: + () => { + if(!SnapshotManager.instance) + SnapshotManager.instance = new SnapshotManager(app, self); + SnapshotManager.instance.show(); + } + }), + $el("button.cm-experimental-button", { + type: "button", + textContent: "Install PIP packages", + onclick: + () => { + var url = prompt("Please enumerate the pip packages to be installed.\n\nExample: insightface opencv-python-headless>=4.1.1\n", ""); + + if (url !== null) { + install_pip(url, self); + } + } + }), $el("button.cm-experimental-button", { type: "button", textContent: "Unload models", @@ -990,7 +1071,7 @@ class ManagerMenuDialog extends ComfyDialog { id: 'cm-manual-button', type: "button", textContent: "Community Manual", - onclick: () => { window.open("https://docs.comfy.org/", "comfyui-community-manual"); } + onclick: () => { window.open("https://blenderneko.github.io/ComfyUI-docs/", "comfyui-community-manual"); } }, [ $el("div.pysssss-workflow-arrow-2", { id: `cm-manual-button-arrow`, @@ -1002,8 +1083,8 @@ class ManagerMenuDialog extends ComfyDialog { const menu = new LiteGraph.ContextMenu( [ { - title: "ComfyUI Examples", - callback: () => { window.open("https://comfyanonymous.github.io/ComfyUI_examples", "comfyui-community-manual3"); }, + title: "ComfyUI Docs", + callback: () => { window.open("https://docs.comfy.org/", "comfyui-official-manual"); }, }, { title: "Comfy Custom Node How To", @@ -1013,6 +1094,10 @@ class ManagerMenuDialog extends ComfyDialog { title: "ComfyUI Guide To Making Custom Nodes", callback: () => { window.open("https://github.com/Suzie1/ComfyUI_Guide_To_Making_Custom_Nodes/wiki", "comfyui-community-manual2"); }, }, + { + title: "ComfyUI Examples", + callback: () => { window.open("https://comfyanonymous.github.io/ComfyUI_examples", "comfyui-community-manual3"); }, + }, { title: "Close", callback: () => { @@ -1033,6 +1118,49 @@ class ManagerMenuDialog extends ComfyDialog { }) ]), + $el("button", { + id: 'workflowgallery-button', + type: "button", + style: { + ...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {}) + }, + onclick: (e) => { + const last_visited_site = localStorage.getItem("wg_last_visited") + if (!!last_visited_site) { + window.open(last_visited_site, last_visited_site); + } else { + this.handleWorkflowGalleryButtonClick(e) + } + }, + }, [ + $el("p", { + textContent: 'Workflow Gallery', + style: { + 'text-align': 'center', + 'color': 'var(--input-text)', + 'font-size': '18px', + 'margin': 0, + 'padding': 0, + } + }, [ + $el("p", { + id: 'workflowgallery-button-last-visited-label', + textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`, + style: { + 'text-align': 'center', + 'color': 'var(--input-text)', + 'font-size': '12px', + 'margin': 0, + 'padding': 0, + } + }) + ]), + $el("div.pysssss-workflow-arrow-2", { + id: `comfyworkflows-button-arrow`, + onclick: this.handleWorkflowGalleryButtonClick + }) + ]), + $el("button.cm-button", { id: 'cm-nodeinfo-button', type: "button", @@ -1084,6 +1212,105 @@ class ManagerMenuDialog extends ComfyDialog { show() { this.element.style.display = "block"; } + + handleWorkflowGalleryButtonClick(e) { + e.preventDefault(); + e.stopPropagation(); + LiteGraph.closeAllContextMenus(); + + // Modify the style of the button so that the UI can indicate the last + // visited site right away. + const modifyButtonStyle = (url) => { + const workflowGalleryButton = document.getElementById('workflowgallery-button'); + workflowGalleryButton.style.height = '50px'; + const lastVisitedLabel = document.getElementById('workflowgallery-button-last-visited-label'); + lastVisitedLabel.textContent = `(${url.split('/')[2]})`; + } + + const menu = new LiteGraph.ContextMenu( + [ + { + title: "Share your art", + callback: () => { + if (share_option === 'openart') { + showOpenArtShareDialog(); + return; + } else if (share_option === 'matrix' || share_option === 'comfyworkflows') { + showShareDialog(share_option); + return; + } else if (share_option === 'youml') { + showYouMLShareDialog(); + return; + } + + if (!ShareDialogChooser.instance) { + ShareDialogChooser.instance = new ShareDialogChooser(); + } + ShareDialogChooser.instance.show(); + }, + }, + { + title: "Open 'openart.ai'", + callback: () => { + const url = "https://openart.ai/workflows/dev"; + localStorage.setItem("wg_last_visited", url); + window.open(url, url); + modifyButtonStyle(url); + }, + }, + { + title: "Open 'youml.com'", + callback: () => { + const url = "https://youml.com/?from=comfyui-share"; + localStorage.setItem("wg_last_visited", url); + window.open(url, url); + modifyButtonStyle(url); + }, + }, + { + title: "Open 'comfyworkflows.com'", + callback: () => { + const url = "https://comfyworkflows.com/"; + localStorage.setItem("wg_last_visited", url); + window.open(url, url); + modifyButtonStyle(url); + }, + }, + { + title: "Open 'esheep'", + callback: () => { + const url = "https://www.esheep.com"; + localStorage.setItem("wg_last_visited", url); + window.open(url, url); + modifyButtonStyle(url); + }, + }, + { + title: "Open 'Copus.io'", + callback: () => { + const url = "https://www.copus.io"; + localStorage.setItem("wg_last_visited", url); + window.open(url, url); + modifyButtonStyle(url); + }, + }, + { + title: "Close", + callback: () => { + LiteGraph.closeAllContextMenus(); + }, + } + ], + { + event: e, + scale: 1.3, + }, + window + ); + // set the id so that we can override the context menu's z-index to be above the comfyui manager menu + menu.root.id = "workflowgallery-button-menu"; + menu.root.classList.add("pysssss-workflow-popup-2"); + } }