701 lines
25 KiB
Python
701 lines
25 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
GeometryPack Installer
|
|
Automatically downloads and installs Blender for UV unwrapping and remeshing nodes.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import platform
|
|
import urllib.request
|
|
import json
|
|
import tarfile
|
|
import zipfile
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
# Try to import optimized libraries, fallback to basic if not available
|
|
try:
|
|
import requests
|
|
HAS_REQUESTS = True
|
|
except ImportError:
|
|
HAS_REQUESTS = False
|
|
|
|
try:
|
|
from tqdm import tqdm
|
|
HAS_TQDM = True
|
|
except ImportError:
|
|
HAS_TQDM = False
|
|
|
|
|
|
def get_platform_info():
|
|
"""Detect current platform and architecture."""
|
|
system = platform.system().lower()
|
|
machine = platform.machine().lower()
|
|
|
|
# Map platform names
|
|
if system == "darwin":
|
|
plat = "macos"
|
|
if machine == "arm64":
|
|
arch = "arm64"
|
|
else:
|
|
arch = "x64"
|
|
elif system == "linux":
|
|
plat = "linux"
|
|
arch = "x64" # Most common
|
|
elif system == "windows":
|
|
plat = "windows"
|
|
arch = "x64"
|
|
else:
|
|
plat = None
|
|
arch = None
|
|
|
|
return plat, arch
|
|
|
|
|
|
def check_tool_available(tool_name):
|
|
"""Check if a system tool is available in PATH."""
|
|
try:
|
|
result = subprocess.run(
|
|
['which', tool_name] if os.name != 'nt' else ['where', tool_name],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=2
|
|
)
|
|
return result.returncode == 0
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def get_cpu_count():
|
|
"""Get number of CPU cores for parallel processing."""
|
|
try:
|
|
return os.cpu_count() or 4
|
|
except Exception:
|
|
return 4
|
|
|
|
|
|
def get_blender_download_url(platform_name, architecture):
|
|
"""
|
|
Get Blender 4.2 LTS download URL for the platform.
|
|
|
|
Args:
|
|
platform_name: "linux", "macos", or "windows"
|
|
architecture: "x64" or "arm64"
|
|
|
|
Returns:
|
|
tuple: (download_url, version, filename) or (None, None, None) if not found
|
|
"""
|
|
version = "4.2.3"
|
|
base_url = "https://download.blender.org/release/Blender4.2"
|
|
|
|
# Platform-specific URLs for Blender 4.2.3 LTS
|
|
urls = {
|
|
("linux", "x64"): (
|
|
f"{base_url}/blender-{version}-linux-x64.tar.xz",
|
|
version,
|
|
f"blender-{version}-linux-x64.tar.xz"
|
|
),
|
|
("macos", "x64"): (
|
|
f"{base_url}/blender-{version}-macos-x64.dmg",
|
|
version,
|
|
f"blender-{version}-macos-x64.dmg"
|
|
),
|
|
("macos", "arm64"): (
|
|
f"{base_url}/blender-{version}-macos-arm64.dmg",
|
|
version,
|
|
f"blender-{version}-macos-arm64.dmg"
|
|
),
|
|
("windows", "x64"): (
|
|
f"{base_url}/blender-{version}-windows-x64.zip",
|
|
version,
|
|
f"blender-{version}-windows-x64.zip"
|
|
),
|
|
}
|
|
|
|
key = (platform_name, architecture)
|
|
if key in urls:
|
|
url, ver, filename = urls[key]
|
|
print(f"[Install] Using Blender {ver} for {platform_name}-{architecture}")
|
|
return url, ver, filename
|
|
|
|
return None, None, None
|
|
|
|
|
|
def download_file_optimized(url, dest_path):
|
|
"""Download file with requests and tqdm for better performance and progress."""
|
|
print(f"[Install] Downloading: {url}")
|
|
print(f"[Install] Destination: {dest_path}")
|
|
|
|
try:
|
|
# Stream download with requests
|
|
response = requests.get(url, stream=True, timeout=30)
|
|
response.raise_for_status()
|
|
|
|
total_size = int(response.headers.get('content-length', 0))
|
|
block_size = 8192 # 8KB chunks
|
|
|
|
if HAS_TQDM:
|
|
# Use tqdm progress bar
|
|
with tqdm(total=total_size, unit='B', unit_scale=True, unit_divisor=1024) as pbar:
|
|
with open(dest_path, 'wb') as f:
|
|
for chunk in response.iter_content(chunk_size=block_size):
|
|
if chunk:
|
|
f.write(chunk)
|
|
pbar.update(len(chunk))
|
|
else:
|
|
# Fallback to basic progress
|
|
downloaded = 0
|
|
with open(dest_path, 'wb') as f:
|
|
for chunk in response.iter_content(chunk_size=block_size):
|
|
if chunk:
|
|
f.write(chunk)
|
|
downloaded += len(chunk)
|
|
if total_size > 0:
|
|
percent = int(downloaded * 100 / total_size)
|
|
sys.stdout.write(f"\r[Install] Progress: {percent}%")
|
|
sys.stdout.flush()
|
|
sys.stdout.write("\n")
|
|
|
|
print("[Install] Download complete!")
|
|
return True
|
|
except Exception as e:
|
|
print(f"\n[Install] Error downloading: {e}")
|
|
return False
|
|
|
|
|
|
def download_file(url, dest_path):
|
|
"""Download file with progress (uses optimized version if available)."""
|
|
if HAS_REQUESTS:
|
|
return download_file_optimized(url, dest_path)
|
|
|
|
# Fallback to urllib
|
|
print(f"[Install] Downloading: {url}")
|
|
print(f"[Install] Destination: {dest_path}")
|
|
|
|
def progress_hook(count, block_size, total_size):
|
|
percent = int(count * block_size * 100 / total_size)
|
|
sys.stdout.write(f"\r[Install] Progress: {percent}%")
|
|
sys.stdout.flush()
|
|
|
|
try:
|
|
urllib.request.urlretrieve(url, dest_path, progress_hook)
|
|
sys.stdout.write("\n")
|
|
print("[Install] Download complete!")
|
|
return True
|
|
except Exception as e:
|
|
print(f"\n[Install] Error downloading: {e}")
|
|
return False
|
|
|
|
|
|
def extract_tar_xz_optimized(archive_path, extract_to):
|
|
"""Extract .tar.xz using pixz for multi-threaded decompression."""
|
|
cpu_cores = get_cpu_count()
|
|
|
|
# Check if pixz is available
|
|
has_pixz = check_tool_available('pixz')
|
|
|
|
if has_pixz:
|
|
print(f"[Install] Using pixz with {cpu_cores} cores for fast extraction...")
|
|
try:
|
|
# Use pixz to decompress, then tar to extract
|
|
# pixz -d -p <cores> archive.tar.xz extracts to archive.tar
|
|
tar_path = archive_path.replace('.tar.xz', '.tar')
|
|
|
|
# Decompress with pixz
|
|
result = subprocess.run(
|
|
['pixz', '-d', '-p', str(cpu_cores), archive_path],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=600 # 10 minute timeout
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
print(f"[Install] pixz failed: {result.stderr}")
|
|
return False
|
|
|
|
# Extract the tar file
|
|
with tarfile.open(tar_path, 'r:') as tar:
|
|
# Use filter for Python 3.14+ compatibility
|
|
if hasattr(tarfile, 'data_filter'):
|
|
tar.extractall(extract_to, filter='data')
|
|
else:
|
|
tar.extractall(extract_to)
|
|
|
|
# Clean up the intermediate .tar file
|
|
if os.path.exists(tar_path):
|
|
os.remove(tar_path)
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"[Install] Error with pixz extraction: {e}")
|
|
return False
|
|
else:
|
|
# Fallback to standard tarfile
|
|
print("[Install] pixz not found, using standard extraction (slower)...")
|
|
try:
|
|
with tarfile.open(archive_path, 'r:*') as tar:
|
|
# Use filter for Python 3.14+ compatibility
|
|
if hasattr(tarfile, 'data_filter'):
|
|
tar.extractall(extract_to, filter='data')
|
|
else:
|
|
tar.extractall(extract_to)
|
|
return True
|
|
except Exception as e:
|
|
print(f"[Install] Error with standard extraction: {e}")
|
|
return False
|
|
|
|
|
|
def extract_zip_parallel(archive_path, extract_to):
|
|
"""Extract .zip using parallel extraction for better performance."""
|
|
try:
|
|
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
|
|
members = zip_ref.namelist()
|
|
|
|
# For small archives, parallel extraction overhead isn't worth it
|
|
if len(members) < 10:
|
|
zip_ref.extractall(extract_to)
|
|
return True
|
|
|
|
print(f"[Install] Extracting {len(members)} files in parallel...")
|
|
|
|
# Extract files in parallel
|
|
def extract_member(member):
|
|
try:
|
|
zip_ref.extract(member, extract_to)
|
|
return True
|
|
except Exception as e:
|
|
print(f"[Install] Error extracting {member}: {e}")
|
|
return False
|
|
|
|
cpu_cores = get_cpu_count()
|
|
with ThreadPoolExecutor(max_workers=min(cpu_cores, 8)) as executor:
|
|
results = list(executor.map(extract_member, members))
|
|
|
|
# Check if all extractions succeeded
|
|
if not all(results):
|
|
print("[Install] Some files failed to extract")
|
|
return False
|
|
|
|
return True
|
|
except Exception as e:
|
|
print(f"[Install] Error with parallel zip extraction: {e}")
|
|
return False
|
|
|
|
|
|
def extract_archive(archive_path, extract_to):
|
|
"""Extract tar.gz, tar.xz, zip, or handle DMG (macOS) with optimizations."""
|
|
print(f"[Install] Extracting: {archive_path}")
|
|
|
|
try:
|
|
if archive_path.endswith('.tar.xz'):
|
|
# Use optimized extraction for .tar.xz
|
|
if not extract_tar_xz_optimized(archive_path, extract_to):
|
|
return False
|
|
elif archive_path.endswith(('.tar.gz', '.tar.bz2')):
|
|
# Standard extraction for other tar formats
|
|
with tarfile.open(archive_path, 'r:*') as tar:
|
|
# Use filter for Python 3.14+ compatibility
|
|
if hasattr(tarfile, 'data_filter'):
|
|
tar.extractall(extract_to, filter='data')
|
|
else:
|
|
tar.extractall(extract_to)
|
|
elif archive_path.endswith('.zip'):
|
|
# Use parallel extraction for .zip
|
|
if not extract_zip_parallel(archive_path, extract_to):
|
|
return False
|
|
elif archive_path.endswith('.dmg'):
|
|
# macOS DMG - mount and copy Blender.app
|
|
print("[Install] DMG detected - mounting disk image...")
|
|
import subprocess
|
|
|
|
# Mount the DMG
|
|
mount_result = subprocess.run(
|
|
['hdiutil', 'attach', '-nobrowse', archive_path],
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
if mount_result.returncode != 0:
|
|
print(f"[Install] Error mounting DMG: {mount_result.stderr}")
|
|
return False
|
|
|
|
# Find the mount point from the output
|
|
mount_point = None
|
|
for line in mount_result.stdout.split('\n'):
|
|
if '/Volumes/' in line:
|
|
mount_point = line.split('\t')[-1].strip()
|
|
break
|
|
|
|
if not mount_point:
|
|
print("[Install] Error: Could not find mount point")
|
|
return False
|
|
|
|
try:
|
|
# Copy Blender.app to destination
|
|
blender_app = Path(mount_point) / "Blender.app"
|
|
if blender_app.exists():
|
|
dest_app = Path(extract_to) / "Blender.app"
|
|
shutil.copytree(blender_app, dest_app)
|
|
print(f"[Install] Copied Blender.app to: {dest_app}")
|
|
else:
|
|
print(f"[Install] Error: Blender.app not found in {mount_point}")
|
|
return False
|
|
|
|
finally:
|
|
# Unmount the DMG
|
|
subprocess.run(['hdiutil', 'detach', mount_point], check=False)
|
|
|
|
else:
|
|
print(f"[Install] Error: Unknown archive format: {archive_path}")
|
|
return False
|
|
|
|
print(f"[Install] Extraction complete!")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"[Install] Error extracting: {e}")
|
|
return False
|
|
|
|
|
|
def install_system_dependencies():
|
|
"""Install required system dependencies (Linux only)."""
|
|
plat, _ = get_platform_info()
|
|
|
|
if plat != "linux":
|
|
return True
|
|
|
|
print("\n" + "="*60)
|
|
print("ComfyUI-GeometryPack: System Dependencies")
|
|
print("="*60 + "\n")
|
|
|
|
print("[Install] Checking for required OpenGL libraries...")
|
|
print("[Install] These are needed for PyMeshLab remeshing to work properly.")
|
|
|
|
try:
|
|
# Define packages with priority levels
|
|
# Critical packages are required for PyMeshLab to function
|
|
# Optional packages provide additional functionality but aren't strictly required
|
|
critical_packages = ["libgl1", "libopengl0", "libglu1-mesa", "libglx-mesa0"]
|
|
optional_packages = ["libosmesa6"] # For offscreen rendering, not always available
|
|
|
|
all_packages = critical_packages + optional_packages
|
|
print(f"[Install] Installing OpenGL libraries: {', '.join(all_packages)}")
|
|
print("[Install] You may be prompted for your sudo password...")
|
|
|
|
# Update apt cache first with sudo
|
|
print("[Install] Updating apt cache...")
|
|
update_result = subprocess.run(
|
|
['sudo', 'apt-get', 'update'],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=120
|
|
)
|
|
|
|
if update_result.returncode != 0:
|
|
print("[Install] Warning: Failed to update apt cache")
|
|
print(f"[Install] You may need to run manually: sudo apt-get update")
|
|
|
|
# Install packages individually to handle failures gracefully
|
|
installed_packages = []
|
|
failed_packages = []
|
|
critical_failed = []
|
|
|
|
# Install critical packages first
|
|
print("[Install] Installing critical OpenGL libraries...")
|
|
for package in critical_packages:
|
|
result = subprocess.run(
|
|
['sudo', 'apt-get', 'install', '-y', package],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60
|
|
)
|
|
if result.returncode == 0:
|
|
installed_packages.append(package)
|
|
print(f"[Install] ✓ {package}")
|
|
else:
|
|
failed_packages.append(package)
|
|
critical_failed.append(package)
|
|
print(f"[Install] ✗ {package} (failed)")
|
|
|
|
# Install optional packages
|
|
print("[Install] Installing optional OpenGL libraries...")
|
|
for package in optional_packages:
|
|
result = subprocess.run(
|
|
['sudo', 'apt-get', 'install', '-y', package],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60
|
|
)
|
|
if result.returncode == 0:
|
|
installed_packages.append(package)
|
|
print(f"[Install] ✓ {package}")
|
|
else:
|
|
failed_packages.append(package)
|
|
print(f"[Install] ⚠ {package} (optional, skipped)")
|
|
|
|
# Verify critical library can be loaded
|
|
print("[Install] Verifying OpenGL libraries...")
|
|
opengl_works = False
|
|
try:
|
|
import ctypes
|
|
ctypes.CDLL("libOpenGL.so.0")
|
|
opengl_works = True
|
|
print("[Install] ✓ libOpenGL.so.0 loaded successfully")
|
|
except OSError as e:
|
|
print(f"[Install] ✗ libOpenGL.so.0 failed to load: {e}")
|
|
|
|
# Report results
|
|
if installed_packages:
|
|
print(f"[Install] ✓ Installed: {', '.join(installed_packages)}")
|
|
|
|
if failed_packages:
|
|
print(f"[Install] ⚠ Failed to install: {', '.join(failed_packages)}")
|
|
|
|
# Determine success based on critical packages and library verification
|
|
if critical_failed:
|
|
print(f"[Install] ERROR: Critical packages failed to install: {', '.join(critical_failed)}")
|
|
print(f"[Install] PyMeshLab remeshing will NOT work!")
|
|
print(f"[Install] You may need to run manually:")
|
|
print(f"[Install] sudo apt-get install {' '.join(critical_failed)}")
|
|
return False # Critical failure
|
|
elif not opengl_works:
|
|
print("[Install] ERROR: OpenGL libraries installed but cannot be loaded!")
|
|
print("[Install] PyMeshLab remeshing will NOT work!")
|
|
print("[Install] Try running: sudo ldconfig")
|
|
return False # Critical failure
|
|
else:
|
|
print("[Install] ✓ OpenGL libraries installed and verified successfully!")
|
|
return True
|
|
|
|
except subprocess.TimeoutExpired:
|
|
print("[Install] Warning: Installation timed out")
|
|
print(f"[Install] You may need to run manually:")
|
|
print(f"[Install] sudo apt-get install libgl1 libopengl0 libglu1-mesa libglx-mesa0")
|
|
return False # Consider this a failure
|
|
except FileNotFoundError:
|
|
print("[Install] Warning: apt-get not found (not a Debian/Ubuntu system?)")
|
|
print("[Install] Please install OpenGL libraries manually for your distribution")
|
|
return True # Don't fail installation on non-Debian systems
|
|
except KeyboardInterrupt:
|
|
print("\n[Install] Installation cancelled by user")
|
|
print(f"[Install] You can install OpenGL libraries later with:")
|
|
print(f"[Install] sudo apt-get install libgl1 libopengl0 libglu1-mesa libglx-mesa0")
|
|
return False # User cancelled
|
|
except Exception as e:
|
|
print(f"[Install] Warning: Could not install system dependencies: {e}")
|
|
print(f"[Install] PyMeshLab remeshing may not work without OpenGL libraries.")
|
|
print(f"[Install] To fix, run: sudo apt-get update && sudo apt-get install libgl1 libopengl0 libglu1-mesa libglx-mesa0")
|
|
return False # Consider this a failure
|
|
|
|
|
|
def install_python_dependencies():
|
|
"""Install Python dependencies from requirements.txt."""
|
|
print("\n" + "="*60)
|
|
print("ComfyUI-GeometryPack: Python Dependencies Installation")
|
|
print("="*60 + "\n")
|
|
|
|
script_dir = Path(__file__).parent.absolute()
|
|
requirements_file = script_dir / "requirements.txt"
|
|
|
|
if not requirements_file.exists():
|
|
print(f"[Install] Warning: requirements.txt not found at {requirements_file}")
|
|
print("[Install] Skipping Python dependencies installation.")
|
|
return True
|
|
|
|
print(f"[Install] Installing core Python dependencies...")
|
|
print(f"[Install] This may take a few minutes...\n")
|
|
|
|
# First, try installing all dependencies
|
|
try:
|
|
result = subprocess.run(
|
|
[sys.executable, "-m", "pip", "install", "-r", str(requirements_file)],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=600 # 10 minute timeout
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
print("\n[Install] ✓ All Python dependencies installed successfully!")
|
|
return True
|
|
else:
|
|
# If full install fails, try installing without optional packages
|
|
print(f"\n[Install] Warning: Some packages failed to install")
|
|
print("[Install] Attempting to install core dependencies without optional packages...")
|
|
|
|
# Try installing without cgal (optional, only for isotropic remesh)
|
|
result_without_optional = subprocess.run(
|
|
[sys.executable, "-m", "pip", "install",
|
|
"requests>=2.25.0", "tqdm>=4.60.0",
|
|
"numpy>=1.21.0", "scipy>=1.7.0",
|
|
"trimesh>=3.15.0", "pymeshlab>=2022.2",
|
|
"matplotlib>=3.5.0", "Pillow>=9.0.0",
|
|
"point-cloud-utils>=0.30.0",
|
|
"fast-simplification>=0.1.5",
|
|
"xatlas>=0.0.11",
|
|
"skeletor>=1.2.0"],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=600
|
|
)
|
|
|
|
if result_without_optional.returncode == 0:
|
|
print("\n[Install] ✓ Core Python dependencies installed successfully!")
|
|
print("[Install] Note: Some optional packages (like cgal) may not be available")
|
|
print("[Install] You can install them manually later if needed")
|
|
return True
|
|
else:
|
|
print(f"\n[Install] Error installing Python dependencies:")
|
|
print(result_without_optional.stderr)
|
|
print("\n[Install] You can try installing manually with:")
|
|
print(f"[Install] pip install -r {requirements_file}")
|
|
return False
|
|
|
|
except subprocess.TimeoutExpired:
|
|
print("\n[Install] Error: Installation timed out after 10 minutes")
|
|
print("[Install] You can try installing manually with:")
|
|
print(f"[Install] pip install -r {requirements_file}")
|
|
return False
|
|
except Exception as e:
|
|
print(f"\n[Install] Error installing Python dependencies: {e}")
|
|
print("[Install] You can try installing manually with:")
|
|
print(f"[Install] pip install -r {requirements_file}")
|
|
return False
|
|
|
|
|
|
def install_blender():
|
|
"""Main installation function."""
|
|
print("\n" + "="*60)
|
|
print("ComfyUI-GeometryPack: Blender Installation")
|
|
print("="*60 + "\n")
|
|
|
|
# Get script directory
|
|
script_dir = Path(__file__).parent.absolute()
|
|
blender_dir = script_dir / "_blender"
|
|
|
|
# Check if Blender already installed
|
|
if blender_dir.exists():
|
|
print("[Install] Blender directory already exists at:")
|
|
print(f"[Install] {blender_dir}")
|
|
print("[Install] Skipping download. Delete '_blender/' folder to reinstall.")
|
|
return True
|
|
|
|
# Detect platform
|
|
plat, arch = get_platform_info()
|
|
if not plat or not arch:
|
|
print("[Install] Error: Could not detect platform")
|
|
print("[Install] Please install Blender manually from: https://www.blender.org/download/")
|
|
return False
|
|
|
|
print(f"[Install] Detected platform: {plat}-{arch}")
|
|
|
|
# Get download URL
|
|
url, version, filename = get_blender_download_url(plat, arch)
|
|
if not url:
|
|
print("[Install] Error: Could not find Blender download for your platform")
|
|
print("[Install] Please install Blender manually from: https://www.blender.org/download/")
|
|
return False
|
|
|
|
# Create temporary download directory
|
|
temp_dir = script_dir / "_temp_download"
|
|
temp_dir.mkdir(exist_ok=True)
|
|
|
|
try:
|
|
# Download
|
|
download_path = temp_dir / filename
|
|
if not download_file(url, str(download_path)):
|
|
return False
|
|
|
|
# Extract
|
|
blender_dir.mkdir(exist_ok=True)
|
|
if not extract_archive(str(download_path), str(blender_dir)):
|
|
return False
|
|
|
|
print("\n[Install] ✓ Blender installation complete!")
|
|
print(f"[Install] Location: {blender_dir}")
|
|
|
|
# Find blender executable
|
|
if plat == "windows":
|
|
blender_exe = list(blender_dir.rglob("blender.exe"))
|
|
else:
|
|
blender_exe = list(blender_dir.rglob("blender"))
|
|
|
|
if blender_exe:
|
|
print(f"[Install] Blender executable: {blender_exe[0]}")
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"\n[Install] Error during installation: {e}")
|
|
return False
|
|
|
|
finally:
|
|
# Cleanup temp files
|
|
if temp_dir.exists():
|
|
print("[Install] Cleaning up temporary files...")
|
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
|
|
|
|
def main():
|
|
"""Entry point."""
|
|
print("\n" + "="*60)
|
|
print("ComfyUI-GeometryPack: Complete Installation")
|
|
print("="*60 + "\n")
|
|
print("This installer will set up:")
|
|
print(" 1. System dependencies (OpenGL libraries on Linux)")
|
|
print(" 2. Python dependencies (trimesh, pymeshlab, cgal, etc.)")
|
|
print(" 3. Blender 4.2.3 LTS (for UV unwrapping and remeshing)")
|
|
print("")
|
|
|
|
# Track installation results
|
|
results = {
|
|
'system_deps': False,
|
|
'python_deps': False,
|
|
'blender': False
|
|
}
|
|
|
|
# Step 1: Install system dependencies (Linux only)
|
|
results['system_deps'] = install_system_dependencies()
|
|
|
|
# Step 2: Install Python dependencies
|
|
results['python_deps'] = install_python_dependencies()
|
|
|
|
# Step 3: Install Blender
|
|
results['blender'] = install_blender()
|
|
|
|
# Display final results
|
|
print("\n" + "="*60)
|
|
print("Installation Summary")
|
|
print("="*60)
|
|
print(f" System Dependencies: {'✓ Success' if results['system_deps'] else '✗ Failed'}")
|
|
print(f" Python Dependencies: {'✓ Success' if results['python_deps'] else '✗ Failed'}")
|
|
print(f" Blender: {'✓ Success' if results['blender'] else '✗ Failed'}")
|
|
print("="*60 + "\n")
|
|
|
|
# Determine overall success
|
|
# System deps are optional (not all platforms need them)
|
|
critical_success = results['python_deps'] and results['blender']
|
|
|
|
if critical_success:
|
|
print("✓ Installation completed successfully!")
|
|
print("You can now use ComfyUI-GeometryPack nodes in ComfyUI.\n")
|
|
sys.exit(0)
|
|
else:
|
|
print("⚠ Installation completed with issues.")
|
|
if not results['python_deps']:
|
|
print("\nPython dependencies failed to install. You can:")
|
|
print(" 1. Try running: pip install -r requirements.txt")
|
|
print(" 2. Check your Python environment and permissions")
|
|
if not results['blender']:
|
|
print("\nBlender installation failed. You can:")
|
|
print(" 1. Install Blender manually: https://www.blender.org/download/")
|
|
print(" 2. Try running this script again: python install.py")
|
|
print("")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|