BATUTO-ART commited on
Commit
7187a33
·
verified ·
1 Parent(s): 02350f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +283 -246
app.py CHANGED
@@ -14,142 +14,124 @@ import threading
14
  DEVICE = "cpu"
15
  torch.set_grad_enabled(False)
16
 
17
- # PARÁMETROS POR DEFECTO AJUSTADOS PARA CPU MÁS RÁPIDO
18
- DEFAULT_STEPS = 15 # Reducido de 20 para más velocidad
19
- DEFAULT_WIDTH = 512 # Reducido de 576 para menos carga
20
- DEFAULT_HEIGHT = 768 # Reducido de 1024 para menos carga (mantiene relación aproximada)
21
-
22
- def load_flux(model_id):
23
- pipe = DiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float32)
24
- pipe.to(DEVICE)
25
- pipe.enable_attention_slicing()
26
- return pipe
27
 
28
  # Cache de modelos
29
  MODEL_CACHE = {}
30
 
 
31
  # ==============================
32
- # GENERADOR FLUX
33
  # ==============================
34
- def generate_flux(model_name, prompt, steps, guidance, width, height, seed):
35
- if model_name not in MODEL_CACHE:
36
- MODEL_CACHE[model_name] = load_flux(model_name)
37
- pipe = MODEL_CACHE[model_name]
38
-
39
- generator = torch.manual_seed(seed) if seed else None
40
-
41
- image = pipe(
42
- prompt=prompt,
43
- num_inference_steps=steps,
44
- guidance_scale=guidance,
45
- width=width,
46
- height=height,
47
- generator=generator
48
- ).images[0]
49
 
50
- out = "/tmp/flux_output.png"
51
- image.save(out)
52
- return out
53
 
54
  # ==============================
55
- # GENERADOR SD1.5
56
  # ==============================
57
- def load_sd15():
58
- pipe = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", torch_dtype=torch.float32)
59
- pipe.to(DEVICE)
60
- pipe.enable_attention_slicing()
61
- return pipe
62
-
63
- # SD15 load único
64
- def generate_sd(prompt, steps, guidance, width, height, seed):
65
- if "sd15" not in MODEL_CACHE:
66
- MODEL_CACHE["sd15"] = load_sd15()
67
- pipe = MODEL_CACHE["sd15"]
68
 
69
- generator = torch.manual_seed(seed) if seed else None
 
 
70
 
71
  image = pipe(
72
  prompt=prompt,
73
- num_inference_steps=steps,
74
- guidance_scale=guidance,
75
- width=width,
76
- height=height,
77
- generator=generator
78
  ).images[0]
79
 
80
- out = "/tmp/sd15_output.png"
 
 
81
  image.save(out)
82
  return out
83
 
 
84
  # ==============================
85
- # REVE CREATE MODIFICADO (Generación múltiple con threads)
86
  # ==============================
87
- def generate_single_reve_image(prompt, key, model, index, results_list, lock, progress_callback=None):
88
- """Función auxiliar para generar una sola imagen"""
89
  try:
90
  url = "https://api.reveai.xyz/v1/images"
91
  headers = {"Authorization": f"Bearer {key}"}
92
  data = {"prompt": prompt, "model": model}
93
-
94
  resp = requests.post(url, json=data, headers=headers, timeout=30)
95
-
96
  if resp.status_code != 200:
97
  print(f"Error en imagen {index+1}: {resp.status_code}")
98
  return
99
-
100
  img_url = resp.json().get("image")
101
  if not img_url:
102
  return
103
-
104
  img_data = requests.get(img_url, timeout=30).content
105
  img = Image.open(BytesIO(img_data))
106
-
107
  out = f"/tmp/reve_{index}_{threading.current_thread().ident}.png"
108
  img.save(out)
109
-
110
  with lock:
111
  results_list.append(out)
112
-
113
- # Notificar progreso si hay callback
114
- if progress_callback:
115
- progress_callback(index + 1)
116
-
117
  except Exception as e:
118
  print(f"Error generando imagen {index+1}: {e}")
119
 
120
- def reve_generate_multiple(prompt, key, model, num_images, progress_callback=None):
 
121
  if not key:
122
  return None
123
-
124
- num_images = min(num_images, 8) # Máximo 8 imágenes
125
  results = []
126
  lock = threading.Lock()
127
-
128
- # Usamos ThreadPoolExecutor para generar imágenes en paralelo
129
- with concurrent.futures.ThreadPoolExecutor(max_workers=min(num_images, 4)) as executor:
130
- futures = []
131
- for i in range(num_images):
132
- future = executor.submit(
133
  generate_single_reve_image,
134
- prompt, key, model, i, results, lock, progress_callback
 
 
 
 
 
135
  )
136
- futures.append(future)
137
-
138
- # Esperar a que todas terminen
139
  concurrent.futures.wait(futures)
140
-
141
  return results if results else None
142
 
 
143
  # ==============================
144
  # UI COMPLETA
145
  # ==============================
146
  def build_ui():
147
- with gr.Blocks(title="BATUTO-ART MIX", theme=gr.themes.Soft()) as demo:
148
-
149
- gr.Markdown("""
150
  # 🖼️ **BATUTO-ART MIX**
151
  *Generador de imágenes con FLUX, Stable Diffusion 1.5 y REVE CREATE*
152
- """)
 
153
 
154
  with gr.Tabs():
155
 
@@ -160,63 +142,89 @@ def build_ui():
160
  with gr.Row():
161
  with gr.Column(scale=1):
162
  flux_prompt = gr.Textbox(
163
- label="Prompt",
164
  lines=3,
165
- placeholder="Describe la imagen que quieres generar con FLUX..."
166
  )
167
- model_select = gr.Dropdown([
168
- "black-forest-labs/FLUX.1-schnell",
169
- "black-forest-labs/FLUX.1-dev",
170
- "black-forest-labs/FLUX.2-dev"
171
- ], value="black-forest-labs/FLUX.1-schnell", label="Modelo FLUX")
172
-
 
 
 
 
173
  with gr.Row():
174
- steps = gr.Slider(5, 50, value=DEFAULT_STEPS, step=1, label="Steps")
175
- guidance = gr.Slider(0, 10, value=3, step=0.1, label="Guidance Scale")
176
-
 
 
 
 
 
 
 
 
177
  with gr.Row():
178
- width = gr.Number(value=DEFAULT_WIDTH, label="Width", precision=0)
179
- height = gr.Number(value=DEFAULT_HEIGHT, label="Height", precision=0)
180
-
181
- seed = gr.Number(value=0, label="Seed (0 = aleatorio)", precision=0)
182
-
183
- btn_flux = gr.Button("✨ Generar Imagen", variant="primary", size="lg")
184
-
 
 
 
 
 
 
 
 
185
  with gr.Column(scale=1):
186
- out_flux_img = gr.Image(
187
  label="Resultado FLUX",
188
  height=400,
189
- interactive=False
190
  )
191
- out_flux_file = gr.File(
192
  label="Descargar imagen",
193
- visible=False
194
  )
195
-
196
- # Acción del botón FLUX
197
- def generate_flux_wrapper(model_name, prompt, steps, guidance, width, height, seed):
198
- if not prompt.strip():
199
- return None, "❌ Error: Ingresa un prompt válido"
200
  try:
201
- file_path = generate_flux(
202
- model_name, prompt, int(steps), float(guidance),
203
- int(width), int(height), int(seed)
204
  )
205
- return file_path, gr.update(visible=True)
206
  except Exception as e:
207
- return None, f" Error: {str(e)}"
208
-
 
209
  btn_flux.click(
210
- fn=generate_flux_wrapper,
211
- inputs=[model_select, flux_prompt, steps, guidance, width, height, seed],
212
- outputs=[out_flux_file, out_flux_file]
 
 
 
 
 
 
 
 
213
  )
214
-
215
- # Mostrar imagen automáticamente
216
- out_flux_file.change(
217
- fn=lambda f: Image.open(f) if f else None,
218
- inputs=[out_flux_file],
219
- outputs=[out_flux_img]
220
  )
221
 
222
  # ============================
@@ -226,199 +234,227 @@ def build_ui():
226
  with gr.Row():
227
  with gr.Column(scale=1):
228
  sd_prompt = gr.Textbox(
229
- label="Prompt",
230
  lines=3,
231
- placeholder="Describe la imagen que quieres generar con SD1.5..."
232
  )
233
-
234
  with gr.Row():
235
- sd_steps = gr.Slider(5, 50, value=DEFAULT_STEPS, step=1, label="Steps")
236
- sd_guidance = gr.Slider(0, 10, value=3, step=0.1, label="Guidance Scale")
237
-
 
 
 
 
 
 
 
 
238
  with gr.Row():
239
- sd_width = gr.Number(value=DEFAULT_WIDTH, label="Width", precision=0)
240
- sd_height = gr.Number(value=DEFAULT_HEIGHT, label="Height", precision=0)
241
-
242
- sd_seed = gr.Number(value=0, label="Seed (0 = aleatorio)", precision=0)
243
-
244
- btn_sd = gr.Button("✨ Generar Imagen", variant="primary", size="lg")
245
-
 
 
 
 
 
 
 
 
246
  with gr.Column(scale=1):
247
- out_sd_img = gr.Image(
248
  label="Resultado SD1.5",
249
  height=400,
250
- interactive=False
251
  )
252
- out_sd_file = gr.File(
253
  label="Descargar imagen",
254
- visible=False
255
  )
256
-
257
- # Acción del botón SD1.5
258
- def generate_sd_wrapper(prompt, steps, guidance, width, height, seed):
259
- if not prompt.strip():
260
- return None, "❌ Error: Ingresa un prompt válido"
261
  try:
262
- file_path = generate_sd(
263
- prompt, int(steps), float(guidance),
264
- int(width), int(height), int(seed)
 
 
 
 
 
265
  )
266
- return file_path, gr.update(visible=True)
267
  except Exception as e:
268
- return None, f" Error: {str(e)}"
269
-
 
270
  btn_sd.click(
271
- fn=generate_sd_wrapper,
272
- inputs=[sd_prompt, sd_steps, sd_guidance, sd_width, sd_height, sd_seed],
273
- outputs=[out_sd_file, out_sd_file]
 
 
 
 
 
 
 
274
  )
275
-
276
- out_sd_file.change(
277
- fn=lambda f: Image.open(f) if f else None,
278
- inputs=[out_sd_file],
279
- outputs=[out_sd_img]
280
  )
281
 
282
  # ============================
283
- # TAB: REVE CREATE MODIFICADA
284
  # ============================
285
  with gr.Tab("REVE CREATE"):
286
  with gr.Row():
287
  with gr.Column(scale=1):
288
  reve_api = gr.Textbox(
289
- label="API Key REVE",
290
  type="password",
291
  placeholder="Ingresa tu API key de REVE",
292
- info="Necesitas una clave API válida de REVE"
293
  )
294
  reve_prompt = gr.Textbox(
295
- label="Prompt",
296
  lines=3,
297
- placeholder="Describe la imagen que quieres generar..."
298
  )
299
- reve_model = gr.Dropdown([
300
- "reve-1",
301
- "reve-2",
302
- "reve-fast"
303
- ], value="reve-fast", label="Modelo REVE")
304
-
305
- # Slider para cantidad de imágenes
306
- num_images_slider = gr.Slider(
307
- minimum=1,
308
- maximum=8,
309
- value=4,
310
  step=1,
311
- label="Cantidad de imágenes a generar"
312
  )
313
-
314
- with gr.Row():
315
- btn_reve = gr.Button("🚀 Generar Imágenes", variant="primary", size="lg")
316
- btn_clear = gr.Button("🗑️ Limpiar", variant="secondary")
317
-
318
- # Indicador de progreso
319
  progress_info = gr.Markdown("Esperando generación...")
320
-
321
  with gr.Column(scale=2):
322
- # Galería para mostrar múltiples imágenes
323
  gallery = gr.Gallery(
324
  label="Imágenes generadas",
325
  show_label=True,
326
  columns=4,
327
  rows=2,
328
  height="auto",
329
- object_fit="contain"
330
  )
331
-
332
- # Información de resultados
333
  result_info = gr.Markdown("")
334
-
335
- # Botón de descarga
336
  with gr.Row():
337
  btn_download_all = gr.Button(
338
- "📥 Descargar todas las imágenes",
339
  size="lg",
340
- variant="secondary"
341
  )
342
  download_output = gr.File(
343
- label="Archivos descargables",
344
  file_count="multiple",
345
- visible=False
346
  )
347
-
348
- # Estado para almacenar las rutas de archivos
349
  last_files = gr.State([])
350
-
351
- # Función para generar múltiples imágenes con progreso
352
- def generate_and_update_gallery(prompt, key, model, num_images, progress=gr.Progress()):
353
  if not key:
354
- return [], [], "❌ Error: Ingresa una API key válida", progress_info.update(value="")
355
-
356
- if not prompt.strip():
357
- return [], [], "❌ Error: Ingresa un prompt válido", progress_info.update(value="")
358
-
359
- progress_info.update(value="⏳ Iniciando generación...")
360
-
361
- # Función de callback para progreso
362
- def update_progress(current):
363
- progress_info.update(value=f"⏳ Generando imagen {current}/{num_images}...")
364
-
365
- files = reve_generate_multiple(prompt, key, model, num_images, update_progress)
366
-
 
 
 
 
367
  if files:
368
- # Crear lista de imágenes para la galería
369
- images = [(file,) for file in files]
370
- info_text = f"✅ Generadas {len(files)} de {num_images} imágenes"
371
- progress_info.update(value="✅ Generación completada")
372
- return images, files, info_text
373
  else:
374
- progress_info.update(value="❌ Error en la generación")
375
- return [], [], "❌ Error: No se pudieron generar imágenes. Verifica tu API key y conexión."
376
-
377
- # Acción del botón de generación
 
 
 
378
  btn_reve.click(
379
- fn=generate_and_update_gallery,
380
- inputs=[reve_prompt, reve_api, reve_model, num_images_slider],
381
- outputs=[gallery, last_files, result_info]
382
  )
383
-
384
- # Acción del botón de descarga
385
  def prepare_download(files):
386
  if files:
387
  return gr.update(value=files, visible=True)
388
  return gr.update(value=[], visible=False)
389
-
390
  btn_download_all.click(
391
- fn=prepare_download,
392
- inputs=[last_files],
393
- outputs=[download_output]
394
  )
395
-
396
- # Acción del botón de limpiar
397
  def clear_gallery():
398
- return [], [], "✅ Galería limpiada", gr.update(value="Listo para nueva generación")
399
-
 
 
 
 
 
400
  btn_clear.click(
401
- fn=clear_gallery,
402
- outputs=[gallery, last_files, result_info, progress_info]
403
  ).then(
404
- fn=lambda: gr.update(visible=False),
405
- outputs=[download_output]
406
  )
407
 
408
  return demo
409
 
410
- # ==============================
411
- # EJECUCIÓN PRINCIPAL
412
- # ==============================
413
  if __name__ == "__main__":
414
- # Configuración para HuggingFace Spaces
415
  import argparse
 
416
  parser = argparse.ArgumentParser()
417
  parser.add_argument("--share", action="store_true", help="Crear enlace público")
418
- parser.add_argument("--server-name", type=str, default="0.0.0.0", help="Dirección del servidor")
419
- parser.add_argument("--server-port", type=int, default=7860, help="Puerto del servidor")
 
 
 
 
420
  args = parser.parse_args()
421
-
422
  print("=" * 50)
423
  print("🚀 Iniciando BATUTO-ART MIX")
424
  print("=" * 50)
@@ -426,11 +462,12 @@ if __name__ == "__main__":
426
  print(f"⚡ Steps por defecto: {DEFAULT_STEPS}")
427
  print(f"📐 Resolución por defecto: {DEFAULT_WIDTH}x{DEFAULT_HEIGHT}")
428
  print("=" * 50)
429
-
430
  demo = build_ui()
431
  demo.launch(
432
  share=args.share,
433
  server_name=args.server_name,
434
  server_port=args.server_port,
435
- show_error=True
436
- )
 
 
14
  DEVICE = "cpu"
15
  torch.set_grad_enabled(False)
16
 
17
+ DEFAULT_STEPS = 15
18
+ DEFAULT_WIDTH = 512
19
+ DEFAULT_HEIGHT = 768
 
 
 
 
 
 
 
20
 
21
  # Cache de modelos
22
  MODEL_CACHE = {}
23
 
24
+
25
  # ==============================
26
+ # CARGA GENÉRICA DE MODELOS
27
  # ==============================
28
+ def load_pipeline(model_id):
29
+ if model_id not in MODEL_CACHE:
30
+ pipe = DiffusionPipeline.from_pretrained(
31
+ model_id, torch_dtype=torch.float32
32
+ )
33
+ pipe.to(DEVICE)
34
+ pipe.enable_attention_slicing()
35
+ MODEL_CACHE[model_id] = pipe
36
+ return MODEL_CACHE[model_id]
 
 
 
 
 
 
37
 
 
 
 
38
 
39
  # ==============================
40
+ # GENERACIÓN DIFFUSERS
41
  # ==============================
42
+ def generate_diffusion(model_id, prompt, steps, guidance, width, height, seed):
43
+ pipe = load_pipeline(model_id)
 
 
 
 
 
 
 
 
 
44
 
45
+ generator = None
46
+ if seed and int(seed) != 0:
47
+ generator = torch.manual_seed(int(seed))
48
 
49
  image = pipe(
50
  prompt=prompt,
51
+ num_inference_steps=int(steps),
52
+ guidance_scale=float(guidance),
53
+ width=int(width),
54
+ height=int(height),
55
+ generator=generator,
56
  ).images[0]
57
 
58
+ # Nombre único por modelo
59
+ safe_name = model_id.split("/")[-1].replace(".", "_")
60
+ out = f"/tmp/{safe_name}_output.png"
61
  image.save(out)
62
  return out
63
 
64
+
65
  # ==============================
66
+ # REVE CREATE (PARALELO)
67
  # ==============================
68
+ def generate_single_reve_image(prompt, key, model, index, results_list, lock):
 
69
  try:
70
  url = "https://api.reveai.xyz/v1/images"
71
  headers = {"Authorization": f"Bearer {key}"}
72
  data = {"prompt": prompt, "model": model}
73
+
74
  resp = requests.post(url, json=data, headers=headers, timeout=30)
 
75
  if resp.status_code != 200:
76
  print(f"Error en imagen {index+1}: {resp.status_code}")
77
  return
78
+
79
  img_url = resp.json().get("image")
80
  if not img_url:
81
  return
82
+
83
  img_data = requests.get(img_url, timeout=30).content
84
  img = Image.open(BytesIO(img_data))
85
+
86
  out = f"/tmp/reve_{index}_{threading.current_thread().ident}.png"
87
  img.save(out)
88
+
89
  with lock:
90
  results_list.append(out)
91
+
 
 
 
 
92
  except Exception as e:
93
  print(f"Error generando imagen {index+1}: {e}")
94
 
95
+
96
+ def reve_generate_multiple(prompt, key, model, num_images):
97
  if not key:
98
  return None
99
+
100
+ num_images = min(int(num_images), 8)
101
  results = []
102
  lock = threading.Lock()
103
+
104
+ with concurrent.futures.ThreadPoolExecutor(
105
+ max_workers=min(num_images, 4)
106
+ ) as executor:
107
+ futures = [
108
+ executor.submit(
109
  generate_single_reve_image,
110
+ prompt,
111
+ key,
112
+ model,
113
+ i,
114
+ results,
115
+ lock,
116
  )
117
+ for i in range(num_images)
118
+ ]
 
119
  concurrent.futures.wait(futures)
120
+
121
  return results if results else None
122
 
123
+
124
  # ==============================
125
  # UI COMPLETA
126
  # ==============================
127
  def build_ui():
128
+ with gr.Blocks(title="BATUTO-ART MIX") as demo:
129
+ gr.Markdown(
130
+ """
131
  # 🖼️ **BATUTO-ART MIX**
132
  *Generador de imágenes con FLUX, Stable Diffusion 1.5 y REVE CREATE*
133
+ """
134
+ )
135
 
136
  with gr.Tabs():
137
 
 
142
  with gr.Row():
143
  with gr.Column(scale=1):
144
  flux_prompt = gr.Textbox(
145
+ label="Prompt",
146
  lines=3,
147
+ placeholder="Describe la imagen que quieres generar con FLUX...",
148
  )
149
+ flux_model = gr.Dropdown(
150
+ [
151
+ "black-forest-labs/FLUX.1-schnell",
152
+ "black-forest-labs/FLUX.1-dev",
153
+ "black-forest-labs/FLUX.2-dev",
154
+ ],
155
+ value="black-forest-labs/FLUX.1-schnell",
156
+ label="Modelo FLUX",
157
+ )
158
+
159
  with gr.Row():
160
+ flux_steps = gr.Slider(
161
+ 5, 50, value=DEFAULT_STEPS, step=1, label="Steps"
162
+ )
163
+ flux_guidance = gr.Slider(
164
+ 0,
165
+ 10,
166
+ value=3,
167
+ step=0.1,
168
+ label="Guidance Scale",
169
+ )
170
+
171
  with gr.Row():
172
+ flux_width = gr.Number(
173
+ value=DEFAULT_WIDTH, label="Width", precision=0
174
+ )
175
+ flux_height = gr.Number(
176
+ value=DEFAULT_HEIGHT, label="Height", precision=0
177
+ )
178
+
179
+ flux_seed = gr.Number(
180
+ value=0, label="Seed (0 = aleatorio)", precision=0
181
+ )
182
+
183
+ btn_flux = gr.Button(
184
+ "✨ Generar Imagen", variant="primary", size="lg"
185
+ )
186
+
187
  with gr.Column(scale=1):
188
+ flux_img = gr.Image(
189
  label="Resultado FLUX",
190
  height=400,
191
+ interactive=False,
192
  )
193
+ flux_file = gr.File(
194
  label="Descargar imagen",
195
+ visible=False,
196
  )
197
+
198
+ def flux_wrapper(model_id, prompt, steps, guidance, width, height, seed):
199
+ if not prompt or not prompt.strip():
200
+ return None, gr.update(visible=False)
 
201
  try:
202
+ path = generate_diffusion(
203
+ model_id, prompt, steps, guidance, width, height, seed
 
204
  )
205
+ return path, gr.update(visible=True)
206
  except Exception as e:
207
+ print(f"Error FLUX: {e}")
208
+ return None, gr.update(visible=False)
209
+
210
  btn_flux.click(
211
+ flux_wrapper,
212
+ inputs=[
213
+ flux_model,
214
+ flux_prompt,
215
+ flux_steps,
216
+ flux_guidance,
217
+ flux_width,
218
+ flux_height,
219
+ flux_seed,
220
+ ],
221
+ outputs=[flux_file, flux_file],
222
  )
223
+
224
+ flux_file.change(
225
+ lambda f: Image.open(f) if f else None,
226
+ inputs=flux_file,
227
+ outputs=flux_img,
 
228
  )
229
 
230
  # ============================
 
234
  with gr.Row():
235
  with gr.Column(scale=1):
236
  sd_prompt = gr.Textbox(
237
+ label="Prompt",
238
  lines=3,
239
+ placeholder="Describe la imagen que quieres generar con SD1.5...",
240
  )
241
+
242
  with gr.Row():
243
+ sd_steps = gr.Slider(
244
+ 5, 50, value=DEFAULT_STEPS, step=1, label="Steps"
245
+ )
246
+ sd_guidance = gr.Slider(
247
+ 0,
248
+ 10,
249
+ value=3,
250
+ step=0.1,
251
+ label="Guidance Scale",
252
+ )
253
+
254
  with gr.Row():
255
+ sd_width = gr.Number(
256
+ value=DEFAULT_WIDTH, label="Width", precision=0
257
+ )
258
+ sd_height = gr.Number(
259
+ value=DEFAULT_HEIGHT, label="Height", precision=0
260
+ )
261
+
262
+ sd_seed = gr.Number(
263
+ value=0, label="Seed (0 = aleatorio)", precision=0
264
+ )
265
+
266
+ btn_sd = gr.Button(
267
+ "✨ Generar Imagen", variant="primary", size="lg"
268
+ )
269
+
270
  with gr.Column(scale=1):
271
+ sd_img = gr.Image(
272
  label="Resultado SD1.5",
273
  height=400,
274
+ interactive=False,
275
  )
276
+ sd_file = gr.File(
277
  label="Descargar imagen",
278
+ visible=False,
279
  )
280
+
281
+ def sd_wrapper(prompt, steps, guidance, width, height, seed):
282
+ if not prompt or not prompt.strip():
283
+ return None, gr.update(visible=False)
 
284
  try:
285
+ path = generate_diffusion(
286
+ "runwayml/stable-diffusion-v1-5",
287
+ prompt,
288
+ steps,
289
+ guidance,
290
+ width,
291
+ height,
292
+ seed,
293
  )
294
+ return path, gr.update(visible=True)
295
  except Exception as e:
296
+ print(f"Error SD1.5: {e}")
297
+ return None, gr.update(visible=False)
298
+
299
  btn_sd.click(
300
+ sd_wrapper,
301
+ inputs=[
302
+ sd_prompt,
303
+ sd_steps,
304
+ sd_guidance,
305
+ sd_width,
306
+ sd_height,
307
+ sd_seed,
308
+ ],
309
+ outputs=[sd_file, sd_file],
310
  )
311
+
312
+ sd_file.change(
313
+ lambda f: Image.open(f) if f else None,
314
+ inputs=sd_file,
315
+ outputs=sd_img,
316
  )
317
 
318
  # ============================
319
+ # TAB: REVE CREATE
320
  # ============================
321
  with gr.Tab("REVE CREATE"):
322
  with gr.Row():
323
  with gr.Column(scale=1):
324
  reve_api = gr.Textbox(
325
+ label="API Key REVE",
326
  type="password",
327
  placeholder="Ingresa tu API key de REVE",
 
328
  )
329
  reve_prompt = gr.Textbox(
330
+ label="Prompt",
331
  lines=3,
332
+ placeholder="Describe la imagen que quieres generar...",
333
  )
334
+ reve_model = gr.Dropdown(
335
+ ["reve-1", "reve-2", "reve-fast"],
336
+ value="reve-fast",
337
+ label="Modelo REVE",
338
+ )
339
+ num_images = gr.Slider(
340
+ 1,
341
+ 8,
342
+ value=4,
 
 
343
  step=1,
344
+ label="Cantidad de imágenes a generar",
345
  )
346
+
347
+ btn_reve = gr.Button(
348
+ "🚀 Generar Imágenes", variant="primary", size="lg"
349
+ )
350
+ btn_clear = gr.Button("🗑️ Limpiar", variant="secondary")
351
+
352
  progress_info = gr.Markdown("Esperando generación...")
353
+
354
  with gr.Column(scale=2):
 
355
  gallery = gr.Gallery(
356
  label="Imágenes generadas",
357
  show_label=True,
358
  columns=4,
359
  rows=2,
360
  height="auto",
361
+ object_fit="contain",
362
  )
 
 
363
  result_info = gr.Markdown("")
 
 
364
  with gr.Row():
365
  btn_download_all = gr.Button(
366
+ "📥 Descargar todas las imágenes",
367
  size="lg",
368
+ variant="secondary",
369
  )
370
  download_output = gr.File(
371
+ label="Archivos descargables",
372
  file_count="multiple",
373
+ visible=False,
374
  )
375
+
 
376
  last_files = gr.State([])
377
+
378
+ def generate_and_update_gallery(prompt, key, model, n, progress=gr.Progress()):
 
379
  if not key:
380
+ return (
381
+ [],
382
+ [],
383
+ "❌ Error: Ingresa una API key válida",
384
+ gr.update(value="❌ Falta API key"),
385
+ )
386
+ if not prompt or not prompt.strip():
387
+ return (
388
+ [],
389
+ [],
390
+ "❌ Error: Ingresa un prompt válido",
391
+ gr.update(value="❌ Prompt vacío o inválido"),
392
+ )
393
+
394
+ progress.tqdm(total=1, desc="Generando con REVE")
395
+ files = reve_generate_multiple(prompt, key, model, n)
396
+
397
  if files:
398
+ imgs = [(f,) for f in files]
399
+ info = f"✅ Generadas {len(files)} imágenes"
400
+ return imgs, files, info, gr.update(
401
+ value="✅ Generación completada"
402
+ )
403
  else:
404
+ return (
405
+ [],
406
+ [],
407
+ "❌ Error: No se pudieron generar imágenes. Revisa API key y conexión.",
408
+ gr.update(value="❌ Error en la generación"),
409
+ )
410
+
411
  btn_reve.click(
412
+ generate_and_update_gallery,
413
+ inputs=[reve_prompt, reve_api, reve_model, num_images],
414
+ outputs=[gallery, last_files, result_info, progress_info],
415
  )
416
+
 
417
  def prepare_download(files):
418
  if files:
419
  return gr.update(value=files, visible=True)
420
  return gr.update(value=[], visible=False)
421
+
422
  btn_download_all.click(
423
+ prepare_download, inputs=last_files, outputs=download_output
 
 
424
  )
425
+
 
426
  def clear_gallery():
427
+ return (
428
+ [],
429
+ [],
430
+ "✅ Galería limpiada",
431
+ gr.update(value="Listo para nueva generación"),
432
+ )
433
+
434
  btn_clear.click(
435
+ clear_gallery,
436
+ outputs=[gallery, last_files, result_info, progress_info],
437
  ).then(
438
+ lambda: gr.update(visible=False),
439
+ outputs=download_output,
440
  )
441
 
442
  return demo
443
 
444
+
 
 
445
  if __name__ == "__main__":
 
446
  import argparse
447
+
448
  parser = argparse.ArgumentParser()
449
  parser.add_argument("--share", action="store_true", help="Crear enlace público")
450
+ parser.add_argument(
451
+ "--server-name", type=str, default="0.0.0.0", help="Dirección del servidor"
452
+ )
453
+ parser.add_argument(
454
+ "--server-port", type=int, default=7860, help="Puerto del servidor"
455
+ )
456
  args = parser.parse_args()
457
+
458
  print("=" * 50)
459
  print("🚀 Iniciando BATUTO-ART MIX")
460
  print("=" * 50)
 
462
  print(f"⚡ Steps por defecto: {DEFAULT_STEPS}")
463
  print(f"📐 Resolución por defecto: {DEFAULT_WIDTH}x{DEFAULT_HEIGHT}")
464
  print("=" * 50)
465
+
466
  demo = build_ui()
467
  demo.launch(
468
  share=args.share,
469
  server_name=args.server_name,
470
  server_port=args.server_port,
471
+ show_error=True,
472
+ )
473
+