Switch to Gemini + update requirements
Browse files
app.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import os
|
| 2 |
import time
|
| 3 |
from datetime import datetime
|
| 4 |
-
from typing import List
|
| 5 |
|
| 6 |
from fastapi import FastAPI, UploadFile, Form
|
| 7 |
from fastapi.responses import FileResponse, JSONResponse
|
|
@@ -14,13 +13,9 @@ import uvicorn
|
|
| 14 |
|
| 15 |
from dotenv import load_dotenv
|
| 16 |
|
| 17 |
-
# Load environment variables
|
| 18 |
load_dotenv()
|
| 19 |
-
|
| 20 |
-
# Confirm API key is loaded
|
| 21 |
-
print("Gemini API Key Loaded:", os.getenv("GOOGLE_API_KEY") is not None)
|
| 22 |
-
|
| 23 |
-
|
| 24 |
|
| 25 |
# ---------------------------
|
| 26 |
# CONFIG
|
|
@@ -34,7 +29,7 @@ LIFETIME = 24 * 60 * 60 # 24 hours
|
|
| 34 |
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
| 35 |
os.makedirs(RESULTS_DIR, exist_ok=True)
|
| 36 |
|
| 37 |
-
# Gemini
|
| 38 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 39 |
model = genai.GenerativeModel("gemini-1.5-flash")
|
| 40 |
|
|
@@ -53,28 +48,54 @@ def check_size(filepath):
|
|
| 53 |
os.remove(filepath)
|
| 54 |
raise ValueError(f"File too large! Max {MAX_SIZE_MB}MB allowed.")
|
| 55 |
|
| 56 |
-
def
|
| 57 |
-
"""
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
result = Image.alpha_composite(bg, fg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
| 67 |
result_path = os.path.join(RESULTS_DIR, f"result_{timestamp}.png")
|
| 68 |
result.save(result_path)
|
| 69 |
cleanup_old_files(RESULTS_DIR)
|
| 70 |
-
return result_path
|
| 71 |
|
| 72 |
-
def process_image(input_img, bg_choice):
|
| 73 |
-
if input_img is None:
|
| 74 |
-
return []
|
| 75 |
-
temp_path = os.path.join(UPLOAD_DIR, f"upload_{int(time.time())}.png")
|
| 76 |
-
input_img.save(temp_path)
|
| 77 |
-
result_path = replace_background(temp_path, bg_choice)
|
| 78 |
return [result_path]
|
| 79 |
|
| 80 |
def generate_caption(prompt="Promote my product"):
|
|
@@ -94,10 +115,9 @@ def generate_caption(prompt="Promote my product"):
|
|
| 94 |
# ---------------------------
|
| 95 |
app = FastAPI(title="SnapLift API")
|
| 96 |
|
| 97 |
-
# Allow CORS for mobile app access
|
| 98 |
app.add_middleware(
|
| 99 |
CORSMiddleware,
|
| 100 |
-
allow_origins=["*"],
|
| 101 |
allow_credentials=True,
|
| 102 |
allow_methods=["*"],
|
| 103 |
allow_headers=["*"],
|
|
@@ -109,9 +129,8 @@ async def process_image_api(file: UploadFile, bg_choice: str = Form(...)):
|
|
| 109 |
input_path = os.path.join(UPLOAD_DIR, file.filename)
|
| 110 |
with open(input_path, "wb") as f:
|
| 111 |
f.write(await file.read())
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
return FileResponse(result_path)
|
| 115 |
except Exception as e:
|
| 116 |
return JSONResponse(content={"error": str(e)}, status_code=400)
|
| 117 |
|
|
@@ -127,56 +146,50 @@ with gr.Blocks(css="footer {display:none !important}") as demo:
|
|
| 127 |
gr.Markdown("# ✨ SnapLift – AI Social Media Booster")
|
| 128 |
gr.Markdown("Upload your product photo, replace background, and auto-generate marketing captions + hashtags!")
|
| 129 |
|
| 130 |
-
# Image Editor
|
| 131 |
-
|
| 132 |
-
# Image Editor Tab
|
| 133 |
with gr.Tab("📸 Image Editor"):
|
| 134 |
with gr.Row():
|
| 135 |
-
input_img = gr.Image(type="pil", label="Upload Photo")
|
| 136 |
|
| 137 |
with gr.Column():
|
| 138 |
bg_choices = gr.Dropdown(
|
| 139 |
-
choices=os.listdir(BG_DIR),
|
| 140 |
value=os.listdir(BG_DIR)[0] if os.listdir(BG_DIR) else None,
|
| 141 |
label="Choose Background"
|
| 142 |
)
|
| 143 |
-
bg_preview = gr.Image(
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
# Update preview whenever background changes
|
| 149 |
def update_preview(bg_choice):
|
| 150 |
-
return os.path.join(BG_DIR, bg_choice) if bg_choice else None
|
| 151 |
|
| 152 |
bg_choices.change(fn=update_preview, inputs=bg_choices, outputs=bg_preview)
|
| 153 |
|
| 154 |
btn = gr.Button("✨ Generate New Photo")
|
| 155 |
output_imgs = gr.Gallery(label="Generated Image", elem_id="gallery", columns=1, rows=1)
|
| 156 |
-
btn.click(
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
with gr.Row():
|
| 162 |
-
input_img = gr.Image(type="pil", label="Upload Photo")
|
| 163 |
-
bg_choices = gr.Dropdown(
|
| 164 |
-
choices=os.listdir(BG_DIR),
|
| 165 |
-
value=os.listdir(BG_DIR)[0] if os.listdir(BG_DIR) else None,
|
| 166 |
-
label="Choose Background"
|
| 167 |
-
)
|
| 168 |
-
btn = gr.Button("✨ Generate New Photo")
|
| 169 |
-
output_imgs = gr.Gallery(label="Generated Image", elem_id="gallery", columns=1, rows=1)
|
| 170 |
-
btn.click(fn=process_image, inputs=[input_img, bg_choices], outputs=output_imgs)
|
| 171 |
|
| 172 |
-
# Caption Generator
|
| 173 |
with gr.Tab("✍️ Caption Generator"):
|
| 174 |
prompt = gr.Textbox(label="Enter product/promotion text", value="Promote my skincare product")
|
| 175 |
btn2 = gr.Button("💡 Suggest Captions + Hashtags")
|
| 176 |
caption_box = gr.Textbox(label="Suggested Posts (multi-platform)", lines=12)
|
| 177 |
btn2.click(fn=generate_caption, inputs=[prompt], outputs=[caption_box])
|
| 178 |
|
| 179 |
-
|
| 180 |
# START SERVER
|
| 181 |
# ---------------------------
|
| 182 |
if __name__ == "__main__":
|
|
@@ -187,7 +200,3 @@ if __name__ == "__main__":
|
|
| 187 |
|
| 188 |
threading.Thread(target=run_gradio).start()
|
| 189 |
uvicorn.run(app, host="0.0.0.0", port=8000)
|
| 190 |
-
|
| 191 |
-
threading.Thread(target=start_fastapi, daemon=True).start()
|
| 192 |
-
|
| 193 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 1 |
import os
|
| 2 |
import time
|
| 3 |
from datetime import datetime
|
|
|
|
| 4 |
|
| 5 |
from fastapi import FastAPI, UploadFile, Form
|
| 6 |
from fastapi.responses import FileResponse, JSONResponse
|
|
|
|
| 13 |
|
| 14 |
from dotenv import load_dotenv
|
| 15 |
|
| 16 |
+
# Load environment variables
|
| 17 |
load_dotenv()
|
| 18 |
+
print("Gemini API Key Loaded:", os.getenv("GEMINI_API_KEY") is not None)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
# ---------------------------
|
| 21 |
# CONFIG
|
|
|
|
| 29 |
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
| 30 |
os.makedirs(RESULTS_DIR, exist_ok=True)
|
| 31 |
|
| 32 |
+
# Gemini setup
|
| 33 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
| 34 |
model = genai.GenerativeModel("gemini-1.5-flash")
|
| 35 |
|
|
|
|
| 48 |
os.remove(filepath)
|
| 49 |
raise ValueError(f"File too large! Max {MAX_SIZE_MB}MB allowed.")
|
| 50 |
|
| 51 |
+
def process_image(input_img, bg_choice, bg_upload=None, brand_color="#FFFFFF", logo_upload=None, logo_opacity=80, logo_position="bottom-right"):
|
| 52 |
+
"""Main image processor with custom bg, brand color, and logo support"""
|
| 53 |
+
if input_img is None:
|
| 54 |
+
return []
|
| 55 |
+
|
| 56 |
+
temp_path = os.path.join(UPLOAD_DIR, f"upload_{int(time.time())}.png")
|
| 57 |
+
input_img.save(temp_path)
|
| 58 |
+
|
| 59 |
+
fg = remove(Image.open(temp_path).convert("RGBA"))
|
| 60 |
|
| 61 |
+
# --- Background selection ---
|
| 62 |
+
if bg_choice and bg_choice in os.listdir(BG_DIR):
|
| 63 |
+
bg = Image.open(os.path.join(BG_DIR, bg_choice)).convert("RGBA").resize(fg.size)
|
| 64 |
+
elif bg_choice == "Custom Upload" and bg_upload is not None:
|
| 65 |
+
bg = bg_upload.convert("RGBA").resize(fg.size)
|
| 66 |
+
elif bg_choice == "Solid Brand Color" and brand_color:
|
| 67 |
+
bg = Image.new("RGBA", fg.size, brand_color)
|
| 68 |
+
else:
|
| 69 |
+
bg = Image.new("RGBA", fg.size, (255, 255, 255, 255))
|
| 70 |
|
| 71 |
result = Image.alpha_composite(bg, fg)
|
| 72 |
+
|
| 73 |
+
# --- Logo overlay ---
|
| 74 |
+
if logo_upload is not None:
|
| 75 |
+
logo = logo_upload.convert("RGBA")
|
| 76 |
+
logo_size = int(result.width * 0.15)
|
| 77 |
+
logo.thumbnail((logo_size, logo_size))
|
| 78 |
+
alpha = logo.split()[3].point(lambda p: int(p * (logo_opacity / 100)))
|
| 79 |
+
logo.putalpha(alpha)
|
| 80 |
+
|
| 81 |
+
if logo_position == "top-left":
|
| 82 |
+
pos = (20, 20)
|
| 83 |
+
elif logo_position == "top-right":
|
| 84 |
+
pos = (result.width - logo.width - 20, 20)
|
| 85 |
+
elif logo_position == "bottom-left":
|
| 86 |
+
pos = (20, result.height - logo.height - 20)
|
| 87 |
+
elif logo_position == "center":
|
| 88 |
+
pos = ((result.width - logo.width) // 2, (result.height - logo.height) // 2)
|
| 89 |
+
else: # default bottom-right
|
| 90 |
+
pos = (result.width - logo.width - 20, result.height - logo.height - 20)
|
| 91 |
+
|
| 92 |
+
result.alpha_composite(logo, pos)
|
| 93 |
+
|
| 94 |
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
| 95 |
result_path = os.path.join(RESULTS_DIR, f"result_{timestamp}.png")
|
| 96 |
result.save(result_path)
|
| 97 |
cleanup_old_files(RESULTS_DIR)
|
|
|
|
| 98 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
return [result_path]
|
| 100 |
|
| 101 |
def generate_caption(prompt="Promote my product"):
|
|
|
|
| 115 |
# ---------------------------
|
| 116 |
app = FastAPI(title="SnapLift API")
|
| 117 |
|
|
|
|
| 118 |
app.add_middleware(
|
| 119 |
CORSMiddleware,
|
| 120 |
+
allow_origins=["*"],
|
| 121 |
allow_credentials=True,
|
| 122 |
allow_methods=["*"],
|
| 123 |
allow_headers=["*"],
|
|
|
|
| 129 |
input_path = os.path.join(UPLOAD_DIR, file.filename)
|
| 130 |
with open(input_path, "wb") as f:
|
| 131 |
f.write(await file.read())
|
| 132 |
+
result_path = process_image(Image.open(input_path), bg_choice)
|
| 133 |
+
return FileResponse(result_path[0])
|
|
|
|
| 134 |
except Exception as e:
|
| 135 |
return JSONResponse(content={"error": str(e)}, status_code=400)
|
| 136 |
|
|
|
|
| 146 |
gr.Markdown("# ✨ SnapLift – AI Social Media Booster")
|
| 147 |
gr.Markdown("Upload your product photo, replace background, and auto-generate marketing captions + hashtags!")
|
| 148 |
|
| 149 |
+
# --- Image Editor ---
|
|
|
|
|
|
|
| 150 |
with gr.Tab("📸 Image Editor"):
|
| 151 |
with gr.Row():
|
| 152 |
+
input_img = gr.Image(type="pil", label="Upload Main Photo")
|
| 153 |
|
| 154 |
with gr.Column():
|
| 155 |
bg_choices = gr.Dropdown(
|
| 156 |
+
choices=os.listdir(BG_DIR) + ["Custom Upload", "Solid Brand Color"],
|
| 157 |
value=os.listdir(BG_DIR)[0] if os.listdir(BG_DIR) else None,
|
| 158 |
label="Choose Background"
|
| 159 |
)
|
| 160 |
+
bg_preview = gr.Image(label="Background Preview", type="filepath")
|
| 161 |
+
bg_upload = gr.Image(type="pil", label="Upload Custom Background")
|
| 162 |
+
brand_color = gr.ColorPicker(label="Pick Brand Colour", value="#FFFFFF")
|
| 163 |
+
|
| 164 |
+
logo_upload = gr.Image(type="pil", label="Upload Brand Logo (Optional)")
|
| 165 |
+
logo_opacity = gr.Slider(minimum=0, maximum=100, value=80, step=5, label="Logo Transparency (%)")
|
| 166 |
+
logo_position = gr.Radio(
|
| 167 |
+
choices=["top-left", "top-right", "bottom-left", "bottom-right", "center"],
|
| 168 |
+
value="bottom-right",
|
| 169 |
+
label="Logo Position"
|
| 170 |
+
)
|
| 171 |
|
|
|
|
| 172 |
def update_preview(bg_choice):
|
| 173 |
+
return os.path.join(BG_DIR, bg_choice) if bg_choice in os.listdir(BG_DIR) else None
|
| 174 |
|
| 175 |
bg_choices.change(fn=update_preview, inputs=bg_choices, outputs=bg_preview)
|
| 176 |
|
| 177 |
btn = gr.Button("✨ Generate New Photo")
|
| 178 |
output_imgs = gr.Gallery(label="Generated Image", elem_id="gallery", columns=1, rows=1)
|
| 179 |
+
btn.click(
|
| 180 |
+
fn=process_image,
|
| 181 |
+
inputs=[input_img, bg_choices, bg_upload, brand_color, logo_upload, logo_opacity, logo_position],
|
| 182 |
+
outputs=output_imgs
|
| 183 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
|
| 185 |
+
# --- Caption Generator ---
|
| 186 |
with gr.Tab("✍️ Caption Generator"):
|
| 187 |
prompt = gr.Textbox(label="Enter product/promotion text", value="Promote my skincare product")
|
| 188 |
btn2 = gr.Button("💡 Suggest Captions + Hashtags")
|
| 189 |
caption_box = gr.Textbox(label="Suggested Posts (multi-platform)", lines=12)
|
| 190 |
btn2.click(fn=generate_caption, inputs=[prompt], outputs=[caption_box])
|
| 191 |
|
| 192 |
+
# ---------------------------
|
| 193 |
# START SERVER
|
| 194 |
# ---------------------------
|
| 195 |
if __name__ == "__main__":
|
|
|
|
| 200 |
|
| 201 |
threading.Thread(target=run_gradio).start()
|
| 202 |
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
|
|
|
|
|
|
|
|
|
|