Mightys/Notebook_Scripts
02.7k
1import ipywidgets as widgets2from IPython.display import display, clear_output, HTML3import os4import subprocess5import requests6import re7import pickle8from pathlib import Path9 10# --- Diccionarios de Modelos Predefinidos ---11PRESET_MODELS = {12 "WaiNSFW V16": "https://huggingface.co/Quiho/best-from-civitai/resolve/main/waiIllustriousSDXL_v160.safetensors",13 "WaiNSFW V15": "https://huggingface.co/WhiteAiZ/WAI-NSFW-illustrious-SDXL-V015/resolve/main/waiNSFWIllustrious_v150.safetensors",14 "waiNSFW V14": "https://huggingface.co/Ine007/waiNSFWIllustrious_v140/resolve/main/waiNSFWIllustrious_v140.safetensors",15 "waiSHUFFLENOOB_v20": "https://huggingface.co/WhiteAiZ/waiSHUFFLENOOB_v20/resolve/main/waiSHUFFLENOOB_v20.safetensors",16 "waiSHUFFLENOOB_vpred20": "https://huggingface.co/WhiteAiZ/waiSHUFFLENOOB_v20/resolve/main/waiSHUFFLENOOB_vPred20.safetensors",17 "ntrMIXIllustriousXL_XIII": "https://huggingface.co/misri/ntrMIXIllustriousXL_xiii/resolve/main/ntrMIXIllustriousXL_xiii.safetensors",18 "NoobaiCyberFix": "https://civitaiarchive.com/api/download/models/1122850",19 "NoobaiCyberFix vpred": "https://civitaiarchive.com/api/download/models/2371102",20 "konanMix Vpred": "https://huggingface.co/wsj1995/Checkpoint/resolve/main/1365468/1542670/konanmixnoobvPredNoob_v10.safetensors",21 "Nova Anime XL": "https://huggingface.co/Chattiori/ChattioriMixesXL/resolve/main/NovaAnimeILV8.safetensors",22 "Illustrious XL personal merge": "https://huggingface.co/nnnn1111/models/resolve/main/illustriousXLPersonalMerge_v30Noob10based.safetensors",23 "Illustrious XL personal merge vpred": "https://huggingface.co/datasets/John6666/model-mirror-14/resolve/main/illustriousXLPersonalMerge_vp05testLowsteps.safetensors",24 "Ikastrious": "https://civitaiarchive.com/api/download/models/2471934",25 "Ikastrious Noobai": "https://huggingface.co/minaiosu/giko/resolve/main/ikastrious_v95.safetensors",26 "RouWei": "https://civitaiarchive.com/api/download/models/1832460",27 "RouWei Vpred": "https://huggingface.co/WhiteAiZ/RouWei/resolve/main/rouwei_v080Vpred.safetensors",28 "PlantMint Walnut": "https://huggingface.co/wsj1995/Checkpoint/resolve/main/1162518/1714002/plantMilkModelSuite_walnut.safetensors"29}30 31PRESET_VAES = {32 "sdxl_vae": "https://huggingface.co/stabilityai/sdxl-vae/resolve/main/sdxl_vae.safetensors",33 "sdxl_vae_fix": "https://huggingface.co/madebyollin/sdxl-vae-fp16-fix/resolve/main/sdxl_vae.safetensors",34 "sdxl_anime_vae": "https://huggingface.co/Anzhc/Anzhcs-VAEs/resolve/main/SDXL%20Anime%20VAE%20Dec-only%20B3.safetensors",35 "sdxl_neptunia_vae": "https://huggingface.co/JustAnotherCibrarian/vae/resolve/main/1290283/1455983/neptuniaXLILNAIVAE_contrastColors.safetensors",36 "sdxl_luna_vae": "https://huggingface.co/yuu062/tameshi/resolve/main/lunaXLVAE_luna.safetensors"37}38 39PRESET_UPSCALERS = {40 "AnimeSharp": "https://huggingface.co/Kim2091/AnimeSharp/resolve/main/4x-AnimeSharp.pth",41 "UltraSharp": "https://huggingface.co/lokCX/4x-Ultrasharp/resolve/main/4x-UltraSharp.pth",42 "Remacri": "https://huggingface.co/LyliaEngine/remacri_original/resolve/main/remacri_original.pt",43 "RealESRGAN_x4plus_anime": "https://huggingface.co/gemasai/RealESRGAN_x4plus_anime_6B/resolve/main/RealESRGAN_x4plus_anime_6B.pth",44 "JaNai": "https://huggingface.co/halllooo/4x_illustrationJaNaiV1/resolve/main/4x_IllustrationJaNai_V1_ESRGAN_135k.pth",45 "YandereNeoXL": "https://huggingface.co/kaeru-shigure/mlx-4x_NMKD-YandereNeoXL_200k/resolve/main/4x_NMKD-YandereNeoXL_200k.safetensors"46}47 48PRESET_CONTROLNETS = {49 "Controlnet Union Pro Max": "https://huggingface.co/xinsir/controlnet-union-sdxl-1.0/resolve/main/diffusion_pytorch_model_promax.safetensors",50 "Controlnet Lite (Todos)": "https://huggingface.co/bdsqlsz/qinglong_controlnet-lllite/resolve/main/bdsqlsz_controlllite_xl_sketch.safetensors, https://huggingface.co/bdsqlsz/qinglong_controlnet-lllite/resolve/main/bdsqlsz_controlllite_xl_softedge.safetensors, https://huggingface.co/bdsqlsz/qinglong_controlnet-lllite/resolve/main/bdsqlsz_controlllite_xl_dw_openpose.safetensors, https://huggingface.co/bdsqlsz/qinglong_controlnet-lllite/resolve/main/bdsqlsz_controlllite_xl_canny.safetensors, https://huggingface.co/bdsqlsz/qinglong_controlnet-lllite/resolve/main/bdsqlsz_controlllite_xl_depth_V2.safetensors, https://huggingface.co/bdsqlsz/qinglong_controlnet-lllite/resolve/main/bdsqlsz_controlllite_xl_lineart_anime_denoise.safetensors"51}52 53PRESET_DIFFUSION = {54 "z-image-turbo-Q4_K_M": "https://huggingface.co/unsloth/Z-Image-Turbo-GGUF/resolve/main/z-image-turbo-Q4_K_M.gguf",55 "z-image-turbo-Q8_0": "https://huggingface.co/unsloth/Z-Image-Turbo-GGUF/resolve/main/z-image-turbo-Q8_0.gguf"56}57 58PRESET_TEXT_ENCODERS = {59 "Qwen3-4B-Q4_K_M": "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q4_K_M.gguf",60 "Qwen3-4B-Q8_0": "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf"61}62 63PRESET_UNET = {} 64PRESET_CLIP = {} 65 66def iniciar():67 # --- Configuración de Rutas para Lightning.ai ---68 BASE_MODELS_DIR = "/tmp/models"69 LORA_DIR = "/tmp/lora"70 VAE_DIR = "/teamspace/studios/this_studio/SwarmUI/Models/VAE"71 UPSCALER_DIR = "/teamspace/studios/this_studio/SwarmUI/Models/upscale_models"72 CONTROLNET_DIR = "/tmp/controlnet"73 DIFFUSION_DIR = "/tmp/diffusion_models"74 TEXT_ENCODER_DIR = "/tmp/text_encoders"75 UNET_DIR = "/tmp/unet"76 CLIP_DIR = "/teamspace/studios/this_studio/SwarmUI/Models/clip"77 COMFY_EXT_DIR = "/teamspace/studios/this_studio/SwarmUI/dlbackend/ComfyUI/custom_nodes"78 79 # Asegurar que las carpetas existan80 os.makedirs(BASE_MODELS_DIR, exist_ok=True)81 os.makedirs(LORA_DIR, exist_ok=True)82 os.makedirs(VAE_DIR, exist_ok=True)83 os.makedirs(UPSCALER_DIR, exist_ok=True)84 os.makedirs(CONTROLNET_DIR, exist_ok=True)85 os.makedirs(DIFFUSION_DIR, exist_ok=True)86 os.makedirs(TEXT_ENCODER_DIR, exist_ok=True)87 os.makedirs(UNET_DIR, exist_ok=True)88 os.makedirs(CLIP_DIR, exist_ok=True)89 os.makedirs(COMFY_EXT_DIR, exist_ok=True)90 91 # --- Leer token desde pickle en Lightning.ai ---92 TOKEN_FILE = Path.home() / ".civitai_token.pkl"93 token_guardado = ""94 if TOKEN_FILE.exists():95 try:96 token_guardado = pickle.loads(TOKEN_FILE.read_bytes())97 except Exception:98 pass99 100 # --- Elementos de la Interfaz ---101 out_console = widgets.Output(layout={'border': '1px solid #ccc', 'padding': '10px', 'margin': '10px 0', 'height': '250px', 'overflow': 'auto'})102 103 token_input = widgets.Password(104 value=token_guardado,105 placeholder='Ingresa tu API Token (Opcional pero recomendado)',106 description='Civitai Token:',107 style={'description_width': 'initial'},108 layout=widgets.Layout(width='500px')109 )110 111 # Widgets de Progreso112 progress_bar = widgets.IntProgress(113 value=0, min=0, max=100, 114 description='Progreso:', 115 bar_style='info', 116 orientation='horizontal', 117 layout=widgets.Layout(width='80%', display='none')118 )119 status_label = widgets.Label(value="", layout=widgets.Layout(display='none'))120 progress_container = widgets.VBox([progress_bar, status_label])121 122 def ejecutar_con_progreso(cmd, is_gdown=False):123 progress_bar.value = 0124 progress_bar.layout.display = 'flex'125 status_label.layout.display = 'flex'126 status_label.value = "Iniciando descarga..."127 128 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, universal_newlines=True)129 130 for line in process.stdout:131 if not is_gdown:132 match_pct = re.search(r'\((\d+)%\)', line)133 if match_pct:134 progress_bar.value = int(match_pct.group(1))135 136 match_speed = re.search(r'DL:([^\s]+)', line)137 if match_speed:138 status_label.value = f"Descargando... Velocidad: {match_speed.group(1)}"139 else:140 match_pct = re.search(r'(\d{1,3})%', line)141 if match_pct:142 progress_bar.value = int(match_pct.group(1))143 status_label.value = "Descargando con gdown..."144 145 process.wait()146 progress_bar.value = 100147 progress_bar.bar_style = 'success'148 status_label.value = "¡Descarga de este archivo completada! ✅"149 150 def procesar_descarga(b, url_widget, method_widget, dest_path):151 raw_urls = url_widget.value.strip()152 method = method_widget.value153 token = token_input.value.strip()154 155 urls = [u.strip() for u in raw_urls.split(",") if u.strip()]156 157 if not urls:158 with out_console:159 clear_output()160 display(HTML("<h4 style='color:red;'>⚠️ Por favor, ingresa al menos una URL válida.</h4>"))161 return162 163 with out_console:164 clear_output()165 display(HTML(f"<h2>🚀 Iniciando cola de descarga: {len(urls)} archivo(s)</h2>"))166 167 for i, url in enumerate(urls, 1):168 progress_bar.bar_style = 'info'169 progress_bar.value = 0170 status_label.value = f"Preparando archivo {i} de {len(urls)}..."171 172 download_url = url173 if ("civitai.com" in url or "civitaiarchive.com" in url) and token:174 download_url = f"{url}{'&' if '?' in url else '?'}token={token}"175 176 pretty_name = "Desconocido"177 178 if url == "https://huggingface.co/xinsir/controlnet-union-sdxl-1.0/resolve/main/diffusion_pytorch_model_promax.safetensors":179 pretty_name = "Controlnet_Union_Pro_Max.safetensors"180 elif method == 'aria2':181 if "civitai.com" in url or "civitaiarchive.com" in url:182 try:183 with requests.get(download_url, stream=True, timeout=5) as r:184 cd = r.headers.get('Content-Disposition', '')185 match = re.findall(r'filename[*]?=(?:UTF-8\'\')?["\']?([^"\';]+)["\']?', cd)186 if match: 187 pretty_name = match[0]188 else:189 pretty_name = url.split('/')[-1].split('?')[0]190 except Exception:191 pretty_name = url.split('/')[-1].split('?')[0]192 else:193 pretty_name = url.split('/')[-1].split('?')[0]194 elif method == 'gdown':195 pretty_name = "Archivo de Google Drive (Gdown gestiona el nombre)"196 197 with out_console:198 display(HTML(f"<hr><h3 style='color:#D4AF37;'>📥 [{i}/{len(urls)}] Descargando: <code>{pretty_name}</code></h3>"))199 display(HTML(f"<h4 style='color:#4682B4;'>📁 Destino: <code>{dest_path}</code></h4>"))200 201 if ("civitai.com" in url or "civitaiarchive.com" in url) and token:202 print("🔑 Token detectado y aplicado desde el gestor.")203 elif ("civitai.com" in url or "civitaiarchive.com" in url) and not token:204 print("⚠️ Descargando sin token (algunos modelos pueden requerirlo).")205 206 if method == 'aria2':207 cmd = [208 "aria2c", "--content-disposition",209 "-c", "-x", "16", "-s", "16", "-k", "1M",210 "--summary-interval=1", "-d", dest_path211 ]212 213 if pretty_name and pretty_name != "Desconocido" and "civitai.com" not in url and "civitaiarchive.com" not in url:214 cmd.extend(["-o", pretty_name, url])215 else:216 cmd.append(download_url)217 218 ejecutar_con_progreso(cmd, is_gdown=False)219 220 if "huggingface.co" in url and pretty_name:221 for f in os.listdir(dest_path):222 if re.fullmatch(r'[0-9a-f]{64}', f):223 os.rename(os.path.join(dest_path, f), os.path.join(dest_path, pretty_name))224 break225 226 elif method == 'gdown':227 cmd = ["gdown", "--fuzzy", url, "-O", f"{dest_path}/"]228 ejecutar_con_progreso(cmd, is_gdown=True)229 230 with out_console:231 display(HTML("<hr><h2 style='color:lightgreen;'>✅ ¡Todas las descargas han finalizado!</h2>"))232 233 def procesar_extensiones(b, url_widget):234 raw_urls = url_widget.value.strip()235 urls = [u.strip() for u in raw_urls.split(",") if u.strip()]236 237 if not urls:238 with out_console:239 clear_output()240 display(HTML("<h4 style='color:red;'>⚠️ Por favor, ingresa al menos una URL de GitHub válida.</h4>"))241 return242 243 with out_console:244 clear_output()245 display(HTML(f"<h2>🚀 Iniciando clonación de {len(urls)} extensión(es)</h2>"))246 247 for i, url in enumerate(urls, 1):248 progress_bar.layout.display = 'flex'249 status_label.layout.display = 'flex'250 progress_bar.bar_style = 'info'251 progress_bar.value = 50 252 253 repo_name = url.rstrip('/').split('/')[-1].replace('.git', '')254 target_path = os.path.join(COMFY_EXT_DIR, repo_name)255 status_label.value = f"Clonando {repo_name} ({i}/{len(urls)})..."256 257 with out_console:258 display(HTML(f"<hr><h3 style='color:#D4AF37;'>📥 [{i}/{len(urls)}] Clonando: <code>{repo_name}</code></h3>"))259 display(HTML(f"<h4 style='color:#4682B4;'>📁 Destino: <code>{target_path}</code></h4>"))260 261 if os.path.exists(target_path):262 with out_console:263 print(f"⚠️ La carpeta {repo_name} ya existe. Saltando git clone...")264 else:265 cmd_clone = ["git", "clone", url, target_path]266 res_clone = subprocess.run(cmd_clone, capture_output=True, text=True)267 if res_clone.returncode != 0:268 with out_console:269 print(f"❌ Error al clonar:\n{res_clone.stderr}")270 continue271 272 req_file = os.path.join(target_path, "requirements.txt")273 if os.path.exists(req_file):274 status_label.value = f"Instalando dependencias para {repo_name}..."275 with out_console:276 print(f"📦 requirements.txt detectado. Instalando con uv...")277 278 cmd_uv = ["uv", "pip", "install", "--system", "-r", req_file]279 res_uv = subprocess.run(cmd_uv, capture_output=True, text=True)280 281 with out_console:282 if res_uv.returncode == 0:283 print("✅ Dependencias instaladas exitosamente.")284 else:285 print(f"⚠️ Hubo un problema al instalar algunas dependencias:\n{res_uv.stderr}")286 else:287 with out_console:288 print("ℹ️ No se encontró requirements.txt. Omitiendo instalación de dependencias.")289 290 progress_bar.value = 100291 progress_bar.bar_style = 'success'292 status_label.value = "¡Extensión procesada! ✅"293 294 with out_console:295 display(HTML("<hr><h2 style='color:lightgreen;'>✅ ¡Todas las extensiones han sido procesadas!</h2>"))296 297 298 # --- Pestaña 1: Modelos Base ---299 opciones_presets_base = ['-- Personalizado / Manual --'] + list(PRESET_MODELS.keys())300 preset_base_dropdown = widgets.Dropdown(options=opciones_presets_base, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))301 base_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))302 303 def actualizar_url_base(change):304 seleccion = change['new']305 if seleccion in PRESET_MODELS:306 base_url.value = PRESET_MODELS[seleccion]307 else:308 base_url.value = ""309 310 preset_base_dropdown.observe(actualizar_url_base, names='value')311 312 base_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')313 base_btn = widgets.Button(description='Descargar Modelos', button_style='primary', icon='download')314 base_btn.on_click(lambda b: procesar_descarga(b, base_url, base_method, BASE_MODELS_DIR))315 316 tab_base = widgets.VBox([317 widgets.HTML("<h4>Descargar Modelos (Checkpoints)</h4><p>Elige un modelo favorito o pega enlaces separados por comas (,)</p>"),318 preset_base_dropdown,319 widgets.HBox([base_url, base_method]),320 base_btn321 ])322 323 # --- Pestaña 2: LoRAs ---324 lora_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URLs:', layout=widgets.Layout(width='80%'))325 lora_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')326 lora_btn = widgets.Button(description='Descargar LoRAs', button_style='success', icon='download')327 lora_btn.on_click(lambda b: procesar_descarga(b, lora_url, lora_method, LORA_DIR))328 329 tab_lora = widgets.VBox([330 widgets.HTML("<h4>Descargar LoRAs</h4><p>Pega múltiples URLs separadas por comas (,)</p>"),331 widgets.HBox([lora_url, lora_method]),332 lora_btn333 ])334 335 # --- Pestaña 3: VAEs ---336 opciones_presets_vae = ['-- Personalizado / Manual --'] + list(PRESET_VAES.keys())337 preset_vae_dropdown = widgets.Dropdown(options=opciones_presets_vae, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))338 vae_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))339 340 def actualizar_url_vae(change):341 seleccion = change['new']342 if seleccion in PRESET_VAES:343 vae_url.value = PRESET_VAES[seleccion]344 else:345 vae_url.value = ""346 347 preset_vae_dropdown.observe(actualizar_url_vae, names='value')348 349 vae_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')350 vae_btn = widgets.Button(description='Descargar VAEs', button_style='info', icon='download')351 vae_btn.on_click(lambda b: procesar_descarga(b, vae_url, vae_method, VAE_DIR))352 353 tab_vae = widgets.VBox([354 widgets.HTML("<h4>Descargar VAEs</h4><p>Elige un VAE favorito o pega enlaces separados por comas (,)</p>"),355 preset_vae_dropdown,356 widgets.HBox([vae_url, vae_method]),357 vae_btn358 ])359 360 # --- Pestaña 4: Upscalers ---361 opciones_presets_upscalers = ['-- Personalizado / Manual --', '-- Descargar Todos --'] + list(PRESET_UPSCALERS.keys())362 preset_upscaler_dropdown = widgets.Dropdown(options=opciones_presets_upscalers, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))363 upscaler_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))364 365 def actualizar_url_upscaler(change):366 seleccion = change['new']367 if seleccion == '-- Descargar Todos --':368 upscaler_url.value = ", ".join(PRESET_UPSCALERS.values())369 elif seleccion in PRESET_UPSCALERS:370 upscaler_url.value = PRESET_UPSCALERS[seleccion]371 else:372 upscaler_url.value = ""373 374 preset_upscaler_dropdown.observe(actualizar_url_upscaler, names='value')375 376 upscaler_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')377 upscaler_btn = widgets.Button(description='Descargar Upscalers', button_style='warning', icon='download')378 upscaler_btn.on_click(lambda b: procesar_descarga(b, upscaler_url, upscaler_method, UPSCALER_DIR))379 380 tab_upscaler = widgets.VBox([381 widgets.HTML("<h4>Descargar Upscalers (ESRGAN)</h4><p>Elige un upscaler, selecciona <b>'-- Descargar Todos --'</b>, o pega enlaces separados por comas (,)</p>"),382 preset_upscaler_dropdown,383 widgets.HBox([upscaler_url, upscaler_method]),384 upscaler_btn385 ])386 387 # --- Pestaña 5: ControlNet ---388 opciones_presets_controlnet = ['-- Personalizado / Manual --'] + list(PRESET_CONTROLNETS.keys())389 preset_controlnet_dropdown = widgets.Dropdown(options=opciones_presets_controlnet, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))390 controlnet_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))391 392 def actualizar_url_controlnet(change):393 seleccion = change['new']394 if seleccion in PRESET_CONTROLNETS:395 controlnet_url.value = PRESET_CONTROLNETS[seleccion]396 else:397 controlnet_url.value = ""398 399 preset_controlnet_dropdown.observe(actualizar_url_controlnet, names='value')400 401 controlnet_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')402 controlnet_btn = widgets.Button(description='Descargar ControlNet', button_style='danger', icon='download')403 controlnet_btn.on_click(lambda b: procesar_descarga(b, controlnet_url, controlnet_method, CONTROLNET_DIR))404 405 tab_controlnet = widgets.VBox([406 widgets.HTML("<h4>Descargar Modelos ControlNet</h4><p>Elige el Pro Max, el paquete Lite, o pega enlaces separados por comas (,)</p>"),407 preset_controlnet_dropdown,408 widgets.HBox([controlnet_url, controlnet_method]),409 controlnet_btn410 ])411 412 # --- Pestaña 6: Diffusion Models ---413 opciones_presets_diffusion = ['-- Personalizado / Manual --'] + list(PRESET_DIFFUSION.keys())414 preset_diffusion_dropdown = widgets.Dropdown(options=opciones_presets_diffusion, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))415 diffusion_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))416 417 def actualizar_url_diffusion(change):418 seleccion = change['new']419 if seleccion in PRESET_DIFFUSION:420 diffusion_url.value = PRESET_DIFFUSION[seleccion]421 else:422 diffusion_url.value = ""423 424 preset_diffusion_dropdown.observe(actualizar_url_diffusion, names='value')425 426 diffusion_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')427 diffusion_btn = widgets.Button(description='Descargar Diffusion Models', button_style='primary', icon='download')428 diffusion_btn.on_click(lambda b: procesar_descarga(b, diffusion_url, diffusion_method, DIFFUSION_DIR))429 430 tab_diffusion = widgets.VBox([431 widgets.HTML("<h4>Descargar Diffusion Models</h4><p>Elige un modelo GGUF o pega enlaces separados por comas (,)</p>"),432 preset_diffusion_dropdown,433 widgets.HBox([diffusion_url, diffusion_method]),434 diffusion_btn435 ])436 437 # --- Pestaña 7: Text Encoders ---438 opciones_presets_te = ['-- Personalizado / Manual --'] + list(PRESET_TEXT_ENCODERS.keys())439 preset_te_dropdown = widgets.Dropdown(options=opciones_presets_te, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))440 te_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))441 442 def actualizar_url_te(change):443 seleccion = change['new']444 if seleccion in PRESET_TEXT_ENCODERS:445 te_url.value = PRESET_TEXT_ENCODERS[seleccion]446 else:447 te_url.value = ""448 449 preset_te_dropdown.observe(actualizar_url_te, names='value')450 451 te_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')452 te_btn = widgets.Button(description='Descargar Text Encoders', button_style='info', icon='download')453 te_btn.on_click(lambda b: procesar_descarga(b, te_url, te_method, TEXT_ENCODER_DIR))454 455 tab_te = widgets.VBox([456 widgets.HTML("<h4>Descargar Text Encoders</h4><p>Elige un modelo Qwen o pega enlaces separados por comas (,)</p>"),457 preset_te_dropdown,458 widgets.HBox([te_url, te_method]),459 te_btn460 ])461 462 # --- Pestaña 8: Unet ---463 opciones_presets_unet = ['-- Personalizado / Manual --'] + list(PRESET_UNET.keys())464 preset_unet_dropdown = widgets.Dropdown(options=opciones_presets_unet, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))465 unet_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))466 467 def actualizar_url_unet(change):468 seleccion = change['new']469 if seleccion in PRESET_UNET:470 unet_url.value = PRESET_UNET[seleccion]471 else:472 unet_url.value = ""473 474 preset_unet_dropdown.observe(actualizar_url_unet, names='value')475 476 unet_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')477 unet_btn = widgets.Button(description='Descargar Unet', button_style='primary', icon='download')478 unet_btn.on_click(lambda b: procesar_descarga(b, unet_url, unet_method, UNET_DIR))479 480 tab_unet = widgets.VBox([481 widgets.HTML("<h4>Descargar Modelos Unet</h4><p>Pega múltiples URLs separadas por comas (,)</p>"),482 preset_unet_dropdown,483 widgets.HBox([unet_url, unet_method]),484 unet_btn485 ])486 487 # --- Pestaña 9: Clip ---488 opciones_presets_clip = ['-- Personalizado / Manual --'] + list(PRESET_CLIP.keys())489 preset_clip_dropdown = widgets.Dropdown(options=opciones_presets_clip, value='-- Personalizado / Manual --', description='Favoritos:', layout=widgets.Layout(width='80%'))490 clip_url = widgets.Text(placeholder='https://url1, https://url2, ...', description='URL:', layout=widgets.Layout(width='80%'))491 492 def actualizar_url_clip(change):493 seleccion = change['new']494 if seleccion in PRESET_CLIP:495 clip_url.value = PRESET_CLIP[seleccion]496 else:497 clip_url.value = ""498 499 preset_clip_dropdown.observe(actualizar_url_clip, names='value')500 501 clip_method = widgets.Dropdown(options=['aria2', 'gdown'], value='aria2', description='Método:')502 clip_btn = widgets.Button(description='Descargar Clip', button_style='info', icon='download')503 clip_btn.on_click(lambda b: procesar_descarga(b, clip_url, clip_method, CLIP_DIR))504 505 tab_clip = widgets.VBox([506 widgets.HTML("<h4>Descargar Modelos Clip</h4><p>Pega múltiples URLs separadas por comas (,)</p>"),507 preset_clip_dropdown,508 widgets.HBox([clip_url, clip_method]),509 clip_btn510 ])511 512 # --- Pestaña 10: Extensiones ComfyUI ---513 comfy_ext_url = widgets.Text(placeholder='https://github.com/autor/repositorio.git, ...', description='URLs (Git):', layout=widgets.Layout(width='80%'))514 comfy_ext_btn = widgets.Button(description='Clonar e Instalar', button_style='danger', icon='code')515 comfy_ext_btn.on_click(lambda b: procesar_extensiones(b, comfy_ext_url))516 517 tab_comfy_ext = widgets.VBox([518 widgets.HTML("<h4>Clonar Extensiones para ComfyUI</h4><p>Pega enlaces de repositorios de GitHub separados por comas (,). Se clonarán e instalarán sus requirements usando uv.</p>"),519 comfy_ext_url,520 comfy_ext_btn521 ])522 523 # --- Ensamblar Interfaz ---524 tabs = widgets.Tab(children=[tab_base, tab_lora, tab_vae, tab_upscaler, tab_controlnet, tab_diffusion, tab_te, tab_unet, tab_clip, tab_comfy_ext])525 tabs.set_title(0, '📦 Modelos Base')526 tabs.set_title(1, '✨ LoRAs')527 tabs.set_title(2, '🎨 VAEs')528 tabs.set_title(3, '🔍 Upscalers')529 tabs.set_title(4, '🕹️ ControlNet')530 tabs.set_title(5, '🌌 Diffusion Models')531 tabs.set_title(6, '📝 Text Encoders')532 tabs.set_title(7, '🧠 Unet')533 tabs.set_title(8, '🔗 Clip')534 tabs.set_title(9, '🧩 Extensiones Comfy')535 536 if token_guardado:537 mensaje_token = "<i>(✅ Token cargado automáticamente desde la memoria)</i>"538 else:539 mensaje_token = "<i>(⚠️ No se encontró token guardado, puedes ingresarlo manualmente)</i>"540 541 ui = widgets.VBox([542 widgets.HTML("<h2>Gestor Total de Descargas para SwarmUI (Lightning.ai)</h2>"),543 widgets.HBox([token_input, widgets.HTML(mensaje_token)]),544 tabs,545 widgets.HTML("<hr>"),546 progress_container,547 out_console548 ])549 550 display(ui)