#!/usr/bin/env bash # ############################################################################################################################# # # Wael Isa # Website: https://www.wael.name # GitHub: https://github.com/waelisa # Version: v1.1.6 # Build Date: 03/19/2026 # License: MIT # # ██╗ ██╗ █████╗ ███████╗██╗ ██╗███████╗ █████╗ # ██║ ██║██╔══██╗██╔════╝██║ ██║██╔════╝██╔══██╗ # ██║ █╗ ██║███████║█████╗ ██║ ██║███████╗███████║ # ██║███╗██║██╔══██║██╔══╝ ██║ ██║╚════██║██╔══██║ # ╚███╔███╔╝██║ ██║███████╗███████╗ ██║███████╗██║ ██║ # ╚══╝╚══╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝╚══════╝╚═╝ ╚═╝ # # Description: # Interactive menu to install and manage Stable Diffusion WebUI (AUTOMATIC1111), Forge, and ComfyUI. # - Uses gum for a beautiful menu. # - Automatically detects GPU (NVIDIA, AMD, Intel) and installs correct PyTorch backend. # - Sets optimal launch arguments based on VRAM and GPU type (best‑practices tuning). # - Shares models between WebUIs via symlinks (A1111/Forge) or extra_model_paths.yaml (ComfyUI). # - Installs ComfyUI‑Manager and its dependencies. # - Enforces Python 3.11 with fallback to 3.10/3.12 if needed, with future‑proof checks for Python 3.13+. # - Distro-agnostic: detects package manager and can install system dependencies. # - Includes “Fix Paths” utility to repair broken symlinks and configs after moving folders. # - Adds ffmpeg for video workflows. # - Applies modern arguments: --theme=dark, --no-half-vae, and --opt-sdp-no-mem-attention. # - Security: verifies repository URLs and directory permissions. # - Caches hardware info to avoid repeated detection. # - Verbose mode with timestamps and debug logs. # - GPU temperature monitoring with thermal warnings. # - Confirmation before resetting virtual environments. # - Automated dependency updates in non‑interactive mode. # - Unified model hub (optional) to save even more space. # - Pre‑flight safety checks (disk space, pacman lock, internet). # - Hardware dashboard with temperature, VRAM usage, and system RAM. # - Architecture‑aware model linking (SD1.5, SDXL, Flux). # - Automatic system cleanup after successful installation. # - Continuous GPU health monitor (thermal throttle warnings). # - Dependency conflict checker (onnxruntime, torch versions). # - One‑click installation of recommended "dvine" model from CivitAI. # - Multi‑model installer for BoRnNo0b’s checkpoint collection. # - Post‑install prompt to install models. # # Features: # • Install AUTOMATIC1111 WebUI # • Install Forge WebUI (uses requirements_versions.txt) # • Install ComfyUI (with Manager) # • Install multiple WebUIs with model sharing # • Link models between A1111/Forge (symlinks) # • Link models to ComfyUI (extra_model_paths.yaml) # • Set up unified model hub (central storage) # • Install recommended model (dvine) # • Install multiple models from BoRnNo0b’s collection # • Auto‑optimize launch configs for all installed UIs # • Fix Paths (repair broken links/configs) # • Toggle attention mechanism (xformers vs sdp-no-mem) # • Hardware status dashboard # • Continuous GPU health monitor # • Check for dependency conflicts # • Update all dependencies (pip upgrade) # • Reset venv for any repository (with confirmation) # • Manual PyTorch installation for specific GPU types # • Install system dependencies # • Verbose mode (--verbose flag) # # Changelog: # v1.1.6 - Configure webui-user.sh with python_cmd, TORCH_COMMAND, and REQS_FILE. # This ensures correct Python and PyTorch are used even after venv reset. # v1.1.5 - Fixed pip installation inside virtual environment (externally-managed-environment error). # v1.1.4 - Fixed all remaining silent exits (AMD detection, temperature retrieval, cache writes). # v1.1.3 - Fixed package manager failures. # ############################################################################################################################# set -euo pipefail # ---------- Configuration ---------- readonly REPO_A1111="https://github.com/AUTOMATIC1111/stable-diffusion-webui.git" readonly REPO_FORGE="https://github.com/lllyasviel/stable-diffusion-webui-forge.git" readonly REPO_COMFY="https://github.com/comfyanonymous/ComfyUI.git" readonly REPO_COMFY_MANAGER="https://github.com/ltdrdata/ComfyUI-Manager.git" readonly DIR_A1111="stable-diffusion-webui" readonly DIR_FORGE="stable-diffusion-webui-forge" readonly DIR_COMFY="ComfyUI" readonly VENV_DIR="venv" readonly REQUIREMENTS_FILE="requirements_versions.txt" # A1111 and Forge use this # Central models directory (unified hub) MODELS_ROOT="${HOME}/sd_models" # Model information for dvine (keep for backward compatibility) readonly MODEL_NAME="dvine" readonly MODEL_VERSION_ID="2795248" # Latest version ID for dvine v2.0 readonly MODEL_DOWNLOAD_URL="https://civitai.com/api/download/models/${MODEL_VERSION_ID}" readonly MODEL_FILENAME="dvine_v20.safetensors" # Associative array of BoRnNo0b models (name -> version ID) # Fill in the actual version IDs from CivitAI model pages declare -A BORNNOOB_MODELS=( ["Dvine"]="2795248" ["NAI"]="CHANGE_ME" # Replace with actual version ID ["Matrix Hentai"]="CHANGE_ME" ["Tales Hentai"]="CHANGE_ME" ["IL"]="CHANGE_ME" ["Pornzilla Hentai"]="CHANGE_ME" ) # Default subfolder for SD1.5 models (used if architecture not specified) DEFAULT_MODEL_SUBDIR="Stable-diffusion/SD15" # Python version preference (fallback order) readonly PYTHON_VERSIONS=("3.11" "3.10" "3.12") PYTHON_CMD="" # Colors for output (fallback when gum not used) readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly BLUE='\033[0;34m' readonly NC='\033[0m' # No Color # GPU variables GPU_TYPE="" # nvidia, amd, intel, cpu GPU_VRAM="" # total VRAM in MB (all vendors) GPU_GFX="" # AMD GFX architecture (e.g., gfx1030) GPU_INFO="" # raw lspci output GPU_CACHE_FILE="${HOME}/.cache/sd_manager_gpu.cache" GPU_TEMP="" # current GPU temperature (if available) GPU_COMPUTE_CAP="" # NVIDIA compute capability (e.g., 8.9, 9.0, 12.0) # Attention mechanism preference (xformers vs sdp-no-mem) ATTENTION_MODE="sdp-no-mem" # default can be toggled # Verbose mode (set by --verbose flag) VERBOSE=false DEBUG_LOG="/tmp/sd_manager_$(date +%Y%m%d_%H%M%S).log" # Thermal monitoring flag (set by menu option) THERMAL_MONITOR_PID="" # ---------- Helper Functions ---------- log() { local level="$1" local msg="$2" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo -e "${timestamp} [${level}] ${msg}" >> "$DEBUG_LOG" if [[ "$VERBOSE" == true ]]; then echo -e "${timestamp} [${level}] ${msg}" fi } show_help() { cat < /dev/null; then echo -e "${RED}gum is required for interactive mode but not installed.${NC}" echo "Please install it using your package manager or run with --help for non-interactive options." if command -v pacman &> /dev/null; then echo " sudo pacman -S gum" elif command -v apt-get &> /dev/null; then echo " # Download the latest .deb from https://github.com/charmbracelet/gum/releases" elif command -v dnf &> /dev/null; then echo " # Download the latest .rpm from https://github.com/charmbracelet/gum/releases" else echo " See https://github.com/charmbracelet/gum#installation" fi log "ERROR" "gum not installed" exit 1 fi log "INFO" "gum check passed" } # Pre‑flight safety checks pre_install_checks() { echo -e "${BLUE}Running pre‑flight safety checks...${NC}" log "INFO" "Running pre‑flight checks" if ! ping -c 1 github.com &> /dev/null; then echo -e "${RED}No internet connection to GitHub. Please check your network.${NC}" log "ERROR" "Internet connection failed" exit 1 fi local free_space=$(df -BG . | awk 'NR==2 {print $4}' | sed 's/G//') if [[ "$free_space" -lt 20 ]]; then echo -e "${RED}Insufficient disk space: ${free_space}GB free (need at least 20GB).${NC}" log "ERROR" "Insufficient disk space: ${free_space}GB" exit 1 fi if command -v pacman &> /dev/null; then if [[ -f /var/lib/pacman/db.lck ]]; then echo -e "${RED}Pacman database is locked (/var/lib/pacman/db.lck exists). Please wait for other package operations to finish.${NC}" log "ERROR" "Pacman lock file present" exit 1 fi fi echo -e "${GREEN}Pre‑flight checks passed.${NC}" log "INFO" "Pre‑flight checks passed" } # Check directory permissions before cloning check_dir_permissions() { local target_dir="$1" local parent_dir=$(dirname "$target_dir") if [[ ! -w "$parent_dir" ]]; then echo -e "${RED}No write permission in ${parent_dir}. Please check permissions.${NC}" log "ERROR" "No write permission in $parent_dir" exit 1 fi log "INFO" "Directory permissions OK for $parent_dir" } # Detect available Python version with future‑proofing for Python 3.13+ detect_python() { # First, try to find exact version in PATH for ver in "${PYTHON_VERSIONS[@]}"; do if command -v "python${ver}" &> /dev/null; then PYTHON_CMD="python${ver}" echo -e "${GREEN}Using Python ${ver}${NC}" log "INFO" "Using Python $ver" return 0 fi done # If system Python is too new (≥3.13), try to find AUR python311 on Arch if command -v python3 &> /dev/null; then local pyver=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') if [[ "$pyver" =~ ^3\.1[3-9] ]]; then echo -e "${YELLOW}System Python is ${pyver} – WebUIs may not be compatible.${NC}" if command -v pacman &> /dev/null; then if pacman -Qq python311 2>/dev/null; then echo -e "${GREEN}Found python311 package. Using python3.11.${NC}" PYTHON_CMD="python3.11" return 0 else echo -e "${YELLOW}Consider installing 'python311' from AUR. Falling back to system python3.${NC}" fi fi fi echo -e "${YELLOW}Warning: Using system python3 (version ${pyver}) – SD WebUIs may not be compatible.${NC}" PYTHON_CMD="python3" log "WARNING" "Using system python3 version $pyver" return 0 fi echo -e "${RED}No Python interpreter found. Please install Python 3.10, 3.11, or 3.12.${NC}" log "ERROR" "No Python found" exit 1 } # Cache GPU detection to avoid repeated calls cache_gpu_info() { mkdir -p "$(dirname "$GPU_CACHE_FILE")" || true cat > "$GPU_CACHE_FILE" < /dev/null; then temp=$(nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | head -1) || true echo "$temp °C" else echo "N/A (nvidia-smi missing)" fi ;; "amd") if command -v rocm-smi &> /dev/null; then temp=$(rocm-smi --showtemp 2>/dev/null | grep "GPU[0-9]* temperature" | awk '{print $3}') || true if [[ -n "$temp" ]]; then echo "$temp °C" else echo "N/A (rocm-smi failed)" fi else echo "N/A (rocm-smi missing)" fi ;; "intel") echo "N/A" ;; *) echo "N/A" ;; esac return 0 } # Get GPU VRAM usage (percentage) get_gpu_vram_usage() { local usage="" case "$GPU_TYPE" in "nvidia") if command -v nvidia-smi &> /dev/null; then usage=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits | head -1) || true local total=$GPU_VRAM if [[ -n "$usage" && -n "$total" && "$total" -gt 0 ]]; then echo "$((usage * 100 / total))%" else echo "N/A" fi else echo "N/A" fi ;; *) echo "N/A" ;; esac } # Get system RAM available (in MB) get_system_ram_available() { if command -v free &> /dev/null; then free -m | awk 'NR==2 {print $7}' || echo "N/A" else echo "N/A" fi } # Show hardware dashboard show_hardware_dashboard() { detect_gpu echo -e "${BLUE}=== Hardware Status ===${NC}" echo -e "GPU Type: $GPU_TYPE" echo -e "GPU Temperature: $(get_gpu_temp)" echo -e "GPU VRAM Usage: $(get_gpu_vram_usage)" echo -e "System RAM Available: $(get_system_ram_available) MB" local temp=$(get_gpu_temp | awk '{print $1}') if [[ "$temp" =~ ^[0-9]+$ ]] && [[ "$temp" -gt 85 ]]; then echo -e "${RED}WARNING: GPU temperature is critically high (${temp}°C). Check cooling!${NC}" fi local usage_pct=$(get_gpu_vram_usage | sed 's/%//') if [[ "$usage_pct" =~ ^[0-9]+$ ]] && [[ "$usage_pct" -gt 90 ]]; then echo -e "${YELLOW}WARNING: VRAM usage is very high (${usage_pct}%). Close some applications.${NC}" fi local ram_avail=$(get_system_ram_available) if [[ "$ram_avail" =~ ^[0-9]+$ ]] && [[ "$ram_avail" -lt 1024 ]]; then echo -e "${YELLOW}WARNING: System RAM is low (${ram_avail} MB available). Close some applications.${NC}" fi log "INFO" "Hardware dashboard displayed" } # Continuous GPU health monitor (background or foreground) start_thermal_monitor() { if [[ -n "$THERMAL_MONITOR_PID" ]] && kill -0 "$THERMAL_MONITOR_PID" 2>/dev/null; then echo -e "${YELLOW}Thermal monitor already running (PID $THERMAL_MONITOR_PID).${NC}" return fi echo -e "${BLUE}Starting background thermal monitor (checks every 30s). Use 'Stop thermal monitor' to end.${NC}" ( while true; do sleep 30 local temp=$(get_gpu_temp | awk '{print $1}') if [[ "$temp" =~ ^[0-9]+$ ]] && [[ "$temp" -gt 85 ]]; then notify-send "GPU Thermal Warning" "Temperature reached ${temp}°C" 2>/dev/null || \ echo -e "${RED}GPU temperature is high: ${temp}°C${NC}" >&2 fi done ) & THERMAL_MONITOR_PID=$! log "INFO" "Thermal monitor started with PID $THERMAL_MONITOR_PID" } stop_thermal_monitor() { if [[ -n "$THERMAL_MONITOR_PID" ]] && kill -0 "$THERMAL_MONITOR_PID" 2>/dev/null; then kill "$THERMAL_MONITOR_PID" wait "$THERMAL_MONITOR_PID" 2>/dev/null || true THERMAL_MONITOR_PID="" echo -e "${GREEN}Thermal monitor stopped.${NC}" log "INFO" "Thermal monitor stopped" else echo -e "${YELLOW}No thermal monitor running.${NC}" fi } # Dependency conflict checker check_dependency_conflicts() { echo -e "${BLUE}Checking for dependency conflicts in installed WebUIs...${NC}" log "INFO" "Running dependency conflict check" local found_conflict=false for dir in "$DIR_A1111" "$DIR_FORGE" "$DIR_COMFY"; do if [[ -d "$dir" && -d "$dir/$VENV_DIR" ]]; then echo -e "\n${YELLOW}Checking $dir...${NC}" source "$dir/$VENV_DIR/bin/activate" if pip list 2>/dev/null | grep -q "onnxruntime" && pip list 2>/dev/null | grep -q "onnxruntime-gpu"; then echo -e "${RED}Conflict: Both onnxruntime and onnxruntime-gpu are installed in $dir. This can cause issues.${NC}" found_conflict=true fi local torch_ver=$(pip show torch 2>/dev/null | grep Version | awk '{print $2}') if [[ -n "$torch_ver" ]]; then echo " PyTorch version: $torch_ver" fi deactivate fi done if [[ "$found_conflict" == false ]]; then echo -e "${GREEN}No major dependency conflicts found.${NC}" fi log "INFO" "Dependency conflict check completed" } # Detect GPU vendor and capabilities detect_gpu() { if load_gpu_cache; then return fi echo -e "${BLUE}Detecting hardware...${NC}" log "INFO" "Starting hardware detection" # Get GPU info from lspci, but don't fail if lspci is missing if command -v lspci &> /dev/null; then GPU_INFO=$(lspci 2>/dev/null | grep -E "VGA compatible controller|Display controller" | head -1) || true else GPU_INFO="" log "WARNING" "lspci not found" fi log "INFO" "Primary GPU info: $GPU_INFO" if [[ -z "$GPU_INFO" ]]; then # Fallback: assume CPU mode if no GPU info GPU_TYPE="cpu" echo -e "${YELLOW}No GPU detected via lspci, falling back to CPU mode.${NC}" cache_gpu_info return fi if echo "$GPU_INFO" | grep -iq "nvidia"; then GPU_TYPE="nvidia" echo -e "${GREEN}NVIDIA GPU detected.${NC}" log "INFO" "NVIDIA GPU detected" if command -v nvidia-smi &> /dev/null; then GPU_VRAM=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits | head -1) || true if [[ -n "$GPU_VRAM" ]]; then echo -e " VRAM: ${GPU_VRAM} MB" log "INFO" "VRAM: $GPU_VRAM MB" fi GPU_COMPUTE_CAP=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader | head -1 | tr -d '.') || true if [[ -n "$GPU_COMPUTE_CAP" ]]; then echo -e " Compute Capability: ${GPU_COMPUTE_CAP}" log "INFO" "Compute capability: $GPU_COMPUTE_CAP" if [[ "$GPU_COMPUTE_CAP" -ge 120 ]]; then echo -e "${YELLOW} Note: RTX 50-series (Blackwell) detected. For optimal performance, use PyTorch 2.7+ with CUDA 12.8.${NC}" log "INFO" "RTX 50-series detected, may need PyTorch 2.7+" fi fi else echo -e "${YELLOW} nvidia-smi not found. Install NVIDIA drivers for VRAM detection.${NC}" log "WARNING" "nvidia-smi not found" fi elif echo "$GPU_INFO" | grep -iq "amd"; then GPU_TYPE="amd" echo -e "${GREEN}AMD GPU detected.${NC}" log "INFO" "AMD GPU detected" if command -v rocminfo &> /dev/null; then GPU_GFX=$(rocminfo 2>/dev/null | awk '/^Agent 2/,/^$/ {if ($1 == "Name:" && $2 ~ /^gfx/) {print $2; exit}}') || true if [[ -n "$GPU_GFX" ]]; then echo -e " GFX architecture: ${GPU_GFX}" log "INFO" "GFX architecture: $GPU_GFX" if [[ "$GPU_GFX" == gfx9* ]]; then export HSA_OVERRIDE_GFX_VERSION=9.0.0 echo -e "${YELLOW} Setting HSA_OVERRIDE_GFX_VERSION=9.0.0 for compatibility${NC}" fi fi else echo -e "${YELLOW} rocminfo not found. Install ROCm for detailed detection.${NC}" log "WARNING" "rocminfo not found" fi if [[ -d /sys/class/drm/ ]]; then for card in /sys/class/drm/card?/device/mem_info_vram_total; do if [[ -f "$card" ]]; then vram_bytes=$(cat "$card" 2>/dev/null) || true if [[ -n "$vram_bytes" ]]; then GPU_VRAM=$((vram_bytes / 1024 / 1024)) echo -e " VRAM: ${GPU_VRAM} MB" log "INFO" "VRAM: $GPU_VRAM MB (from sysfs)" fi break fi done fi if [[ -z "$GPU_VRAM" ]] && command -v glxinfo &> /dev/null; then GPU_VRAM=$(glxinfo -B 2>/dev/null | awk '/Video memory:/ {print $3}') || true if [[ -n "$GPU_VRAM" ]]; then echo -e " VRAM: ${GPU_VRAM} MB" log "INFO" "VRAM: $GPU_VRAM MB (from glxinfo)" fi fi elif echo "$GPU_INFO" | grep -iq "intel"; then GPU_TYPE="intel" echo -e "${GREEN}Intel GPU detected.${NC}" log "INFO" "Intel GPU detected" if [[ -d /sys/class/drm/ ]]; then for card in /sys/class/drm/card?/device/mem_info_vram_total; do if [[ -f "$card" ]]; then vram_bytes=$(cat "$card" 2>/dev/null) || true if [[ -n "$vram_bytes" ]]; then GPU_VRAM=$((vram_bytes / 1024 / 1024)) echo -e " VRAM: ${GPU_VRAM} MB" log "INFO" "VRAM: $GPU_VRAM MB (from sysfs)" fi break fi done fi if [[ -z "$GPU_VRAM" ]] && command -v glxinfo &> /dev/null; then GPU_VRAM=$(glxinfo -B 2>/dev/null | awk '/Video memory:/ {print $3}') || true if [[ -n "$GPU_VRAM" ]]; then echo -e " VRAM: ${GPU_VRAM} MB" log "INFO" "VRAM: $GPU_VRAM MB (from glxinfo)" fi fi else GPU_TYPE="cpu" echo -e "${YELLOW}No supported GPU found, will use CPU mode.${NC}" log "WARNING" "No supported GPU, falling back to CPU" fi GPU_TEMP=$(get_gpu_temp) echo -e " Temperature: $GPU_TEMP" log "INFO" "GPU temperature: $GPU_TEMP" cache_gpu_info } # Generate the correct PyTorch pip command based on GPU type get_torch_command() { local target_gpu="${1:-$GPU_TYPE}" case "$target_gpu" in "nvidia") if [[ -n "$GPU_COMPUTE_CAP" && "$GPU_COMPUTE_CAP" -ge 120 ]]; then echo "pip install torch torchvision torchaudio xformers --index-url https://download.pytorch.org/whl/cu128" else echo "pip install torch torchvision torchaudio xformers --index-url https://download.pytorch.org/whl/cu124" fi ;; "amd") if [[ -n "$GPU_GFX" ]]; then if [[ "$GPU_GFX" == gfx9* ]]; then echo "pip install torch==1.13.1+rocm5.2 torchvision==0.14.1+rocm5.2 --index-url https://download.pytorch.org/whl/rocm5.2" elif [[ "$GPU_GFX" == gfx10* ]]; then echo "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.7" elif [[ "$GPU_GFX" == gfx11* ]]; then echo "pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.0" else echo "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm7.1" fi else echo "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm7.1" fi ;; "intel") echo "pip install torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 --index-url https://download.pytorch.org/whl/xpu && pip install intel-extension-for-pytorch==2.1.10+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/" ;; *) echo "pip install torch torchvision torchaudio" ;; esac } # Configure webui-user.sh for A1111/Forge configure_webui_user_sh() { local target_dir="$1" local config_file="${target_dir}/webui-user.sh" if [[ ! -f "$config_file" ]]; then # Create a default if missing (should exist after clone, but just in case) cat > "$config_file" <<'EOF' #!/bin/bash ################################################################################ ### AUTOMATIC1111 Stable Diffusion WebUI Launch Script ################################################################################ # Command line arguments export COMMANDLINE_ARGS="" # Python executable (optional) # export PYTHON="" # Git executable (optional) # export GIT="" # Virtual environment directory (optional) # export VENV_DIR="venv" # Requirements file (optional) # export REQS_FILE="requirements_versions.txt" # PyTorch installation command (optional) # export TORCH_COMMAND="" ################################################################################ exec "${0/sh/bat}" "$@" EOF fi local backup="${config_file}.bak.$(date +%Y%m%d_%H%M%S)" cp "$config_file" "$backup" log "INFO" "Backup of webui-user.sh created at $backup" # Set python_cmd (the variable name is `python_cmd`, not `PYTHON`) if grep -q "^python_cmd=" "$config_file"; then sed -i "s|^python_cmd=.*|python_cmd=\"$PYTHON_CMD\"|" "$config_file" else # Insert after shebang or at top sed -i "2i python_cmd=\"$PYTHON_CMD\"" "$config_file" fi # Set TORCH_COMMAND local torch_cmd=$(get_torch_command) if grep -q "^export TORCH_COMMAND=" "$config_file"; then sed -i "s|^export TORCH_COMMAND=.*|export TORCH_COMMAND=\"$torch_cmd\"|" "$config_file" else echo "export TORCH_COMMAND=\"$torch_cmd\"" >> "$config_file" fi # Set REQS_FILE if grep -q "^export REQS_FILE=" "$config_file"; then sed -i "s|^export REQS_FILE=.*|export REQS_FILE=\"$REQUIREMENTS_FILE\"|" "$config_file" else echo "export REQS_FILE=\"$REQUIREMENTS_FILE\"" >> "$config_file" fi log "INFO" "Configured $config_file with python_cmd=$PYTHON_CMD, TORCH_COMMAND, REQS_FILE" } # Detect package manager and install system dependencies install_system_deps() { echo -e "${YELLOW}Installing system dependencies (Python, git, wget, pciutils, coreutils, ffmpeg)...${NC}" log "INFO" "Installing system dependencies" if command -v pacman &> /dev/null; then if ! sudo pacman -S --needed python python-pip python-virtualenv git wget pciutils coreutils ffmpeg; then echo -e "${RED}Failed to install dependencies with pacman. Please check your internet connection and package database.${NC}" log "ERROR" "pacman installation failed" exit 1 fi elif command -v apt-get &> /dev/null; then sudo apt-get update || { echo -e "${RED}apt-get update failed.${NC}"; log "ERROR" "apt-get update failed"; exit 1; } if ! sudo apt-get install -y git wget pciutils coreutils ffmpeg; then echo -e "${RED}Failed to install base dependencies with apt-get.${NC}" log "ERROR" "apt-get install failed" exit 1 fi if ! grep -q ^deb .*deadsnakes /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then sudo add-apt-repository -y ppa:deadsnakes/ppa || { echo -e "${RED}Failed to add deadsnakes PPA.${NC}"; log "ERROR" "add-apt-repository failed"; exit 1; } sudo apt-get update || { echo -e "${RED}apt-get update after PPA failed.${NC}"; log "ERROR" "apt-get update after PPA failed"; exit 1; } fi sudo apt-get install -y python3.11 python3.11-venv python3-pip || \ sudo apt-get install -y python3.10 python3.10-venv python3-pip || \ sudo apt-get install -y python3 python3-venv python3-pip || { echo -e "${RED}Failed to install Python.${NC}"; log "ERROR" "Python installation failed"; exit 1; } elif command -v dnf &> /dev/null; then sudo dnf makecache --refresh || { echo -e "${RED}dnf makecache failed.${NC}"; log "ERROR" "dnf makecache failed"; exit 1; } if ! sudo dnf install -y python3.11 python3-pip git wget pciutils coreutils ffmpeg-free; then # Try fallback without specific Python version if ! sudo dnf install -y python3 python3-pip git wget pciutils coreutils ffmpeg-free; then echo -e "${RED}Failed to install dependencies with dnf.${NC}" log "ERROR" "dnf install failed" exit 1 fi fi else echo -e "${RED}Unsupported package manager. Please install dependencies manually:${NC}" echo " - Python 3.10/3.11/3.12" echo " - git" echo " - wget" echo " - pciutils (for hardware detection)" echo " - coreutils (for realpath)" echo " - ffmpeg (for video workflows)" log "ERROR" "Unsupported package manager" exit 1 fi echo -e "${GREEN}System dependencies installed.${NC}" log "INFO" "System dependencies installed" if [[ -n "${NON_INTERACTIVE:-}" ]]; then echo -e "${YELLOW}Performing system cleanup...${NC}" if command -v apt-get &> /dev/null; then sudo apt autoremove -y && sudo apt clean elif command -v dnf &> /dev/null; then sudo dnf autoremove -y && sudo dnf clean all elif command -v pacman &> /dev/null; then sudo pacman -Sc --noconfirm fi fi } # Create virtual environment create_venv() { local repo_dir="$1" local venv_path="${repo_dir}/${VENV_DIR}" if [[ -d "$venv_path" ]]; then echo -e "${YELLOW}Virtual environment already exists in ${repo_dir}. Skipping creation.${NC}" log "INFO" "Venv already exists in $repo_dir" else echo -e "${GREEN}Creating virtual environment in ${repo_dir} ...${NC}" log "INFO" "Creating venv in $repo_dir with $PYTHON_CMD" (cd "$repo_dir" && "$PYTHON_CMD" -m venv "$VENV_DIR") fi } # Install requirements (now runs pip inside the venv) install_requirements() { local repo_dir="$1" local req_file="$2" local upgrade_flag="${3:-}" echo -e "${GREEN}Installing requirements from ${req_file} ...${NC}" log "INFO" "Installing requirements from $req_file in $repo_dir" ( cd "$repo_dir" || exit 1 source "${VENV_DIR}/bin/activate" pip install --upgrade pip pip install $upgrade_flag -r "$req_file" ) } # Hardware-specific PyTorch installation (now runs pip inside the venv) install_pytorch() { local repo_dir="$1" local target_gpu="${2:-$GPU_TYPE}" if [[ ! -d "${repo_dir}/${VENV_DIR}" ]]; then echo -e "${RED}Virtual environment not found in ${repo_dir}. Please create it first.${NC}" log "ERROR" "Venv not found in $repo_dir" return 1 fi echo -e "${GREEN}Installing PyTorch for ${target_gpu} GPU in ${repo_dir} ...${NC}" log "INFO" "Installing PyTorch for $target_gpu in $repo_dir" ( cd "$repo_dir" || exit 1 source "${VENV_DIR}/bin/activate" eval "$(get_torch_command "$target_gpu")" ) log "INFO" "PyTorch installation completed in $repo_dir" } # Verify Git remote URL verify_repo_url() { local repo_url="$1" local repo_dir="$2" if [[ -d "$repo_dir/.git" ]]; then local remote_url=$(git -C "$repo_dir" config --get remote.origin.url) local norm_repo="${repo_url%.git}" local norm_remote="${remote_url%.git}" if [[ "$norm_remote" != "$norm_repo" ]]; then echo -e "${RED}Security warning: existing repository remote URL (${remote_url}) does not match expected (${repo_url}).${NC}" echo "Please check the repository manually." log "ERROR" "Repository URL mismatch for $repo_dir" exit 1 fi log "INFO" "Repository URL verified for $repo_dir" fi } # Clone or update a repository clone_repo() { local repo_url="$1" local repo_dir="$2" check_dir_permissions "$repo_dir" if [[ -d "$repo_dir" ]]; then verify_repo_url "$repo_url" "$repo_dir" echo -e "${YELLOW}Repository ${repo_dir} already exists. Pulling latest changes...${NC}" log "INFO" "Pulling latest changes for $repo_dir" (cd "$repo_dir" && git pull) else echo -e "${GREEN}Cloning ${repo_url} ...${NC}" log "INFO" "Cloning $repo_url into $repo_dir" git clone "$repo_url" "$repo_dir" fi } # Reset venv reset_venv() { local repo_dir="$1" local venv_path="${repo_dir}/${VENV_DIR}" if [[ -d "$venv_path" ]]; then if [[ -z "${NON_INTERACTIVE:-}" ]]; then gum confirm "Are you sure you want to reset the virtual environment in ${repo_dir}? This will delete the current venv." || return fi echo -e "${YELLOW}Removing existing virtual environment in ${repo_dir} ...${NC}" log "INFO" "Removing venv in $repo_dir" rm -rf "$venv_path" fi create_venv "$repo_dir" echo -e "${GREEN}Virtual environment reset complete. You can now install PyTorch and requirements.${NC}" log "INFO" "Venv reset in $repo_dir" } # ---------- Unified Model Hub Setup ---------- setup_central_models() { echo -e "${BLUE}Setting up unified model hub at ${MODELS_ROOT}...${NC}" mkdir -p "$MODELS_ROOT"/{Stable-diffusion/SD15,Stable-diffusion/SDXL,Stable-diffusion/Flux,Lora,VAE,embeddings,ControlNet,ESRGAN,RealESRGAN,SwinIR,GFPGAN} echo -e "${YELLOW}Do you want to move existing models from your installations to the hub?${NC}" if gum confirm "This will move (not copy) model files to $MODELS_ROOT and symlink them back. Proceed?"; then for dir in "$DIR_A1111" "$DIR_FORGE" "$DIR_COMFY"; do if [[ -d "$dir" ]]; then # Handle main model folders with architecture subfolders for sub in "models/Stable-diffusion" "models/Lora" "models/VAE" "embeddings" "models/ControlNet" "models/ESRGAN" "models/RealESRGAN" "models/SwinIR" "models/GFPGAN"; do local src="$dir/$sub" local dst_base=$(basename "$sub") if [[ "$sub" == models/* ]]; then dst_base="${sub#models/}" else dst_base="$sub" fi local dst="$MODELS_ROOT/$dst_base" if [[ -d "$src" && ! -L "$src" ]]; then echo "Moving $src to $dst..." mkdir -p "$dst" # For Stable-diffusion, we need to move architecture subfolders individually if [[ "$dst_base" == "Stable-diffusion" ]]; then for arch in "SD15" "SDXL" "Flux"; do if [[ -d "$src/$arch" ]]; then mv "$src/$arch"/* "$dst/$arch"/ 2>/dev/null || true rmdir "$src/$arch" 2>/dev/null || true fi done # Move any loose files into SD15 as default find "$src" -maxdepth 1 -type f -name "*.safetensors" -o -name "*.ckpt" -exec mv {} "$dst/SD15"/ \; else mv "$src"/* "$dst"/ 2>/dev/null || true fi rm -rf "$src" ln -s "$dst" "$src" fi done fi done echo -e "${GREEN}Models moved and symlinked.${NC}" fi for ui in "$DIR_A1111" "$DIR_FORGE"; do if [[ -d "$ui" ]]; then local config="$ui/webui-user.sh" if [[ -f "$config" ]]; then cp "$config" "$config.bak.central" local current_args="" if grep -q "^export COMMANDLINE_ARGS=" "$config"; then current_args=$(grep "^export COMMANDLINE_ARGS=" "$config" | sed 's/^export COMMANDLINE_ARGS=//' | sed 's/"//g') fi local new_args="$current_args" for arch in "SD15" "SDXL" "Flux"; do new_args="$new_args --ckpt-dir $MODELS_ROOT/Stable-diffusion/$arch" done new_args="$new_args --lora-dir $MODELS_ROOT/Lora --vae-dir $MODELS_ROOT/VAE --embeddings-dir $MODELS_ROOT/embeddings" if grep -q "^export COMMANDLINE_ARGS=" "$config"; then sed -i "s|^export COMMANDLINE_ARGS=.*|export COMMANDLINE_ARGS=\"${new_args}\"|" "$config" else echo "export COMMANDLINE_ARGS=\"${new_args}\"" >> "$config" fi fi fi done if [[ -d "$DIR_COMFY" ]]; then local comfy_config="$DIR_COMFY/extra_model_paths.yaml" cat > "$comfy_config" </dev/null || true rm -rf "$dst" fi if [[ ! -L "$dst" ]]; then ln -s "$(realpath "$src")" "$dst" echo -e "${GREEN}Linked: ${folder}${NC}" log "INFO" "Linked $folder" else echo -e "${YELLOW}Link already exists for: ${folder}${NC}" fi done } # ---------- ComfyUI extra_model_paths.yaml generation ---------- generate_extra_model_paths() { local comfy_dir="$1" local source_ui="$2" local source_dir="" if [[ "$source_ui" == "A1111" ]]; then source_dir="$DIR_A1111" elif [[ "$source_ui" == "Forge" ]]; then source_dir="$DIR_FORGE" else echo -e "${RED}Invalid source UI.${NC}" log "ERROR" "Invalid source UI: $source_ui" return 1 fi if [[ ! -d "$source_dir" ]]; then echo -e "${RED}Source directory ${source_dir} does not exist. Please install it first.${NC}" log "ERROR" "Source dir $source_dir not found" return 1 fi local config_file="${comfy_dir}/extra_model_paths.yaml" local base_path base_path=$(realpath "$source_dir") cat > "$config_file" < "$config_file" <<'EOF' #!/bin/bash ################################################################################ ### AUTOMATIC1111 Stable Diffusion WebUI Launch Script ################################################################################ # Command line arguments export COMMANDLINE_ARGS="" # Python executable (optional) # export PYTHON="" # Git executable (optional) # export GIT="" # Virtual environment directory (optional) # export VENV_DIR="venv" # Requirements file (optional) # export REQS_FILE="requirements_versions.txt" # PyTorch installation command (optional) # export TORCH_COMMAND="" ################################################################################ exec "${0/sh/bat}" "$@" EOF fi # Backup before modifying local backup="${config_file}.bak.$(date +%Y%m%d_%H%M%S)" cp "$config_file" "$backup" log "INFO" "Backup created at $backup" # Update COMMANDLINE_ARGS if grep -q "^export COMMANDLINE_ARGS=" "$config_file"; then sed -i "s|^export COMMANDLINE_ARGS=.*|export COMMANDLINE_ARGS=\"${args}\"|" "$config_file" else echo "export COMMANDLINE_ARGS=\"${args}\"" >> "$config_file" fi echo -e "${GREEN}Applied optimal arguments: ${args}${NC}" log "INFO" "Applied args: $args" } # ---------- ComfyUI Hardware Optimization ---------- apply_comfy_tips() { local target_dir="$1" local launch_script="${target_dir}/launch_comfy.sh" local args="" echo -e "${BLUE}Generating optimized launch script for ComfyUI (${GPU_TYPE})...${NC}" log "INFO" "Generating ComfyUI launch script for $GPU_TYPE" case "$GPU_TYPE" in "nvidia") args="--preview-method taesd" if [[ -n "$GPU_VRAM" ]]; then if [[ "$GPU_VRAM" -lt 4000 ]]; then args="--lowvram $args" elif [[ "$GPU_VRAM" -lt 8000 ]]; then args="--normalvram $args" elif [[ "$GPU_VRAM" -ge 16000 ]]; then args="--highvram $args" fi fi ;; "amd"|"intel") args="--use-split-cross-attention --preview-method taesd" if [[ -n "$GPU_VRAM" && "$GPU_VRAM" -lt 8000 ]]; then args="--lowvram $args" fi ;; *) args="--cpu --preview-method taesd" ;; esac cat > "$launch_script" < /dev/null; then wget --show-progress -qO "$output_file" "$download_url" elif command -v curl &> /dev/null; then curl -L --progress-bar -o "$output_file" "$download_url" else echo -e "${RED}No downloader found.${NC}" return 1 fi if [[ $? -eq 0 ]]; then echo -e "${GREEN}Successfully downloaded $model_name${NC}" else echo -e "${RED}Failed to download $model_name${NC}" fi } # ---------- Install Recommended Model (dvine) ---------- install_dvine_model() { echo -e "${BLUE}Installing recommended model: ${MODEL_NAME}...${NC}" log "INFO" "Starting dvine model installation" download_civitai_model "$MODEL_NAME" "$MODEL_VERSION_ID" } # ---------- Install Multiple Models from BoRnNo0b ---------- install_bornnoob_models() { echo -e "${BLUE}Select models to install from BoRnNo0b's collection:${NC}" local model_names=() for name in "${!BORNNOOB_MODELS[@]}"; do model_names+=("$name") done # Let user choose multiple models (or all) local selected selected=$(gum choose --no-limit --header "Use space to select, enter to confirm" "${model_names[@]}") if [[ -z "$selected" ]]; then echo -e "${YELLOW}No models selected.${NC}" return fi # Process each selected model echo "$selected" | while read -r model; do local version_id="${BORNNOOB_MODELS[$model]}" if [[ "$version_id" == "CHANGE_ME" ]]; then echo -e "${RED}Version ID for '$model' is not set. Please edit the script and add the correct version ID from CivitAI.${NC}" log "WARNING" "Skipping $model: version ID not set" continue fi download_civitai_model "$model" "$version_id" done } # ---------- Full installation routines ---------- install_repo() { local repo_url="$1" local repo_dir="$2" local req_file="$3" local upgrade_flag="${4:-}" clone_repo "$repo_url" "$repo_dir" create_venv "$repo_dir" # Configure webui-user.sh before installing PyTorch so that TORCH_COMMAND is available if ever needed configure_webui_user_sh "$repo_dir" install_pytorch "$repo_dir" install_requirements "$repo_dir" "$req_file" "$upgrade_flag" apply_best_tips "$repo_dir" echo -e "${GREEN}Installation complete for ${repo_dir}.${NC}" log "INFO" "Installation complete for $repo_dir" if [[ -z "${NON_INTERACTIVE:-}" ]]; then if gum confirm "Would you like to install the recommended 'dvine' model now?"; then install_dvine_model fi fi } install_comfy() { mkdir -p "$DIR_COMFY" clone_repo "$REPO_COMFY" "$DIR_COMFY" create_venv "$DIR_COMFY" install_pytorch "$DIR_COMFY" local manager_dir="${DIR_COMFY}/custom_nodes/ComfyUI-Manager" if [[ -d "$manager_dir" ]]; then verify_repo_url "$REPO_COMFY_MANAGER" "$manager_dir" echo -e "${YELLOW}ComfyUI-Manager already exists, pulling updates...${NC}" log "INFO" "Pulling ComfyUI-Manager updates" (cd "$manager_dir" && git pull) else echo -e "${GREEN}Cloning ComfyUI-Manager...${NC}" log "INFO" "Cloning ComfyUI-Manager" git clone "$REPO_COMFY_MANAGER" "$manager_dir" fi if [[ -f "${manager_dir}/requirements.txt" ]]; then echo -e "${GREEN}Installing ComfyUI-Manager dependencies...${NC}" log "INFO" "Installing ComfyUI-Manager requirements" (cd "$DIR_COMFY" && source "${VENV_DIR}/bin/activate" && pip install -r "${manager_dir}/requirements.txt") fi if [[ -f "$DIR_COMFY/extra_model_paths.yaml" ]]; then sed -i '/base_path:/c\ base_path: /usr/local/share/comfyui' "$DIR_COMFY/extra_model_paths.yaml" 2>/dev/null || true fi apply_comfy_tips "$DIR_COMFY" echo -e "${GREEN}ComfyUI installation complete.${NC}" log "INFO" "ComfyUI installation complete" if [[ -z "${NON_INTERACTIVE:-}" ]]; then if gum confirm "Would you like to install the recommended 'dvine' model now?"; then install_dvine_model fi fi } auto_configure() { local repo_dir="$1" if [[ ! -d "$repo_dir" ]]; then echo -e "${RED}Repository ${repo_dir} not found.${NC}" log "ERROR" "auto_configure: $repo_dir not found" return 1 fi detect_gpu # Ensure webui-user.sh is properly configured for this repo if [[ "$repo_dir" != "$DIR_COMFY" ]]; then configure_webui_user_sh "$repo_dir" fi install_pytorch "$repo_dir" if [[ "$repo_dir" == "$DIR_COMFY" ]]; then apply_comfy_tips "$repo_dir" if [[ "$GPU_TYPE" == "nvidia" ]]; then echo -e "${GREEN}NVIDIA GPU with ${GPU_VRAM}MB VRAM${NC}" elif [[ "$GPU_TYPE" == "amd" ]]; then echo -e "${GREEN}AMD GPU with ${GPU_GFX} architecture${NC}" fi else apply_best_tips "$repo_dir" fi echo -e "${GREEN}Hardware configuration complete for ${repo_dir}.${NC}" log "INFO" "Hardware configuration complete for $repo_dir" } # ---------- Interactive Menu ---------- main_menu() { local temp=$(get_gpu_temp) local header="Stable Diffusion WebUI Manager v1.1.6 | Attention: $ATTENTION_MODE | GPU Temp: $temp" local choice choice=$(gum choose --header "$header" \ "Install AUTOMATIC1111 WebUI" \ "Install Forge WebUI" \ "Install ComfyUI + Manager" \ "Install BOTH A1111 & Forge (with model linking)" \ "Link A1111/Forge models (symlinks)" \ "Link models to ComfyUI (extra_model_paths.yaml)" \ "Set up unified model hub (central storage)" \ "Install recommended model (dvine)" \ "Install multiple models from BoRnNo0b" \ "Auto-optimize configs for installed UIs" \ "Fix Paths (repair broken links/configs)" \ "Toggle attention mechanism (xformers ↔ sdp-no-mem)" \ "Hardware status dashboard" \ "Start thermal monitor (background)" \ "Stop thermal monitor" \ "Check for dependency conflicts" \ "Update all dependencies (pip upgrade)" \ "Reset venv for AUTOMATIC1111" \ "Reset venv for Forge" \ "Reset venv for ComfyUI" \ "Install/Reinstall PyTorch (choose GPU type)" \ "Install system dependencies" \ "Exit") case "$choice" in "Install AUTOMATIC1111 WebUI") pre_install_checks detect_gpu install_repo "$REPO_A1111" "$DIR_A1111" "$REQUIREMENTS_FILE" if [[ -n "${NON_INTERACTIVE:-}" ]]; then pip cache purge || true fi ;; "Install Forge WebUI") pre_install_checks detect_gpu install_repo "$REPO_FORGE" "$DIR_FORGE" "$REQUIREMENTS_FILE" if [[ -n "${NON_INTERACTIVE:-}" ]]; then pip cache purge || true fi ;; "Install ComfyUI + Manager") pre_install_checks detect_gpu install_comfy if [[ -n "${NON_INTERACTIVE:-}" ]]; then pip cache purge || true fi if gum confirm "Do you want to link models from an existing A1111/Forge installation?"; then source_ui=$(gum choose "A1111" "Forge") generate_extra_model_paths "$DIR_COMFY" "$source_ui" fi ;; "Install BOTH A1111 & Forge (with model linking)") pre_install_checks detect_gpu install_repo "$REPO_A1111" "$DIR_A1111" "$REQUIREMENTS_FILE" install_repo "$REPO_FORGE" "$DIR_FORGE" "$REQUIREMENTS_FILE" master=$(gum choose --header "Choose the master directory (where models will be physically stored)" "AUTOMATIC1111" "Forge") if [[ "$master" == "AUTOMATIC1111" ]]; then link_models "$DIR_A1111" "$DIR_FORGE" else link_models "$DIR_FORGE" "$DIR_A1111" fi gum toast --title "Success" "Both WebUIs installed and models linked!" if [[ -n "${NON_INTERACTIVE:-}" ]]; then pip cache purge || true fi ;; "Link A1111/Forge models (symlinks)") master=$(gum choose --header "Choose the MASTER directory (where your models are actually stored)" "AUTOMATIC1111" "Forge") if [[ "$master" == "AUTOMATIC1111" ]]; then link_models "$DIR_A1111" "$DIR_FORGE" else link_models "$DIR_FORGE" "$DIR_A1111" fi gum toast --status success "Models linked successfully!" ;; "Link models to ComfyUI (extra_model_paths.yaml)") if [[ ! -d "$DIR_COMFY" ]]; then gum style --foreground 196 "ComfyUI not installed. Please install it first." else source_ui=$(gum choose "A1111" "Forge") generate_extra_model_paths "$DIR_COMFY" "$source_ui" fi ;; "Set up unified model hub (central storage)") setup_central_models gum toast --status success "Unified model hub configured!" ;; "Install recommended model (dvine)") install_dvine_model gum toast --status success "Model installation attempted!" ;; "Install multiple models from BoRnNo0b") install_bornnoob_models gum toast --status success "Batch installation attempted!" ;; "Auto-optimize configs for installed UIs") detect_gpu for dir in "$DIR_A1111" "$DIR_FORGE" "$DIR_COMFY"; do if [[ -d "$dir" ]]; then if [[ "$dir" == "$DIR_COMFY" ]]; then apply_comfy_tips "$dir" else # Ensure webui-user.sh has the right python and torch command configure_webui_user_sh "$dir" apply_best_tips "$dir" fi fi done gum toast --status success "Optimal settings applied based on your $GPU_TYPE GPU!" ;; "Fix Paths (repair broken links/configs)") fix_paths gum toast --status success "Paths fixed!" ;; "Toggle attention mechanism (xformers ↔ sdp-no-mem)") toggle_attention ;; "Hardware status dashboard") show_hardware_dashboard gum confirm "Press Enter to continue" ;; "Start thermal monitor (background)") start_thermal_monitor ;; "Stop thermal monitor") stop_thermal_monitor ;; "Check for dependency conflicts") check_dependency_conflicts gum confirm "Press Enter to continue" ;; "Update all dependencies (pip upgrade)") update_all_deps gum toast --status success "Dependencies updated!" ;; "Reset venv for AUTOMATIC1111") reset_venv "$DIR_A1111" # After reset, reconfigure and reinstall PyTorch? The user can do it manually, but we can prompt if gum confirm "Do you want to reinstall PyTorch now?"; then install_pytorch "$DIR_A1111" fi ;; "Reset venv for Forge") reset_venv "$DIR_FORGE" if gum confirm "Do you want to reinstall PyTorch now?"; then install_pytorch "$DIR_FORGE" fi ;; "Reset venv for ComfyUI") reset_venv "$DIR_COMFY" if gum confirm "Do you want to reinstall PyTorch now?"; then install_pytorch "$DIR_COMFY" fi ;; "Install/Reinstall PyTorch (choose GPU type)") dir_choice=$(gum choose "AUTOMATIC1111" "Forge" "ComfyUI") case "$dir_choice" in "AUTOMATIC1111") target="$DIR_A1111" ;; "Forge") target="$DIR_FORGE" ;; "ComfyUI") target="$DIR_COMFY" ;; esac gpu_choice=$(gum choose "Auto-detect (recommended)" "NVIDIA (CUDA)" "AMD (ROCm)" "Intel (IPEX)" "CPU only") case "$gpu_choice" in "Auto-detect (recommended)") detect_gpu install_pytorch "$target" ;; "NVIDIA (CUDA)") install_pytorch "$target" "nvidia" ;; "AMD (ROCm)") install_pytorch "$target" "amd" ;; "Intel (IPEX)") install_pytorch "$target" "intel" ;; "CPU only") install_pytorch "$target" "cpu" ;; esac ;; "Install system dependencies") pre_install_checks install_system_deps ;; "Exit") stop_thermal_monitor echo "Goodbye!" log "INFO" "Script terminated by user" exit 0 ;; *) echo -e "${RED}Invalid option.${NC}" ;; esac } # ---------- Non-Interactive Mode ---------- handle_non_interactive() { local upgrade_flag="--upgrade" pre_install_checks case "${NON_INTERACTIVE:-}" in "a1111") detect_gpu install_repo "$REPO_A1111" "$DIR_A1111" "$REQUIREMENTS_FILE" "$upgrade_flag" ;; "forge") detect_gpu install_repo "$REPO_FORGE" "$DIR_FORGE" "$REQUIREMENTS_FILE" "$upgrade_flag" ;; "comfy") detect_gpu install_comfy if [[ -d "$DIR_A1111" ]]; then generate_extra_model_paths "$DIR_COMFY" "A1111" elif [[ -d "$DIR_FORGE" ]]; then generate_extra_model_paths "$DIR_COMFY" "Forge" fi ;; "both") detect_gpu install_repo "$REPO_A1111" "$DIR_A1111" "$REQUIREMENTS_FILE" "$upgrade_flag" install_repo "$REPO_FORGE" "$DIR_FORGE" "$REQUIREMENTS_FILE" "$upgrade_flag" link_models "$DIR_A1111" "$DIR_FORGE" ;; "dvine") install_dvine_model ;; "bornnoob") for name in "${!BORNNOOB_MODELS[@]}"; do local version_id="${BORNNOOB_MODELS[$name]}" if [[ "$version_id" != "CHANGE_ME" ]]; then download_civitai_model "$name" "$version_id" fi done ;; "configure") detect_gpu for dir in "$DIR_A1111" "$DIR_FORGE" "$DIR_COMFY"; do if [[ -d "$dir" ]]; then auto_configure "$dir" fi done ;; esac pip cache purge || true if [[ ! -d "$DIR_A1111" && ! -d "$DIR_FORGE" ]]; then echo "Cleaning up unused repositories..." if [[ -d "$REPO_A1111" ]]; then rm -rf "$REPO_A1111" fi if [[ -d "$REPO_FORGE" ]]; then rm -rf "$REPO_FORGE" fi fi if [[ ! -d "$DIR_COMFY" ]]; then if [[ -d "$REPO_COMFY_MANAGER" ]]; then rm -rf "$REPO_COMFY_MANAGER" fi fi } # ---------- Initialization ---------- check_gum detect_python # ---------- Main Execution ---------- if [[ -n "${NON_INTERACTIVE:-}" ]]; then handle_non_interactive else while true; do main_menu echo gum confirm "Return to main menu?" --affirmative="Yes" --negative="Exit" || break done fi log "INFO" "Script finished successfully"