#!/bin/bash # # ComfyUI Model Downloader - A Beautiful CLI Tool # Downloads AI models from HuggingFace and creates symlinks to ComfyUI directories # # Usage: ./artifact_comfyui_download.sh [COMMAND] [options] # # Commands: download, link, both (default) # set -euo pipefail # ============================================================================ # COLOR PALETTE - Beautiful Terminal Colors # ============================================================================ # Reset RESET='\033[0m' # Foreground Colors BLACK='\033[0;30m' RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[0;37m' # Bold BOLD_BLACK='\033[1;30m' BOLD_RED='\033[1;31m' BOLD_GREEN='\033[1;32m' BOLD_YELLOW='\033[1;33m' BOLD_BLUE='\033[1;34m' BOLD_MAGENTA='\033[1;35m' BOLD_CYAN='\033[1;36m' BOLD_WHITE='\033[1;37m' # Background Colors BG_BLACK='\033[40m' BG_RED='\033[41m' BG_GREEN='\033[42m' BG_YELLOW='\033[43m' BG_BLUE='\033[44m' BG_MAGENTA='\033[45m' BG_CYAN='\033[46m' BG_WHITE='\033[47m' # Styles DIM='\033[2m' ITALIC='\033[3m' UNDERLINE='\033[4m' BLINK='\033[5m' REVERSE='\033[7m' # ============================================================================ # UNICODE CHARACTERS - Make it Pretty # ============================================================================ CHECK_MARK="โœ“" CROSS_MARK="โœ—" ROCKET="๐Ÿš€" PACKAGE="๐Ÿ“ฆ" DOWNLOAD="โฌ‡๏ธ" SPARKLES="โœจ" FIRE="๐Ÿ”ฅ" CLOCK="โฑ๏ธ" FOLDER="๐Ÿ“" LINK="๐Ÿ”—" STAR="โญ" WARNING="โš ๏ธ" INFO="โ„น๏ธ" ARROW_RIGHT="โ†’" DOUBLE_ARROW="ยป" BOX_LIGHT="โ”€" BOX_HEAVY="โ”" BOX_DOUBLE="โ•" # ============================================================================ # CONFIGURATION # ============================================================================ # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" # Default configuration file path # Try multiple possible locations if [[ -f "${HOME}/Projects/runpod/comfyui_models.yaml" ]]; then CONFIG_FILE="${HOME}/Projects/runpod/comfyui_models.yaml" elif [[ -f "${PROJECT_ROOT}/comfyui_models.yaml" ]]; then CONFIG_FILE="${PROJECT_ROOT}/comfyui_models.yaml" elif [[ -f "${SCRIPT_DIR}/comfyui_models.yaml" ]]; then CONFIG_FILE="${SCRIPT_DIR}/comfyui_models.yaml" else CONFIG_FILE="" # No config file by default fi # Default cache directory (use HuggingFace default) CACHE_DIR="${CACHE_DIR:-${HOME}/.cache/huggingface}" # Default ComfyUI models directory COMFYUI_DIR="${COMFYUI_DIR:-${HOME}/ComfyUI/models}" # Default command COMMAND="both" # HuggingFace token from environment or .env file # Initialize HF_TOKEN if not set HF_TOKEN="${HF_TOKEN:-}" # Try multiple locations for .env file if [[ -z "${HF_TOKEN}" ]] && [[ -f "${PROJECT_ROOT}/ai/.env" ]]; then HF_TOKEN=$(grep ^HF_TOKEN "${PROJECT_ROOT}/ai/.env" | cut -d'=' -f2- | tr -d '"' | tr -d "'" || true) fi if [[ -z "${HF_TOKEN}" ]] && [[ -f "${PROJECT_ROOT}/.env" ]]; then HF_TOKEN=$(grep ^HF_TOKEN "${PROJECT_ROOT}/.env" | cut -d'=' -f2- | tr -d '"' | tr -d "'" || true) fi if [[ -z "${HF_TOKEN}" ]] && [[ -f "/workspace/ai/.env" ]]; then HF_TOKEN=$(grep ^HF_TOKEN "/workspace/ai/.env" | cut -d'=' -f2- | tr -d '"' | tr -d "'" || true) fi # ============================================================================ # UTILITY FUNCTIONS - The Magic Happens Here # ============================================================================ # Print functions with beautiful formatting print_banner() { local text="$1" local width=80 local padding=$(( (width - ${#text} - 2) / 2 )) echo -e "" echo -e "${BOLD_CYAN}${BOX_DOUBLE}$(printf '%.0s'"${BOX_DOUBLE}" $(seq 1 $width))${BOX_DOUBLE}${RESET}" echo -e "${BOLD_CYAN}${BOX_DOUBLE}$(printf '%.0s ' $(seq 1 $padding))${BOLD_MAGENTA}${text}$(printf '%.0s ' $(seq 1 $padding))${BOLD_CYAN}${BOX_DOUBLE}${RESET}" echo -e "${BOLD_CYAN}${BOX_DOUBLE}$(printf '%.0s'"${BOX_DOUBLE}" $(seq 1 $width))${BOX_DOUBLE}${RESET}" echo -e "" } print_section() { local text="$1" echo -e "\n${BOLD_YELLOW}${DOUBLE_ARROW} ${text}${RESET}" echo -e "${CYAN}$(printf '%.0s'"${BOX_LIGHT}" $(seq 1 80))${RESET}" } print_success() { echo -e "${BOLD_GREEN}${CHECK_MARK} $1${RESET}" } print_error() { echo -e "${BOLD_RED}${CROSS_MARK} $1${RESET}" >&2 } print_warning() { echo -e "${BOLD_YELLOW}${WARNING} $1${RESET}" } print_info() { echo -e "${BOLD_CYAN}${INFO} $1${RESET}" } print_step() { local current="$1" local total="$2" local text="$3" echo -e "${BOLD_BLUE}[${current}/${total}]${RESET} ${MAGENTA}${DOWNLOAD}${RESET} ${text}" } print_detail() { echo -e " ${DIM}${CYAN}${ARROW_RIGHT} $1${RESET}" } # Progress bar function show_progress() { local current="$1" local total="$2" local width=50 local percentage=$((current * 100 / total)) local filled=$((current * width / total)) local empty=$((width - filled)) printf "\r ${BOLD_CYAN}Progress: ${RESET}[" printf "${BG_GREEN}${BOLD_WHITE}%${filled}s${RESET}" | tr ' ' 'โ–ˆ' printf "${DIM}%${empty}s${RESET}" | tr ' ' 'โ–‘' printf "] ${BOLD_YELLOW}%3d%%${RESET} ${DIM}(%d/%d)${RESET}" "$percentage" "$current" "$total" } # Parse YAML (simple implementation) parse_yaml() { local yaml_file="$1" local category="$2" python3 < /dev/null; then missing_deps+=("python3") fi # Check pip if ! command -v pip3 &> /dev/null; then missing_deps+=("pip3") fi # Check required Python packages if ! python3 -c "import yaml" 2>/dev/null; then print_warning "PyYAML not installed, installing..." pip3 install pyyaml -q fi if ! python3 -c "import huggingface_hub" 2>/dev/null; then print_warning "huggingface_hub not installed, installing..." pip3 install huggingface_hub -q fi if [[ ${#missing_deps[@]} -gt 0 ]]; then print_error "Missing dependencies: ${missing_deps[*]}" exit 1 fi print_success "All dependencies satisfied" } # Validate configuration validate_config() { print_section "Validating Configuration" # Show current command print_info "Command: ${BOLD_CYAN}${COMMAND}${RESET}" if [[ -n "$CONFIG_FILE" ]]; then if [[ ! -f "$CONFIG_FILE" ]]; then print_error "Configuration file not found: $CONFIG_FILE" exit 1 fi print_success "Configuration file found: ${CYAN}${CONFIG_FILE}${RESET}" else print_warning "No configuration file specified" fi # HF_TOKEN only required for download and both commands if [[ "$COMMAND" == "download" ]] || [[ "$COMMAND" == "both" ]]; then if [[ -z "$HF_TOKEN" ]]; then print_error "HF_TOKEN not set. Please set it in .env file or environment." exit 1 fi print_success "HuggingFace token configured: ${DIM}${HF_TOKEN:0:10}...${RESET}" fi # Cache directory if [[ "$COMMAND" == "download" ]] || [[ "$COMMAND" == "both" ]]; then if [[ ! -d "$CACHE_DIR" ]]; then print_info "Creating cache directory: ${CYAN}${CACHE_DIR}${RESET}" mkdir -p "$CACHE_DIR" fi print_success "Cache directory ready: ${CYAN}${CACHE_DIR}${RESET}" else print_info "Cache directory: ${CYAN}${CACHE_DIR}${RESET}" fi # ComfyUI directory if [[ "$COMMAND" == "link" ]] || [[ "$COMMAND" == "both" ]]; then print_info "ComfyUI directory: ${CYAN}${COMFYUI_DIR}${RESET}" fi } # Find model files in HuggingFace cache find_model_files() { local repo_id="$1" local filename_filter="$2" python3 - <