Spaces:
Running
Running
from flask import Flask, render_template, request, jsonify | |
import os, re, json | |
app = Flask(__name__) | |
# βββββββββββββββββββββββββ 1. CURATED CATEGORIES βββββββββββββββββββββββββ | |
CATEGORIES = { | |
"Productivity": [ | |
"https://huggingface.co/spaces/ginigen/perflexity-clone", | |
"https://huggingface.co/spaces/ginipick/IDEA-DESIGN", | |
"https://huggingface.co/spaces/VIDraft/mouse-webgen", | |
"https://huggingface.co/spaces/openfree/Vibe-Game", | |
"https://huggingface.co/spaces/openfree/Game-Gallery", | |
"https://huggingface.co/spaces/aiqtech/Contributors-Leaderboard", | |
"https://huggingface.co/spaces/fantaxy/Model-Leaderboard", | |
"https://huggingface.co/spaces/fantaxy/Space-Leaderboard", | |
"https://huggingface.co/spaces/openfree/Korean-Leaderboard", | |
], | |
"Multimodal": [ | |
"https://huggingface.co/spaces/openfree/DreamO-video", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo", | |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored", | |
"https://huggingface.co/spaces/fantaxy/Sound-AI-SFX", | |
"https://huggingface.co/spaces/ginigen/SFX-Sound-magic", | |
"https://huggingface.co/spaces/ginigen/VoiceClone-TTS", | |
"https://huggingface.co/spaces/aiqcamp/MCP-kokoro", | |
"https://huggingface.co/spaces/aiqcamp/ENGLISH-Speaking-Scoring", | |
], | |
"Professional": [ | |
"https://huggingface.co/spaces/ginigen/blogger", | |
"https://huggingface.co/spaces/VIDraft/money-radar", | |
"https://huggingface.co/spaces/immunobiotech/drug-discovery", | |
"https://huggingface.co/spaces/immunobiotech/Gemini-MICHELIN", | |
"https://huggingface.co/spaces/Heartsync/Papers-Leaderboard", | |
"https://huggingface.co/spaces/VIDraft/PapersImpact", | |
"https://huggingface.co/spaces/ginipick/AgentX-Papers", | |
"https://huggingface.co/spaces/openfree/Cycle-Navigator", | |
], | |
"Image": [ | |
"https://huggingface.co/spaces/ginigen/interior-design", | |
"https://huggingface.co/spaces/ginigen/Workflow-Canvas", | |
"https://huggingface.co/spaces/ginigen/Multi-LoRAgen", | |
"https://huggingface.co/spaces/ginigen/Every-Text", | |
"https://huggingface.co/spaces/ginigen/text3d-r1", | |
"https://huggingface.co/spaces/ginipick/FLUXllama", | |
"https://huggingface.co/spaces/Heartsync/FLUX-Vision", | |
"https://huggingface.co/spaces/ginigen/VisualCloze", | |
"https://huggingface.co/spaces/seawolf2357/Ghibli-Multilingual-Text-rendering", | |
"https://huggingface.co/spaces/ginigen/Ghibli-Meme-Studio", | |
"https://huggingface.co/spaces/VIDraft/Open-Meme-Studio", | |
"https://huggingface.co/spaces/ginigen/3D-LLAMA", | |
], | |
"LLM / VLM": [ | |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-4B", | |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-12B", | |
"https://huggingface.co/spaces/ginigen/Mistral-Perflexity", | |
"https://huggingface.co/spaces/aiqcamp/gemini-2.5-flash-preview", | |
"https://huggingface.co/spaces/openfree/qwen3-30b-a3b-research", | |
"https://huggingface.co/spaces/openfree/qwen3-235b-a22b-research", | |
"https://huggingface.co/spaces/openfree/Llama-4-Maverick-17B-Research", | |
], | |
} | |
# βββββββββββ 2. HELPERS: build embed & direct URLs from HF link βββββββββββ | |
def space_embed_url(hf_url: str) -> str: | |
m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", hf_url) | |
if not m: | |
return hf_url | |
owner, name = m.groups() | |
return f"https://huggingface.co/spaces/{owner}/{name}/embed" | |
def space_direct_url(hf_url: str) -> str: | |
m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", hf_url) | |
if not m: | |
return hf_url | |
owner, name = m.groups() | |
# dots/underscores β dashes, lowercase | |
owner = owner.lower() | |
name = name.replace('.', '-').replace('_', '-').lower() | |
return f"https://{owner}-{name}.hf.space" | |
# βββββββββββββ 3. API: return spaces for a given category ββββββββββββββ | |
def api_category(): | |
cat = request.args.get('name', '') | |
urls = CATEGORIES.get(cat, []) | |
spaces = [{ | |
"title": url.split('/')[-1], | |
"embedUrl": space_embed_url(url), | |
"directUrl": space_direct_url(url) | |
} for url in urls] | |
return jsonify({"spaces": spaces}) | |
# βββββββββββββββββββββββββββββ 4. ROUTES ββββββββββββββββββββββββββββββββ | |
def home(): | |
return render_template('index.html', categories=list(CATEGORIES.keys())) | |
# βββββββββββββ 5. CREATE index.html with tabbed UI (once) βββββββββββββββ | |
if not os.path.exists('templates'): | |
os.makedirs('templates') | |
with open('templates/index.html', 'w', encoding='utf-8') as f: | |
f.write(r'''<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
<title>Curated HF Spaces</title> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;500;700&display=swap'); | |
body{margin:0;font-family:Nunito,sans-serif;background:#f0f2f8;} | |
.tabs{display:flex;gap:6px;flex-wrap:wrap;padding:16px;} | |
.tab-btn{padding:8px 14px;border:none;border-radius:20px;background:#e2e8f0;cursor:pointer;font-weight:600} | |
.tab-btn.active{background:#c4b5fd;color:#1a202c} | |
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:14px;padding:0 16px 40px} | |
.card{background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.06);overflow:hidden;display:flex;flex-direction:column;height:420px;position:relative} | |
.tag{position:absolute;top:6px;left:6px;background:#10b981;color:#fff;font-size:.65rem;font-weight:700;padding:2px 6px;border-radius:4px;z-index:2} | |
.frame{flex:1;overflow:hidden} | |
.frame iframe{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0} | |
.foot{height:40px;background:#fafafa;display:flex;align-items:center;justify-content:center;border-top:1px solid #eee} | |
.foot a{font-size:.82rem;font-weight:600;color:#4a6dd8;text-decoration:none} | |
</style> | |
</head> | |
<body> | |
<div class="tabs" id="tabs"></div> | |
<div id="grid" class="grid"></div> | |
<script> | |
const cats={{ categories|tojson }}; | |
const tabsEl=document.getElementById('tabs'); | |
const gridEl=document.getElementById('grid'); | |
let active=''; | |
function loadTab(cat){ | |
if(cat===active) return; | |
active=cat; | |
[...tabsEl.children].forEach(b=>b.classList.toggle('active',b.dataset.cat===cat)); | |
gridEl.innerHTML='<p style="grid-column:1/-1;text-align:center;padding:40px">Loadingβ¦</p>'; | |
fetch(`/api/category?name=${encodeURIComponent(cat)}`) | |
.then(r=>r.json()).then(data=>{ | |
gridEl.innerHTML=''; | |
data.spaces.forEach(sp=>{ | |
const card=document.createElement('div'); | |
card.className='card'; | |
card.innerHTML=` | |
<span class="tag">LIVE</span> | |
<div class="frame"><iframe src="${sp.embedUrl}" loading="lazy" allow="accelerometer; gyroscope;"></iframe></div> | |
<div class="foot"><a href="${sp.directUrl}" target="_blank">Open β</a></div> | |
`; | |
gridEl.appendChild(card); | |
}); | |
}); | |
} | |
cats.forEach((c,i)=>{ | |
const b=document.createElement('button'); | |
b.textContent=c; | |
b.className='tab-btn'; | |
b.dataset.cat=c; | |
b.onclick=()=>loadTab(c); | |
tabsEl.appendChild(b); | |
if(i===0) loadTab(c); | |
}); | |
</script> | |
</body> | |
</html>''') | |
# βββββββββββββββββββββββββββββββ 6. RUN ββββββββββββββββββββββββββββββββ | |
if __name__ == '__main__': | |
app.run(host='0.0.0.0', port=7860) | |