Creative-Design / app.py
openfree's picture
Update app.py
eed20b1 verified
raw
history blame
13.3 kB
import gradio as gr
import replicate
import os
from PIL import Image
import requests
from io import BytesIO
import time
import tempfile
import base64
# Set up Replicate API key from environment variable
os.environ['REPLICATE_API_TOKEN'] = os.getenv('REPLICATE_API_TOKEN')
def upload_image_to_hosting(image):
"""
Upload image to multiple hosting services with fallback
"""
# Method 1: Try imgbb.com (most reliable)
try:
buffered = BytesIO()
image.save(buffered, format="PNG")
buffered.seek(0)
img_base64 = base64.b64encode(buffered.getvalue()).decode()
response = requests.post(
"https://api.imgbb.com/1/upload",
data={
'key': '6d207e02198a847aa98d0a2a901485a5',
'image': img_base64,
}
)
if response.status_code == 200:
data = response.json()
if data.get('success'):
return data['data']['url']
except:
pass
# Method 2: Try 0x0.st (simple and reliable)
try:
buffered = BytesIO()
image.save(buffered, format="PNG")
buffered.seek(0)
files = {'file': ('image.png', buffered, 'image/png')}
response = requests.post("https://0x0.st", files=files)
if response.status_code == 200:
return response.text.strip()
except:
pass
# Method 3: Fallback to base64
buffered = BytesIO()
image.save(buffered, format="PNG")
buffered.seek(0)
img_base64 = base64.b64encode(buffered.getvalue()).decode()
return f"data:image/png;base64,{img_base64}"
def process_images(prompt, image1, image2=None):
"""
Process with Nano Banana - works with or without images
"""
if not os.getenv('REPLICATE_API_TOKEN'):
return None, "Please set REPLICATE_API_TOKEN"
try:
# Prepare input for Nano Banana model
model_input = {
"prompt": prompt
}
# Only add image_input if images are provided
if image1 or image2:
image_urls = []
if image1:
url1 = upload_image_to_hosting(image1)
image_urls.append(url1)
if image2:
url2 = upload_image_to_hosting(image2)
image_urls.append(url2)
model_input["image_input"] = image_urls
status_msg = "✨ Generated with style transfer!"
else:
# No images - text-only generation with Nano Banana
status_msg = "✨ Generated from text!"
# Run Nano Banana model (it should handle both cases)
output = replicate.run(
"google/nano-banana",
input=model_input
)
if output is None:
return None, "No output received"
# Get the generated image
try:
if hasattr(output, 'read'):
img_data = output.read()
img = Image.open(BytesIO(img_data))
return img, status_msg
except:
pass
try:
if hasattr(output, 'url'):
output_url = output.url()
response = requests.get(output_url, timeout=30)
if response.status_code == 200:
img = Image.open(BytesIO(response.content))
return img, status_msg
except:
pass
output_url = None
if isinstance(output, str):
output_url = output
elif isinstance(output, list) and len(output) > 0:
output_url = output[0]
if output_url:
response = requests.get(output_url, timeout=30)
if response.status_code == 200:
img = Image.open(BytesIO(response.content))
return img, status_msg
return None, "Could not process output"
except Exception as e:
error_msg = str(e)
if "image_input" in error_msg.lower():
# If the model requires images, provide a helpful message
return None, "Note: This model may require at least one image. Try uploading an image or check the error: " + error_msg[:100]
return None, f"Error: {error_msg[:100]}"
# Enhanced CSS with modern, minimal design
css = """
.gradio-container {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
min-height: 100vh;
}
.header-container {
background: linear-gradient(135deg, #ffd93d 0%, #ffb347 100%);
padding: 2.5rem;
border-radius: 24px;
margin-bottom: 2.5rem;
box-shadow: 0 20px 60px rgba(255, 179, 71, 0.25);
}
.logo-text {
font-size: 3.5rem;
font-weight: 900;
color: #2d3436;
text-align: center;
margin: 0;
letter-spacing: -2px;
}
.subtitle {
color: #2d3436;
text-align: center;
font-size: 1rem;
margin-top: 0.5rem;
opacity: 0.8;
}
.mode-indicator {
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 0.5rem 1rem;
margin-top: 1rem;
text-align: center;
font-weight: 600;
color: #2d3436;
}
.main-content {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 24px;
padding: 2.5rem;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
}
.gr-button-primary {
background: linear-gradient(135deg, #ffd93d 0%, #ffb347 100%) !important;
border: none !important;
color: #2d3436 !important;
font-weight: 700 !important;
font-size: 1.1rem !important;
padding: 1.2rem 2rem !important;
border-radius: 14px !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
text-transform: uppercase;
letter-spacing: 1px;
width: 100%;
margin-top: 1rem !important;
}
.gr-button-primary:hover {
transform: translateY(-3px) !important;
box-shadow: 0 15px 40px rgba(255, 179, 71, 0.35) !important;
}
.gr-input, .gr-textarea {
background: #ffffff !important;
border: 2px solid #e1e8ed !important;
border-radius: 14px !important;
color: #2d3436 !important;
font-size: 1rem !important;
padding: 0.8rem 1rem !important;
}
.gr-input:focus, .gr-textarea:focus {
border-color: #ffd93d !important;
box-shadow: 0 0 0 4px rgba(255, 217, 61, 0.15) !important;
}
.gr-form {
background: transparent !important;
border: none !important;
}
.gr-panel {
background: #ffffff !important;
border: 2px solid #e1e8ed !important;
border-radius: 16px !important;
padding: 1.5rem !important;
}
.gr-box {
border-radius: 14px !important;
border-color: #e1e8ed !important;
}
label {
color: #636e72 !important;
font-weight: 600 !important;
font-size: 0.85rem !important;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.5rem !important;
}
.status-text {
font-family: 'SF Mono', 'Monaco', monospace;
color: #00b894;
font-size: 0.9rem;
}
.image-container {
border-radius: 14px !important;
overflow: hidden;
border: 2px solid #e1e8ed !important;
background: #fafbfc !important;
}
footer {
display: none !important;
}
/* Equal sizing for all image containers */
.image-upload {
min-height: 200px !important;
max-height: 200px !important;
}
.output-image {
min-height: 420px !important;
max-height: 420px !important;
}
/* Ensure consistent spacing */
.gr-row {
gap: 1rem !important;
}
.gr-column {
gap: 1rem !important;
}
.info-box {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
border-radius: 12px;
padding: 1rem;
margin-bottom: 1rem;
border-left: 4px solid #2196f3;
}
"""
with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
with gr.Column(elem_classes="header-container"):
gr.HTML("""
<h1 class="logo-text">🍌 Free Nano Banana</h1>
<p class="subtitle">AI-Powered Image Generation & Style Transfer</p>
<div class="mode-indicator">
💡 Works with or without images - Just describe what you want!
</div>
<div style="display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 20px;">
<a href="https://huggingface.co/spaces/ginigen/Nano-Banana-PRO" target="_blank">
<img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=PRO&color=%230000ff&labelColor=%23800080&logo=HUGGINGFACE&logoColor=white&style=for-the-badge" alt="badge">
</a>
<a href="https://huggingface.co/spaces/openfree/Nano-Banana-Upscale" target="_blank">
<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">
</a>
<a href="https://huggingface.co/spaces/aiqtech/Nano-Banana-API" target="_blank">
<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">
</a>
<a href="https://huggingface.co/spaces/ginigen/Nano-Banana-Video" target="_blank">
<img src="https://img.shields.io/static/v1?label=NANO%20BANANA&message=VIDEO&color=%230000ff&labelColor=%23800080&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Nano Banana VIDEO">
</a>
<a href="https://discord.gg/openfreeai" target="_blank">
<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">
</a>
</div>
""")
with gr.Column(elem_classes="main-content"):
# Info box
gr.HTML("""
<div class="info-box">
<strong>How to use Nano Banana:</strong><br>
• <b>Text Generation:</b> Just enter a prompt - no images needed!<br>
• <b>Style Transfer:</b> Add images to apply specific styles<br>
• The powerful Nano Banana model handles both modes seamlessly
</div>
""")
with gr.Row(equal_height=True):
# Left Column - Inputs
with gr.Column(scale=1):
prompt = gr.Textbox(
label="Prompt / Style Description",
placeholder="Describe what you want to generate...",
lines=3,
value="A beautiful banana-themed paradise with golden sunset",
elem_classes="prompt-input"
)
with gr.Row(equal_height=True):
image1 = gr.Image(
label="Primary Image (Optional)",
type="pil",
height=200,
elem_classes="image-container image-upload"
)
image2 = gr.Image(
label="Secondary Image (Optional)",
type="pil",
height=200,
elem_classes="image-container image-upload"
)
generate_btn = gr.Button(
"Generate Magic ✨",
variant="primary",
size="lg"
)
# Right Column - Output
with gr.Column(scale=1):
output_image = gr.Image(
label="Generated Result",
type="pil",
height=420,
elem_classes="image-container output-image"
)
status = gr.Textbox(
label="Status",
interactive=False,
lines=1,
elem_classes="status-text",
value="Ready to generate with Nano Banana..."
)
# Event handler
generate_btn.click(
fn=process_images,
inputs=[prompt, image1, image2],
outputs=[output_image, status]
)
# Examples for text-only generation
gr.Examples(
examples=[
["A majestic banana kingdom floating in the clouds", None, None],
["Cyberpunk banana city with neon lights", None, None],
["Ancient banana temple in a mystical forest", None, None],
["Banana spaceship exploring the cosmos", None, None],
["Make the sheets in the style of the logo. Make the scene natural.", None, None],
],
inputs=[prompt, image1, image2],
label="Text Generation Examples (No Images Needed!)"
)
# Launch
if __name__ == "__main__":
demo.launch(
share=True,
server_name="0.0.0.0",
server_port=7860
)