190 lines
7.1 KiB
Python
190 lines
7.1 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
ComfyUI-ZSNodes Installation Script
|
|
Automatically installs required dependencies
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
import platform
|
|
import importlib.util
|
|
from typing import List, Tuple
|
|
|
|
def find_python_executable():
|
|
"""Find the correct Python executable to use"""
|
|
# First, try to detect ComfyUI's python_embeded directory
|
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
# Navigate up to find ComfyUI root
|
|
comfyui_root = current_dir
|
|
while comfyui_root and not os.path.exists(os.path.join(comfyui_root, "main.py")):
|
|
parent = os.path.dirname(comfyui_root)
|
|
if parent == comfyui_root: # Reached root directory
|
|
break
|
|
comfyui_root = parent
|
|
|
|
# Check for python_embeded (Windows portable)
|
|
if platform.system() == "Windows":
|
|
python_embeded = os.path.join(comfyui_root, "python_embeded", "python.exe")
|
|
if os.path.exists(python_embeded):
|
|
print(f"📍 Found Windows portable Python: {python_embeded}")
|
|
return python_embeded
|
|
|
|
# Alternative path structure
|
|
python_embeded_alt = os.path.join(os.path.dirname(comfyui_root), "python_embeded", "python.exe")
|
|
if os.path.exists(python_embeded_alt):
|
|
print(f"📍 Found Windows portable Python: {python_embeded_alt}")
|
|
return python_embeded_alt
|
|
|
|
# Fall back to system Python
|
|
print(f"📍 Using system Python: {sys.executable}")
|
|
return sys.executable
|
|
|
|
def check_package_installed(package_name: str) -> bool:
|
|
"""Check if a package is already installed"""
|
|
spec = importlib.util.find_spec(package_name.split('[')[0].split('@')[0].split('>')[0].split('=')[0])
|
|
return spec is not None
|
|
|
|
def install_package(package: str, python_exe: str) -> bool:
|
|
"""Install a single package using pip"""
|
|
try:
|
|
print(f"Installing {package}...")
|
|
subprocess.check_call([python_exe, "-m", "pip", "install", package],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.STDOUT)
|
|
print(f"✓ Successfully installed {package}")
|
|
return True
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"✗ Failed to install {package}: {e}")
|
|
return False
|
|
|
|
def get_requirements() -> Tuple[List[str], List[str]]:
|
|
"""Get lists of required and optional packages"""
|
|
required = [
|
|
"Pillow>=9.0.0",
|
|
"numpy>=1.21.0",
|
|
]
|
|
|
|
optional = [
|
|
"torch>=2.0.0",
|
|
"torchvision>=0.15.0",
|
|
"opencv-python>=4.7.0",
|
|
"safetensors>=0.3.1",
|
|
"huggingface-hub>=0.19.0",
|
|
"protobuf>=3.20.2,<6.0.0",
|
|
]
|
|
|
|
return required, optional
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("ComfyUI-ZSNodes Installation")
|
|
print("=" * 60)
|
|
|
|
# Find the correct Python executable
|
|
python_exe = find_python_executable()
|
|
|
|
# Get requirements
|
|
required_packages, optional_packages = get_requirements()
|
|
|
|
# Install required packages
|
|
print("\n📦 Installing required packages...")
|
|
failed_required = []
|
|
for package in required_packages:
|
|
package_name = package.split('>')[0].split('=')[0]
|
|
if check_package_installed(package_name):
|
|
print(f"✓ {package_name} already installed")
|
|
else:
|
|
if not install_package(package, python_exe):
|
|
failed_required.append(package)
|
|
|
|
# Install optional packages (for Bounding Box Crop node)
|
|
print("\n📦 Installing optional packages (for Bounding Box Crop node)...")
|
|
failed_optional = []
|
|
|
|
for package in optional_packages:
|
|
package_name = package.split('>')[0].split('=')[0]
|
|
if check_package_installed(package_name):
|
|
print(f"✓ {package_name} already installed")
|
|
else:
|
|
if not install_package(package, python_exe):
|
|
failed_optional.append(package)
|
|
|
|
# Special handling for GroundingDINO
|
|
print("\n📦 Checking GroundingDINO installation...")
|
|
if check_package_installed("groundingdino"):
|
|
print("✓ GroundingDINO already installed")
|
|
else:
|
|
print("Installing GroundingDINO (this may take a while)...")
|
|
|
|
# Try pip version first (easier to install)
|
|
print(" Trying groundingdino-py package...")
|
|
if install_package("groundingdino-py>=0.4.0", python_exe):
|
|
print("✓ Successfully installed GroundingDINO (pip version)")
|
|
else:
|
|
# Fall back to GitHub version if pip version fails
|
|
print(" Pip version failed, trying GitHub version...")
|
|
try:
|
|
# First, ensure build dependencies are installed
|
|
subprocess.check_call([python_exe, "-m", "pip", "install", "wheel", "setuptools"],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.STDOUT)
|
|
|
|
# Install GroundingDINO from GitHub
|
|
subprocess.check_call([
|
|
python_exe, "-m", "pip", "install",
|
|
"git+https://github.com/IDEA-Research/GroundingDINO.git"
|
|
])
|
|
print("✓ Successfully installed GroundingDINO (GitHub version)")
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"⚠ GroundingDINO installation failed: {e}")
|
|
print(" The Bounding Box Crop node will not be available.")
|
|
print(" You can try installing manually with either:")
|
|
print(" pip install groundingdino-py>=0.4.0")
|
|
print(" pip install git+https://github.com/IDEA-Research/GroundingDINO.git")
|
|
failed_optional.append("groundingdino")
|
|
|
|
# Summary
|
|
print("\n" + "=" * 60)
|
|
print("Installation Summary")
|
|
print("=" * 60)
|
|
|
|
if not failed_required:
|
|
print("✓ All required packages installed successfully")
|
|
print(" - Save Image (ZS Custom) node is ready to use")
|
|
else:
|
|
print("✗ Some required packages failed to install:")
|
|
for package in failed_required:
|
|
print(f" - {package}")
|
|
print("\nPlease install them manually with:")
|
|
print(f"pip install {' '.join(failed_required)}")
|
|
|
|
if not failed_optional:
|
|
if "groundingdino" not in failed_optional:
|
|
print("✓ All optional packages installed successfully")
|
|
print(" - Bounding Box Crop (ZS) node is ready to use")
|
|
else:
|
|
print("\n⚠ Some optional packages failed to install:")
|
|
for package in failed_optional:
|
|
print(f" - {package}")
|
|
print("\nThe Bounding Box Crop node may not be available.")
|
|
print("You can install these manually if needed.")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("Installation complete! Please restart ComfyUI.")
|
|
print("=" * 60)
|
|
|
|
# Return 0 for success, 1 if required packages failed
|
|
return 1 if failed_required else 0
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
exit_code = main()
|
|
sys.exit(exit_code)
|
|
except KeyboardInterrupt:
|
|
print("\n\nInstallation cancelled by user.")
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"\n✗ Installation failed with error: {e}")
|
|
sys.exit(1) |