#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m' # No Color
# Base directories
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
MODELS_DIR="$PROJECT_ROOT/models"
# Model URLs
BASE_URL="https://huggingface.co/Comfy-Org"
QWEN_IMAGE_REPO="Qwen-Image_ComfyUI"
QWEN_EDIT_REPO="Qwen-Image-Edit_ComfyUI"
# Download settings
MAX_RETRIES=3
RETRY_DELAY=5
CHUNK_SIZE="10M" # Download in 10MB chunks
TIMEOUT=30 # Initial connection timeout
SPEED_LIMIT="0" # No speed limit by default (can be set to e.g., "1M" for 1MB/s)
FORCE_DOWNLOAD=false # Force re-download even if file exists
# Model files and sizes
declare -A MODEL_FILES=(
["diffusion_base"]="split_files/diffusion_models/qwen_image_fp8_e4m3fn.safetensors"
["diffusion_edit"]="split_files/diffusion_models/qwen_image_edit_fp8_e4m3fn.safetensors"
["diffusion_distilled"]="non_official/diffusion_models/qwen_image_distill_full_fp8_e4m3fn.safetensors"
["text_encoder"]="split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors"
["vae"]="split_files/vae/qwen_image_vae.safetensors"
)
declare -A MODEL_SIZES=(
["diffusion_base"]="20.4GB"
["diffusion_edit"]="20.4GB"
["diffusion_distilled"]="20.4GB"
["text_encoder"]="9.38GB"
["vae"]="254MB"
)
# We'll fetch actual sizes dynamically - no hardcoding!
declare -A MODEL_BYTES
declare -A MODEL_CHECKSUMS
# Cache for API responses
declare -A API_CACHE
# Function to print colored output
print_color() {
local color=$1
shift
echo -e "${color}$@${NC}"
}
# Function to get file info from Hugging Face API
get_hf_file_info() {
local repo=$1
local file_path=$2
local cache_key="${repo}/${file_path}"
# Check cache first
if [ -n "${API_CACHE[$cache_key]:-}" ]; then
echo "${API_CACHE[$cache_key]}"
return 0
fi
# API URL to get file metadata
local api_url="https://huggingface.co/api/models/Comfy-Org/${repo}/tree/main"
# Fetch repo tree
local response
if command -v curl &> /dev/null; then
response=$(curl -s -f -L "$api_url" 2>/dev/null)
elif command -v wget &> /dev/null; then
response=$(wget -q -O - "$api_url" 2>/dev/null)
else
return 1
fi
# Parse JSON to find our file (using basic parsing since jq might not be available)
local file_info=""
local in_file=false
local size=""
local oid=""
# Extract file info using grep and sed (more portable than jq)
while IFS= read -r line; do
if [[ "$line" == *"\"path\":\"$file_path\""* ]]; then
in_file=true
fi
if [ "$in_file" = true ]; then
if [[ "$line" == *"\"size\":"* ]]; then
size=$(echo "$line" | sed -n 's/.*"size":\s*\([0-9]*\).*/\1/p')
fi
if [[ "$line" == *"\"oid\":"* ]]; then
oid=$(echo "$line" | sed -n 's/.*"oid":\s*"\([^"]*\)".*/\1/p')
fi
if [[ "$line" == *"}"* ]] && [ -n "$size" ]; then
break
fi
fi
done <<< "$response"
if [ -n "$size" ]; then
file_info="${size}:${oid}"
API_CACHE[$cache_key]="$file_info"
echo "$file_info"
return 0
fi
return 1
}
# Function to fetch actual file size from URL headers
get_remote_file_size() {
local url=$1
local size=0
if command -v curl &> /dev/null; then
size=$(curl -sI -L "$url" 2>/dev/null | grep -i "content-length" | tail -1 | awk '{print $2}' | tr -d '\r')
elif command -v wget &> /dev/null; then
size=$(wget --spider --server-response -O - "$url" 2>&1 | grep -i "content-length" | tail -1 | awk '{print $2}' | tr -d '\r')
fi
# Return size or 0 if failed
echo "${size:-0}"
}
# Function to populate model sizes dynamically
populate_model_info() {
local model_key=$1
local repo=$2
local file_path="${MODEL_FILES[$model_key]}"
# Skip if already populated
if [ -n "${MODEL_BYTES[$model_key]:-}" ]; then
return 0
fi
print_color "$DIM" " Fetching metadata for $(basename "$file_path")..."
# Try API first
local file_info
if file_info=$(get_hf_file_info "$repo" "$file_path"); then
local size=$(echo "$file_info" | cut -d: -f1)
local oid=$(echo "$file_info" | cut -d: -f2)
if [ -n "$size" ] && [ "$size" -gt 0 ]; then
MODEL_BYTES[$model_key]=$size
[ -n "$oid" ] && MODEL_CHECKSUMS[$model_key]=$oid
return 0
fi
fi
# Fallback to HEAD request
local url="$BASE_URL/$repo/resolve/main/$file_path"
local size=$(get_remote_file_size "$url")
if [ "$size" -gt 0 ]; then
MODEL_BYTES[$model_key]=$size
return 0
fi
# If all else fails, we'll proceed without size validation
print_color "$YELLOW" " ⚠ Could not fetch size for $(basename "$file_path")"
MODEL_BYTES[$model_key]=0
return 1
}
# Function to format bytes to human readable
format_bytes() {
local bytes=$1
if [ "$bytes" -lt 1024 ]; then
echo "${bytes}B"
elif [ "$bytes" -lt 1048576 ]; then
echo "$(awk "BEGIN {printf \"%.1f\", $bytes/1024}")KB"
elif [ "$bytes" -lt 1073741824 ]; then
echo "$(awk "BEGIN {printf \"%.1f\", $bytes/1048576}")MB"
else
echo "$(awk "BEGIN {printf \"%.2f\", $bytes/1073741824}")GB"
fi
}
# Function to get file size
get_file_size() {
local file=$1
if [ -f "$file" ]; then
stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo 0
else
echo 0
fi
}
# Function to draw progress bar
draw_progress_bar() {
local current=$1
local total=$2
local width=50
if [ "$total" -eq 0 ]; then
local percent=0
else
local percent=$((current * 100 / total))
fi
local filled=$((width * percent / 100))
local empty=$((width - filled))
printf "\r${CYAN}["
printf "%${filled}s" | tr ' ' '█'
printf "%${empty}s" | tr ' ' '░'
printf "] ${BOLD}%3d%%${NC} " "$percent"
# Add size info
printf "($(format_bytes "$current") / $(format_bytes "$total"))"
}
# Function to create directories
create_directories() {
print_color "$BLUE" "Creating model directories..."
mkdir -p "$MODELS_DIR/diffusion_models"
mkdir -p "$MODELS_DIR/text_encoders"
mkdir -p "$MODELS_DIR/vae"
print_color "$GREEN" "✓ Directories created"
}
# Function to check if file exists and has correct size
file_exists_with_content() {
local file=$1
local expected_size=$2
if [ ! -f "$file" ]; then
return 1
fi
local actual_size=$(get_file_size "$file")
# If expected size is provided, check if file is complete
if [ -n "$expected_size" ] && [ "$expected_size" -gt 0 ]; then
# Allow 1% tolerance for size differences
local tolerance=$((expected_size / 100))
local min_size=$((expected_size - tolerance))
local max_size=$((expected_size + tolerance))
if [ "$actual_size" -ge "$min_size" ] && [ "$actual_size" -le "$max_size" ]; then
return 0
else
return 1
fi
fi
# If no expected size, just check if file has content
[ "$actual_size" -gt 0 ]
}
# Function to download with curl and progress
download_with_progress() {
local url=$1
local output=$2
local expected_size=$3
# Create temp file for download
local temp_file="${output}.tmp"
# Check if we can resume
local resume_from=0
if [ -f "$temp_file" ]; then
resume_from=$(get_file_size "$temp_file")
print_color "$CYAN" " Resuming from $(format_bytes $resume_from)"
fi
# Download with curl
curl -L \
--fail \
--retry $MAX_RETRIES \
--retry-delay $RETRY_DELAY \
--connect-timeout $TIMEOUT \
--continue-at - \
--output "$temp_file" \
--progress-bar \
"$url" 2>&1 | while IFS= read -r line; do
# Parse curl's progress output
if [[ "$line" =~ ^[[:space:]]*([0-9]+)[[:space:]]+([0-9.]+[kMG]?)[[:space:]]+ ]]; then
echo -ne "\r${CYAN}Downloading... ${line}${NC}"
fi
done
local exit_code=${PIPESTATUS[0]}
if [ $exit_code -eq 0 ]; then
# Move temp file to final location
mv "$temp_file" "$output"
echo "" # New line after progress
return 0
else
echo "" # New line after progress
return $exit_code
fi
}
# Function to download with wget (fallback)
download_with_wget() {
local url=$1
local output=$2
local expected_size=$3
wget \
--continue \
--timeout=$TIMEOUT \
--tries=$MAX_RETRIES \
--retry-connrefused \
--waitretry=$RETRY_DELAY \
--progress=bar:force:noscroll \
--output-document="$output" \
"$url" 2>&1 | while IFS= read -r line; do
# Parse wget's progress
if [[ "$line" =~ ([0-9]+)% ]]; then
local percent="${BASH_REMATCH[1]}"
draw_progress_bar $((expected_size * percent / 100)) "$expected_size"
fi
done
echo "" # New line after progress
return ${PIPESTATUS[0]}
}
# Function to verify download integrity
verify_download() {
local file=$1
local expected_size=$2
if [ ! -f "$file" ]; then
return 1
fi
local actual_size=$(get_file_size "$file")
# Check if size matches (with 1% tolerance)
if [ -n "$expected_size" ] && [ "$expected_size" -gt 0 ]; then
local tolerance=$((expected_size / 100))
local min_size=$((expected_size - tolerance))
if [ "$actual_size" -lt "$min_size" ]; then
print_color "$RED" " ✗ File size mismatch: expected $(format_bytes $expected_size), got $(format_bytes $actual_size)"
return 1
fi
fi
# Basic safetensors validation (check magic bytes)
if command -v xxd &> /dev/null; then
local magic=$(xxd -l 8 -p "$file" 2>/dev/null | head -n1)
if [[ ! "$magic" =~ ^[0-9a-f]+$ ]]; then
print_color "$RED" " ✗ File appears corrupted (invalid format)"
return 1
fi
fi
return 0
}
# Function to download a model file
download_model() {
local model_key=$1
local repo=$2
local file_path="${MODEL_FILES[$model_key]}"
local file_name=$(basename "$file_path")
local size="${MODEL_SIZES[$model_key]}"
local expected_bytes="${MODEL_BYTES[$model_key]:-0}"
# Determine target directory based on file path
local target_dir=""
case "$file_path" in
*diffusion_models*)
target_dir="$MODELS_DIR/diffusion_models"
;;
*text_encoders*)
target_dir="$MODELS_DIR/text_encoders"
;;
*vae*)
target_dir="$MODELS_DIR/vae"
;;
esac
local target_file="$target_dir/$file_name"
local url="$BASE_URL/$repo/resolve/main/$file_path"
# Check if file already exists and is valid (unless forcing)
if [ "$FORCE_DOWNLOAD" = false ] && file_exists_with_content "$target_file" "$expected_bytes"; then
print_color "$GREEN" "✓ $file_name already downloaded ($(format_bytes ${expected_bytes:-0}))"
return 0
elif [ "$FORCE_DOWNLOAD" = true ] && [ -f "$target_file" ]; then
print_color "$YELLOW" " Force re-downloading $file_name..."
rm -f "$target_file" "${target_file}.tmp"
fi
# Check for partial download
local current_size=0
if [ -f "$target_file" ]; then
current_size=$(get_file_size "$target_file")
if [ "$current_size" -gt 0 ] && [ "$current_size" -lt "$expected_bytes" ]; then
print_color "$YELLOW" "⚠ Partial download detected for $file_name"
print_color "$YELLOW" " Current: $(format_bytes $current_size) / Expected: $(format_bytes $expected_bytes)"
fi
fi
print_color "$BOLD" "\n📦 Downloading: $file_name"
print_color "$DIM" " Size: $size"
print_color "$DIM" " From: $repo"
# Try downloading with retries
local attempt=1
while [ $attempt -le $MAX_RETRIES ]; do
if [ $attempt -gt 1 ]; then
print_color "$YELLOW" " Retry $attempt/$MAX_RETRIES..."
sleep $RETRY_DELAY
fi
# Try curl first, then wget
if command -v curl &> /dev/null; then
if download_with_progress "$url" "$target_file" "$expected_bytes"; then
break
fi
elif command -v wget &> /dev/null; then
if download_with_wget "$url" "$target_file" "$expected_bytes"; then
break
fi
else
print_color "$RED" "✗ Neither curl nor wget is installed!"
return 1
fi
attempt=$((attempt + 1))
done
# Verify the download
if verify_download "$target_file" "$expected_bytes"; then
print_color "$GREEN" "✓ Successfully downloaded $file_name"
return 0
else
print_color "$RED" "✗ Download verification failed for $file_name"
print_color "$YELLOW" " Removing corrupted file..."
rm -f "$target_file" "${target_file}.tmp"
return 1
fi
}
# Function to show download summary
show_download_summary() {
local total_size=0
local downloaded_size=0
local files_to_download=()
local files_complete=()
print_color "$BOLD" "\n📊 Download Summary"
print_color "$DIM" "─────────────────────────────────────"
for model_key in "$@"; do
local file_path="${MODEL_FILES[$model_key]}"
local file_name=$(basename "$file_path")
local expected_bytes="${MODEL_BYTES[$model_key]:-0}"
# Determine target directory
local target_dir=""
case "$file_path" in
*diffusion_models*) target_dir="$MODELS_DIR/diffusion_models" ;;
*text_encoders*) target_dir="$MODELS_DIR/text_encoders" ;;
*vae*) target_dir="$MODELS_DIR/vae" ;;
esac
local target_file="$target_dir/$file_name"
total_size=$((total_size + expected_bytes))
if file_exists_with_content "$target_file" "$expected_bytes"; then
files_complete+=("$file_name")
downloaded_size=$((downloaded_size + expected_bytes))
print_color "$GREEN" " ✓ $file_name ($(format_bytes $expected_bytes))"
else
files_to_download+=("$file_name")
local current_size=$(get_file_size "$target_file")
if [ "$current_size" -gt 0 ]; then
print_color "$YELLOW" " ⚠ $file_name ($(format_bytes $current_size)/$(format_bytes $expected_bytes))"
downloaded_size=$((downloaded_size + current_size))
else
print_color "$CYAN" " ○ $file_name ($(format_bytes $expected_bytes))"
fi
fi
done
print_color "$DIM" "─────────────────────────────────────"
print_color "$BOLD" "Total: $(format_bytes $total_size)"
if [ ${#files_to_download[@]} -gt 0 ]; then
local remaining=$((total_size - downloaded_size))
print_color "$CYAN" "To download: $(format_bytes $remaining)"
else
print_color "$GREEN" "All files already downloaded!"
fi
echo ""
}
# Function to download base Qwen-Image model
download_base() {
print_color "$MAGENTA" "\n╔══════════════════════════════════════╗"
print_color "$MAGENTA" "║ Qwen-Image Base Model Download ║"
print_color "$MAGENTA" "╚══════════════════════════════════════╝"
# Fetch metadata for all files
print_color "$CYAN" "\n📡 Fetching file information..."
populate_model_info "diffusion_base" "$QWEN_IMAGE_REPO"
populate_model_info "text_encoder" "$QWEN_IMAGE_REPO"
populate_model_info "vae" "$QWEN_IMAGE_REPO"
show_download_summary "diffusion_base" "text_encoder" "vae"
local failed=false
download_model "diffusion_base" "$QWEN_IMAGE_REPO" || failed=true
download_model "text_encoder" "$QWEN_IMAGE_REPO" || failed=true
download_model "vae" "$QWEN_IMAGE_REPO" || failed=true
if [ "$failed" = true ]; then
return 1
fi
}
# Function to download Qwen-Image-Edit model
download_edit() {
print_color "$MAGENTA" "\n╔══════════════════════════════════════╗"
print_color "$MAGENTA" "║ Qwen-Image-Edit Model Download ║"
print_color "$MAGENTA" "╚══════════════════════════════════════╝"
# Fetch metadata
print_color "$CYAN" "\n📡 Fetching file information..."
populate_model_info "diffusion_edit" "$QWEN_EDIT_REPO"
populate_model_info "text_encoder" "$QWEN_IMAGE_REPO"
populate_model_info "vae" "$QWEN_IMAGE_REPO"
# Check if base text encoder and VAE exist
local encoder_bytes="${MODEL_BYTES[text_encoder]:-0}"
local vae_bytes="${MODEL_BYTES[vae]:-0}"
local need_encoder=false
local need_vae=false
if [ "$FORCE_DOWNLOAD" = true ] || ! file_exists_with_content "$MODELS_DIR/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors" "$encoder_bytes"; then
need_encoder=true
fi
if [ "$FORCE_DOWNLOAD" = true ] || ! file_exists_with_content "$MODELS_DIR/vae/qwen_image_vae.safetensors" "$vae_bytes"; then
need_vae=true
fi
# Show summary
local models_to_download=("diffusion_edit")
[ "$need_encoder" = true ] && models_to_download+=("text_encoder")
[ "$need_vae" = true ] && models_to_download+=("vae")
show_download_summary "${models_to_download[@]}"
# Download files
local failed=false
download_model "diffusion_edit" "$QWEN_EDIT_REPO" || failed=true
if [ "$need_encoder" = true ]; then
download_model "text_encoder" "$QWEN_IMAGE_REPO" || failed=true
fi
if [ "$need_vae" = true ]; then
download_model "vae" "$QWEN_IMAGE_REPO" || failed=true
fi
if [ "$failed" = true ]; then
return 1
fi
}
# Function to download distilled model
download_distilled() {
print_color "$MAGENTA" "\n╔══════════════════════════════════════╗"
print_color "$MAGENTA" "║ Qwen-Image Distilled Model Download ║"
print_color "$MAGENTA" "╚══════════════════════════════════════╝"
# Fetch metadata
print_color "$CYAN" "\n📡 Fetching file information..."
populate_model_info "diffusion_distilled" "$QWEN_IMAGE_REPO"
populate_model_info "text_encoder" "$QWEN_IMAGE_REPO"
populate_model_info "vae" "$QWEN_IMAGE_REPO"
# Check if base text encoder and VAE exist
local encoder_bytes="${MODEL_BYTES[text_encoder]:-0}"
local vae_bytes="${MODEL_BYTES[vae]:-0}"
local need_encoder=false
local need_vae=false
if [ "$FORCE_DOWNLOAD" = true ] || ! file_exists_with_content "$MODELS_DIR/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors" "$encoder_bytes"; then
need_encoder=true
fi
if [ "$FORCE_DOWNLOAD" = true ] || ! file_exists_with_content "$MODELS_DIR/vae/qwen_image_vae.safetensors" "$vae_bytes"; then
need_vae=true
fi
# Show summary
local models_to_download=("diffusion_distilled")
[ "$need_encoder" = true ] && models_to_download+=("text_encoder")
[ "$need_vae" = true ] && models_to_download+=("vae")
show_download_summary "${models_to_download[@]}"
# Download files
local failed=false
download_model "diffusion_distilled" "$QWEN_IMAGE_REPO" || failed=true
if [ "$need_encoder" = true ]; then
download_model "text_encoder" "$QWEN_IMAGE_REPO" || failed=true
fi
if [ "$need_vae" = true ]; then
download_model "vae" "$QWEN_IMAGE_REPO" || failed=true
fi
if [ "$failed" = true ]; then
return 1
fi
}
# Function to check disk space
check_disk_space() {
local required_gb=$1
local available_kb=$(df "$MODELS_DIR" | awk 'NR==2 {print $4}')
local available_gb=$((available_kb / 1024 / 1024))
if [ "$available_gb" -lt "$required_gb" ]; then
print_color "$RED" "✗ Insufficient disk space. Required: ${required_gb}GB, Available: ${available_gb}GB"
exit 1
fi
print_color "$GREEN" "✓ Sufficient disk space available (${available_gb}GB)"
}
# Function to show usage
show_usage() {
cat << EOF
Usage: $0 [OPTIONS] [COMMAND]
Download Qwen-Image models for ComfyUI
Commands:
base Download Qwen-Image base model (30GB total)
edit Download Qwen-Image-Edit model (20GB + shared files)
distilled Download Qwen-Image distilled model (20GB + shared files)
all Download all Qwen models (~70GB total)
status Show current installation status
help Show this help message
Options:
--force, -f Force re-download even if files exist
Examples:
$0 status # Check what models are installed
$0 base # Download base Qwen-Image model
$0 --force base # Force re-download base model
$0 edit # Download edit model (reuses shared files)
$0 all # Download everything
Features:
✓ Dynamic file size detection from Hugging Face
✓ Resume interrupted downloads automatically
✓ Verify file integrity after download
✓ Rich progress tracking with real-time info
✓ Retry failed downloads (3 attempts)
✓ Support for both curl and wget
✓ No hardcoded file sizes - always current
Model Sizes:
- Diffusion models: 20.4GB each
- Text encoder: 9.38GB (shared between models)
- VAE: 254MB (shared between models)
Total Space Requirements:
- Base only: ~30GB
- Base + Edit: ~50GB
- Base + Distilled: ~50GB
- All models: ~70GB
EOF
}
# Function to show final status
show_final_status() {
print_color "$BOLD" "\n📁 Model Status:"
print_color "$DIM" "─────────────────────────────────────"
local total_size=0
local installed_count=0
# Check diffusion models
for file in "$MODELS_DIR/diffusion_models/"qwen*.safetensors; do
if [ -f "$file" ]; then
local size=$(get_file_size "$file")
total_size=$((total_size + size))
installed_count=$((installed_count + 1))
print_color "$GREEN" " ✓ $(basename "$file") ($(format_bytes $size))"
fi
done
# Check text encoders
for file in "$MODELS_DIR/text_encoders/"qwen*.safetensors; do
if [ -f "$file" ]; then
local size=$(get_file_size "$file")
total_size=$((total_size + size))
installed_count=$((installed_count + 1))
print_color "$GREEN" " ✓ $(basename "$file") ($(format_bytes $size))"
fi
done
# Check VAE
for file in "$MODELS_DIR/vae/"qwen*.safetensors; do
if [ -f "$file" ]; then
local size=$(get_file_size "$file")
total_size=$((total_size + size))
installed_count=$((installed_count + 1))
print_color "$GREEN" " ✓ $(basename "$file") ($(format_bytes $size))"
fi
done
if [ "$installed_count" -gt 0 ]; then
print_color "$DIM" "─────────────────────────────────────"
print_color "$BOLD" "Total: $installed_count files, $(format_bytes $total_size)"
else
print_color "$YELLOW" " No Qwen models installed yet"
fi
}
# Main script
main() {
# Parse command line arguments
local action=""
while [[ $# -gt 0 ]]; do
case $1 in
--force|-f)
FORCE_DOWNLOAD=true
shift
;;
base|edit|distilled|all|status|help|--help|-h)
action="$1"
shift
;;
*)
print_color "$RED" "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Default to help if no action specified
action="${action:-help}"
# Terminal setup
export TERM=${TERM:-xterm-256color}
# Banner
print_color "$BOLD$MAGENTA" "\n╔══════════════════════════════════════╗"
print_color "$BOLD$MAGENTA" "║ Qwen-Image Model Downloader ║"
print_color "$BOLD$MAGENTA" "║ ComfyUI FP8 Edition ║"
print_color "$BOLD$MAGENTA" "╚══════════════════════════════════════╝"
# Check for download tools
local has_curl=false
local has_wget=false
if command -v curl &> /dev/null; then
has_curl=true
print_color "$DIM" " Using: curl $(curl --version | head -n1 | cut -d' ' -f2)"
fi
if command -v wget &> /dev/null; then
has_wget=true
print_color "$DIM" " Using: wget $(wget --version | head -n1 | cut -d' ' -f3)"
fi
if [ "$has_curl" = false ] && [ "$has_wget" = false ]; then
print_color "$RED" "\n✗ Neither curl nor wget is installed!"
print_color "$YELLOW" "Please install one of them:"
print_color "$DIM" " Ubuntu/Debian: sudo apt-get install curl"
print_color "$DIM" " macOS: brew install curl"
print_color "$DIM" " Or: sudo apt-get install wget"
exit 1
fi
local download_success=true
case "$action" in
base)
check_disk_space 35
create_directories
if ! download_base; then
download_success=false
fi
;;
edit)
check_disk_space 25
create_directories
if ! download_edit; then
download_success=false
fi
;;
distilled)
check_disk_space 25
create_directories
if ! download_distilled; then
download_success=false
fi
;;
all)
check_disk_space 75
create_directories
local any_failed=false
download_base || any_failed=true
download_edit || any_failed=true
download_distilled || any_failed=true
if [ "$any_failed" = true ]; then
download_success=false
fi
;;
status)
show_final_status
exit 0
;;
help|--help|-h)
show_usage
exit 0
;;
*)
print_color "$RED" "\n✗ Unknown option: $action"
show_usage
exit 1
;;
esac
# Show final status
if [ "$action" != "help" ] && [ "$action" != "--help" ] && [ "$action" != "-h" ] && [ "$action" != "status" ]; then
if [ "$download_success" = true ]; then
print_color "$GREEN" "\n╔══════════════════════════════════════╗"
print_color "$GREEN" "║ Download Complete! ✓ ║"
print_color "$GREEN" "╚══════════════════════════════════════╝"
show_final_status
print_color "$CYAN" "\n💡 Quick Start:"
print_color "$DIM" " 1. Restart ComfyUI containers:"
print_color "$BLUE" " docker-compose -p mcp-comfyui restart"
print_color "$DIM" " 2. Access ComfyUI at:"
print_color "$BLUE" " http://localhost:8188"
print_color "$DIM" " 3. Load a Qwen workflow template"
else
print_color "$RED" "\n╔══════════════════════════════════════╗"
print_color "$RED" "║ Download Failed! ✗ ║"
print_color "$RED" "╚══════════════════════════════════════╝"
print_color "$YELLOW" "\n⚠ Some downloads failed. You can:"
print_color "$DIM" " 1. Run the command again to resume"
print_color "$DIM" " 2. Check your internet connection"
print_color "$DIM" " 3. Try with --retry flag (if implemented)"
show_final_status
exit 1
fi
fi
}
# Run main function
main "$@"