Spaces:
Running
Running
File size: 8,735 Bytes
03dcabd 93d180e d927a6e 769faf5 93d180e b1a8e9d 769faf5 93d180e 769faf5 b1a8e9d 769faf5 cf37bae 769faf5 3140ee8 769faf5 93d180e 769faf5 4372751 769faf5 fb08ce6 3140ee8 fb08ce6 769faf5 3140ee8 769faf5 93d180e 769faf5 3140ee8 769faf5 3140ee8 769faf5 3140ee8 769faf5 3140ee8 fb08ce6 93d180e 3140ee8 769faf5 93d180e 769faf5 3140ee8 769faf5 93d180e 769faf5 93d180e 769faf5 93d180e 3140ee8 93d180e 769faf5 93d180e 769faf5 e2255ea |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
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. URL HELPERS (iframe + screenshot) โโโโโโโโโโโโโโโโ
def 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()
owner = owner.lower()
name = name.replace('.', '-').replace('_', '-').lower()
return f"https://{owner}-{name}.hf.space"
def screenshot_url(hf_url: str) -> str:
return f"https://image.thum.io/get/fullpage/{direct_url(hf_url)}"
# โโโโโโโโโโโโโโโโ 3. CATEGORY API (used by tabs) โโโโโโโโโโโโโโโโ
@app.route('/api/category')
def api_category():
cat = request.args.get('name', '')
urls = CATEGORIES.get(cat, [])
data = []
for url in urls:
m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", url)
owner, name = m.groups() if m else ('', url.split('/')[-1])
data.append({
"title": name,
"owner": owner,
"name": name,
"iframe": direct_url(url),
"shot": screenshot_url(url),
"hf": url
})
return jsonify(data)
# โโโโโโโโโโโโโโโโโโโโโ 4. FRONT PAGE ROUTE โโโโโโโโโโโโโโโโโโโโโ
@app.route('/')
def home():
return render_template('index.html', cats=list(CATEGORIES.keys()))
# โโโโโโโโโโโโโ 5. BUILD index.html (once) โโโโโโโโโโโโโโ
os.makedirs('templates', exist_ok=True)
with open('templates/index.html', 'w', encoding='utf-8') as fp:
fp.write(r'''<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport"content="width=device-width,initial-scale=1">
<title>Dynamic HF Spaces Gallery</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;600&display=swap');
body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb}
.tabs{display:flex;flex-wrap:wrap;gap:8px;padding:16px}
.tab{padding:6px 14px;border:none;border-radius:18px;background:#e2e8f0;font-weight:600;cursor:pointer}
.tab.active{background:#a78bfa;color:#1a202c}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(330px,1fr));gap:14px;padding:0 16px 60px}
.card{background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.08);overflow:hidden;height:420px;display:flex;flex-direction:column;position:relative}
.frame{flex:1;position:relative;overflow:hidden}
.frame iframe,.frame img{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0}
.err{display:none;align-items:center;justify-content:center;width:100%;height:100%;background:#fafafa;text-align:center;padding:10px;color:#666;font-size:.9rem}
.foot{height:44px;background:#fafafa;display:flex;align-items:center;justify-content:center;border-top:1px solid #eee}
.foot a{font-size:.82rem;font-weight:700;color:#4a6dd8;text-decoration:none}
</style></head>
<body>
<div class="tabs" id="tabs"></div>
<div class="grid" id="grid"></div>
<script>
// ----- helpers --------------------------------------------------
function createCard(sp){
const c=document.createElement('div');c.className='card';
const f=document.createElement('div');f.className='frame';
const ifr=document.createElement('iframe');
ifr.src=sp.iframe; ifr.loading='lazy'; f.appendChild(ifr);
const err=document.createElement('div');err.className='err';
err.innerHTML=`Preview blocked.<br><a href="${sp.hf}" target="_blank">Open on HF โ</a>`;
f.appendChild(err);
ifr.onerror=()=>toSnapshot();
// after 10 s fallback if still blank
setTimeout(()=>{try{
const ok=ifr.contentWindow && ifr.contentWindow.document.body.innerHTML.length>30;
if(!ok) toSnapshot();}catch(e){toSnapshot();}},10000);
function toSnapshot(){
ifr.remove(); const img=new Image();
img.src=sp.shot; img.loading='lazy';
img.onerror=()=>{err.style.display='flex';};
f.prepend(img);
}
const foot=document.createElement('div');foot.className='foot';
foot.innerHTML=`<a href="${sp.iframe}" target="_blank">${sp.title}</a>`;
c.appendChild(f); c.appendChild(foot);
return c;
}
// ----- load tab -------------------------------------------------
const cats={{cats|tojson}};
const tabs=document.getElementById('tabs');
const grid=document.getElementById('grid');
let active="";
function load(cat){
if(cat===active)return; active=cat;
[...tabs.children].forEach(b=>b.classList.toggle('active',b.dataset.c===cat));
grid.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(arr=>{
grid.innerHTML='';
arr.forEach(sp=>grid.appendChild(createCard(sp)));
});
}
// tabs init
cats.forEach((c,i)=>{
const b=document.createElement('button');
b.className='tab';b.textContent=c;b.dataset.c=c;
b.onclick=()=>load(c);tabs.appendChild(b);
if(i===0)load(c);
});
</script>
</body></html>''')
# โโโโโโโโโโโโโโโโโโ 6. RUN APP โโโโโโโโโโโโโโโโโโ
if __name__ == '__main__':
# hugggingface spaces use 7860 by convention
app.run(host='0.0.0.0', port=7860)
|