RetroGazzaSpurs's picture
Update app.py
f724384 verified
import gradio as gr
import shutil
from pathlib import Path
import os
from huggingface_hub import list_models
# Optional: Spaces API might not exist
try:
from huggingface_hub import list_spaces
except ImportError:
list_spaces = None
# ===============================
# Configuration
# ===============================
# Password is stored securely as a Hugging Face secret, not in code
UPLOAD_PASSWORD = os.environ.get("UPLOAD_PASSWORD") # must be set in Space secrets
WORKFLOW_DIR = Path("/data/workflows")
WORKFLOW_IMG_DIR = Path("/data/workflows_images")
LORA_DIR = Path("/data/loras")
LORA_IMG_DIR = Path("/data/loras_images")
for d in [WORKFLOW_DIR, WORKFLOW_IMG_DIR, LORA_DIR, LORA_IMG_DIR]:
d.mkdir(parents=True, exist_ok=True)
ALLOWED_FILE_EXTS = {".json", ".yaml", ".yml", ".png", ".safetensors", ".ckpt"}
ALLOWED_IMG_EXTS = {".png", ".jpg", ".jpeg", ".webp"}
# ===============================
# Upload handlers
# ===============================
def upload_workflow(file, image=None, password=""):
if UPLOAD_PASSWORD is None:
return "❌ Upload password not set in Space secrets."
if password != UPLOAD_PASSWORD:
return "❌ Unauthorized"
if file is None:
return "No file uploaded."
src = Path(file.name)
ext = src.suffix.lower()
if ext not in ALLOWED_FILE_EXTS:
return f"❌ Unsupported file type: `{ext}`"
dest = WORKFLOW_DIR / src.name
shutil.copy(src, dest)
if image:
img_ext = Path(image.name).suffix.lower()
if img_ext in ALLOWED_IMG_EXTS:
shutil.copy(image.name, WORKFLOW_IMG_DIR / (src.stem + img_ext))
return f"βœ… Saved workflow: `{dest.name}`"
def upload_lora(file, image=None, password=""):
if UPLOAD_PASSWORD is None:
return "❌ Upload password not set in Space secrets."
if password != UPLOAD_PASSWORD:
return "❌ Unauthorized"
if file is None:
return "No file uploaded."
src = Path(file.name)
ext = src.suffix.lower()
if ext not in ALLOWED_FILE_EXTS:
return f"❌ Unsupported file type: `{ext}`"
dest = LORA_DIR / src.name
shutil.copy(src, dest)
if image:
img_ext = Path(image.name).suffix.lower()
if img_ext in ALLOWED_IMG_EXTS:
shutil.copy(image.name, LORA_IMG_DIR / (src.stem + img_ext))
return f"βœ… Saved LoRA: `{dest.name}`"
# ===============================
# Listing functions
# ===============================
def list_uploaded_workflows():
files = sorted(WORKFLOW_DIR.glob("*"))
if not files:
return "No uploaded workflows yet."
md = ""
for f in files:
img_path = WORKFLOW_IMG_DIR / f"{f.stem}.png"
if img_path.exists():
md += f"### {f.name}\n![ref image]({img_path})\n---\n"
else:
md += f"### {f.name}\n(no image)\n---\n"
return md
def list_uploaded_loras():
files = sorted(LORA_DIR.glob("*"))
if not files:
return "No uploaded LoRAs yet."
md = ""
for f in files:
img_path = LORA_IMG_DIR / f"{f.stem}.png"
if img_path.exists():
md += f"### {f.name}\n![ref image]({img_path})\n---\n"
else:
md += f"### {f.name}\n(no image)\n---\n"
return md
# ===============================
# Hub browsing functions
# ===============================
def browse_loras(limit=20):
models = list(list_models(
tags=["lora"],
sort="downloads",
direction=-1,
limit=limit,
))
if not models:
return "No LoRAs found."
return "\n---\n".join(
f"### {m.modelId}\n⬇️ {m.downloads or 0} | ⭐ {m.likes or 0}\nhttps://huggingface.co/{m.modelId}"
for m in models
)
def browse_workflows(limit=20):
uploaded_files = sorted(WORKFLOW_DIR.glob("*"))
uploaded_md = ""
if uploaded_files:
uploaded_md = "## πŸ§‘β€πŸ’» Uploaded Workflows\n\n" + "\n---\n".join(
f"### {f.name}\nLocal upload"
for f in uploaded_files
)
if list_spaces is None:
remote_md = "Spaces API unavailable."
else:
spaces = list(list_spaces(
sort="likes",
direction=-1,
limit=limit,
))
workflows = [
s for s in spaces
if any(tag in (s.tags or []) for tag in ["comfyui", "workflow", "pipeline", "diffusers"])
]
if workflows:
remote_md = "## 🌍 Community Workflows\n\n" + "\n---\n".join(
f"### {s.id}\n⭐ {s.likes or 0}\nhttps://huggingface.co/spaces/{s.id}"
for s in workflows[:limit]
)
else:
remote_md = "No community workflows found."
return uploaded_md + "\n\n" + remote_md
# ===============================
# Gradio UI
# ===============================
with gr.Blocks() as demo:
gr.Markdown("# πŸ€— Workflow & LoRA Browser")
with gr.Tabs():
# Workflows Tab
with gr.Tab("βš™οΈ Workflows"):
wf_out = gr.Markdown()
gr.Button("Browse Workflows").click(
browse_workflows,
outputs=wf_out,
)
# LoRAs Tab
with gr.Tab("🧩 LoRAs"):
lora_out = gr.Markdown()
gr.Button("Browse LoRAs").click(
browse_loras,
outputs=lora_out,
)
# Upload Workflow Tab (always visible, password-protected)
with gr.Tab("⬆️ Upload Workflow"):
gr.Markdown("Upload workflow files with optional reference image (.json/.yaml/.png). Owner only (password required).")
wf_file = gr.File(label="Workflow file")
wf_image = gr.File(label="Reference image (optional)")
wf_password = gr.Textbox(label="Password", type="password")
wf_status = gr.Markdown()
gr.Button("Upload").click(
upload_workflow,
inputs=[wf_file, wf_image, wf_password],
outputs=wf_status,
)
wf_list = gr.Markdown()
gr.Button("Refresh Uploaded Workflows").click(
list_uploaded_workflows,
outputs=wf_list,
)
# Upload LoRA Tab (always visible, password-protected)
with gr.Tab("⬆️ Upload LoRA"):
gr.Markdown("Upload LoRA files with optional reference image (.safetensors/.ckpt/.png). Owner only (password required).")
lora_file = gr.File(label="LoRA file")
lora_image = gr.File(label="Reference image (optional)")
lora_password = gr.Textbox(label="Password", type="password")
lora_status = gr.Markdown()
gr.Button("Upload").click(
upload_lora,
inputs=[lora_file, lora_image, lora_password],
outputs=lora_status,
)
lora_list = gr.Markdown()
gr.Button("Refresh Uploaded LoRAs").click(
list_uploaded_loras,
outputs=lora_list,
)
demo.launch()