Spaces:
Build error
Build error
| # 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() |