kythours commited on
Commit
5944d9a
·
verified ·
1 Parent(s): f4eb4a8

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +620 -0
app.py ADDED
@@ -0,0 +1,620 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ ComfyUI Hugging Face Space
4
+ Versão otimizada para execução gratuita no Hugging Face Spaces
5
+ """
6
+
7
+ import subprocess
8
+ import os
9
+ import time
10
+ import socket
11
+ import threading
12
+ import logging
13
+ import json
14
+ import gradio as gr
15
+ from pathlib import Path
16
+ from typing import Dict, List, Optional, Any
17
+ import psutil
18
+ import signal
19
+ import sys
20
+ import requests
21
+ from datetime import datetime
22
+
23
+ # Configuração de logging
24
+ logging.basicConfig(
25
+ level=logging.INFO,
26
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
27
+ handlers=[
28
+ logging.StreamHandler(sys.stdout),
29
+ logging.FileHandler('/tmp/comfyui.log')
30
+ ]
31
+ )
32
+ logger = logging.getLogger(__name__)
33
+
34
+ class ComfyUISpaceManager:
35
+ """Gerenciador principal do ComfyUI no Hugging Face Space"""
36
+
37
+ def __init__(self):
38
+ self.app_dir = Path("/tmp/ComfyUI")
39
+ self.models_dir = Path("/data/models") # Persistente no HF Spaces
40
+ self.port = 7860 # Porta padrão do Gradio
41
+ self.comfy_port = 8188 # Porta do ComfyUI
42
+ self.process = None
43
+ self.setup_complete = False
44
+ self.setup_logs = []
45
+
46
+ # Cria diretórios necessários
47
+ self.app_dir.mkdir(parents=True, exist_ok=True)
48
+ self.models_dir.mkdir(parents=True, exist_ok=True)
49
+
50
+ def log_setup(self, message: str, level: str = "info"):
51
+ """Adiciona mensagem aos logs de setup"""
52
+ timestamp = datetime.now().strftime("%H:%M:%S")
53
+ log_entry = f"[{timestamp}] {message}"
54
+ self.setup_logs.append(log_entry)
55
+
56
+ if level == "info":
57
+ logger.info(message)
58
+ elif level == "warning":
59
+ logger.warning(message)
60
+ elif level == "error":
61
+ logger.error(message)
62
+
63
+ # Mantém apenas últimas 100 entradas
64
+ if len(self.setup_logs) > 100:
65
+ self.setup_logs = self.setup_logs[-100:]
66
+
67
+ def get_hf_token(self) -> str:
68
+ """Obtém token do Hugging Face"""
69
+ token = os.getenv("HF_TOKEN") or os.getenv("HUGGING_FACE_TOKEN")
70
+ if not token:
71
+ self.log_setup("⚠️ Token HF não encontrado, alguns modelos podem não baixar", "warning")
72
+ return ""
73
+ self.log_setup(f"✅ Token HF configurado: ...{token[-8:]}")
74
+ return token
75
+
76
+ def setup_comfyui(self) -> bool:
77
+ """Setup completo do ComfyUI"""
78
+ try:
79
+ self.log_setup("🚀 Iniciando setup do ComfyUI...")
80
+
81
+ # 1. Clona repositório
82
+ if not self._clone_repository():
83
+ return False
84
+
85
+ # 2. Instala dependências
86
+ if not self._install_dependencies():
87
+ return False
88
+
89
+ # 3. Setup de modelos
90
+ if not self._setup_models():
91
+ return False
92
+
93
+ # 4. Configura custom nodes
94
+ if not self._setup_custom_nodes():
95
+ return False
96
+
97
+ self.log_setup("✅ Setup completo! ComfyUI pronto para usar")
98
+ self.setup_complete = True
99
+ return True
100
+
101
+ except Exception as e:
102
+ self.log_setup(f"❌ Erro no setup: {e}", "error")
103
+ return False
104
+
105
+ def _clone_repository(self) -> bool:
106
+ """Clona repositório do ComfyUI"""
107
+ if (self.app_dir / ".git").exists():
108
+ self.log_setup("📁 Repositório já existe, atualizando...")
109
+ try:
110
+ os.chdir(self.app_dir)
111
+ subprocess.run(["git", "pull"], check=True, capture_output=True)
112
+ return True
113
+ except subprocess.CalledProcessError as e:
114
+ self.log_setup(f"⚠️ Erro ao atualizar repositório: {e}", "warning")
115
+ # Continua mesmo se falhar
116
+
117
+ self.log_setup("📥 Clonando ComfyUI...")
118
+ try:
119
+ subprocess.run([
120
+ "git", "clone", "https://github.com/comfyanonymous/ComfyUI.git",
121
+ str(self.app_dir)
122
+ ], check=True, capture_output=True)
123
+ self.log_setup("✅ ComfyUI clonado com sucesso")
124
+ return True
125
+ except subprocess.CalledProcessError as e:
126
+ self.log_setup(f"❌ Erro ao clonar ComfyUI: {e}", "error")
127
+ return False
128
+
129
+ def _install_dependencies(self) -> bool:
130
+ """Instala dependências do ComfyUI"""
131
+ self.log_setup("📦 Instalando dependências...")
132
+
133
+ # Lista de dependências essenciais
134
+ dependencies = [
135
+ "torch>=2.0.0",
136
+ "torchvision",
137
+ "torchaudio",
138
+ "numpy<2.0",
139
+ "pillow",
140
+ "transformers>=4.28.1",
141
+ "accelerate",
142
+ "diffusers",
143
+ "huggingface-hub[hf_transfer]",
144
+ "safetensors>=0.4.2",
145
+ "aiohttp",
146
+ "psutil",
147
+ "opencv-python-headless"
148
+ ]
149
+
150
+ for dep in dependencies:
151
+ try:
152
+ self.log_setup(f"📦 Instalando {dep}...")
153
+ subprocess.run([
154
+ "pip", "install", "--upgrade", dep
155
+ ], check=True, capture_output=True)
156
+ except subprocess.CalledProcessError as e:
157
+ self.log_setup(f"⚠️ Falha ao instalar {dep}: {e}", "warning")
158
+ # Continua mesmo se falhar
159
+
160
+ self.log_setup("✅ Dependências instaladas")
161
+ return True
162
+
163
+ def _setup_models(self) -> bool:
164
+ """Setup de modelos com cache persistente"""
165
+ self.log_setup("🤖 Configurando modelos...")
166
+
167
+ # Verifica se modelos já existem
168
+ models_exist = self._check_existing_models()
169
+
170
+ if models_exist:
171
+ self.log_setup("✅ Modelos já existem no cache, vinculando...")
172
+ self._link_models()
173
+ return True
174
+
175
+ # Download de modelos essenciais
176
+ self.log_setup("📥 Primeira execução - baixando modelos essenciais...")
177
+ self._download_essential_models()
178
+
179
+ return True
180
+
181
+ def _check_existing_models(self) -> bool:
182
+ """Verifica se modelos já existem no storage persistente"""
183
+ essential_models = [
184
+ "checkpoints/flux1-schnell.safetensors",
185
+ "vae/ae.safetensors",
186
+ "clip/clip_l.safetensors"
187
+ ]
188
+
189
+ for model_path in essential_models:
190
+ full_path = self.models_dir / model_path
191
+ if not full_path.exists() or full_path.stat().st_size < 1024*1024: # < 1MB
192
+ return False
193
+
194
+ return True
195
+
196
+ def _link_models(self):
197
+ """Vincula modelos do storage persistente ao ComfyUI"""
198
+ comfy_models = self.app_dir / "models"
199
+
200
+ # Remove diretório de modelos se existir
201
+ if comfy_models.exists():
202
+ import shutil
203
+ shutil.rmtree(comfy_models)
204
+
205
+ # Cria symlink para storage persistente
206
+ comfy_models.symlink_to(self.models_dir)
207
+ self.log_setup("🔗 Modelos vinculados ao storage persistente")
208
+
209
+ def _download_essential_models(self):
210
+ """Download de modelos essenciais"""
211
+ token = self.get_hf_token()
212
+
213
+ # Modelos essenciais (versões menores para HF Spaces)
214
+ essential_models = [
215
+ {
216
+ "repo": "black-forest-labs/FLUX.1-schnell",
217
+ "file": "flux1-schnell.safetensors",
218
+ "dir": "checkpoints"
219
+ },
220
+ {
221
+ "repo": "black-forest-labs/FLUX.1-schnell",
222
+ "file": "ae.safetensors",
223
+ "dir": "vae"
224
+ },
225
+ {
226
+ "repo": "openai/clip-vit-large-patch14",
227
+ "file": "pytorch_model.bin",
228
+ "dir": "clip",
229
+ "rename": "clip_l.safetensors"
230
+ }
231
+ ]
232
+
233
+ for model in essential_models:
234
+ self._download_hf_model(
235
+ model["repo"],
236
+ model["file"],
237
+ model["dir"],
238
+ token,
239
+ model.get("rename")
240
+ )
241
+
242
+ # LoRAs populares (menores)
243
+ self._download_popular_loras()
244
+
245
+ # Vincula modelos após download
246
+ self._link_models()
247
+
248
+ def _download_hf_model(self, repo: str, filename: str, target_dir: str, token: str, rename: str = None):
249
+ """Download de modelo individual do HuggingFace"""
250
+ target_path = self.models_dir / target_dir
251
+ target_path.mkdir(parents=True, exist_ok=True)
252
+
253
+ final_name = rename if rename else filename
254
+ file_path = target_path / final_name
255
+
256
+ if file_path.exists():
257
+ self.log_setup(f"✅ {final_name} já existe")
258
+ return
259
+
260
+ self.log_setup(f"📥 Baixando {final_name} de {repo}...")
261
+
262
+ try:
263
+ from huggingface_hub import hf_hub_download
264
+
265
+ downloaded_path = hf_hub_download(
266
+ repo_id=repo,
267
+ filename=filename,
268
+ token=token if token else None,
269
+ cache_dir="/tmp/hf_cache"
270
+ )
271
+
272
+ # Copia para storage persistente
273
+ import shutil
274
+ shutil.copy2(downloaded_path, file_path)
275
+
276
+ size_mb = file_path.stat().st_size / (1024*1024)
277
+ self.log_setup(f"✅ {final_name} baixado ({size_mb:.1f}MB)")
278
+
279
+ except Exception as e:
280
+ self.log_setup(f"⚠️ Erro ao baixar {final_name}: {e}", "warning")
281
+
282
+ def _download_popular_loras(self):
283
+ """Download de LoRAs populares e pequenos"""
284
+ loras_dir = self.models_dir / "loras"
285
+ loras_dir.mkdir(exist_ok=True)
286
+
287
+ # LoRAs pequenos e populares
288
+ popular_loras = [
289
+ "https://huggingface.co/alvdansen/pony-realism/resolve/main/ponyRealismV21MainVAE.safetensors",
290
+ "https://huggingface.co/Hyper-SD/Hyper-FLUX.1-dev-8steps-lora/resolve/main/Hyper-FLUX.1-dev-8steps-lora.safetensors"
291
+ ]
292
+
293
+ for url in popular_loras:
294
+ try:
295
+ filename = url.split("/")[-1]
296
+ file_path = loras_dir / filename
297
+
298
+ if file_path.exists():
299
+ continue
300
+
301
+ self.log_setup(f"📥 Baixando LoRA {filename}...")
302
+ subprocess.run([
303
+ "wget", "-q", "--timeout=60", url, "-O", str(file_path)
304
+ ], check=True)
305
+
306
+ if file_path.stat().st_size > 1024: # > 1KB
307
+ self.log_setup(f"✅ LoRA {filename} baixado")
308
+ else:
309
+ file_path.unlink()
310
+
311
+ except Exception as e:
312
+ self.log_setup(f"⚠️ Erro ao baixar LoRA: {e}", "warning")
313
+
314
+ def _setup_custom_nodes(self) -> bool:
315
+ """Setup de custom nodes essenciais"""
316
+ self.log_setup("🔧 Configurando custom nodes...")
317
+
318
+ custom_nodes_dir = self.app_dir / "custom_nodes"
319
+ custom_nodes_dir.mkdir(exist_ok=True)
320
+
321
+ # Custom nodes essenciais e leves
322
+ essential_nodes = [
323
+ "https://github.com/ltdrdata/ComfyUI-Manager.git",
324
+ "https://github.com/WASasquatch/was-node-suite-comfyui.git"
325
+ ]
326
+
327
+ for repo_url in essential_nodes:
328
+ try:
329
+ repo_name = repo_url.split("/")[-1].replace(".git", "")
330
+ node_dir = custom_nodes_dir / repo_name
331
+
332
+ if node_dir.exists():
333
+ self.log_setup(f"✅ {repo_name} já existe")
334
+ continue
335
+
336
+ self.log_setup(f"📥 Clonando {repo_name}...")
337
+ subprocess.run([
338
+ "git", "clone", "--depth", "1", repo_url, str(node_dir)
339
+ ], check=True, capture_output=True, timeout=120)
340
+
341
+ # Instala requirements se existir
342
+ req_file = node_dir / "requirements.txt"
343
+ if req_file.exists():
344
+ subprocess.run([
345
+ "pip", "install", "-r", str(req_file)
346
+ ], check=False, capture_output=True, timeout=120)
347
+
348
+ self.log_setup(f"✅ {repo_name} instalado")
349
+
350
+ except Exception as e:
351
+ self.log_setup(f"⚠️ Erro ao instalar {repo_name}: {e}", "warning")
352
+
353
+ self.log_setup("✅ Custom nodes configurados")
354
+ return True
355
+
356
+ def start_comfyui(self) -> bool:
357
+ """Inicia o servidor ComfyUI"""
358
+ if self.process and self.process.poll() is None:
359
+ self.log_setup("✅ ComfyUI já está rodando")
360
+ return True
361
+
362
+ self.log_setup("🚀 Iniciando servidor ComfyUI...")
363
+
364
+ try:
365
+ os.chdir(self.app_dir)
366
+
367
+ # Inicia ComfyUI em background
368
+ self.process = subprocess.Popen([
369
+ "python", "main.py",
370
+ "--listen", "0.0.0.0",
371
+ "--port", str(self.comfy_port),
372
+ "--preview-method", "auto",
373
+ "--dont-print-server"
374
+ ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
375
+
376
+ # Aguarda servidor ficar pronto
377
+ for attempt in range(60): # 60 tentativas = 2 minutos
378
+ if self._test_comfyui_connection():
379
+ self.log_setup("✅ ComfyUI está rodando!")
380
+ return True
381
+
382
+ if self.process.poll() is not None:
383
+ stdout, stderr = self.process.communicate()
384
+ self.log_setup(f"❌ ComfyUI parou: {stderr}", "error")
385
+ return False
386
+
387
+ time.sleep(2)
388
+
389
+ self.log_setup("❌ Timeout aguardando ComfyUI", "error")
390
+ return False
391
+
392
+ except Exception as e:
393
+ self.log_setup(f"❌ Erro ao iniciar ComfyUI: {e}", "error")
394
+ return False
395
+
396
+ def _test_comfyui_connection(self) -> bool:
397
+ """Testa conexão com ComfyUI"""
398
+ try:
399
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
400
+ sock.settimeout(1)
401
+ return sock.connect_ex(('127.0.0.1', self.comfy_port)) == 0
402
+ except:
403
+ return False
404
+
405
+ def get_status(self) -> Dict[str, Any]:
406
+ """Retorna status do sistema"""
407
+ return {
408
+ "setup_complete": self.setup_complete,
409
+ "comfyui_running": self.process and self.process.poll() is None,
410
+ "comfyui_url": f"http://127.0.0.1:{self.comfy_port}" if self.setup_complete else None,
411
+ "logs": self.setup_logs[-20:], # Últimas 20 linhas
412
+ "memory_usage": psutil.virtual_memory().percent,
413
+ "disk_usage": psutil.disk_usage('/').percent if os.path.exists('/') else 0,
414
+ "models_dir_size": self._get_dir_size(self.models_dir)
415
+ }
416
+
417
+ def _get_dir_size(self, path: Path) -> str:
418
+ """Retorna tamanho do diretório formatado"""
419
+ try:
420
+ total_size = sum(f.stat().st_size for f in path.glob('**/*') if f.is_file())
421
+ size_gb = total_size / (1024**3)
422
+ return f"{size_gb:.2f} GB"
423
+ except:
424
+ return "N/A"
425
+
426
+ def stop_comfyui(self):
427
+ """Para o ComfyUI"""
428
+ if self.process:
429
+ self.log_setup("🛑 Parando ComfyUI...")
430
+ self.process.terminate()
431
+ try:
432
+ self.process.wait(timeout=10)
433
+ except subprocess.TimeoutExpired:
434
+ self.process.kill()
435
+ self.process = None
436
+
437
+ # Instância global
438
+ comfy_manager = ComfyUISpaceManager()
439
+
440
+ def setup_comfyui():
441
+ """Interface para setup do ComfyUI"""
442
+ if comfy_manager.setup_complete:
443
+ return "✅ Setup já completo!", comfy_manager.get_status()
444
+
445
+ # Executa setup em thread separada
446
+ def run_setup():
447
+ comfy_manager.setup_comfyui()
448
+ comfy_manager.start_comfyui()
449
+
450
+ setup_thread = threading.Thread(target=run_setup, daemon=True)
451
+ setup_thread.start()
452
+
453
+ return "🚀 Setup iniciado! Acompanhe o progresso abaixo...", comfy_manager.get_status()
454
+
455
+ def get_status_update():
456
+ """Atualização de status para interface"""
457
+ status = comfy_manager.get_status()
458
+
459
+ # Formata logs
460
+ logs_text = "\n".join(status["logs"]) if status["logs"] else "Aguardando logs..."
461
+
462
+ # Status geral
463
+ if status["setup_complete"] and status["comfyui_running"]:
464
+ main_status = "🟢 ComfyUI Rodando - Pronto para usar!"
465
+ comfyui_link = f'<a href="http://127.0.0.1:{comfy_manager.comfy_port}" target="_blank">🎯 Abrir ComfyUI</a>'
466
+ elif status["setup_complete"]:
467
+ main_status = "🟡 Setup completo - Iniciando ComfyUI..."
468
+ comfyui_link = "⏳ Aguardando..."
469
+ else:
470
+ main_status = "🟡 Setup em progresso..."
471
+ comfyui_link = "⏳ Aguardando setup..."
472
+
473
+ # Informações do sistema
474
+ system_info = f"""
475
+ 💾 Memória: {status['memory_usage']:.1f}%
476
+ 💿 Disco: {status['disk_usage']:.1f}%
477
+ 📁 Modelos: {status['models_dir_size']}
478
+ """
479
+
480
+ return main_status, logs_text, system_info, comfyui_link
481
+
482
+ def restart_comfyui():
483
+ """Reinicia ComfyUI"""
484
+ comfy_manager.stop_comfyui()
485
+ time.sleep(2)
486
+ success = comfy_manager.start_comfyui()
487
+
488
+ if success:
489
+ return "✅ ComfyUI reiniciado com sucesso!"
490
+ else:
491
+ return "❌ Erro ao reiniciar ComfyUI"
492
+
493
+ # Interface Gradio
494
+ def create_interface():
495
+ """Cria interface Gradio"""
496
+
497
+ with gr.Blocks(
498
+ title="ComfyUI Hugging Face Space",
499
+ theme=gr.themes.Soft(),
500
+ css="""
501
+ .status-box { padding: 20px; border-radius: 10px; margin: 10px 0; }
502
+ .running { background-color: #d4edda; border: 1px solid #c3e6cb; }
503
+ .setup { background-color: #fff3cd; border: 1px solid #ffeaa7; }
504
+ .error { background-color: #f8d7da; border: 1px solid #f5c6cb; }
505
+ """
506
+ ) as demo:
507
+
508
+ gr.Markdown("""
509
+ # 🚀 ComfyUI Hugging Face Space
510
+
511
+ **ComfyUI executando gratuitamente no Hugging Face Spaces!**
512
+
513
+ ✅ GPU Tesla T4 gratuita
514
+ ✅ Modelos persistentes
515
+ ✅ Interface web completa
516
+ ✅ Custom nodes incluídos
517
+ """)
518
+
519
+ with gr.Row():
520
+ with gr.Column(scale=2):
521
+ # Status principal
522
+ status_display = gr.Markdown("🟡 Aguardando inicialização...", elem_classes=["status-box", "setup"])
523
+
524
+ # Botões de controle
525
+ with gr.Row():
526
+ setup_btn = gr.Button("🚀 Iniciar Setup", variant="primary", scale=2)
527
+ restart_btn = gr.Button("🔄 Reiniciar", scale=1)
528
+
529
+ # Link para ComfyUI
530
+ comfyui_link = gr.HTML("⏳ Aguardando setup...")
531
+
532
+ # Informações do sistema
533
+ system_info = gr.Textbox(
534
+ label="📊 Informações do Sistema",
535
+ interactive=False,
536
+ lines=4
537
+ )
538
+
539
+ with gr.Column(scale=3):
540
+ # Logs em tempo real
541
+ logs_display = gr.Textbox(
542
+ label="📋 Logs do Setup",
543
+ lines=20,
544
+ max_lines=30,
545
+ interactive=False,
546
+ autoscroll=True
547
+ )
548
+
549
+ # Informações de ajuda
550
+ with gr.Accordion("📚 Como Usar", open=False):
551
+ gr.Markdown("""
552
+ ### 🎯 Passos para usar:
553
+
554
+ 1. **Clique em "Iniciar Setup"** - O sistema irá baixar e configurar tudo automaticamente
555
+ 2. **Aguarde ~10-15 minutos** na primeira execução (downloads dos modelos)
556
+ 3. **Clique em "Abrir ComfyUI"** quando aparecer o link
557
+ 4. **Use normalmente** - Todos os dados ficam salvos automaticamente!
558
+
559
+ ### 🔧 Recursos incluídos:
560
+ - **FLUX.1-schnell** - Modelo de geração rápida
561
+ - **VAE e CLIP** - Encoders necessários
562
+ - **LoRAs populares** - Para estilos específicos
563
+ - **ComfyUI Manager** - Para instalar novos nodes
564
+ - **WAS Node Suite** - Nodes úteis extras
565
+
566
+ ### 💡 Dicas:
567
+ - ✅ Seus modelos ficam salvos entre sessões
568
+ - ✅ Pode fechar e abrir o Space sem perder dados
569
+ - ✅ Use "Reiniciar" se algo der errado
570
+ - ✅ Logs mostram o progresso detalhado
571
+ """)
572
+
573
+ # Event handlers
574
+ setup_btn.click(
575
+ fn=setup_comfyui,
576
+ outputs=[status_display, logs_display]
577
+ )
578
+
579
+ restart_btn.click(
580
+ fn=restart_comfyui,
581
+ outputs=[status_display]
582
+ )
583
+
584
+ # Auto-refresh de status a cada 3 segundos
585
+ demo.load(
586
+ fn=get_status_update,
587
+ outputs=[status_display, logs_display, system_info, comfyui_link],
588
+ every=3
589
+ )
590
+
591
+ return demo
592
+
593
+ # Tratamento de sinais para shutdown gracioso
594
+ def signal_handler(signum, frame):
595
+ """Handler para shutdown gracioso"""
596
+ logger.info(f"Recebido sinal {signum}, parando ComfyUI...")
597
+ comfy_manager.stop_comfyui()
598
+ sys.exit(0)
599
+
600
+ signal.signal(signal.SIGINT, signal_handler)
601
+ signal.signal(signal.SIGTERM, signal_handler)
602
+
603
+ # Inicia interface
604
+ if __name__ == "__main__":
605
+ # Setup inicial em background se não foi feito
606
+ if not comfy_manager.setup_complete:
607
+ setup_thread = threading.Thread(
608
+ target=lambda: comfy_manager.setup_comfyui() and comfy_manager.start_comfyui(),
609
+ daemon=True
610
+ )
611
+ setup_thread.start()
612
+
613
+ # Cria e lança interface
614
+ demo = create_interface()
615
+ demo.launch(
616
+ server_name="0.0.0.0",
617
+ server_port=7860,
618
+ share=False, # HF Spaces já é público
619
+ inbrowser=False
620
+ )