ViberLu's picture
Update app.py
6853a89 verified
# app.py
import os
import base64
import requests
from io import BytesIO
from datetime import datetime
from typing import Optional, Dict
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
from huggingface_hub import InferenceClient
# ---------------------------
# HF Client
# ---------------------------
HF_TOKEN = os.environ.get("HUGGINGFACE_TOKEN")
hf = InferenceClient(token=HF_TOKEN)
MODELS = [
"stabilityai/stable-diffusion-xl-base-1.0",
"runwayml/stable-diffusion-v1-5",
]
# ---------------------------
# Utilities
# ---------------------------
def wrap_text(text, max_chars=60, max_lines=4):
words = text.split()
lines, cur = [], []
for w in words:
test = " ".join(cur + [w])
if len(test) <= max_chars:
cur.append(w)
else:
lines.append(" ".join(cur))
cur = [w]
if len(lines) >= max_lines:
break
if cur and len(lines) < max_lines:
lines.append(" ".join(cur))
return lines[:max_lines]
# ---------------------------
# Core Generator
# ---------------------------
class Generator:
def __init__(self, client: InferenceClient):
self.client = client
# ---- GitHub ----
def fetch_repo(self, url: str):
parts = url.replace("https://github.com/", "").split("/")
owner, repo = parts[0], parts[1]
r = requests.get(f"https://api.github.com/repos/{owner}/{repo}", timeout=10)
j = r.json()
return {
"name": j["name"],
"description": j.get("description") or "",
"stars": j["stargazers_count"],
"forks": j["forks_count"],
"language": j.get("language") or "Mixed",
}
# ---- Prompt ----
def build_prompt(self, base, style, colors, custom):
p = f"{style} GitHub graphic. {base}. Color scheme: {colors}. Clean background."
if custom:
p += " " + custom
return p
# ---- AI ----
def generate_background(
self,
prompt,
model,
width,
height,
negative=None,
init_image: Optional[Image.Image] = None,
):
if init_image:
return self.client.image_to_image(
image=init_image,
prompt=prompt,
negative_prompt=negative,
model=model,
width=width,
height=height,
)
return self.client.text_to_image(
prompt=prompt,
negative_prompt=negative,
model=model,
width=width,
height=height,
)
# ---- Layout ----
def compose(
self,
bg: Image.Image,
data: Dict,
include_desc: bool,
include_stats: bool,
include_lang: bool,
):
draw = ImageDraw.Draw(bg)
try:
title = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 48
)
body = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20
)
except Exception:
title = body = ImageFont.load_default()
draw.text((48, 40), data["name"], font=title, fill="white")
y = 110
if include_desc and data.get("description"):
for line in wrap_text(data["description"]):
draw.text((48, y), line, font=body, fill="white")
y += 28
footer = []
if include_stats:
footer.append(f"★ {data['stars']}{data['forks']}")
if include_lang:
footer.append(data["language"])
if footer:
draw.text((48, bg.height - 60), " ".join(footer), font=body, fill="white")
return bg
gen = Generator(hf)
# ---------------------------
# Handlers
# ---------------------------
def handle_github(
repo_url, style, colors, custom_prompt, negative,
include_desc, include_stats, include_lang,
model, width, height
):
data = gen.fetch_repo(repo_url)
base = f"{data['name']}. {data['description']}"
prompt = gen.build_prompt(base, style, colors, custom_prompt)
bg = gen.generate_background(prompt, model, width, height, negative)
img = gen.compose(bg, data, include_desc, include_stats, include_lang)
buf = BytesIO()
img.save(buf, format="PNG")
link = "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
return img, link
def handle_prompt(
text, name, style, colors, custom_prompt, negative,
include_desc, model, width, height
):
data = {
"name": name or "Custom Project",
"description": text,
"stars": 0,
"forks": 0,
"language": "N/A",
}
base = text
prompt = gen.build_prompt(base, style, colors, custom_prompt)
bg = gen.generate_background(prompt, model, width, height, negative)
img = gen.compose(bg, data, include_desc, False, False)
buf = BytesIO()
img.save(buf, format="PNG")
link = "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
return img, link
def handle_screenshot(
image, style, colors, custom_prompt, negative,
model, width, height
):
prompt = gen.build_prompt("Application illustration", style, colors, custom_prompt)
bg = gen.generate_background(prompt, model, width, height, negative, image)
buf = BytesIO()
bg.save(buf, format="PNG")
link = "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
return bg, link
# ---------------------------
# UI
# ---------------------------
with gr.Blocks(title="GitHub Graphics Generator") as demo:
gr.Markdown("## 🎨 GitHub Graphics Generator")
model = gr.Dropdown(MODELS, value=MODELS[0], label="Model")
width = gr.Slider(512, 1600, 1200, step=64, label="Width")
height = gr.Slider(256, 1000, 628, step=64, label="Height")
negative = gr.Textbox(label="Negative prompt", lines=2)
custom_prompt = gr.Textbox(label="Custom prompt override", lines=2)
with gr.Tabs():
with gr.Tab("GitHub Repo"):
repo = gr.Textbox(label="Repository URL")
style = gr.Dropdown(["Modern", "Minimal", "Professional"], value="Modern")
colors = gr.Textbox(value="Blue gradient")
inc_desc = gr.Checkbox(True, label="Include description")
inc_stats = gr.Checkbox(True, label="Include stats")
inc_lang = gr.Checkbox(True, label="Include language")
btn = gr.Button("Generate")
out = gr.Image()
link = gr.Textbox(label="Download link")
btn.click(
handle_github,
[repo, style, colors, custom_prompt, negative,
inc_desc, inc_stats, inc_lang,
model, width, height],
[out, link],
)
with gr.Tab("README / Prompt"):
text = gr.Textbox(lines=8, label="Description or README")
name = gr.Textbox(label="Project name")
style2 = gr.Dropdown(["Modern", "Minimal", "Professional"], value="Modern")
colors2 = gr.Textbox(value="Neutral")
inc_desc2 = gr.Checkbox(True, label="Include text")
btn2 = gr.Button("Generate")
out2 = gr.Image()
link2 = gr.Textbox(label="Download link")
btn2.click(
handle_prompt,
[text, name, style2, colors2, custom_prompt, negative,
inc_desc2, model, width, height],
[out2, link2],
)
with gr.Tab("Screenshot"):
img = gr.Image(type="pil", label="Upload image")
style3 = gr.Dropdown(["Modern", "Minimal", "Professional"], value="Modern")
colors3 = gr.Textbox(value="Match image")
btn3 = gr.Button("Generate")
out3 = gr.Image()
link3 = gr.Textbox(label="Download link")
btn3.click(
handle_screenshot,
[img, style3, colors3, custom_prompt, negative,
model, width, height],
[out3, link3],
)
demo.launch()