aiqtech commited on
Commit
a781d6b
Β·
verified Β·
1 Parent(s): ccf2d49

Create app-backup1.py

Browse files
Files changed (1) hide show
  1. app-backup1.py +602 -0
app-backup1.py ADDED
@@ -0,0 +1,602 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import replicate
3
+ import os
4
+ from typing import Optional, List
5
+ from huggingface_hub import whoami
6
+ from PIL import Image
7
+ import requests
8
+ from io import BytesIO
9
+ import tempfile
10
+ import base64
11
+
12
+ # --- Replicate API Configuration ---
13
+ REPLICATE_API_TOKEN = os.getenv("REPLICATE_API_TOKEN")
14
+
15
+ if not REPLICATE_API_TOKEN:
16
+ raise ValueError("REPLICATE_API_TOKEN environment variable is not set.")
17
+
18
+ # Initialize Replicate client
19
+ os.environ["REPLICATE_API_TOKEN"] = REPLICATE_API_TOKEN
20
+
21
+ def verify_login_status(token: Optional[gr.OAuthToken]) -> bool:
22
+ """Verifies if the user is logged in to Hugging Face."""
23
+ if not token:
24
+ return False
25
+ try:
26
+ user_info = whoami(token=token.token)
27
+ return True if user_info else False
28
+ except Exception as e:
29
+ print(f"Could not verify user's login status: {e}")
30
+ return False
31
+
32
+ def upload_image_to_hosting(image_path: str) -> str:
33
+ """
34
+ Upload image to hosting service and return URL.
35
+ Using multiple fallback methods for reliability.
36
+ """
37
+ # Open the image
38
+ img = Image.open(image_path)
39
+
40
+ # Method 1: Try imgbb.com (most reliable)
41
+ try:
42
+ buffered = BytesIO()
43
+ img.save(buffered, format="PNG")
44
+ buffered.seek(0)
45
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
46
+
47
+ response = requests.post(
48
+ "https://api.imgbb.com/1/upload",
49
+ data={
50
+ 'key': '6d207e02198a847aa98d0a2a901485a5', # Free API key
51
+ 'image': img_base64,
52
+ }
53
+ )
54
+
55
+ if response.status_code == 200:
56
+ data = response.json()
57
+ if data.get('success'):
58
+ return data['data']['url']
59
+ except Exception as e:
60
+ print(f"imgbb upload failed: {e}")
61
+
62
+ # Method 2: Try 0x0.st (simple and reliable)
63
+ try:
64
+ buffered = BytesIO()
65
+ img.save(buffered, format="PNG")
66
+ buffered.seek(0)
67
+
68
+ files = {'file': ('image.png', buffered, 'image/png')}
69
+ response = requests.post("https://0x0.st", files=files)
70
+
71
+ if response.status_code == 200:
72
+ url = response.text.strip()
73
+ if url.startswith('http'):
74
+ return url
75
+ except Exception as e:
76
+ print(f"0x0.st upload failed: {e}")
77
+
78
+ # Method 3: Fallback to data URI (last resort)
79
+ buffered = BytesIO()
80
+ img.save(buffered, format="PNG")
81
+ buffered.seek(0)
82
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
83
+ return f"data:image/png;base64,{img_base64}"
84
+
85
+ def image_to_data_uri(image_path: str) -> str:
86
+ """Convert local image file to data URI format (kept for backwards compatibility)."""
87
+ with open(image_path, "rb") as img_file:
88
+ img_data = img_file.read()
89
+ img_base64 = base64.b64encode(img_data).decode('utf-8')
90
+
91
+ # Get the image format
92
+ img = Image.open(image_path)
93
+ img_format = img.format.lower() if img.format else 'png'
94
+
95
+ # Create data URI
96
+ data_uri = f"data:image/{img_format};base64,{img_base64}"
97
+ return data_uri
98
+
99
+ def process_output(output, progress=gr.Progress()) -> str:
100
+ """Process the output from Replicate API and return a local file path."""
101
+ try:
102
+ # Check if output has a url attribute (FileObject)
103
+ if hasattr(output, 'url'):
104
+ # If url is a method, call it; if it's a property, just access it
105
+ image_url = output.url() if callable(output.url) else output.url
106
+ # If output is already a string URL
107
+ elif isinstance(output, str):
108
+ image_url = output
109
+ # If output is a list of URLs
110
+ elif isinstance(output, list) and len(output) > 0:
111
+ # Check first item in list
112
+ first_item = output[0]
113
+ if hasattr(first_item, 'url'):
114
+ image_url = first_item.url() if callable(first_item.url) else first_item.url
115
+ else:
116
+ image_url = first_item
117
+ else:
118
+ raise ValueError(f"Unexpected output format from Replicate: {type(output)}")
119
+
120
+ # Download the image from URL
121
+ response = requests.get(image_url)
122
+ response.raise_for_status()
123
+
124
+ # Save to temporary file
125
+ img = Image.open(BytesIO(response.content))
126
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmpfile:
127
+ img.save(tmpfile.name)
128
+ progress(1.0, desc="βœ… Complete!")
129
+ return tmpfile.name
130
+
131
+ except Exception as e:
132
+ print(f"Error processing output: {e}")
133
+ raise ValueError(f"Failed to process output: {str(e)}")
134
+
135
+ def run_single_image_logic(prompt: str, image_path: Optional[str] = None, progress=gr.Progress()) -> str:
136
+ """Handles text-to-image or single image-to-image using Replicate's Nano Banana."""
137
+ try:
138
+ progress(0.2, desc="🎨 Preparing...")
139
+
140
+ # Prepare input for Replicate API
141
+ input_data = {
142
+ "prompt": prompt
143
+ }
144
+
145
+ # If there's an input image, upload it to get a proper URL
146
+ if image_path:
147
+ progress(0.3, desc="πŸ“€ Uploading image...")
148
+ # Upload to hosting service for proper URL
149
+ image_url = upload_image_to_hosting(image_path)
150
+
151
+ if image_url.startswith('http'):
152
+ print(f"Image uploaded successfully: {image_url[:50]}...")
153
+ else:
154
+ print("Using data URI fallback")
155
+
156
+ input_data["image_input"] = [image_url]
157
+
158
+ progress(0.5, desc="✨ Generating...")
159
+
160
+ # Run the model on Replicate
161
+ output = replicate.run(
162
+ "google/nano-banana",
163
+ input=input_data
164
+ )
165
+
166
+ progress(0.8, desc="πŸ–ΌοΈ Finalizing...")
167
+
168
+ # Handle the output - output is already a URL string or FileObject
169
+ if output:
170
+ return process_output(output, progress)
171
+ else:
172
+ raise ValueError("No output received from Replicate API")
173
+
174
+ except Exception as e:
175
+ print(f"Error details: {e}")
176
+ print(f"Error type: {type(e)}")
177
+ if 'output' in locals():
178
+ print(f"Output value: {output}")
179
+ print(f"Output type: {type(output)}")
180
+ raise gr.Error(f"Image generation failed: {str(e)[:200]}")
181
+
182
+ def run_multi_image_logic(prompt: str, images: List[str], progress=gr.Progress()) -> str:
183
+ """
184
+ Handles multi-image editing by sending a list of images and a prompt.
185
+ """
186
+ if not images:
187
+ raise gr.Error("Please upload at least one image in the 'Multiple Images' tab.")
188
+
189
+ try:
190
+ progress(0.2, desc="🎨 Preparing images...")
191
+
192
+ # Upload all images to get proper URLs
193
+ image_urls = []
194
+ for idx, image_path in enumerate(images):
195
+ if isinstance(image_path, (list, tuple)):
196
+ image_path = image_path[0]
197
+
198
+ progress(0.2 + (0.2 * idx / len(images)), desc=f"πŸ“€ Uploading image {idx+1}/{len(images)}...")
199
+ image_url = upload_image_to_hosting(image_path)
200
+ image_urls.append(image_url)
201
+
202
+ # Prepare input for Replicate API with multiple images
203
+ input_data = {
204
+ "prompt": prompt,
205
+ "image_input": image_urls
206
+ }
207
+
208
+ progress(0.5, desc="✨ Generating...")
209
+
210
+ # Run the model on Replicate
211
+ output = replicate.run(
212
+ "google/nano-banana",
213
+ input=input_data
214
+ )
215
+
216
+ progress(0.8, desc="πŸ–ΌοΈ Finalizing...")
217
+
218
+ # Handle the output using the process_output function
219
+ if output:
220
+ return process_output(output, progress)
221
+ else:
222
+ raise ValueError("No output received from Replicate API")
223
+
224
+ except Exception as e:
225
+ print(f"Multi-image error details: {e}")
226
+ print(f"Output value: {output if 'output' in locals() else 'Not set'}")
227
+ print(f"Output type: {type(output) if 'output' in locals() else 'Not set'}")
228
+ raise gr.Error(f"Image generation failed: {e}")
229
+
230
+ # --- Gradio App UI ---
231
+ css = '''
232
+ /* Header Styling */
233
+ .main-header {
234
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
235
+ padding: 2rem;
236
+ border-radius: 1rem;
237
+ margin-bottom: 2rem;
238
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
239
+ }
240
+ .header-title {
241
+ font-size: 2.5rem !important;
242
+ font-weight: bold;
243
+ color: white;
244
+ text-align: center;
245
+ margin: 0 !important;
246
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
247
+ }
248
+ .header-subtitle {
249
+ color: rgba(255,255,255,0.9);
250
+ text-align: center;
251
+ margin-top: 0.5rem !important;
252
+ font-size: 1.1rem;
253
+ }
254
+ /* Card Styling */
255
+ .card {
256
+ background: white;
257
+ border-radius: 1rem;
258
+ padding: 1.5rem;
259
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
260
+ border: 1px solid rgba(0,0,0,0.05);
261
+ }
262
+ .dark .card {
263
+ background: #1f2937;
264
+ border: 1px solid #374151;
265
+ }
266
+ /* Tab Styling */
267
+ .tabs {
268
+ border-radius: 0.5rem;
269
+ overflow: hidden;
270
+ margin-bottom: 1rem;
271
+ }
272
+ .tabitem {
273
+ padding: 1rem !important;
274
+ }
275
+ button.selected {
276
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
277
+ color: white !important;
278
+ }
279
+ /* Button Styling */
280
+ .generate-btn {
281
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
282
+ border: none !important;
283
+ color: white !important;
284
+ font-size: 1.1rem !important;
285
+ font-weight: 600 !important;
286
+ padding: 0.8rem 2rem !important;
287
+ border-radius: 0.5rem !important;
288
+ cursor: pointer !important;
289
+ transition: all 0.3s ease !important;
290
+ width: 100% !important;
291
+ margin-top: 1rem !important;
292
+ }
293
+ .generate-btn:hover {
294
+ transform: translateY(-2px) !important;
295
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4) !important;
296
+ }
297
+ .use-btn {
298
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
299
+ border: none !important;
300
+ color: white !important;
301
+ font-weight: 600 !important;
302
+ padding: 0.6rem 1.5rem !important;
303
+ border-radius: 0.5rem !important;
304
+ cursor: pointer !important;
305
+ transition: all 0.3s ease !important;
306
+ width: 100% !important;
307
+ }
308
+ .use-btn:hover {
309
+ transform: translateY(-1px) !important;
310
+ box-shadow: 0 5px 15px rgba(16, 185, 129, 0.4) !important;
311
+ }
312
+ /* Input Styling */
313
+ .prompt-input textarea {
314
+ border-radius: 0.5rem !important;
315
+ border: 2px solid #e5e7eb !important;
316
+ padding: 0.8rem !important;
317
+ font-size: 1rem !important;
318
+ transition: border-color 0.3s ease !important;
319
+ }
320
+ .prompt-input textarea:focus {
321
+ border-color: #667eea !important;
322
+ outline: none !important;
323
+ }
324
+ .dark .prompt-input textarea {
325
+ border-color: #374151 !important;
326
+ background: #1f2937 !important;
327
+ }
328
+ /* Image Output Styling */
329
+ #output {
330
+ border-radius: 0.5rem !important;
331
+ overflow: hidden !important;
332
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1) !important;
333
+ }
334
+ /* Progress Bar Styling */
335
+ .progress-bar {
336
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
337
+ }
338
+ /* Examples Styling */
339
+ .examples {
340
+ background: #f9fafb;
341
+ border-radius: 0.5rem;
342
+ padding: 1rem;
343
+ margin-top: 1rem;
344
+ }
345
+ .dark .examples {
346
+ background: #1f2937;
347
+ }
348
+ /* Login Message Styling */
349
+ .login-message {
350
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
351
+ border-radius: 1rem;
352
+ padding: 2rem;
353
+ text-align: center;
354
+ border: 2px solid #f59e0b;
355
+ }
356
+ .dark .login-message {
357
+ background: linear-gradient(135deg, #7c2d12 0%, #92400e 100%);
358
+ border-color: #f59e0b;
359
+ }
360
+ /* Emoji Animations */
361
+ @keyframes bounce {
362
+ 0%, 100% { transform: translateY(0); }
363
+ 50% { transform: translateY(-10px); }
364
+ }
365
+ .emoji-icon {
366
+ display: inline-block;
367
+ animation: bounce 2s infinite;
368
+ }
369
+ /* Responsive Design */
370
+ @media (max-width: 768px) {
371
+ .header-title {
372
+ font-size: 2rem !important;
373
+ }
374
+
375
+ .main-container {
376
+ padding: 1rem !important;
377
+ }
378
+ }
379
+ '''
380
+
381
+ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
382
+ # Header
383
+ gr.HTML('''
384
+ <div class="main-header">
385
+ <h1 class="header-title">
386
+ 🍌 Real Nano Banana
387
+ </h1>
388
+ <p class="header-subtitle">
389
+ AI Image Generator powered by Google Nano Banana
390
+ </p>
391
+ <div style="display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 20px;">
392
+ <a href="https://huggingface.co/spaces/openfree/Nano-Banana-Upscale" target="_blank">
393
+ <img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=UPSCALE&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Nano Banana Upscale">
394
+ </a>
395
+ <a href="https://huggingface.co/spaces/openfree/Free-Nano-Banana" target="_blank">
396
+ <img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=FREE&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Free Nano Banana">
397
+ </a>
398
+ <a href="https://huggingface.co/spaces/aiqtech/Nano-Banana-API" target="_blank">
399
+ <img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=API&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Nano Banana API">
400
+ </a>
401
+ <a href="https://discord.gg/openfreeai" target="_blank">
402
+ <img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="Discord Openfree AI">
403
+ </a>
404
+ </div>
405
+ </div>
406
+ ''')
407
+
408
+ # Login Notice
409
+ gr.HTML('''
410
+ <div style="background: linear-gradient(135deg, #e0f2fe 0%, #bae6fd 100%);
411
+ border-radius: 0.5rem; padding: 1rem; margin-bottom: 1.5rem;
412
+ border-left: 4px solid #0284c7;">
413
+ <p style="margin: 0; color: #075985; font-weight: 600;">
414
+ πŸ” Please sign in with your Hugging Face account to use this service.
415
+ </p>
416
+ </div>
417
+ ''')
418
+
419
+ login_message = gr.Markdown(visible=False)
420
+ main_interface = gr.Column(visible=False, elem_classes="main-container")
421
+
422
+ with main_interface:
423
+ with gr.Row():
424
+ with gr.Column(scale=1):
425
+ gr.HTML('<div class="card">')
426
+
427
+ # Mode Selection
428
+ gr.HTML('<h3 style="margin-top: 0;">πŸ“Έ Select Mode</h3>')
429
+ active_tab_state = gr.State(value="single")
430
+
431
+ with gr.Tabs(elem_classes="tabs") as tabs:
432
+ with gr.TabItem("πŸ–ΌοΈ Single Image", id="single") as single_tab:
433
+ image_input = gr.Image(
434
+ type="filepath",
435
+ label="Input Image (Optional)",
436
+ elem_classes="image-input"
437
+ )
438
+ gr.HTML('''
439
+ <p style="text-align: center; color: #6b7280; font-size: 0.9rem; margin-top: 0.5rem;">
440
+ πŸ’‘ Leave empty for text-to-image generation
441
+ </p>
442
+ ''')
443
+
444
+ with gr.TabItem("🎨 Multiple Images", id="multiple") as multi_tab:
445
+ gallery_input = gr.Gallery(
446
+ label="Input Images (Max 2 images)",
447
+ file_types=["image"],
448
+ elem_classes="gallery-input"
449
+ )
450
+ gr.HTML('''
451
+ <p style="text-align: center; color: #6b7280; font-size: 0.9rem; margin-top: 0.5rem;">
452
+ πŸ’‘ Upload up to 2 images for combination/editing
453
+ </p>
454
+ ''')
455
+
456
+ # Prompt Input
457
+ gr.HTML('<h3>✍️ Prompt</h3>')
458
+ prompt_input = gr.Textbox(
459
+ label="",
460
+ info="Describe what you want the AI to generate",
461
+ placeholder="e.g., A delicious pizza, a cat in space, futuristic cityscape...",
462
+ lines=3,
463
+ elem_classes="prompt-input"
464
+ )
465
+
466
+ # Generate Button
467
+ generate_button = gr.Button(
468
+ "πŸš€ Generate",
469
+ variant="primary",
470
+ elem_classes="generate-btn"
471
+ )
472
+
473
+ # Examples
474
+ with gr.Accordion("πŸ’‘ Example Prompts", open=False):
475
+ gr.Examples(
476
+ examples=[
477
+ ["A delicious looking pizza with melting cheese"],
478
+ ["A cat in a spacesuit walking on the moon surface"],
479
+ ["Cyberpunk city at night with neon lights"],
480
+ ["Japanese garden with cherry blossoms in spring"],
481
+ ["Fantasy wizard tower in a magical world"],
482
+ ["Make the scene more dramatic and cinematic"],
483
+ ["Transform this into a watercolor painting style"],
484
+ ],
485
+ inputs=prompt_input
486
+ )
487
+
488
+ gr.HTML('</div>')
489
+
490
+ with gr.Column(scale=1):
491
+ gr.HTML('<div class="card">')
492
+ gr.HTML('<h3 style="margin-top: 0;">🎨 Generated Result</h3>')
493
+
494
+ output_image = gr.Image(
495
+ label="",
496
+ interactive=False,
497
+ elem_id="output"
498
+ )
499
+
500
+ use_image_button = gr.Button(
501
+ "♻️ Use this image for next edit",
502
+ elem_classes="use-btn",
503
+ visible=False
504
+ )
505
+
506
+ # Tips
507
+ gr.HTML('''
508
+ <div style="background: #f0f9ff; border-radius: 0.5rem; padding: 1rem; margin-top: 1rem;">
509
+ <h4 style="margin-top: 0; color: #0369a1;">πŸ’‘ Tips</h4>
510
+ <ul style="margin: 0; padding-left: 1.5rem; color: #0c4a6e;">
511
+ <li>Use specific and detailed prompts for better results</li>
512
+ <li>You can reuse generated images for iterative improvements</li>
513
+ <li>Multiple image mode supports up to 2 images for combination</li>
514
+ <li>English prompts tend to produce better results</li>
515
+ </ul>
516
+ </div>
517
+ ''')
518
+
519
+ gr.HTML('</div>')
520
+
521
+ # Footer
522
+ gr.HTML('''
523
+ <div style="text-align: center; margin-top: 2rem; padding: 1rem;
524
+ border-top: 1px solid #e5e7eb;">
525
+ <p style="color: #6b7280;">
526
+ Made with πŸ’œ using Replicate API | Powered by Google Nano Banana
527
+ </p>
528
+ </div>
529
+ ''')
530
+
531
+ login_button = gr.LoginButton()
532
+
533
+ # --- Event Handlers ---
534
+ def unified_generator(
535
+ prompt: str,
536
+ single_image: Optional[str],
537
+ multi_images: Optional[List[str]],
538
+ active_tab: str,
539
+ oauth_token: Optional[gr.OAuthToken] = None,
540
+ ):
541
+ if not verify_login_status(oauth_token):
542
+ raise gr.Error("Login required. Please click the 'Sign in with Hugging Face' button at the top.")
543
+ if not prompt:
544
+ raise gr.Error("Please enter a prompt.")
545
+ if active_tab == "multiple" and multi_images:
546
+ result = run_multi_image_logic(prompt, multi_images)
547
+ else:
548
+ result = run_single_image_logic(prompt, single_image)
549
+ return result, gr.update(visible=True)
550
+
551
+ single_tab.select(lambda: "single", None, active_tab_state)
552
+ multi_tab.select(lambda: "multiple", None, active_tab_state)
553
+
554
+ generate_button.click(
555
+ unified_generator,
556
+ inputs=[prompt_input, image_input, gallery_input, active_tab_state],
557
+ outputs=[output_image, use_image_button],
558
+ )
559
+
560
+ use_image_button.click(
561
+ lambda img: (img, gr.update(visible=False)),
562
+ inputs=[output_image],
563
+ outputs=[image_input, use_image_button]
564
+ )
565
+
566
+ # --- Access Control Logic ---
567
+ def control_access(
568
+ profile: Optional[gr.OAuthProfile] = None,
569
+ oauth_token: Optional[gr.OAuthToken] = None
570
+ ):
571
+ if not profile:
572
+ return gr.update(visible=False), gr.update(visible=False)
573
+ if verify_login_status(oauth_token):
574
+ return gr.update(visible=True), gr.update(visible=False)
575
+ else:
576
+ message = '''
577
+ <div class="login-message">
578
+ <h2>πŸ” Login Required</h2>
579
+ <p style="font-size: 1.1rem; margin: 1rem 0;">
580
+ Please sign in with your Hugging Face account to use this AI image generation tool.
581
+ </p>
582
+ <p style="margin: 1rem 0;">
583
+ After logging in, you can access:
584
+ </p>
585
+ <ul style="text-align: left; display: inline-block; margin: 1rem 0;">
586
+ <li>πŸš€ High-quality image generation via Google Nano Banana</li>
587
+ <li>⚑ Fast image generation and editing</li>
588
+ <li>🎨 Text-to-image conversion</li>
589
+ <li>πŸ”§ Multiple image editing and combining</li>
590
+ </ul>
591
+ <p style="margin-top: 1.5rem; font-weight: bold;">
592
+ Click the "Sign in with Hugging Face" button at the top to get started!
593
+ </p>
594
+ </div>
595
+ '''
596
+ return gr.update(visible=False), gr.update(visible=True, value=message)
597
+
598
+ demo.load(control_access, inputs=None, outputs=[main_interface, login_message])
599
+
600
+ if __name__ == "__main__":
601
+ demo.queue(max_size=None, default_concurrency_limit=None)
602
+ demo.launch()