Spaces:
Sleeping
Sleeping
File size: 8,473 Bytes
03dcabd 93d180e d927a6e 769faf5 93d180e b1a8e9d 857f721 769faf5 93d180e 769faf5 b1a8e9d 769faf5 cf37bae 769faf5 3140ee8 857f721 93d180e 769faf5 857f721 769faf5 857f721 4372751 857f721 fb08ce6 3140ee8 fb08ce6 857f721 3140ee8 769faf5 93d180e 769faf5 3140ee8 857f721 769faf5 3140ee8 769faf5 3140ee8 769faf5 857f721 fb08ce6 93d180e 3140ee8 769faf5 93d180e 857f721 769faf5 857f721 769faf5 857f721 769faf5 857f721 769faf5 857f721 769faf5 857f721 769faf5 857f721 3140ee8 857f721 769faf5 857f721 93d180e 857f721 93d180e 769faf5 857f721 93d180e 3140ee8 93d180e 857f721 93d180e 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 181 182 183 184 185 186 187 188 189 |
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 โโโโโโโโโโโโโโโโ
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. API FOR TABS โโโโโโโโโโโโโโโ
@app.route('/api/category')
def api_category():
cat = request.args.get('name', '')
urls = CATEGORIES.get(cat, [])
return jsonify([
{
"title": url.split('/')[-1],
"owner": url.split('/')[-2] if '/spaces/' in url else '',
"iframe": direct_url(url),
"shot": screenshot_url(url),
"hf": url
} for url in urls
])
# โโโโโโโโโโโโโโโ 4. ROUTES โโโโโโโโโโโโโโโ
@app.route('/')
def home():
return render_template('index.html', cats=list(CATEGORIES.keys()))
# โโโโโโโโโโโโโโโ 5. CREATE TEMPLATE โโโโโโโโโโโโโโโ
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(3,1fr);gap:14px;padding:0 16px 60px}
@media(max-width:800px){.grid{grid-template-columns:1fr}}
.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>
const cats={{cats|tojson}};
const tabs=document.getElementById('tabs');
const grid=document.getElementById('grid');
let active="";
/* ---------- card builder (dynamic + fallback) ---------- */
function createCard(sp){
const card=document.createElement('div');card.className='card';
const frameBox=document.createElement('div');frameBox.className='frame';
const ifr=document.createElement('iframe');
ifr.src=sp.iframe; ifr.loading='lazy';
frameBox.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>`;
frameBox.appendChild(err);
let loaded=false;
ifr.onload = ()=>{ loaded=true; }; // success
ifr.onerror= fallback; // immediate fail
setTimeout(()=>{ if(!loaded) fallback(); },10000); // watchdog
function fallback(){
ifr.remove();
const img=new Image(); img.src=sp.shot; img.loading='lazy';
img.onerror=()=>{ err.style.display='flex'; };
frameBox.prepend(img);
}
const foot=document.createElement('div');foot.className='foot';
foot.innerHTML=`<a href="${sp.iframe}" target="_blank">${sp.title}</a>`;
card.appendChild(frameBox);
card.appendChild(foot);
return card;
}
/* ---------- load tab ---------- */
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)));
});
}
/* ---------- init tabs ---------- */
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 โโโโโโโโโโโโโโโ
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860)
|