|
|
#!/usr/bin/env bash |
|
|
|
|
|
|
|
|
|
|
|
set -euo pipefail |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RED='\033[0;31m' |
|
|
GREEN='\033[0;32m' |
|
|
YELLOW='\033[1;33m' |
|
|
BLUE='\033[0;34m' |
|
|
NC='\033[0m' |
|
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } |
|
|
log_success() { echo -e "${GREEN}[✓]${NC} $1"; } |
|
|
log_warn() { echo -e "${YELLOW}[!]${NC} $1"; } |
|
|
log_error() { echo -e "${RED}[✗]${NC} $1"; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
COMFY_DIR="/root/comfy/ComfyUI" |
|
|
MODELS_DIR="$COMFY_DIR/models" |
|
|
COMFY_HOST="${COMFY_HOST:-0.0.0.0}" |
|
|
COMFY_PORT="${COMFY_PORT:-8818}" |
|
|
CIVITAI_TOKEN="${CIVITAI_TOKEN:-4fcb2834969399006a736ee402b061e5}" |
|
|
HF_TOKEN="${HF_TOKEN:-}" |
|
|
|
|
|
|
|
|
export MAX_JOBS=32 |
|
|
export NVCC_APPEND_FLAGS="--threads 8" |
|
|
export UV_SYSTEM_PYTHON=1 |
|
|
export PYTORCH_CUDA_ALLOC_CONF="expandable_segments:True" |
|
|
export HF_HUB_ENABLE_HF_TRANSFER=1 |
|
|
export HF_TRANSFER_CONCURRENCY=16 |
|
|
export EXT_PARALLEL=4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readonly DOWNLOAD_FILES=( |
|
|
|
|
|
"https://civitai.com/api/download/models/1761560?type=Model&format=SafeTensor&size=pruned&fp=fp16|checkpoints|" |
|
|
"https://civitai.com/api/download/models/1410435?type=Model&format=SafeTensor&size=pruned&fp=fp16|checkpoints|" |
|
|
"https://civitai.com/api/download/models/1522463?type=Model&format=SafeTensor&size=full&fp=fp16|checkpoints|" |
|
|
"https://civitai.com/api/download/models/2122278?type=Model&format=SafeTensor&size=pruned&fp=fp16|checkpoints|raehoshiIllustXL_v60.safetensors" |
|
|
|
|
|
|
|
|
"https://civitai.com/api/download/models/1268294?type=Model&format=SafeTensor|loras|" |
|
|
"https://civitai.com/api/download/models/1715330?type=Model&format=SafeTensor|loras|" |
|
|
"https://civitai.com/api/download/models/1499397?type=Model&format=SafeTensor|loras|" |
|
|
"https://civitai.com/api/download/models/1779002?type=Model&format=SafeTensor|loras|" |
|
|
"https://civitai.com/api/download/models/1114313?type=Model&format=SafeTensor|loras|" |
|
|
"https://civitai.com/api/download/models/1780244?type=Model&format=SafeTensor|loras|" |
|
|
|
|
|
|
|
|
"https://huggingface.co/Kim2091/AnimeSharp/resolve/main/4x-AnimeSharp.pth|upscale_models|4x-AnimeSharp.pth" |
|
|
"https://huggingface.co/Kim2091/AnimeSharpV3/resolve/main/2x-AnimeSharpV3.pth|upscale_models|2x-AnimeSharpV3.pth" |
|
|
"https://huggingface.co/ABDALLALSWAITI/Upscalers/resolve/main/anime/2x-AnimeSharpV2_MoSR_Soft.pth|upscale_models|2x-AnimeSharpV2_MoSR_Soft.pth" |
|
|
|
|
|
|
|
|
"https://huggingface.co/adbrasi/testedownload/resolve/main/99coins_anime_girl_face_m_seg.pt|ultralytics/bbox|99coins_anime_girl_face_m_seg.pt" |
|
|
|
|
|
|
|
|
"hf://xinsir/controlnet-union-sdxl-1.0/diffusion_pytorch_model_promax.safetensors|controlnet|controlnet-union.safetensors" |
|
|
) |
|
|
|
|
|
|
|
|
readonly CUSTOM_NODES=( |
|
|
"https://github.com/adbrasi/huggpackreator" |
|
|
"https://github.com/adbrasi/packreator_processor" |
|
|
"https://github.com/adbrasi/Packreator_manager" |
|
|
"https://github.com/adbrasi/cezarsave34" |
|
|
"https://github.com/adbrasi/pageonetor" |
|
|
"https://github.com/adbrasi/pakreatorio" |
|
|
"https://github.com/adbrasi/WaterMark_bumbumzin" |
|
|
"https://github.com/adbrasi/marcadaguita" |
|
|
"https://github.com/adbrasi/randomico" |
|
|
"https://github.com/adbrasi/groqrouter" |
|
|
"https://github.com/adbrasi/find_charakito" |
|
|
"https://github.com/adbrasi/randomsizito" |
|
|
"https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes" |
|
|
"https://github.com/sipherxyz/comfyui-art-venture" |
|
|
"https://github.com/pamparamm/sd-perturbed-attention" |
|
|
"https://github.com/KoreTeknology/ComfyUI-Universal-Styler" |
|
|
"https://github.com/WASasquatch/was-node-suite-comfyui" |
|
|
"https://github.com/chflame163/ComfyUI_LayerStyle" |
|
|
"https://github.com/ltdrdata/ComfyUI-Impact-Pack" |
|
|
"https://github.com/pythongosssss/ComfyUI-Custom-Scripts" |
|
|
"https://github.com/rgthree/rgthree-comfy" |
|
|
"https://github.com/ssitu/ComfyUI_UltimateSDUpscale" |
|
|
"https://github.com/adbrasi/Importador" |
|
|
"https://github.com/adbrasi/GetFirstTag" |
|
|
"https://github.com/adbrasi/comfydodi" |
|
|
"https://github.com/omar92/ComfyUI-QualityOfLifeSuit_Omar92" |
|
|
"https://github.com/Cezarsaint/blacklisto" |
|
|
"https://github.com/TinyTerra/ComfyUI_tinyterraNodes" |
|
|
"https://github.com/ltdrdata/ComfyUI-Impact-Subpack" |
|
|
"https://github.com/Cezarsaint/rand0micoUploaderLoven" |
|
|
"https://github.com/adbrasi/pixivmosaic" |
|
|
"https://github.com/adbrasi/storitadifusita" |
|
|
"https://github.com/adbrasi/attentionPPM" |
|
|
"https://github.com/adbrasi/futfilter" |
|
|
"https://github.com/Artificial-Sweetener/comfyui-WhiteRabbit" |
|
|
"https://github.com/shiimizu/ComfyUI_smZNodes" |
|
|
"https://github.com/CoreyCorza/ComfyUI-CRZnodes" |
|
|
) |
|
|
|
|
|
|
|
|
readonly SPECIAL_NODES=( |
|
|
"Civicomfy" |
|
|
"ComfyUI-RMBG" |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
command_exists() { |
|
|
command -v "$1" >/dev/null 2>&1 |
|
|
} |
|
|
|
|
|
|
|
|
add_civitai_token() { |
|
|
local url="$1" |
|
|
if [[ "$url" == *"civitai.com/api/download"* ]] && [[ "$url" != *"token="* ]]; then |
|
|
echo "${url}&token=${CIVITAI_TOKEN}" |
|
|
else |
|
|
echo "$url" |
|
|
fi |
|
|
} |
|
|
|
|
|
|
|
|
download_hf() { |
|
|
local repo="$1" |
|
|
local file_path="$2" |
|
|
local target_dir="$3" |
|
|
local filename="$4" |
|
|
|
|
|
|
|
|
if [ -z "$filename" ]; then |
|
|
filename=$(basename "$file_path") |
|
|
fi |
|
|
|
|
|
local target_file="$target_dir/$filename" |
|
|
|
|
|
|
|
|
if [ -f "$target_file" ] && [ $(stat -c%s "$target_file" 2>/dev/null || echo 0) -gt 1000000 ]; then |
|
|
log_success "Arquivo já existe: $filename" |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
log_info "Baixando de HF: $filename" |
|
|
log_info " Repo: $repo" |
|
|
log_info " Path: $file_path" |
|
|
|
|
|
|
|
|
local temp_dir=$(mktemp -d) |
|
|
|
|
|
|
|
|
local download_success=false |
|
|
|
|
|
if command_exists hf; then |
|
|
if HF_HUB_ENABLE_HF_TRANSFER=1 hf download "$repo" "$file_path" \ |
|
|
--local-dir "$temp_dir" \ |
|
|
--local-dir-use-symlinks False 2>/dev/null; then |
|
|
download_success=true |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if [ "$download_success" = false ] && command_exists huggingface-cli; then |
|
|
log_warn "Tentando com huggingface-cli..." |
|
|
if HF_HUB_ENABLE_HF_TRANSFER=1 huggingface-cli download "$repo" "$file_path" \ |
|
|
--local-dir "$temp_dir" \ |
|
|
--local-dir-use-symlinks False 2>/dev/null; then |
|
|
download_success=true |
|
|
fi |
|
|
fi |
|
|
|
|
|
if [ "$download_success" = true ]; then |
|
|
|
|
|
local downloaded_file=$(find "$temp_dir" -type f \( -name "*.safetensors" -o -name "*.pth" -o -name "*.pt" -o -name "*.bin" \) 2>/dev/null | head -1) |
|
|
|
|
|
if [ -n "$downloaded_file" ]; then |
|
|
|
|
|
mv "$downloaded_file" "$target_file" |
|
|
rm -rf "$temp_dir" |
|
|
log_success "Download concluído: $filename" |
|
|
return 0 |
|
|
else |
|
|
log_error "Arquivo baixado mas não encontrado em $temp_dir" |
|
|
fi |
|
|
fi |
|
|
|
|
|
rm -rf "$temp_dir" |
|
|
log_error "Falha ao baixar: $filename" |
|
|
return 1 |
|
|
} |
|
|
|
|
|
|
|
|
download_file() { |
|
|
local url="$1" |
|
|
local target_dir="$2" |
|
|
local filename="$3" |
|
|
|
|
|
|
|
|
url=$(add_civitai_token "$url") |
|
|
|
|
|
|
|
|
if [ -n "$filename" ] && [ -f "$target_dir/$filename" ] && [ $(stat -c%s "$target_dir/$filename" 2>/dev/null || echo 0) -gt 1000000 ]; then |
|
|
log_success "Arquivo já existe: $filename" |
|
|
return 0 |
|
|
fi |
|
|
|
|
|
log_info "Baixando: ${filename:-$(basename "$url" | cut -d'?' -f1)}" |
|
|
|
|
|
|
|
|
if command_exists aria2c; then |
|
|
local aria_opts="-c -s 16 -x 16 -k 1M --console-log-level=warn --summary-interval=10" |
|
|
if [ -n "$filename" ]; then |
|
|
aria2c $aria_opts --dir="$target_dir" --out="$filename" "$url" && return 0 |
|
|
else |
|
|
aria2c $aria_opts --dir="$target_dir" "$url" && return 0 |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if command_exists wget; then |
|
|
if [ -n "$filename" ]; then |
|
|
wget -q --show-progress -c -O "$target_dir/$filename" "$url" && return 0 |
|
|
else |
|
|
wget -q --show-progress -c --content-disposition -P "$target_dir" "$url" && return 0 |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if command_exists curl; then |
|
|
if [ -n "$filename" ]; then |
|
|
curl -L - |
|
|
else |
|
|
local headers=$(curl -sI -L "$url") |
|
|
local detected_name=$(echo "$headers" | grep -i "content-disposition" | sed -n 's/.*filename="\([^"]*\)".*/\1/p' | tr -d '\r') |
|
|
if [ -z "$detected_name" ]; then |
|
|
detected_name="downloaded_$(date +%s).safetensors" |
|
|
fi |
|
|
curl -L - |
|
|
fi |
|
|
fi |
|
|
|
|
|
log_error "Falha ao baixar: $url" |
|
|
return 1 |
|
|
} |
|
|
|
|
|
|
|
|
process_downloads() { |
|
|
local failed_count=0 |
|
|
|
|
|
for entry in "${DOWNLOAD_FILES[@]}"; do |
|
|
IFS='|' read -r url type filename <<< "$entry" |
|
|
|
|
|
|
|
|
url=$(echo "$url" | xargs) |
|
|
type=$(echo "$type" | xargs) |
|
|
filename=$(echo "$filename" | xargs) |
|
|
|
|
|
|
|
|
local target_dir="$MODELS_DIR" |
|
|
case "$type" in |
|
|
checkpoints) target_dir="$MODELS_DIR/checkpoints" ;; |
|
|
diffusion_models) target_dir="$MODELS_DIR/diffusion_models" ;; |
|
|
loras) target_dir="$MODELS_DIR/loras" ;; |
|
|
vae) target_dir="$MODELS_DIR/vae" ;; |
|
|
vae_approx) target_dir="$MODELS_DIR/vae_approx" ;; |
|
|
text_encoders) target_dir="$MODELS_DIR/text_encoders" ;; |
|
|
clip_vision) target_dir="$MODELS_DIR/clip_vision" ;; |
|
|
controlnet) target_dir="$MODELS_DIR/controlnet" ;; |
|
|
upscale_models) target_dir="$MODELS_DIR/upscale_models" ;; |
|
|
ultralytics/bbox) target_dir="$MODELS_DIR/ultralytics/bbox" ;; |
|
|
*) target_dir="$MODELS_DIR/$type" ;; |
|
|
esac |
|
|
|
|
|
|
|
|
mkdir -p "$target_dir" |
|
|
|
|
|
|
|
|
if [[ "$url" == hf://* ]]; then |
|
|
|
|
|
url="${url#hf://}" |
|
|
|
|
|
|
|
|
local repo=$(echo "$url" | cut -d'/' -f1-2) |
|
|
local file_path=$(echo "$url" | cut -d'/' -f3-) |
|
|
|
|
|
download_hf "$repo" "$file_path" "$target_dir" "$filename" || ((failed_count++)) |
|
|
else |
|
|
|
|
|
download_file "$url" "$target_dir" "$filename" || ((failed_count++)) |
|
|
fi |
|
|
done |
|
|
|
|
|
return $failed_count |
|
|
} |
|
|
|
|
|
|
|
|
clone_or_update() { |
|
|
local url="$1" |
|
|
local dest="$2" |
|
|
local node_name=$(basename "$dest") |
|
|
|
|
|
|
|
|
if [ -d "$dest/.git" ]; then |
|
|
log_info "Atualizando $node_name..." |
|
|
timeout 60 git -C "$dest" pull --ff-only 2>/dev/null || { |
|
|
log_warn "Timeout ou erro ao atualizar $node_name" |
|
|
return 0 |
|
|
} |
|
|
else |
|
|
log_info "Clonando $node_name..." |
|
|
timeout 60 git clone --recursive --depth 1 "$url" "$dest" 2>/dev/null || { |
|
|
log_warn "Falha ao clonar $node_name" |
|
|
return 0 |
|
|
} |
|
|
fi |
|
|
|
|
|
|
|
|
if [ -f "$dest/requirements.txt" ]; then |
|
|
timeout 120 python3 -m pip install --no-warn-script-location -q -r "$dest/requirements.txt" 2>/dev/null || { |
|
|
log_warn "Falha ao instalar requirements para $node_name" |
|
|
} |
|
|
fi |
|
|
|
|
|
return 0 |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
echo "" |
|
|
log_info "=========================================" |
|
|
log_info " ComfyUI Custom Setup" |
|
|
log_info "=========================================" |
|
|
echo "" |
|
|
|
|
|
|
|
|
log_info "[1/7] Verificando e instalando dependências..." |
|
|
|
|
|
|
|
|
if ! command_exists aria2c; then |
|
|
log_info "Instalando aria2c do sistema..." |
|
|
apt-get update -qq && apt-get install -y -qq aria2 2>/dev/null |
|
|
fi |
|
|
|
|
|
for cmd in python3 git wget curl; do |
|
|
if ! command_exists "$cmd"; then |
|
|
log_error "Dependência faltando: $cmd" |
|
|
exit 1 |
|
|
fi |
|
|
done |
|
|
log_success "Dependências OK" |
|
|
|
|
|
|
|
|
log_info "[2/7] Preparando ferramentas Python..." |
|
|
python3 -m pip install --upgrade pip wheel setuptools -q |
|
|
|
|
|
|
|
|
python3 -m pip install --upgrade "huggingface_hub[cli,hf_transfer]>=0.26.0" comfy-cli -q |
|
|
|
|
|
|
|
|
HF_TOKEN="${HF_TOKEN:-}" |
|
|
if [ -n "$HF_TOKEN" ]; then |
|
|
if huggingface-cli login --token "$HF_TOKEN" 2>/dev/null; then |
|
|
log_success "Login HF configurado" |
|
|
else |
|
|
log_warn "Falha ao configurar login HF, continuando sem autenticação" |
|
|
fi |
|
|
fi |
|
|
|
|
|
|
|
|
if command_exists hf; then |
|
|
log_success "Comando 'hf' disponível (recomendado)" |
|
|
elif command_exists huggingface-cli; then |
|
|
log_warn "Usando 'huggingface-cli' (deprecated, mas funcional)" |
|
|
else |
|
|
log_error "Nenhum comando HF disponível!" |
|
|
fi |
|
|
|
|
|
log_success "Ferramentas instaladas" |
|
|
|
|
|
|
|
|
log_info "[3/7] Instalando PyTorch com CUDA 12.8..." |
|
|
python3 -m pip install --upgrade torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128 |
|
|
log_success "PyTorch com CUDA 12.8 instalado" |
|
|
|
|
|
|
|
|
log_info "[4/7] Instalando ComfyUI..." |
|
|
if [ -f "$COMFY_DIR/main.py" ]; then |
|
|
log_warn "ComfyUI já existe, pulando instalação base" |
|
|
else |
|
|
comfy --skip-prompt install --fast-deps --nvidia |
|
|
|
|
|
|
|
|
if [ ! -f "$COMFY_DIR/main.py" ]; then |
|
|
log_error "ComfyUI não foi instalado corretamente!" |
|
|
exit 1 |
|
|
fi |
|
|
fi |
|
|
log_success "ComfyUI instalado em: $COMFY_DIR" |
|
|
|
|
|
|
|
|
log_info "[5/7] Baixando modelos..." |
|
|
mkdir -p "$MODELS_DIR"/{diffusion_models,loras,vae,vae_approx,text_encoders,clip_vision,controlnet,upscale_models,checkpoints,ultralytics/bbox} |
|
|
|
|
|
|
|
|
if command_exists aria2c; then |
|
|
log_success "Usando aria2c para downloads (rápido)" |
|
|
else |
|
|
log_warn "aria2c não encontrado, usando wget/curl (mais lento)" |
|
|
fi |
|
|
|
|
|
|
|
|
if process_downloads; then |
|
|
log_success "Todos os modelos baixados com sucesso" |
|
|
else |
|
|
log_warn "Alguns downloads falharam, mas continuando..." |
|
|
fi |
|
|
|
|
|
|
|
|
log_info "[6/7] Instalando custom nodes..." |
|
|
CN_DIR="$COMFY_DIR/custom_nodes" |
|
|
mkdir -p "$CN_DIR" |
|
|
|
|
|
|
|
|
for repo_url in "${CUSTOM_NODES[@]}"; do |
|
|
node_name=$(basename "$repo_url") |
|
|
clone_or_update "$repo_url" "$CN_DIR/$node_name" |
|
|
done |
|
|
|
|
|
|
|
|
for node_name in "${SPECIAL_NODES[@]}"; do |
|
|
log_info "Instalando $node_name via comfy-cli..." |
|
|
comfy node install "$node_name" 2>/dev/null || log_warn "Falha ao instalar $node_name" |
|
|
done |
|
|
|
|
|
log_success "Custom nodes instalados" |
|
|
|
|
|
|
|
|
echo "" |
|
|
log_info "[7/7] Verificando instalação..." |
|
|
|
|
|
|
|
|
check_critical_files() { |
|
|
local files=( |
|
|
"$COMFY_DIR/main.py" |
|
|
) |
|
|
|
|
|
local missing=0 |
|
|
for file in "${files[@]}"; do |
|
|
if [ -f "$file" ]; then |
|
|
log_success "✓ $(basename "$file")" |
|
|
else |
|
|
log_error "✗ $(basename "$file") não encontrado" |
|
|
((missing++)) |
|
|
fi |
|
|
done |
|
|
|
|
|
|
|
|
echo "" |
|
|
log_info "Modelos disponíveis:" |
|
|
for dir in checkpoints loras vae vae_approx text_encoders clip_vision controlnet upscale_models ultralytics; do |
|
|
if [ -d "$MODELS_DIR/$dir" ]; then |
|
|
local count=$(find "$MODELS_DIR/$dir" -type f \( -name "*.safetensors" -o -name "*.pth" -o -name "*.pt" -o -name "*.ckpt" \) 2>/dev/null | wc -l) |
|
|
if [ $count -gt 0 ]; then |
|
|
log_success " $dir: $count arquivo(s)" |
|
|
fi |
|
|
fi |
|
|
done |
|
|
|
|
|
|
|
|
echo "" |
|
|
log_info "Custom nodes instalados:" |
|
|
local node_count=$(find "$CN_DIR" -maxdepth 1 -type d | wc -l) |
|
|
log_success " Total: $((node_count - 1)) nodes" |
|
|
|
|
|
return $missing |
|
|
} |
|
|
|
|
|
if check_critical_files; then |
|
|
log_success "Instalação concluída com sucesso!" |
|
|
echo "" |
|
|
log_info "=========================================" |
|
|
log_info "Iniciando ComfyUI..." |
|
|
log_info "URL: http://localhost:$COMFY_PORT" |
|
|
log_info "=========================================" |
|
|
|
|
|
cd "$COMFY_DIR" |
|
|
exec comfy launch -- --listen "$COMFY_HOST" --port "$COMFY_PORT" |
|
|
else |
|
|
log_error "Alguns arquivos críticos não foram encontrados!" |
|
|
log_info "Tentando iniciar ComfyUI mesmo assim..." |
|
|
|
|
|
cd "$COMFY_DIR" |
|
|
exec comfy launch -- --listen "$COMFY_HOST" --port "$COMFY_PORT" |
|
|
fi |