mirror of
https://git.datalinker.icu/ltdrdata/ComfyUI-Manager
synced 2025-12-08 21:54:26 +08:00
Add NodePackage
This commit is contained in:
parent
f8e5521b50
commit
9d1ef85af8
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@ github-stats-cache.json
|
|||||||
pip_overrides.json
|
pip_overrides.json
|
||||||
*.json
|
*.json
|
||||||
check2.sh
|
check2.sh
|
||||||
|
/venv/
|
||||||
@ -224,7 +224,7 @@ def fix_node(node_spec_str, is_all=False, cnt_msg=''):
|
|||||||
print(f"ERROR: f{res.msg}")
|
print(f"ERROR: f{res.msg}")
|
||||||
|
|
||||||
|
|
||||||
def uninstall_node(node_spec_str, is_all=False, cnt_msg=''):
|
def uninstall_node(node_spec_str: str, is_all: bool = False, cnt_msg: str = ''):
|
||||||
spec = node_spec_str.split('@')
|
spec = node_spec_str.split('@')
|
||||||
if len(spec) == 2 and spec[1] == 'unknown':
|
if len(spec) == 2 and spec[1] == 'unknown':
|
||||||
node_name = spec[0]
|
node_name = spec[0]
|
||||||
|
|||||||
@ -437,6 +437,16 @@ def setup_environment():
|
|||||||
git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=config['default']['git_exe'])
|
git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=config['default']['git_exe'])
|
||||||
|
|
||||||
|
|
||||||
|
def is_git_repo(path: str) -> bool:
|
||||||
|
""" Check if the path is a git repository. """
|
||||||
|
try:
|
||||||
|
# Try to create a Repo object from the path
|
||||||
|
_ = git.Repo(path).git_dir
|
||||||
|
return True
|
||||||
|
except git.exc.InvalidGitRepositoryError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
setup_environment()
|
setup_environment()
|
||||||
|
|
||||||
|
|
||||||
@ -467,5 +477,5 @@ try:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import cm_global
|
|||||||
import cnr_utils
|
import cnr_utils
|
||||||
import manager_util
|
import manager_util
|
||||||
import manager_downloader
|
import manager_downloader
|
||||||
|
from node_package import InstalledNodePackage
|
||||||
|
|
||||||
|
|
||||||
version_code = [3, 1]
|
version_code = [3, 1]
|
||||||
@ -329,6 +330,8 @@ def get_commit_hash(fullpath):
|
|||||||
|
|
||||||
class UnifiedManager:
|
class UnifiedManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.installed_node_packages: dict[str, InstalledNodePackage] = {}
|
||||||
|
|
||||||
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
||||||
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
||||||
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
||||||
@ -462,94 +465,26 @@ class UnifiedManager:
|
|||||||
else:
|
else:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
|
|
||||||
def resolve_id_from_repo(self, fullpath):
|
def update_cache_at_path(self, fullpath):
|
||||||
git_config_path = os.path.join(fullpath, '.git', 'config')
|
node_package = InstalledNodePackage.from_fullpath(fullpath)
|
||||||
|
self.installed_node_packages[node_package.id] = node_package
|
||||||
|
|
||||||
if not os.path.exists(git_config_path):
|
if node_package.is_disabled and node_package.is_unknown:
|
||||||
return None
|
# TODO: figure out where url is used.
|
||||||
|
self.unknown_inactive_nodes[node_package.id] = ('', node_package.fullpath)
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
if node_package.is_disabled and node_package.is_nightly:
|
||||||
config.read(git_config_path)
|
self.nightly_inactive_nodes[node_package.id] = node_package.fullpath
|
||||||
|
|
||||||
for k, v in config.items():
|
if node_package.is_enabled:
|
||||||
if k.startswith('remote ') and 'url' in v:
|
self.active_nodes[node_package.id] = node_package.version, node_package.fullpath
|
||||||
cnr = self.get_cnr_by_repo(v['url'])
|
|
||||||
if cnr:
|
|
||||||
return "nightly", cnr['id'], v['url']
|
|
||||||
else:
|
|
||||||
return "unknown", v['url'].split('/')[-1], v['url']
|
|
||||||
|
|
||||||
def resolve_unknown(self, node_id, fullpath):
|
if node_package.is_enabled and node_package.is_unknown:
|
||||||
res = self.resolve_id_from_repo(fullpath)
|
# TODO: figure out where url is used.
|
||||||
|
self.unknown_active_nodes[node_package.id] = ('', node_package.fullpath)
|
||||||
|
|
||||||
if res is None:
|
if node_package.is_from_cnr and node_package.is_disabled:
|
||||||
self.unknown_inactive_nodes[node_id] = '', fullpath
|
self.add_to_cnr_inactive_nodes(node_package.id, node_package.version, node_package.fullpath)
|
||||||
return
|
|
||||||
|
|
||||||
ver_spec, node_id, url = res
|
|
||||||
|
|
||||||
if ver_spec == 'nightly':
|
|
||||||
self.nightly_inactive_nodes[node_id] = fullpath
|
|
||||||
else:
|
|
||||||
self.unknown_inactive_nodes[node_id] = url, fullpath
|
|
||||||
|
|
||||||
def update_cache_at_path(self, fullpath, is_disabled):
|
|
||||||
name = os.path.basename(fullpath)
|
|
||||||
|
|
||||||
if name.endswith(".disabled"):
|
|
||||||
node_spec = name[:-9]
|
|
||||||
is_disabled = True
|
|
||||||
else:
|
|
||||||
node_spec = name
|
|
||||||
|
|
||||||
if '@' in node_spec:
|
|
||||||
node_spec = node_spec.split('@')
|
|
||||||
node_id = node_spec[0]
|
|
||||||
if node_id is None:
|
|
||||||
node_version = 'unknown'
|
|
||||||
else:
|
|
||||||
node_version = node_spec[1].replace("_", ".")
|
|
||||||
|
|
||||||
if node_version != 'unknown':
|
|
||||||
if node_id not in self.cnr_map:
|
|
||||||
# fallback
|
|
||||||
v = node_version
|
|
||||||
|
|
||||||
self.cnr_map[node_id] = {
|
|
||||||
'id': node_id,
|
|
||||||
'name': node_id,
|
|
||||||
'latest_version': {'version': v},
|
|
||||||
'publisher': {'id': 'N/A', 'name': 'N/A'}
|
|
||||||
}
|
|
||||||
|
|
||||||
elif node_version == 'unknown':
|
|
||||||
res = self.resolve_id_from_repo(fullpath)
|
|
||||||
if res is None:
|
|
||||||
print(f"Custom node unresolved: {fullpath}")
|
|
||||||
return
|
|
||||||
|
|
||||||
node_version, node_id, _ = res
|
|
||||||
else:
|
|
||||||
res = self.resolve_id_from_repo(fullpath)
|
|
||||||
if res is None:
|
|
||||||
print(f"Custom node unresolved: {fullpath}")
|
|
||||||
return
|
|
||||||
|
|
||||||
node_version, node_id, _ = res
|
|
||||||
|
|
||||||
if not is_disabled:
|
|
||||||
# active nodes
|
|
||||||
if node_version == 'unknown':
|
|
||||||
self.unknown_active_nodes[node_id] = node_version, fullpath
|
|
||||||
else:
|
|
||||||
self.active_nodes[node_id] = node_version, fullpath
|
|
||||||
else:
|
|
||||||
if node_version == 'unknown':
|
|
||||||
self.resolve_unknown(node_id, fullpath)
|
|
||||||
elif node_version == 'nightly':
|
|
||||||
self.nightly_inactive_nodes[node_id] = fullpath
|
|
||||||
else:
|
|
||||||
self.add_to_cnr_inactive_nodes(node_id, node_version, fullpath)
|
|
||||||
|
|
||||||
def is_updatable(self, node_id):
|
def is_updatable(self, node_id):
|
||||||
cur_ver = self.get_cnr_active_version(node_id)
|
cur_ver = self.get_cnr_active_version(node_id)
|
||||||
@ -722,7 +657,7 @@ class UnifiedManager:
|
|||||||
fullpath = os.path.join(custom_nodes_path, x)
|
fullpath = os.path.join(custom_nodes_path, x)
|
||||||
if os.path.isdir(fullpath):
|
if os.path.isdir(fullpath):
|
||||||
if x not in ['__pycache__', '.disabled']:
|
if x not in ['__pycache__', '.disabled']:
|
||||||
self.update_cache_at_path(fullpath, is_disabled=False)
|
self.update_cache_at_path(fullpath)
|
||||||
|
|
||||||
# reload node status info from custom_nodes/.disabled/*
|
# reload node status info from custom_nodes/.disabled/*
|
||||||
for custom_nodes_path in folder_paths.get_folder_paths('custom_nodes'):
|
for custom_nodes_path in folder_paths.get_folder_paths('custom_nodes'):
|
||||||
@ -731,7 +666,7 @@ class UnifiedManager:
|
|||||||
for x in os.listdir(disabled_dir):
|
for x in os.listdir(disabled_dir):
|
||||||
fullpath = os.path.join(disabled_dir, x)
|
fullpath = os.path.join(disabled_dir, x)
|
||||||
if os.path.isdir(fullpath):
|
if os.path.isdir(fullpath):
|
||||||
self.update_cache_at_path(fullpath, is_disabled=True)
|
self.update_cache_at_path(fullpath)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def load_nightly(channel, mode):
|
async def load_nightly(channel, mode):
|
||||||
@ -1112,7 +1047,7 @@ class UnifiedManager:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def unified_uninstall(self, node_id, is_unknown):
|
def unified_uninstall(self, node_id: str, is_unknown: bool):
|
||||||
"""
|
"""
|
||||||
Remove whole installed custom nodes including inactive nodes
|
Remove whole installed custom nodes including inactive nodes
|
||||||
"""
|
"""
|
||||||
|
|||||||
70
glob/node_package.py
Normal file
70
glob/node_package.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import os
|
||||||
|
|
||||||
|
import toml
|
||||||
|
|
||||||
|
from git_helper import is_git_repo
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class InstalledNodePackage:
|
||||||
|
"""Information about an installed node package."""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
fullpath: str
|
||||||
|
disabled: bool
|
||||||
|
version: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_unknown(self) -> bool:
|
||||||
|
return self.version == "unknown"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_nightly(self) -> bool:
|
||||||
|
return self.version == "nightly"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_from_cnr(self) -> bool:
|
||||||
|
return not self.is_unknown and not self.is_nightly
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_enabled(self) -> bool:
|
||||||
|
return not self.disabled
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_disabled(self) -> bool:
|
||||||
|
return self.disabled
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_fullpath(fullpath: str) -> InstalledNodePackage:
|
||||||
|
parent_folder_name = os.path.split(fullpath)[-2]
|
||||||
|
module_name = os.path.basename(fullpath)
|
||||||
|
pyproject_toml_path = os.path.join(fullpath, "pyproject.toml")
|
||||||
|
|
||||||
|
if module_name.endswith(".disabled"):
|
||||||
|
node_id = module_name[:-9]
|
||||||
|
disabled = True
|
||||||
|
elif parent_folder_name == ".disabled":
|
||||||
|
# Nodes under custom_nodes/.disabled/* are disabled
|
||||||
|
node_id = module_name
|
||||||
|
disabled = True
|
||||||
|
else:
|
||||||
|
node_id = module_name
|
||||||
|
disabled = False
|
||||||
|
|
||||||
|
if is_git_repo(fullpath):
|
||||||
|
version = "nightly"
|
||||||
|
elif os.path.exists(pyproject_toml_path):
|
||||||
|
# Read project.toml to get the version
|
||||||
|
with open(pyproject_toml_path, "r", encoding="utf-8") as f:
|
||||||
|
pyproject_toml = toml.load(f)
|
||||||
|
# Fallback to 'unknown' if project.version doesn't exist
|
||||||
|
version = pyproject_toml.get("project", {}).get("version", "unknown")
|
||||||
|
else:
|
||||||
|
version = "unknown"
|
||||||
|
|
||||||
|
return InstalledNodePackage(
|
||||||
|
id=node_id, fullpath=fullpath, disabled=disabled, version=version
|
||||||
|
)
|
||||||
@ -47,7 +47,7 @@ class WorkflowMetadataExtension {
|
|||||||
const modules = nodeData.python_module.split(".");
|
const modules = nodeData.python_module.split(".");
|
||||||
|
|
||||||
if (modules[0] === "custom_nodes") {
|
if (modules[0] === "custom_nodes") {
|
||||||
const nodePackageName = modules[1].split("@")[0];
|
const nodePackageName = modules[1];
|
||||||
const nodeVersion = this.installedNodeVersions[nodePackageName];
|
const nodeVersion = this.installedNodeVersions[nodePackageName];
|
||||||
nodeVersions[nodePackageName] = nodeVersion;
|
nodeVersions[nodePackageName] = nodeVersion;
|
||||||
} else if (["nodes", "comfy_extras"].includes(modules[0])) {
|
} else if (["nodes", "comfy_extras"].includes(modules[0])) {
|
||||||
|
|||||||
@ -5,4 +5,5 @@ transformers
|
|||||||
huggingface-hub>0.20
|
huggingface-hub>0.20
|
||||||
typer
|
typer
|
||||||
rich
|
rich
|
||||||
typing-extensions
|
typing-extensions
|
||||||
|
toml
|
||||||
Loading…
x
Reference in New Issue
Block a user