diff --git a/__init__.py b/__init__.py index 0331649..ecb6540 100644 --- a/__init__.py +++ b/__init__.py @@ -1,16 +1,18 @@ # Title: ComfyUI Install Customs Nodes and javascript files # Author: AlekPet -# Version: 2024.07.15 +# Version: 2024.07.27 import os import importlib.util import subprocess import sys import shutil import __main__ -import pkgutil + +# import pkgutil import re import threading import ast +from concurrent.futures import ThreadPoolExecutor python = sys.executable @@ -35,7 +37,8 @@ humanReadableTextReg = re.compile("(?<=[a-z])([A-Z])|(?<=[A-Z])([A-Z][a-z]+)") module_name_cut_version = re.compile("[>=<]") -installed_modules = list(m[1] for m in pkgutil.iter_modules(None)) +installed_modules = {} +# installed_modules = {m[1] for m in pkgutil.iter_modules()} def log(*text): @@ -45,61 +48,22 @@ def log(*text): def information(datas): for info in datas: - if DEBUG: + if not DEBUG: print(info, end="") -def get_classes(code): - cls = [] - tree = ast.parse(code) - for n in ast.walk(tree): - if isinstance(n, ast.ClassDef) and "Node" in n.name: - cls.append(n.name) - return cls - - -def getNamesNodesInsidePyFile(nodeElement): - node_folder = os.path.join(extension_folder, nodeElement) - cls_name = [] - for f in os.listdir(node_folder): - ext = os.path.splitext(f) - path_to_py = os.path.join(node_folder, f) - # Find files extensions .py - if ( - os.path.isfile(path_to_py) - and not f.startswith("__") - and ext[1] == ".py" - and ext[0] != "__init__" - ): - with open(path_to_py, "r") as pyf: - cls_name = get_classes(pyf.read()) - - return cls_name - -def module_install(commands, cwd="."): - result = subprocess.Popen( - commands, - cwd=cwd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - ) - out = threading.Thread(target=information, args=(result.stdout,)) - err = threading.Thread(target=information, args=(result.stderr,)) - out.start() - err.start() - out.join() - err.join() - - return result.wait() +def printColorInfo(text, color="\033[92m"): + CLEAR = "\033[0m" + print(f"{color}{text}{CLEAR}") -def checkModules(nodeElement): - file_requir = os.path.join(extension_folder, nodeElement, "requirements.txt") - if os.path.exists(file_requir): - log(" -> File 'requirements.txt' found!") - module_install([sys.executable, "-m", "pip", "install", "-r", file_requir]) +def get_classes(code): + tree = ast.parse(code) + return [ + n.name + for n in ast.walk(tree) + if isinstance(n, ast.ClassDef) and "Node" in n.name + ] def addComfyUINodesToMapping(nodeElement): @@ -149,6 +113,22 @@ def addComfyUINodesToMapping(nodeElement): ) +def getNamesNodesInsidePyFile(nodeElement): + node_folder = os.path.join(extension_folder, nodeElement) + cls_names = [] + for f in os.listdir(node_folder): + ext = os.path.splitext(f) + if ( + os.path.isfile(os.path.join(node_folder, f)) + and not f.startswith("__") + and ext[1] == ".py" + and ext[0] != "__init__" + ): + with open(os.path.join(node_folder, f), "r") as pyf: + cls_names.extend(get_classes(pyf.read())) + return cls_names + + def checkFolderIsset(): log(f"* Check and make not isset dirs...") for d in extension_dirs: @@ -159,21 +139,87 @@ def checkFolderIsset(): log(f"* Dir <{d}> created!") -def printColorInfo(text, color="\033[92m"): - CLEAR = "\033[0m" - print(f"{color}{text}{CLEAR}") +def module_install(commands, cwd="."): + result = subprocess.Popen( + commands, + cwd=cwd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + ) + out = threading.Thread(target=information, args=(result.stdout,)) + err = threading.Thread(target=information, args=(result.stderr,)) + out.start() + err.start() + out.join() + err.join() + + return result.wait() + + +def get_installed_modules(): + result = subprocess.run( + [sys.executable, "-m", "pip", "list", "--format=freeze"], + capture_output=True, + text=True, + check=True, + ) + return {line.split("==")[0].lower() for line in result.stdout.splitlines()} + + +def checkModules(nodeElement): + file_requir = os.path.join(extension_folder, nodeElement, "requirements.txt") + if os.path.exists(file_requir): + log(" -> File 'requirements.txt' found!") + with open(file_requir) as f: + required_modules = { + module_name_cut_version.split(line.strip())[0] + for line in f + if not line.startswith("#") + } + + modules_to_install = required_modules - installed_modules + + if modules_to_install: + module_install( + [sys.executable, "-m", "pip", "install", *modules_to_install] + ) + + +def install_node(nodeElement): + log(f"* Node <{nodeElement}> is found, installing...") + web_extensions_dir = os.path.join(extension_folder, extension_dirs[0]) + + extensions_dirs_copy = ["js", "css", "assets", "lib"] + for dir_name in extensions_dirs_copy: + folder_curr = os.path.join(extension_folder, nodeElement, dir_name) + if os.path.exists(folder_curr): + folder_curr_dist = os.path.join( + web_extensions_dir, + dir_name, + nodeElement.lower() if dir_name != "js" else web_extensions_dir, + ) + shutil.copytree(folder_curr, folder_curr_dist, dirs_exist_ok=True) + + clsNodes = getNamesNodesInsidePyFile(nodeElement) + clsNodesText = "\033[93m" + ", ".join(clsNodes) + "\033[0m" if clsNodes else "" + printColorInfo(f"Node -> {nodeElement}: {clsNodesText} \033[92m[Loading]") + + checkModules(nodeElement) + # addComfyUINodesToMapping(nodeElement) # dynamic class nodes append in mappings def installNodes(): + global installed_modules log(f"\n-------> AlekPet Node Installing [DEBUG] <-------") printColorInfo(f"### [START] ComfyUI AlekPet Nodes ###", "\033[1;35m") - web_extensions_dir = os.path.join(extension_folder, extension_dirs[0]) # Remove files in lib directory libfiles = ["fabric.js"] for file in libfiles: filePath = os.path.join(folder__web_lib, file) - if os.path.exists(filePath): + if os.path.isfile(filePath): os.remove(filePath) # Remove old folder if exist @@ -182,39 +228,24 @@ def installNodes(): shutil.rmtree(oldDirNodes) # Clear folder web_alekpet_nodes + web_extensions_dir = os.path.join(extension_folder, extension_dirs[0]) if os.path.exists(web_extensions_dir): shutil.rmtree(web_extensions_dir) checkFolderIsset() - extensions_dirs_copy = ["js", "css", "assets", "lib"] - - for nodeElement in os.listdir(extension_folder): - if ( - not nodeElement.startswith("__") - and nodeElement.endswith("Node") - and os.path.isdir(os.path.join(extension_folder, nodeElement)) - ): - log(f"* Node <{nodeElement}> is found, installing...") - - # Add missing or updates files - for dir_name in extensions_dirs_copy: - folder_curr = os.path.join(extension_folder, nodeElement, dir_name) - if os.path.exists(folder_curr): - folder_curr_dist = os.path.join( - web_extensions_dir, - dir_name, - nodeElement.lower() if dir_name != "js" else web_extensions_dir, - ) - shutil.copytree(folder_curr, folder_curr_dist, dirs_exist_ok=True) + installed_modules = get_installed_modules() - # Loading node info - clsNodes = getNamesNodesInsidePyFile(nodeElement) - clsNodesText = "\033[93m"+", ".join(clsNodes)+"\033[0m" if len(clsNodes) else "" - printColorInfo(f"Node -> {nodeElement}: {clsNodesText} \033[92m[Loading]") + nodes = [ + nodeElement + for nodeElement in os.listdir(extension_folder) + if not nodeElement.startswith("__") + and nodeElement.endswith("Node") + and os.path.isdir(os.path.join(extension_folder, nodeElement)) + ] - checkModules(nodeElement) - # addComfyUINodesToMapping(nodeElement) # dynamic class nodes append in mappings + with ThreadPoolExecutor() as executor: + executor.map(install_node, nodes) printColorInfo(f"### [END] ComfyUI AlekPet Nodes ###", "\033[1;35m") diff --git a/pyproject.toml b/pyproject.toml index 5b001dc..f1d7c31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_custom_nodes_alekpet" description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode." -version = "1.0.11" +version = "1.0.12" license = "LICENSE" [project.urls]