import os import io import random import base64 import json import gradio as gr from huggingface_hub import HfFileSystem from PIL import Image DATASET_ID = "roxopastel/999" HF_TOKEN = os.environ.get("HF_TOKEN", "") fs = HfFileSystem(token=HF_TOKEN if HF_TOKEN else None) def list_images() -> list[str]: try: files = fs.ls(f"datasets/{DATASET_ID}", detail=False) return [f for f in files if f.lower().endswith((".png", ".jpg", ".jpeg"))] except Exception: return [] def read_captions() -> dict[str, str]: captions = {} try: path = f"datasets/{DATASET_ID}/metadata.jsonl" with fs.open(path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue obj = json.loads(line) fname = obj.get("file_name", "") cap = obj.get("caption", "") if fname: captions[fname] = cap except Exception: pass return captions def get_random_image() -> str: images = list_images() if not images: return _error_html("⚠️ Não foi possível acessar o dataset. Verifique o HF_TOKEN.") path = random.choice(images) file_name = path.split("/")[-1] captions = read_captions() caption = captions.get(file_name, "") if not caption: caption = f"#{file_name.split('.')[0]}" try: with fs.open(path, "rb") as f: img_bytes = f.read() img = Image.open(io.BytesIO(img_bytes)).convert("RGB") buf = io.BytesIO() img.save(buf, format="JPEG", quality=92) b64 = base64.b64encode(buf.getvalue()).decode() return f"""
{msg}
' CSS = """ @import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Space+Mono:wght@400;700&display=swap'); :root { --cream: #faf6f0; --ink: #1a1310; --rust: #c45c3a; } body, .gradio-container, .gradio-container > .main, .gradio-container > .main > .wrap, .contain { background: var(--cream) !important; padding: 0 !important; margin: 0 auto !important; max-width: 100% !important; gap: 0 !important; } .gradio-container > .main > .wrap > .gap, .block { display: flex !important; flex-direction: column !important; align-items: center !important; gap: 0 !important; } /* ── title ── */ #header { width: 100%; text-align: center; padding: 1.4rem 1rem 0.5rem; } #header h1 { font-family: 'Instrument Serif', serif !important; font-size: clamp(2.2rem, 8vw, 4rem) !important; font-weight: 400 !important; color: var(--ink) !important; letter-spacing: -0.02em; line-height: 1; margin: 0 !important; } /* ── button ── */ #spin-btn { clip-path: polygon( 10px 0%, calc(100% - 10px) 0%, 100% 10px, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0% calc(100% - 10px), 0% 10px ) !important; width: 52px !important; height: 52px !important; min-width: 52px !important; max-width: 52px !important; min-height: 52px !important; max-height: 52px !important; flex: 0 0 52px !important; border-radius: 0 !important; background: var(--ink) !important; border: none !important; font-size: 1.3rem !important; padding: 0 !important; margin: 0.6rem auto 0.8rem !important; cursor: pointer !important; transition: background 0.15s, transform 0.1s !important; box-shadow: none !important; filter: drop-shadow(3px 3px 0px var(--rust)) !important; } #spin-btn:hover { background: var(--rust) !important; transform: scale(1.08) !important; } #spin-btn:active { transform: scale(0.92) !important; } /* ── html output block ── */ #polaroid-out { width: 100% !important; padding: 0 !important; margin: 0 !important; background: transparent !important; border: none !important; box-shadow: none !important; } footer { display: none !important; } """ with gr.Blocks(title="999 Doodles") as demo: gr.HTML('