JCscrew commited on
Commit
50bb958
·
1 Parent(s): a73dfea

Update script.sh

Browse files
Files changed (1) hide show
  1. script.sh +318 -159
script.sh CHANGED
@@ -1,229 +1,388 @@
1
  #!/bin/bash
2
  set -uo pipefail
3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  log_info() { echo "--> $1"; }
5
  log_warn() { echo " ⚠️ $1"; }
6
  log_error() { echo " ❌ $1"; }
7
  log_success() { echo " ✅ $1"; }
8
  log_step() { echo ""; echo "=== [Step $1] $2 ==="; }
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  install_node() {
11
  local repo_url="$1"
12
  local branch_name="${2:-}"
13
  local repo_name
14
  repo_name=$(basename "$repo_url" .git)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- if [ -d "$repo_name" ]; then
17
- log_info "'$repo_name' 已存在,跳過。"
18
- return
 
19
  fi
20
 
21
- log_info "正在處理: $repo_name"
22
- if [ -n "$branch_name" ]; then
23
- git clone --depth 1 --branch "$branch_name" "$repo_url" > /dev/null 2>&1 || log_error "Clone 倉庫 '$repo_name' (分支: $branch_name) 失敗"
24
- else
25
- git clone --depth 1 "$repo_url" > /dev/null 2>&1 || log_error "Clone 倉庫 '$repo_name' 失敗。"
26
  fi
27
  }
28
 
29
  download_file() {
30
  local dest_path="$1"
31
  local url="$2"
32
-
33
  local filename
34
  filename=$(basename "$dest_path")
 
35
  mkdir -p "$(dirname "$dest_path")"
36
 
37
  if [ -f "$dest_path" ]; then
38
- log_info "檔案 '$filename' 已存在,跳過下載"
39
  return 0
40
  fi
41
 
42
- log_info "--> 開始下載 '$filename'..."
43
 
44
- local max_retries=5
45
  local attempt=0
46
- local success=false
 
 
 
 
 
 
 
 
47
 
48
- while [ "$success" = false ] && [ "$attempt" -lt "$max_retries" ]; do
49
  attempt=$((attempt + 1))
50
-
51
- if [ "$attempt" -gt 1 ]; then
52
- log_warn "第 $attempt 次嘗試下載 '$filename'..."
53
- sleep 15
54
- fi
55
 
56
  if command -v aria2c >/dev/null 2>&1; then
57
- log_info "使用 aria2c (3 個連線) 進行下載..."
58
- aria2c --console-log-level=error \
59
- -c -x 16 -s 16 -k 1M \
60
- --max-connection-per-server=16 \
61
- --min-split-size=1M \
62
- --max-tries=5 \
63
- --retry-wait=3 \
64
- --timeout=60 \
65
- --check-certificate=false \
66
- --allow-overwrite=true \
67
- --file-allocation=none \
68
- -d "$(dirname "$dest_path")" \
69
- -o "$filename" \
70
- "$url"
 
 
 
71
  else
72
- log_info "使用 wget 進行下載..."
73
- wget -c -O "$dest_path" --quiet --show-progress "$url"
74
- fi
75
-
76
- if [ $? -eq 0 ] && [ -f "$dest_path" ]; then
77
- success=true
78
- log_success "成功下載 '$filename'。"
79
- elif [ "$attempt" -ge "$max_retries" ]; then
80
- log_error "下載 '$filename' 失敗,已達最大重試次數。"
 
 
81
  fi
82
  done
83
 
84
- if [ "$success" = false ]; then
 
 
 
 
85
  return 1
86
  fi
87
- return 0
88
  }
89
 
90
- download_models_pack() {
91
- log_info "[並行任務] 開始下載模型包..."
 
 
92
 
93
- local MODELS_ZIP_URL="https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/backup_models/models_pack.zip"
94
- local ZIP_PATH="/workspace/ComfyUI/models_pack.zip"
95
- local MAX_ATTEMPTS=2
96
- local ATTEMPT=1
97
- local SUCCESS=false
98
-
99
- while [ $ATTEMPT -le $MAX_ATTEMPTS ] && [ "$SUCCESS" = false ]; do
100
- log_info "[模型包] 嘗試 ${ATTEMPT}/${MAX_ATTEMPTS}..."
 
 
 
 
 
 
 
 
 
 
101
 
102
- download_file "$ZIP_PATH" "$MODELS_ZIP_URL"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- if [ $? -eq 0 ]; then
105
- log_info "[模型包] 正在解壓縮..."
106
- unzip -o -q "$ZIP_PATH" -d /workspace/ComfyUI/
107
-
108
- if [ $? -eq 0 ]; then
109
- log_success "[模型包] 成功解壓縮。"
110
- SUCCESS=true
111
- rm -f "$ZIP_PATH"
112
- log_info "[模型包] 已刪除壓縮檔。"
113
- else
114
- log_error "[模型包] 解壓縮失敗!"
115
- rm -f "$ZIP_PATH"
116
- ATTEMPT=$((ATTEMPT + 1))
117
- [ $ATTEMPT -le $MAX_ATTEMPTS ] && sleep 5
118
- fi
119
- else
120
- ATTEMPT=$((ATTEMPT + 1))
121
  fi
122
  done
123
-
124
- if [ "$SUCCESS" = false ]; then
125
- log_error "[模型包] 下載失敗,已達最大重試次數。"
126
- echo "MODELS_DOWNLOAD_FAILED" > /tmp/download_status
127
- return 1
128
- fi
129
 
130
- echo "MODELS_DOWNLOAD_SUCCESS" > /tmp/download_status
131
- return 0
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  }
133
 
134
- log_info "Provisioning Started at $(date)"
135
-
136
- log_step "1/4" "運行 Vast.ai 基礎腳本"
137
- wget -q -O /tmp/default.sh https://raw.githubusercontent.com/vast-ai/base-image/refs/heads/main/derivatives/pytorch/derivatives/comfyui/provisioning_scripts/default.sh
138
- bash /tmp/default.sh || log_warn "基礎腳本遇到非致命錯誤,繼續執行..."
139
-
140
- log_step "2/4" "準備 ComfyUI 目錄與環境"
141
- cd /workspace/ComfyUI || { log_error "找不到 ComfyUI 目錄,中止執行。"; exit 1; }
 
 
 
142
 
143
- log_info "檢測 Python 環境..."
144
- if [ -f "/venv/main/bin/python" ]; then
145
- PYTHON_BIN="/venv/main/bin/python"
146
- PIP_BIN="/venv/main/bin/pip"
147
- log_success "使用 venv Python: $PYTHON_BIN"
148
- else
149
- PYTHON_BIN="python3"
150
- PIP_BIN="pip3"
151
- log_warn "使用系統 Python: $PYTHON_BIN"
152
- fi
 
153
 
154
- log_step "3/4" "並行處理:節點安裝 + 模型下載"
 
 
155
 
156
- # ============ 啟動並行下載任務 ============
157
- log_info "🚀 啟動模型包下載(後台任務)..."
158
- download_models_pack &
159
- DOWNLOAD_PID=$!
160
 
161
- # ============ 同時進行節點安裝 ============
162
- log_info "🚀 開始安裝自定義節點(前台任務)..."
163
- cd custom_nodes
 
 
 
 
 
 
164
 
165
- install_node "https://github.com/ltdrdata/ComfyUI-Impact-Pack.git"
166
- install_node "https://github.com/ltdrdata/ComfyUI-Impact-Subpack.git"
167
- install_node "https://github.com/cubiq/ComfyUI_IPAdapter_plus.git"
168
- install_node "https://github.com/ka-puna/comfyui-yanc.git"
169
 
170
- log_info "正在強制更新 ComfyUI-Impact-Pack..."
171
- if [ -d "ComfyUI-Impact-Pack" ]; then
172
- (cd ComfyUI-Impact-Pack && git pull)
 
 
 
 
 
 
 
 
 
 
 
 
173
  fi
174
 
175
- log_info "正在從 Impact Pack 的需求文件中移除 torch 和 sam2..."
176
- IMPACT_PACK_REQS="ComfyUI-Impact-Pack/requirements.txt"
177
- if [ -f "$IMPACT_PACK_REQS" ]; then
178
- sed -i -e '/torch/d' -e '/sam2/d' "$IMPACT_PACK_REQS"
179
  fi
180
 
181
- log_info " ComfyUI-Impact-Pack 執行安裝程序..."
182
- if [ -f "ComfyUI-Impact-Pack/install.py" ]; then
183
- $PYTHON_BIN "ComfyUI-Impact-Pack/install.py" || log_warn "Impact Pack 的 install.py 腳本執行失敗。"
184
- fi
185
- if [ -f "$IMPACT_PACK_REQS" ]; then
186
- $PIP_BIN install --no-cache-dir -r "$IMPACT_PACK_REQS" || log_warn "Impact Pack 的依賴安裝失敗。"
187
- mv "$IMPACT_PACK_REQS" "${IMPACT_PACK_REQS}.processed"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  fi
189
 
190
- log_info "安裝其餘所有節點的 Python 依賴..."
191
- for req_file in */requirements.txt; do
192
- if [ -f "$req_file" ]; then
193
- log_info "正在安裝依賴: $req_file"
194
- $PIP_BIN install --no-cache-dir -r "$req_file" || log_warn "依賴安裝失敗: $req_file"
195
- fi
196
- done
197
-
198
- cd /workspace/ComfyUI
199
-
200
- log_info "⏳ 等待模型包下載完成..."
201
- wait $DOWNLOAD_PID
202
- DOWNLOAD_EXIT_CODE=$?
203
 
204
- if [ -f /tmp/download_status ]; then
205
- DOWNLOAD_STATUS=$(cat /tmp/download_status)
206
- if [ "$DOWNLOAD_STATUS" = "MODELS_DOWNLOAD_FAILED" ]; then
207
- log_error "模型包下載失敗,請檢查網路或稍後重試。"
208
- exit 1
209
- fi
210
- fi
211
-
212
- log_success "所有並行任務已完成!"
213
 
214
- download_file "user/default/workflows/Single_Posture.json" "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/Single_Posture.json?download=true"
215
 
216
- log_step "4/4" "清理與最終配置"
 
217
 
218
- if [ -f "/workspace/ComfyUI/input/example.jpg" ]; then
219
- log_info "正在刪除 example.jpg..."
220
- rm -f /workspace/ComfyUI/input/example.jpg
221
- log_success "已刪除 example.jpg。"
222
- else
223
- log_info "example.jpg 不存在,無需刪除。"
224
- fi
225
 
226
- log_success "環境配置完成!"
227
- log_info "ComfyUI 即將啟動。實例已準備就緒。"
228
- echo ""
229
- echo "📊 總耗時: $(($(date +%s) - $(date -d "$(head -1 /tmp/provision_start_time 2>/dev/null || echo '0 seconds ago')" +%s 2>/dev/null || echo 0))) 秒"
 
1
  #!/bin/bash
2
  set -uo pipefail
3
 
4
+ export PIP_NO_CACHE_DIR=1
5
+ export PIP_DISABLE_PIP_VERSION_CHECK=1
6
+ export GIT_TERMINAL_PROMPT=0
7
+
8
+ source /venv/main/bin/activate
9
+ COMFYUI_DIR="${WORKSPACE:-/workspace}/ComfyUI"
10
+
11
+ APT_PACKAGES=(
12
+ )
13
+
14
+ PIP_PACKAGES=(
15
+ )
16
+
17
+ NODES=(
18
+ "https://github.com/ltdrdata/ComfyUI-Impact-Pack.git"
19
+ "https://github.com/ltdrdata/ComfyUI-Impact-Subpack.git"
20
+ "https://github.com/cubiq/ComfyUI_IPAdapter_plus.git"
21
+ "https://github.com/ka-puna/comfyui-yanc.git"
22
+ )
23
+
24
+ CHECKPOINT_MODELS=(
25
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/checkpoints/waiNSFWIllustrious_v150.safetensors"
26
+ )
27
+
28
+ CLIP_VISION_MODELS=(
29
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/clip_vision/CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors"
30
+ )
31
+
32
+ IPADAPTER_MODELS=(
33
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/ipadapter/ip-adapter-plus-face_sdxl_vit-h.safetensors"
34
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/ipadapter/ip-adapter-plus_sdxl_vit-h.safetensors"
35
+ )
36
+
37
+ SAM_MODELS=(
38
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/sams/sam_vit_h_4b8939.pth"
39
+ )
40
+
41
+ ULTRALYTICS_BBOX_MODELS=(
42
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/ultralytics/bbox/face_yolov9c.pt"
43
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/ultralytics/bbox/hand_yolov9c.pt"
44
+ )
45
+
46
+ LORA_MODELS=(
47
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/loras/Better_detailed_pussy_and_anus_v3.0.safetensors"
48
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/loras/Breast_size_slider_NNFFS_alpha16.0_rank32_full_last.safetensors"
49
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/loras/Penis_Size_Slider_-_Illustrious_-_Girthy_alpha1.0_rank4_noxattn_250steps.safetensors"
50
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/loras/eye_size_addift.safetensors"
51
+ )
52
+
53
+ ESRGAN_MODELS=(
54
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/models/upscale_models/RealESRGAN_x4plus_anime_6B.pth"
55
+ )
56
+
57
+ WORKFLOWS=(
58
+ "https://huggingface.co/JCscrew/RisuAI_asset_generator/resolve/main/Single_Posture.json"
59
+ )
60
+
61
  log_info() { echo "--> $1"; }
62
  log_warn() { echo " ⚠️ $1"; }
63
  log_error() { echo " ❌ $1"; }
64
  log_success() { echo " ✅ $1"; }
65
  log_step() { echo ""; echo "=== [Step $1] $2 ==="; }
66
 
67
+ if [ -f "/workspace/.provision_complete" ]; then
68
+ log_success "環境已配置,跳過重複執行"
69
+ log_info "如需重新配置,請刪除 /workspace/.provision_complete"
70
+ exit 0
71
+ fi
72
+
73
+ if [[ -f /.noprovisioning ]]; then
74
+ log_warn "檢測到 /.noprovisioning 文件,跳過配置"
75
+ exit 0
76
+ fi
77
+
78
+ if [ -f "/venv/main/bin/python" ]; then
79
+ PYTHON_BIN="/venv/main/bin/python"
80
+ PIP_BIN="/venv/main/bin/pip"
81
+ else
82
+ PYTHON_BIN="python3"
83
+ PIP_BIN="pip3"
84
+ fi
85
+
86
+ provisioning_has_valid_hf_token() {
87
+ [[ -n "${HF_TOKEN:-}" ]] || return 1
88
+ local response
89
+ response=$(curl -o /dev/null -s -w "%{http_code}" -X GET \
90
+ "https://huggingface.co/api/whoami-v2" \
91
+ -H "Authorization: Bearer $HF_TOKEN" \
92
+ -H "Content-Type: application/json")
93
+ [ "$response" -eq 200 ]
94
+ }
95
+
96
+ provisioning_has_valid_civitai_token() {
97
+ [[ -n "${CIVITAI_TOKEN:-}" ]] || return 1
98
+ local response
99
+ response=$(curl -o /dev/null -s -w "%{http_code}" -X GET \
100
+ "https://civitai.com/api/v1/models?hidden=1&limit=1" \
101
+ -H "Authorization: Bearer $CIVITAI_TOKEN" \
102
+ -H "Content-Type: application/json")
103
+ [ "$response" -eq 200 ]
104
+ }
105
+
106
  install_node() {
107
  local repo_url="$1"
108
  local branch_name="${2:-}"
109
  local repo_name
110
  repo_name=$(basename "$repo_url" .git)
111
+ local install_path="${COMFYUI_DIR}/custom_nodes/${repo_name}"
112
+
113
+ if [ -d "$install_path" ]; then
114
+ if [[ "${AUTO_UPDATE:-true}" != "false" ]]; then
115
+ log_info "更新節點: $repo_name"
116
+ (cd "$install_path" && git pull -q 2>&1 | grep -v "Already up to date" || true)
117
+ else
118
+ log_info "'$repo_name' 已存在,跳過"
119
+ return
120
+ fi
121
+ else
122
+ log_info "克隆節點: $repo_name"
123
+ if [ -n "$branch_name" ]; then
124
+ git clone --depth 1 --single-branch --branch "$branch_name" "$repo_url" "$install_path" -q 2>&1 || true
125
+ else
126
+ git clone --depth 1 --single-branch "$repo_url" "$install_path" -q 2>&1 || true
127
+ fi
128
+ fi
129
 
130
+ if [ -f "$install_path/requirements.txt" ]; then
131
+ log_info "安裝 $repo_name 的依賴..."
132
+ sed -i -e '/^torch/d' -e '/^sam2/d' "$install_path/requirements.txt" 2>/dev/null || true
133
+ $PIP_BIN install -q --no-cache-dir -r "$install_path/requirements.txt" 2>&1 | grep -v "Requirement already satisfied" || log_warn "部分��賴安裝失敗"
134
  fi
135
 
136
+ if [ -f "$install_path/install.py" ]; then
137
+ log_info "運行 $repo_name 的安裝腳本..."
138
+ $PYTHON_BIN "$install_path/install.py" 2>&1 || log_warn "安裝腳本執行失敗"
 
 
139
  fi
140
  }
141
 
142
  download_file() {
143
  local dest_path="$1"
144
  local url="$2"
 
145
  local filename
146
  filename=$(basename "$dest_path")
147
+
148
  mkdir -p "$(dirname "$dest_path")"
149
 
150
  if [ -f "$dest_path" ]; then
151
+ log_info "檔案 '$filename' 已存在,跳過下載"
152
  return 0
153
  fi
154
 
155
+ log_info "下載: $filename"
156
 
157
+ local max_retries=3
158
  local attempt=0
159
+ local auth_header=""
160
+
161
+ if [[ "$url" =~ huggingface\.co ]] && provisioning_has_valid_hf_token; then
162
+ auth_header="Authorization: Bearer $HF_TOKEN"
163
+ log_info "使用 HuggingFace Token"
164
+ elif [[ "$url" =~ civitai\.com ]] && provisioning_has_valid_civitai_token; then
165
+ auth_header="Authorization: Bearer $CIVITAI_TOKEN"
166
+ log_info "使用 CivitAI Token"
167
+ fi
168
 
169
+ while [ "$attempt" -lt "$max_retries" ]; do
170
  attempt=$((attempt + 1))
171
+ [ "$attempt" -gt 1 ] && sleep 10
 
 
 
 
172
 
173
  if command -v aria2c >/dev/null 2>&1; then
174
+ log_info "使用 aria2c (3 線) 下載: $filename (嘗試 $attempt/$max_retries)"
175
+ local aria_opts=(
176
+ --console-log-level=error
177
+ -c -x 3 -s 3 -k 1M # <-- **已修改為 3 個連接**
178
+ --max-connection-per-server=3 # <-- **已修改為 3 個連接**
179
+ --max-tries=3
180
+ --retry-wait=5
181
+ --timeout=180
182
+ --file-allocation=falloc
183
+ --auto-file-renaming=false
184
+ -d "$(dirname "$dest_path")"
185
+ -o "$filename"
186
+ )
187
+
188
+ [[ -n "$auth_header" ]] && aria_opts+=(--header="$auth_header")
189
+
190
+ aria2c "${aria_opts[@]}" "$url" 2>&1 && break
191
  else
192
+ log_info "使用 wget 下載: $filename (嘗試 $attempt/$max_retries)"
193
+ local wget_opts=(
194
+ -qnc -O "$dest_path"
195
+ --timeout=60
196
+ --tries=3
197
+ --content-disposition
198
+ --show-progress
199
+ )
200
+ [[ -n "$auth_header" ]] && wget_opts+=(--header="$auth_header")
201
+
202
+ wget "${wget_opts[@]}" "$url" 2>&1 && break
203
  fi
204
  done
205
 
206
+ if [ -f "$dest_path" ]; then
207
+ log_success "下載完成: $filename"
208
+ return 0
209
+ else
210
+ log_error "下載失敗: $filename"
211
  return 1
212
  fi
 
213
  }
214
 
215
+ download_to_directory() {
216
+ local dest_dir="$1"
217
+ shift
218
+ local urls=("$@")
219
 
220
+ if [ ${#urls[@]} -eq 0 ]; then
221
+ return 0
222
+ fi
223
+
224
+ mkdir -p "$dest_dir"
225
+ log_info "下載 ${#urls[@]} 個文件到 $dest_dir"
226
+
227
+ local MAX_PARALLEL=3
228
+ for url in "${urls[@]}"; do
229
+ while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do
230
+ sleep 1
231
+ done
232
+ local filename
233
+ filename=$(basename "$url" | sed 's/?.*//')
234
+ download_file "${dest_dir}/${filename}" "$url" &
235
+ done
236
+ wait
237
+ }
238
 
239
+ verify_and_retry_downloads() {
240
+ local dest_dir="$1"
241
+ shift
242
+ local urls=("$@")
243
+
244
+ if [ ${#urls[@]} -eq 0 ]; then
245
+ return 0
246
+ fi
247
+
248
+ log_info "檢查 $dest_dir 中的文件..."
249
+
250
+ local missing_files=()
251
+ for url in "${urls[@]}"; do
252
+ local filename
253
+ filename=$(basename "$url" | sed 's/?.*//')
254
+ local dest_path="${dest_dir}/${filename}"
255
 
256
+ if [ ! -f "$dest_path" ]; then
257
+ log_warn "檔案缺失: $filename"
258
+ missing_files+=("$url")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  fi
260
  done
 
 
 
 
 
 
261
 
262
+ if [ ${#missing_files[@]} -gt 0 ]; then
263
+ log_warn "發現 ${#missing_files[@]} 個缺失文件,重新下載..."
264
+ local MAX_PARALLEL=3
265
+ for url in "${missing_files[@]}"; do
266
+ while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do
267
+ sleep 1
268
+ done
269
+ local filename
270
+ filename=$(basename "$url" | sed 's/?.*//')
271
+ download_file "${dest_dir}/${filename}" "$url" &
272
+ done
273
+ wait
274
+ else
275
+ log_success "所有文件已確認存在"
276
+ fi
277
  }
278
 
279
+ provisioning_print_header() {
280
+ echo ""
281
+ echo "##############################################"
282
+ echo "# #"
283
+ echo "# Provisioning Container #"
284
+ echo "# #"
285
+ echo "# This will take some time #"
286
+ echo "# #"
287
+ echo "##############################################"
288
+ echo ""
289
+ }
290
 
291
+ provisioning_print_end() {
292
+ echo ""
293
+ echo "##############################################"
294
+ echo "# #"
295
+ echo "# Provisioning Complete! #"
296
+ echo "# #"
297
+ echo "# Total time: $((END_TIME - START_TIME)) seconds"
298
+ echo "# #"
299
+ echo "##############################################"
300
+ echo ""
301
+ }
302
 
303
+ START_TIME=$(date +%s)
304
+ log_info "開始時間: $(date)"
305
+ provisioning_print_header
306
 
307
+ cd "${COMFYUI_DIR}" || exit 1
 
 
 
308
 
309
+ if [ ${#APT_PACKAGES[@]} -gt 0 ]; then
310
+ log_step "1" "安裝系統套件"
311
+ if command -v sudo &>/dev/null && sudo -n true 2>/dev/null; then
312
+ sudo apt-get update -qq
313
+ sudo apt-get install -y -qq "${APT_PACKAGES[@]}"
314
+ else
315
+ log_warn "無 sudo 權限,跳過 APT 套件安裝"
316
+ fi
317
+ fi
318
 
319
+ log_step "2" "安裝節點與套件"
 
 
 
320
 
321
+ if [ ${#NODES[@]} -gt 0 ]; then
322
+ cd "${COMFYUI_DIR}/custom_nodes" || exit 1
323
+ log_info "並行安裝 ${#NODES[@]} 個節點..."
324
+
325
+ MAX_PARALLEL=2
326
+ for node in "${NODES[@]}"; do
327
+ while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do
328
+ sleep 1
329
+ done
330
+ install_node "$node" &
331
+ done
332
+ wait
333
+
334
+ log_success "節點安裝完成"
335
+ cd "${COMFYUI_DIR}" || exit 1
336
  fi
337
 
338
+ if [ ${#PIP_PACKAGES[@]} -gt 0 ]; then
339
+ log_info "安裝額外的 Python 套件..."
340
+ $PIP_BIN install -q --no-cache-dir "${PIP_PACKAGES[@]}" 2>&1 | grep -v "Requirement already satisfied" || true
 
341
  fi
342
 
343
+ log_step "3" "下載模型文件"
344
+
345
+ download_to_directory "${COMFYUI_DIR}/models/checkpoints" "${CHECKPOINT_MODELS[@]}"
346
+ download_to_directory "${COMFYUI_DIR}/models/clip_vision" "${CLIP_VISION_MODELS[@]}"
347
+ download_to_directory "${COMFYUI_DIR}/models/ipadapter" "${IPADAPTER_MODELS[@]}"
348
+ download_to_directory "${COMFYUI_DIR}/models/sams" "${SAM_MODELS[@]}"
349
+ download_to_directory "${COMFYUI_DIR}/models/ultralytics/bbox" "${ULTRALYTICS_BBOX_MODELS[@]}"
350
+ download_to_directory "${COMFYUI_DIR}/models/loras" "${LORA_MODELS[@]}"
351
+ download_to_directory "${COMFYUI_DIR}/models/upscale_models" "${ESRGAN_MODELS[@]}"
352
+
353
+ if [ ${#WORKFLOWS[@]} -gt 0 ]; then
354
+ log_info "下載工作流文件..."
355
+ mkdir -p "${COMFYUI_DIR}/user/default/workflows"
356
+ local MAX_PARALLEL=3
357
+ for workflow_url in "${WORKFLOWS[@]}"; do
358
+ while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do
359
+ sleep 1
360
+ done
361
+ local workflow_name
362
+ workflow_name=$(basename "$workflow_url" | sed 's/?.*//')
363
+ download_file "${COMFYUI_DIR}/user/default/workflows/${workflow_name}" "$workflow_url" &
364
+ done
365
+ wait
366
  fi
367
 
368
+ log_step "4" "驗證下載完整性"
 
 
 
 
 
 
 
 
 
 
 
 
369
 
370
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/checkpoints" "${CHECKPOINT_MODELS[@]}"
371
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/clip_vision" "${CLIP_VISION_MODELS[@]}"
372
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/ipadapter" "${IPADAPTER_MODELS[@]}"
373
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/sams" "${SAM_MODELS[@]}"
374
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/ultralytics/bbox" "${ULTRALYTICS_BBOX_MODELS[@]}"
375
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/loras" "${LORA_MODELS[@]}"
376
+ verify_and_retry_downloads "${COMFYUI_DIR}/models/upscale_models" "${ESRGAN_MODELS[@]}"
 
 
377
 
378
+ log_step "5" "完成配置"
379
 
380
+ touch "/workspace/.provision_complete"
381
+ log_success "配置標記文件已創建"
382
 
383
+ END_TIME=$(date +%s)
384
+ log_info "結束時間: $(date)"
385
+ provisioning_print_end
 
 
 
 
386
 
387
+ log_success "所有配置步驟已完成!"
388
+ exit 0