File size: 8,012 Bytes
03dcabd
93d180e
d927a6e
 
 
93d180e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b1a8e9d
93d180e
 
 
 
 
 
 
fb08ce6
93d180e
 
 
 
 
 
b1a8e9d
93d180e
fbf1f3d
cf37bae
93d180e
 
 
 
 
 
 
 
 
 
 
4372751
93d180e
fb08ce6
 
93d180e
fb08ce6
93d180e
 
 
fb08ce6
93d180e
 
b1a8e9d
fb08ce6
93d180e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fb08ce6
 
93d180e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b1a8e9d
93d180e
b1a8e9d
84739ea
93d180e
 
 
 
 
 
 
 
 
 
 
 
fb08ce6
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
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 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@app.route('/api/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 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@app.route('/')
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)