#!/bin/bash # Print system information echo "=== System Information ===" echo "Python version: $(python3 --version)" echo "CUDA version: $(nvcc --version 2>/dev/null || echo 'NVCC not found')" echo "GPU information: $(nvidia-smi || echo 'NVIDIA-SMI not found')" echo "===========================" # Create writable directories in /tmp (where we have permissions) echo "Creating writable directories..." mkdir -p /tmp/comfyui_input mkdir -p /tmp/comfyui_output mkdir -p /tmp/comfyui_temp mkdir -p /tmp/comfyui_user mkdir -p /tmp/workflows mkdir -p /tmp/comfyui_models/pulid mkdir -p /tmp/comfyui_models/evaclip mkdir -p /tmp/comfyui_models/insightface # Try to set permissions, but don't fail if we can't chmod -R 777 /tmp/comfyui_input /tmp/comfyui_output /tmp/comfyui_temp /tmp/comfyui_user /tmp/workflows /tmp/comfyui_models || echo "Failed to set permissions, continuing anyway" # Download files from the dataset repository echo "Downloading images from dataset repository..." curl -L -o /tmp/comfyui_input/pose4.jpg "https://huggingface.co/datasets/Defter77/appdata/resolve/main/pose4.jpg" || echo "Failed to download pose4.jpg" curl -L -o /tmp/comfyui_input/paita2.jpg "https://huggingface.co/datasets/Defter77/appdata/resolve/main/paita2.jpg" || echo "Failed to download paita2.jpg" echo "Downloading workflow from dataset repository..." curl -L -o /tmp/workflows/Workflow_12_11.json "https://huggingface.co/datasets/Defter77/appdata/resolve/main/Workflow_12_11.json" || echo "Failed to download Workflow_12_11.json" # List downloaded files echo "Verifying downloaded files:" ls -la /tmp/comfyui_input/ ls -la /tmp/workflows/ # Run model downloader to get models and PuLID echo "Downloading models and PuLID code..." cd /app python3 download_models.py # Setup folder paths for ComfyUI directly in Python code # Instead of relying on a YAML file that might have formatting issues echo "Setting up model paths in Python code..." cat > /tmp/setup_paths.py << EOF import os import folder_paths # Setup base folders (even if they already exist in folder_paths) folder_paths.folder_names_and_paths["checkpoints"] = (["/tmp/comfyui_models/checkpoints"], folder_paths.supported_pt_extensions) folder_paths.folder_names_and_paths["controlnet"] = (["/tmp/comfyui_models/controlnet"], folder_paths.supported_pt_extensions) folder_paths.folder_names_and_paths["clip_vision"] = (["/tmp/comfyui_models/clip_vision"], folder_paths.supported_pt_extensions) folder_paths.folder_names_and_paths["ipadapter"] = (["/tmp/comfyui_models/ipadapter"], folder_paths.supported_pt_extensions) folder_paths.folder_names_and_paths["pulid"] = (["/tmp/comfyui_models/pulid"], folder_paths.supported_pt_extensions) folder_paths.folder_names_and_paths["evaclip"] = (["/tmp/comfyui_models/evaclip"], folder_paths.supported_pt_extensions + [".pt"]) folder_paths.folder_names_and_paths["insightface"] = (["/tmp/comfyui_models/insightface"], [".onnx"]) print("Model paths set up successfully") EOF # Install additional Python packages (but don't fail if they can't be installed) echo "Installing required Python packages..." pip install ftfy regex onnxruntime scikit-learn PyYAML comfyui-frontend-package comfyui-workflow-templates || echo "Package installation failed, continuing anyway" # Check if PuLID is properly set up if [ -f "/app/ComfyUI/custom_nodes/PuLID/__init__.py" ]; then echo "PuLID node found at /app/ComfyUI/custom_nodes/PuLID" # Check if it has all needed classes if ! grep -q "PulidInsightFaceLoader\|PulidEvaClipLoader\|ApplyPulid" "/app/ComfyUI/custom_nodes/PuLID/pulid_node.py"; then echo "Updating PuLID node with missing classes..." cat > /app/ComfyUI/custom_nodes/PuLID/pulid_node.py << EOF import torch import os import numpy as np import folder_paths class PulidModelLoader: @classmethod def INPUT_TYPES(s): return {"required": {"model_name": (folder_paths.get_filename_list("pulid"), )}} RETURN_TYPES = ("PULID_MODEL",) FUNCTION = "load_model" CATEGORY = "loaders" def load_model(self, model_name): model_path = folder_paths.get_full_path("pulid", model_name) return (model_path,) class PulidInsightFaceLoader: @classmethod def INPUT_TYPES(s): return {"required": {}} RETURN_TYPES = ("INSIGHTFACE",) FUNCTION = "load_insight_face" CATEGORY = "loaders" def load_insight_face(self): # This is a simplified implementation that just returns a dummy value # In a real setup, this would load the actual InsightFace model try: # Try to load insightface model path model_path = folder_paths.get_full_path("insightface", "1k3d68.onnx") return (model_path,) except: # Return dummy if model not found return ("insightface_model",) class PulidEvaClipLoader: @classmethod def INPUT_TYPES(s): return {"required": {}} RETURN_TYPES = ("EVACLIP",) FUNCTION = "load_evaclip" CATEGORY = "loaders" def load_evaclip(self): # This is a simplified implementation that just returns a dummy value # In a real setup, this would load the actual EVA CLIP model try: # Try to load the EVA CLIP model path model_path = folder_paths.get_full_path("evaclip", "EVA02-CLIP-bigE-14-plus.pt") return (model_path,) except: # Return dummy if model not found return ("evaclip_model",) class ApplyPulid: @classmethod def INPUT_TYPES(s): return { "required": { "model": ("PULID_MODEL",), "image": ("IMAGE",), "insightface_model": ("INSIGHTFACE",), "evaclip_model": ("EVACLIP",), "weight": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}), "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}), "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), } } RETURN_TYPES = ("IMAGE",) FUNCTION = "apply_pulid" CATEGORY = "image/facetools" def apply_pulid(self, model, image, insightface_model, evaclip_model, weight, start_at, end_at): # This is a simplified implementation that just returns the input image # In a real setup, this would apply the PuLID model to the image return (image,) NODE_CLASS_MAPPINGS = { "PulidModelLoader": PulidModelLoader, "PulidInsightFaceLoader": PulidInsightFaceLoader, "PulidEvaClipLoader": PulidEvaClipLoader, "ApplyPulid": ApplyPulid } NODE_DISPLAY_NAME_MAPPINGS = { "PulidModelLoader": "Load PuLID Model", "PulidInsightFaceLoader": "Load InsightFace Model", "PulidEvaClipLoader": "Load EVA CLIP Model", "ApplyPulid": "Apply PuLID" } EOF echo "Updated PuLID implementation with all required classes" fi else echo "WARNING: PuLID node not found! Creating complete implementation..." mkdir -p /app/ComfyUI/custom_nodes/PuLID # Create init file cat > /app/ComfyUI/custom_nodes/PuLID/__init__.py << EOF from .pulid_node import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS EOF # Create complete node implementation cat > /app/ComfyUI/custom_nodes/PuLID/pulid_node.py << EOF import torch import os import numpy as np import folder_paths class PulidModelLoader: @classmethod def INPUT_TYPES(s): return {"required": {"model_name": (folder_paths.get_filename_list("pulid"), )}} RETURN_TYPES = ("PULID_MODEL",) FUNCTION = "load_model" CATEGORY = "loaders" def load_model(self, model_name): model_path = folder_paths.get_full_path("pulid", model_name) return (model_path,) class PulidInsightFaceLoader: @classmethod def INPUT_TYPES(s): return {"required": {}} RETURN_TYPES = ("INSIGHTFACE",) FUNCTION = "load_insight_face" CATEGORY = "loaders" def load_insight_face(self): # This is a simplified implementation that just returns a dummy value # In a real setup, this would load the actual InsightFace model try: # Try to load insightface model path model_path = folder_paths.get_full_path("insightface", "1k3d68.onnx") return (model_path,) except: # Return dummy if model not found return ("insightface_model",) class PulidEvaClipLoader: @classmethod def INPUT_TYPES(s): return {"required": {}} RETURN_TYPES = ("EVACLIP",) FUNCTION = "load_evaclip" CATEGORY = "loaders" def load_evaclip(self): # This is a simplified implementation that just returns a dummy value # In a real setup, this would load the actual EVA CLIP model try: # Try to load the EVA CLIP model path model_path = folder_paths.get_full_path("evaclip", "EVA02-CLIP-bigE-14-plus.pt") return (model_path,) except: # Return dummy if model not found return ("evaclip_model",) class ApplyPulid: @classmethod def INPUT_TYPES(s): return { "required": { "model": ("PULID_MODEL",), "image": ("IMAGE",), "insightface_model": ("INSIGHTFACE",), "evaclip_model": ("EVACLIP",), "weight": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}), "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}), "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), } } RETURN_TYPES = ("IMAGE",) FUNCTION = "apply_pulid" CATEGORY = "image/facetools" def apply_pulid(self, model, image, insightface_model, evaclip_model, weight, start_at, end_at): # This is a simplified implementation that just returns the input image # In a real setup, this would apply the PuLID model to the image return (image,) NODE_CLASS_MAPPINGS = { "PulidModelLoader": PulidModelLoader, "PulidInsightFaceLoader": PulidInsightFaceLoader, "PulidEvaClipLoader": PulidEvaClipLoader, "ApplyPulid": ApplyPulid } NODE_DISPLAY_NAME_MAPPINGS = { "PulidModelLoader": "Load PuLID Model", "PulidInsightFaceLoader": "Load InsightFace Model", "PulidEvaClipLoader": "Load EVA CLIP Model", "ApplyPulid": "Apply PuLID" } EOF echo "Created complete PuLID implementation with all required classes" fi # Start ComfyUI in the background with custom directories cd /app/ComfyUI echo "Starting ComfyUI server..." # Create a simple startup script for ComfyUI that imports our path setup cat > /tmp/comfyui_starter.py << EOF # Import folder path setup first import sys import os # Add the temp directory to Python path so we can import setup_paths sys.path.append('/tmp') # Try to set up folder paths - ignore errors try: import setup_paths except Exception as e: print(f"Warning: Failed to import setup_paths: {e}") print("Continuing anyway with default paths") # Now run the main ComfyUI script sys.path.append('/app/ComfyUI') # Set environment variables os.environ['PYTHONPATH'] = f"{os.environ.get('PYTHONPATH', '')}:/app/ComfyUI:/app/ComfyUI/custom_nodes" # Import and run ComfyUI try: import main except Exception as e: print(f"Error starting ComfyUI: {e}") print("ComfyUI failed to start, but we'll continue with the Gradio interface") EOF # Run ComfyUI with our custom starter script python3 /tmp/comfyui_starter.py --listen 0.0.0.0 --port 8188 \ --input-directory /tmp/comfyui_input \ --output-directory /tmp/comfyui_output \ --temp-directory /tmp/comfyui_temp \ --user-dir /tmp/comfyui_user & COMFY_PID=$! # Wait for ComfyUI to start echo "Waiting for ComfyUI server to initialize..." sleep 30 # Give it more time to start # Check if ComfyUI is running if curl -s "http://localhost:8188/system_stats" > /dev/null; then echo "ComfyUI server is up and running!" else echo "WARNING: ComfyUI server might not be running correctly." echo "Proceeding anyway..." fi # List loaded custom nodes for debugging echo "Custom nodes check:" curl -s "http://localhost:8188/object_info" | grep -i "pulid" || echo "PuLID nodes not found" # Start the web interface with updated workflow path cd /app echo "Starting Gradio interface..." WORKFLOW_PATH="/tmp/workflows/Workflow_12_11.json" python3 app.py # If the Gradio app exits, kill ComfyUI too kill $COMFY_PID