openfree commited on
Commit
899e4c0
ยท
verified ยท
1 Parent(s): cf05348

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -201
app.py CHANGED
@@ -1,11 +1,7 @@
1
  import gradio as gr
2
- from flask import Flask, render_template_string, request, jsonify
3
  import re
4
 
5
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ FLASK APP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
6
- flask_app = Flask(__name__)
7
-
8
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ CURATED CATEGORIES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
9
  CATEGORIES = {
10
  "Popular": [
11
  "https://huggingface.co/spaces/fantos/Heatmap-Leaderboard-KOREA",
@@ -14,14 +10,9 @@ CATEGORIES = {
14
  "https://huggingface.co/spaces/ginigen/SAJU",
15
  "https://huggingface.co/spaces/ginipick/SAJU-Couple",
16
  "https://huggingface.co/spaces/openfree/News-AI",
17
- "https://huggingface.co/spaces/openfree/Creative-Arena-Leaderboard",
18
  "https://huggingface.co/spaces/openfree/Face-blurring",
19
  "https://huggingface.co/spaces/ginigen/Fashion-Fit360",
20
  "https://huggingface.co/spaces/VIDraft/DNA-Diffusion",
21
- "https://huggingface.co/spaces/Heartsync/Nano-Banana",
22
- "https://huggingface.co/spaces/openfree/AGI-Screenplay-Pro",
23
- "https://huggingface.co/spaces/ginipick/FLUXllama",
24
- "https://huggingface.co/spaces/VIDraft/gpt-oss-RAG",
25
  "https://huggingface.co/spaces/ginigen/VEO3-Free",
26
  "https://huggingface.co/spaces/ginigen/text3d-r1",
27
  "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
@@ -30,35 +21,19 @@ CATEGORIES = {
30
  "https://huggingface.co/spaces/ginipick/AGI-Personal",
31
  "https://huggingface.co/spaces/ginigen/AI",
32
  "https://huggingface.co/spaces/ginigen/AGI-WebToon-KOREA",
33
- "https://huggingface.co/spaces/ginigen/webtoon-studio",
34
- "https://huggingface.co/spaces/MaziyarPanahi/FACTS-Leaderboard",
35
  "https://huggingface.co/spaces/ginigen/Flux-Kontext-Style",
36
- "https://huggingface.co/spaces/openfree/Cycle-Navigator",
37
- "https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
38
  "https://huggingface.co/spaces/ginigen/Seedance-Free",
39
  "https://huggingface.co/spaces/VIDraft/SOMA-AGI",
40
  "https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard",
41
  "https://huggingface.co/spaces/VIDraft/DNA-CASINO",
42
- "https://huggingface.co/spaces/aiqtech/SOMA-Oriental",
43
  ],
44
  "NEW": [
45
  "https://huggingface.co/spaces/ginigen/Family",
46
- "https://huggingface.co/spaces/ginigen/AGI-Screenplay",
47
  "https://huggingface.co/spaces/openfree/OpenAI-gpt-oss",
48
  "https://huggingface.co/spaces/ginipick/Private-AI",
49
  "https://huggingface.co/spaces/VIDraft/ACE-Singer",
50
  "https://huggingface.co/spaces/ginipick/AI-BOOK",
51
- "https://huggingface.co/spaces/openfree/Best-AI",
52
  "https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
53
- "https://huggingface.co/spaces/openfree/Open-GAMMA",
54
- ],
55
- "Productivity": [
56
- "https://huggingface.co/spaces/ginigen/Markets",
57
- "https://huggingface.co/spaces/VIDraft/Robo-Beam",
58
- "https://huggingface.co/spaces/VIDraft/voice-trans",
59
- "https://huggingface.co/spaces/openfree/Chart-GPT",
60
- "https://huggingface.co/spaces/VIDraft/Voice-Clone-Podcast",
61
- "https://huggingface.co/spaces/ginipick/PDF-EXAM",
62
  ],
63
  "Multimodal": [
64
  "https://huggingface.co/spaces/ginigen/Seedance-Free",
@@ -66,37 +41,18 @@ CATEGORIES = {
66
  "https://huggingface.co/spaces/ginigen/VEO3-Free",
67
  "https://huggingface.co/spaces/ginigen/Flux-VIDEO",
68
  "https://huggingface.co/spaces/VIDraft/ACE-Singer",
69
- "https://huggingface.co/spaces/openfree/DreamO-video",
70
  "https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
71
  ],
72
- "Professional": [
73
- "https://huggingface.co/spaces/aiqtech/SOMA-Oriental",
74
- "https://huggingface.co/spaces/VIDraft/SOMA-AGI",
75
- "https://huggingface.co/spaces/VIDraft/money-radar",
76
- "https://huggingface.co/spaces/immunobiotech/drug-discovery",
77
- "https://huggingface.co/spaces/openfree/Cycle-Navigator",
78
- "https://huggingface.co/spaces/VIDraft/Fashion-Fit",
79
- ],
80
  "Image": [
81
  "https://huggingface.co/spaces/openfree/Face-blurring",
82
  "https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
83
  "https://huggingface.co/spaces/ginigen/text3d-r1",
84
  "https://huggingface.co/spaces/ginipick/FLUXllama",
85
- "https://huggingface.co/spaces/ginigen/Workflow-Canvas",
86
  "https://huggingface.co/spaces/ginigen/MagicFace-V3",
87
  "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
88
- "https://huggingface.co/spaces/ginigen/3D-LLAMA",
89
- ],
90
- "LLM / VLM": [
91
- "https://huggingface.co/spaces/ginigen/deepseek-r1-0528-API",
92
- "https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API",
93
- "https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
94
- "https://huggingface.co/spaces/VIDraft/Mistral-RAG-BitSix",
95
- "https://huggingface.co/spaces/ginigen/Mistral-Perflexity",
96
  ],
97
  }
98
 
99
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ URL HELPERS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
100
  def direct_url(hf_url):
101
  m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", hf_url)
102
  if not m:
@@ -106,166 +62,59 @@ def direct_url(hf_url):
106
  name = name.replace('.', '-').replace('_', '-').lower()
107
  return f"https://{owner}-{name}.hf.space"
108
 
109
- def screenshot_url(url):
110
- return f"https://image.thum.io/get/fullpage/{url}"
111
-
112
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ FLASK ROUTES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
113
- @flask_app.route('/api/category')
114
- def api_category():
115
- cat = request.args.get('name', '')
116
- urls = CATEGORIES.get(cat, [])
117
-
118
- page = int(request.args.get('page', 1))
119
- per_page = int(request.args.get('per_page', 4))
120
-
121
- total_pages = max(1, (len(urls) + per_page - 1) // per_page)
122
- start = (page - 1) * per_page
123
- end = min(start + per_page, len(urls))
124
-
125
- urls_page = urls[start:end]
126
 
127
- items = [
128
- {
129
- "title": url.split('/')[-1],
130
- "owner": url.split('/')[-2] if '/spaces/' in url else '',
131
- "iframe": direct_url(url),
132
- "shot": screenshot_url(url),
133
- "hf": url
134
- } for url in urls_page
135
- ]
136
 
137
- return jsonify({
138
- "items": items,
139
- "page": page,
140
- "total_pages": total_pages
141
- })
142
-
143
- HTML_TEMPLATE = r'''<!DOCTYPE html>
144
- <html>
145
- <head>
146
- <meta charset="utf-8">
147
- <meta name="viewport" content="width=device-width, initial-scale=1">
148
- <title>Web Gallery</title>
149
- <style>
150
- @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;600&display=swap');
151
- body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb;}
152
- .tabs{display:flex;flex-wrap:wrap;gap:8px;padding:16px;}
153
- .tab{padding:6px 14px;border:none;border-radius:18px;background:#e2e8f0;font-weight:600;cursor:pointer;}
154
- .tab.active{background:#a78bfa;color:#1a202c;}
155
- .tab.popular{background:#ff6b6b;color:white;}
156
- .tab.popular.active{background:#fa5252;color:white;}
157
- .tab.best{background:#4ecdc4;color:white;}
158
- .tab.best.active{background:#38d9a9;color:white;}
159
- .tab.new{background:#ffe066;color:#1a202c;}
160
- .tab.new.active{background:#ffd43b;color:#1a202c;}
161
- .grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;padding:0 16px 60px;max-width:1200px;margin:0 auto;}
162
- @media(max-width:800px){.grid{grid-template-columns:1fr;}}
163
- .card{background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.08);overflow:hidden;height:540px;display:flex;flex-direction:column;position:relative;}
164
- .frame{flex:1;position:relative;overflow:hidden;}
165
- .frame iframe{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0;}
166
- .card-label{position:absolute;top:10px;left:10px;padding:4px 8px;border-radius:4px;font-size:11px;font-weight:bold;z-index:100;text-transform:uppercase;letter-spacing:0.5px;box-shadow:0 2px 4px rgba(0,0,0,0.2);}
167
- .label-live{background:linear-gradient(135deg, #00c6ff, #0072ff);color:white;}
168
- .foot{height:44px;background:#fafafa;display:flex;align-items:center;justify-content:center;border-top:1px solid #eee;}
169
- .foot a{font-size:.82rem;font-weight:700;color:#4a6dd8;text-decoration:none;}
170
- .pagination{display:flex;justify-content:center;margin:20px 0;gap:10px;}
171
- .pagination button{padding:5px 15px;border:none;border-radius:20px;background:#e2e8f0;cursor:pointer;}
172
- .pagination button:disabled{opacity:0.5;cursor:not-allowed;}
173
- </style>
174
- </head>
175
- <body>
176
- <header style="text-align: center; padding: 20px; background: linear-gradient(135deg, #f6f8fb, #e2e8f0); border-bottom: 1px solid #ddd;">
177
- <h1 style="margin-bottom: 10px;">๐ŸŒŸOPEN & Free: BEST AI Playground</h1>
178
- <p>
179
- <a href="https://huggingface.co/OpenFreeAI" target="_blank"><img src="https://img.shields.io/static/v1?label=Community&message=OpenFree_AI&color=%23800080&labelColor=%23000080&logo=HUGGINGFACE&logoColor=%23ffa500&style=for-the-badge" alt="badge"></a>
180
- <a href="https://discord.gg/openfreeai" target="_blank"><img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="badge"></a>
181
- </p>
182
- </header>
183
- <div class="tabs" id="tabs"></div>
184
- <div id="content"></div>
185
- <script>
186
- const cats = {{ cats|tojson }};
187
- const tabs = document.getElementById('tabs');
188
- const content = document.getElementById('content');
189
- let active = "";
190
- let currentPage = 1;
191
- function makeRequest(url, callback) {
192
- fetch(url).then(r => r.json()).then(callback);
193
- }
194
- function updateTabs() {
195
- Array.from(tabs.children).forEach(b => {
196
- b.classList.toggle('active', b.dataset.c === active);
197
- });
198
- }
199
- function loadCategory(cat, page) {
200
- if(cat === active && currentPage === page) return;
201
- active = cat;
202
- currentPage = page || 1;
203
- updateTabs();
204
-
205
- content.innerHTML = '<p style="text-align:center;padding:40px">Loadingโ€ฆ</p>';
206
-
207
- makeRequest('/api/category?name=' + encodeURIComponent(cat) + '&page=' + currentPage + '&per_page=4', function(data) {
208
- let html = '<div class="grid">';
209
-
210
- if(data.items.length === 0) {
211
- html += '<p style="grid-column:1/-1;text-align:center;padding:40px">No items.</p>';
212
- } else {
213
- data.items.forEach(item => {
214
- html += `
215
- <div class="card">
216
- <div class="card-label label-live">LIVE</div>
217
- <div class="frame">
218
- <iframe src="${item.iframe}" loading="lazy" sandbox="allow-forms allow-modals allow-popups allow-same-origin allow-scripts allow-downloads"></iframe>
219
  </div>
220
- <div class="foot">
221
- <a href="${item.hf}" target="_blank">${item.title}</a>
222
  </div>
223
- </div>
224
- `;
225
- });
226
- }
227
-
228
- html += '</div>';
229
- html += `
230
- <div class="pagination">
231
- <button ${currentPage <= 1 ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage-1})">ยซ Previous</button>
232
- <span>Page ${currentPage} of ${data.total_pages}</span>
233
- <button ${currentPage >= data.total_pages ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage+1})">Next ยป</button>
234
- </div>
235
- `;
236
 
237
- content.innerHTML = html;
238
- });
239
- }
240
- ['Popular', 'BEST', 'NEW'].forEach(specialCat => {
241
- const b = document.createElement('button');
242
- b.className = 'tab ' + specialCat.toLowerCase();
243
- b.textContent = specialCat;
244
- b.dataset.c = specialCat;
245
- b.onclick = function() { loadCategory(specialCat, 1); };
246
- tabs.appendChild(b);
247
- });
248
- cats.forEach(c => {
249
- if (!['Popular', 'BEST', 'NEW'].includes(c)) {
250
- const b = document.createElement('button');
251
- b.className = 'tab';
252
- b.textContent = c;
253
- b.dataset.c = c;
254
- b.onclick = function() { loadCategory(c, 1); };
255
- tabs.appendChild(b);
256
- }
257
- });
258
- loadCategory('Popular', 1);
259
- </script>
260
- </body>
261
- </html>'''
262
-
263
- @flask_app.route('/')
264
- def home():
265
- return render_template_string(HTML_TEMPLATE, cats=list(CATEGORIES.keys()))
266
 
267
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ GRADIO WRAPPER โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
268
- app = gr.mount_gradio_app(flask_app, gr.Blocks(), path="/")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
- if __name__ == '__main__':
271
- app.launch()
 
1
  import gradio as gr
 
2
  import re
3
 
4
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ CATEGORIES โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
 
 
5
  CATEGORIES = {
6
  "Popular": [
7
  "https://huggingface.co/spaces/fantos/Heatmap-Leaderboard-KOREA",
 
10
  "https://huggingface.co/spaces/ginigen/SAJU",
11
  "https://huggingface.co/spaces/ginipick/SAJU-Couple",
12
  "https://huggingface.co/spaces/openfree/News-AI",
 
13
  "https://huggingface.co/spaces/openfree/Face-blurring",
14
  "https://huggingface.co/spaces/ginigen/Fashion-Fit360",
15
  "https://huggingface.co/spaces/VIDraft/DNA-Diffusion",
 
 
 
 
16
  "https://huggingface.co/spaces/ginigen/VEO3-Free",
17
  "https://huggingface.co/spaces/ginigen/text3d-r1",
18
  "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
 
21
  "https://huggingface.co/spaces/ginipick/AGI-Personal",
22
  "https://huggingface.co/spaces/ginigen/AI",
23
  "https://huggingface.co/spaces/ginigen/AGI-WebToon-KOREA",
 
 
24
  "https://huggingface.co/spaces/ginigen/Flux-Kontext-Style",
 
 
25
  "https://huggingface.co/spaces/ginigen/Seedance-Free",
26
  "https://huggingface.co/spaces/VIDraft/SOMA-AGI",
27
  "https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard",
28
  "https://huggingface.co/spaces/VIDraft/DNA-CASINO",
 
29
  ],
30
  "NEW": [
31
  "https://huggingface.co/spaces/ginigen/Family",
 
32
  "https://huggingface.co/spaces/openfree/OpenAI-gpt-oss",
33
  "https://huggingface.co/spaces/ginipick/Private-AI",
34
  "https://huggingface.co/spaces/VIDraft/ACE-Singer",
35
  "https://huggingface.co/spaces/ginipick/AI-BOOK",
 
36
  "https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
 
 
 
 
 
 
 
 
 
37
  ],
38
  "Multimodal": [
39
  "https://huggingface.co/spaces/ginigen/Seedance-Free",
 
41
  "https://huggingface.co/spaces/ginigen/VEO3-Free",
42
  "https://huggingface.co/spaces/ginigen/Flux-VIDEO",
43
  "https://huggingface.co/spaces/VIDraft/ACE-Singer",
 
44
  "https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
45
  ],
 
 
 
 
 
 
 
 
46
  "Image": [
47
  "https://huggingface.co/spaces/openfree/Face-blurring",
48
  "https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
49
  "https://huggingface.co/spaces/ginigen/text3d-r1",
50
  "https://huggingface.co/spaces/ginipick/FLUXllama",
 
51
  "https://huggingface.co/spaces/ginigen/MagicFace-V3",
52
  "https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
 
 
 
 
 
 
 
 
53
  ],
54
  }
55
 
 
56
  def direct_url(hf_url):
57
  m = re.match(r"https?://huggingface\.co/spaces/([^/]+)/([^/?#]+)", hf_url)
58
  if not m:
 
62
  name = name.replace('.', '-').replace('_', '-').lower()
63
  return f"https://{owner}-{name}.hf.space"
64
 
65
+ def create_gallery_html(category):
66
+ urls = CATEGORIES.get(category, [])[:8] # Show first 8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ html = '<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:20px;padding:20px;max-width:1400px;margin:0 auto;">'
 
 
 
 
 
 
 
 
69
 
70
+ for url in urls:
71
+ title = url.split('/')[-1]
72
+ iframe_url = direct_url(url)
73
+
74
+ html += f'''
75
+ <div style="background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.08);overflow:hidden;height:500px;display:flex;flex-direction:column;">
76
+ <div style="flex:1;position:relative;overflow:hidden;background:#f5f5f5;">
77
+ <div style="position:absolute;top:10px;left:10px;padding:4px 8px;border-radius:4px;background:linear-gradient(135deg,#00c6ff,#0072ff);color:white;font-size:11px;font-weight:bold;z-index:10;">LIVE</div>
78
+ <iframe src="{iframe_url}" style="width:100%;height:100%;border:0;" loading="lazy" sandbox="allow-forms allow-modals allow-popups allow-same-origin allow-scripts"></iframe>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  </div>
80
+ <div style="padding:12px;background:#fafafa;border-top:1px solid #eee;text-align:center;">
81
+ <a href="{url}" target="_blank" style="font-size:14px;font-weight:600;color:#4a6dd8;text-decoration:none;">{title}</a>
82
  </div>
83
+ </div>
84
+ '''
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ html += '</div>'
87
+ return html
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ # Create Gradio interface
90
+ with gr.Blocks(
91
+ title="๐ŸŒŸ BEST AI Playground",
92
+ css="""
93
+ @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap');
94
+ body {font-family: 'Nunito', sans-serif !important;}
95
+ .gradio-container {max-width: 100% !important;}
96
+ """
97
+ ) as demo:
98
+
99
+ gr.HTML("""
100
+ <div style="text-align:center;padding:30px 20px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:white;border-radius:10px;margin-bottom:20px;">
101
+ <h1 style="margin:0 0 15px 0;font-size:2.5em;font-weight:700;">๐ŸŒŸ BEST AI Playground</h1>
102
+ <p style="margin:10px 0;font-size:1.1em;">Curated Collection of Amazing AI Spaces on Hugging Face</p>
103
+ <div style="margin-top:20px;">
104
+ <a href="https://huggingface.co/OpenFreeAI" target="_blank" style="display:inline-block;margin:5px;">
105
+ <img src="https://img.shields.io/badge/Community-OpenFree_AI-purple?style=for-the-badge&logo=huggingface" alt="Community">
106
+ </a>
107
+ <a href="https://discord.gg/openfreeai" target="_blank" style="display:inline-block;margin:5px;">
108
+ <img src="https://img.shields.io/badge/Discord-Join_Us-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord">
109
+ </a>
110
+ </div>
111
+ </div>
112
+ """)
113
+
114
+ with gr.Tabs():
115
+ for category_name, urls in CATEGORIES.items():
116
+ with gr.Tab(f"โœจ {category_name}"):
117
+ gr.HTML(create_gallery_html(category_name))
118
 
119
+ if __name__ == "__main__":
120
+ demo.launch()