Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from flask import Flask,
|
| 2 |
import os, re, json
|
| 3 |
|
| 4 |
app = Flask(__name__)
|
|
@@ -76,7 +76,6 @@ CATEGORIES = {
|
|
| 76 |
"https://huggingface.co/spaces/VIDraft/FramePack_rotate_landscape",
|
| 77 |
"https://huggingface.co/spaces/fantaxy/Sound-AI-SFX",
|
| 78 |
"https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
|
| 79 |
-
|
| 80 |
"https://huggingface.co/spaces/Heartsync/NSFW-image",
|
| 81 |
"https://huggingface.co/spaces/Heartsync/NSFW-detection",
|
| 82 |
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
|
|
@@ -84,7 +83,6 @@ CATEGORIES = {
|
|
| 84 |
"https://huggingface.co/spaces/ginigen/FLUX-Text-Tree-Image",
|
| 85 |
"https://huggingface.co/spaces/ginigen/text3d-r1",
|
| 86 |
"https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
|
| 87 |
-
|
| 88 |
],
|
| 89 |
"BEST": [
|
| 90 |
"https://huggingface.co/spaces/Heartsync/Novel-NSFW",
|
|
@@ -179,8 +177,6 @@ CATEGORIES = {
|
|
| 179 |
"https://huggingface.co/spaces/seawolf2357/flxloraexp",
|
| 180 |
"https://huggingface.co/spaces/ginipick/Time-Stream",
|
| 181 |
"https://huggingface.co/spaces/fantos/textcutobject",
|
| 182 |
-
|
| 183 |
-
|
| 184 |
],
|
| 185 |
"NEW": [
|
| 186 |
"https://huggingface.co/spaces/ginigen/Family",
|
|
@@ -237,8 +233,6 @@ CATEGORIES = {
|
|
| 237 |
"https://huggingface.co/spaces/openfree/Cycle-Navigator",
|
| 238 |
"https://huggingface.co/spaces/openfree/DreamO-video",
|
| 239 |
"https://huggingface.co/spaces/Heartsync/NSFW-detection",
|
| 240 |
-
|
| 241 |
-
|
| 242 |
],
|
| 243 |
"Productivity": [
|
| 244 |
"https://huggingface.co/spaces/ginigen/Markets",
|
|
@@ -373,31 +367,6 @@ def direct_url(hf_url):
|
|
| 373 |
def screenshot_url(url):
|
| 374 |
return f"https://image.thum.io/get/fullpage/{url}"
|
| 375 |
|
| 376 |
-
def process_url_for_preview(url):
|
| 377 |
-
"""Returns (preview_url, mode)"""
|
| 378 |
-
# Handle blocked domains first
|
| 379 |
-
if any(d for d in BLOCKED_DOMAINS if d in url):
|
| 380 |
-
return screenshot_url(url), "snapshot"
|
| 381 |
-
|
| 382 |
-
# Special case handling for problematic URLs
|
| 383 |
-
if "vibe-coding-tetris" in url or "World-of-Tank-GAME" in url or "Minesweeper-Game" in url:
|
| 384 |
-
return screenshot_url(url), "snapshot"
|
| 385 |
-
|
| 386 |
-
# General HF space handling
|
| 387 |
-
try:
|
| 388 |
-
if "huggingface.co/spaces" in url:
|
| 389 |
-
parts = url.rstrip("/").split("/")
|
| 390 |
-
if len(parts) >= 5:
|
| 391 |
-
owner = parts[-2]
|
| 392 |
-
name = parts[-1]
|
| 393 |
-
embed_url = f"https://huggingface.co/spaces/{owner}/{name}/embed"
|
| 394 |
-
return embed_url, "iframe"
|
| 395 |
-
except Exception:
|
| 396 |
-
return screenshot_url(url), "snapshot"
|
| 397 |
-
|
| 398 |
-
# Default handling
|
| 399 |
-
return url, "iframe"
|
| 400 |
-
|
| 401 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโ 4. API ROUTES โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 402 |
@app.route('/api/category')
|
| 403 |
def api_category():
|
|
@@ -431,12 +400,7 @@ def api_category():
|
|
| 431 |
})
|
| 432 |
|
| 433 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโ 5. MAIN ROUTES โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 434 |
-
|
| 435 |
-
def home():
|
| 436 |
-
os.makedirs('templates', exist_ok=True)
|
| 437 |
-
|
| 438 |
-
with open('templates/index.html', 'w', encoding='utf-8') as fp:
|
| 439 |
-
fp.write(r'''<!DOCTYPE html>
|
| 440 |
<html>
|
| 441 |
<head>
|
| 442 |
<meta charset="utf-8">
|
|
@@ -454,10 +418,8 @@ body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb;}
|
|
| 454 |
.tab.best.active{background:#38d9a9;color:white;}
|
| 455 |
.tab.new{background:#ffe066;color:#1a202c;}
|
| 456 |
.tab.new.active{background:#ffd43b;color:#1a202c;}
|
| 457 |
-
/* Updated grid to show 2x2 layout */
|
| 458 |
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;padding:0 16px 60px;max-width:1200px;margin:0 auto;}
|
| 459 |
@media(max-width:800px){.grid{grid-template-columns:1fr;}}
|
| 460 |
-
/* Increased card height for larger display */
|
| 461 |
.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;}
|
| 462 |
.frame{flex:1;position:relative;overflow:hidden;}
|
| 463 |
.frame iframe{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0;}
|
|
@@ -484,13 +446,11 @@ body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb;}
|
|
| 484 |
<div class="tabs" id="tabs"></div>
|
| 485 |
<div id="content"></div>
|
| 486 |
<script>
|
| 487 |
-
|
| 488 |
-
const cats = {{cats|tojson}};
|
| 489 |
const tabs = document.getElementById('tabs');
|
| 490 |
const content = document.getElementById('content');
|
| 491 |
let active = "";
|
| 492 |
let currentPage = 1;
|
| 493 |
-
// Simple utility functions
|
| 494 |
function makeRequest(url, method, data, callback) {
|
| 495 |
const xhr = new XMLHttpRequest();
|
| 496 |
xhr.open(method, url, true);
|
|
@@ -510,7 +470,6 @@ function updateTabs() {
|
|
| 510 |
b.classList.toggle('active', b.dataset.c === active);
|
| 511 |
});
|
| 512 |
}
|
| 513 |
-
// Tab handlers
|
| 514 |
function loadCategory(cat, page) {
|
| 515 |
if(cat === active && currentPage === page) return;
|
| 516 |
active = cat;
|
|
@@ -542,7 +501,6 @@ function loadCategory(cat, page) {
|
|
| 542 |
|
| 543 |
html += '</div>';
|
| 544 |
|
| 545 |
-
// Add pagination
|
| 546 |
html += `
|
| 547 |
<div class="pagination">
|
| 548 |
<button ${currentPage <= 1 ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage-1})">ยซ Previous</button>
|
|
@@ -554,8 +512,6 @@ function loadCategory(cat, page) {
|
|
| 554 |
content.innerHTML = html;
|
| 555 |
});
|
| 556 |
}
|
| 557 |
-
// Create tabs
|
| 558 |
-
// Special tabs first (Popular, BEST, NEW)
|
| 559 |
['Popular', 'BEST', 'NEW'].forEach(specialCat => {
|
| 560 |
const b = document.createElement('button');
|
| 561 |
b.className = 'tab ' + specialCat.toLowerCase();
|
|
@@ -564,7 +520,6 @@ function loadCategory(cat, page) {
|
|
| 564 |
b.onclick = function() { loadCategory(specialCat, 1); };
|
| 565 |
tabs.appendChild(b);
|
| 566 |
});
|
| 567 |
-
// Regular category tabs
|
| 568 |
cats.forEach(c => {
|
| 569 |
if (!['Popular', 'BEST', 'NEW'].includes(c)) {
|
| 570 |
const b = document.createElement('button');
|
|
@@ -575,14 +530,14 @@ cats.forEach(c => {
|
|
| 575 |
tabs.appendChild(b);
|
| 576 |
}
|
| 577 |
});
|
| 578 |
-
// Start with Popular tab
|
| 579 |
loadCategory('Popular', 1);
|
| 580 |
</script>
|
| 581 |
</body>
|
| 582 |
-
</html>'''
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
|
|
|
| 586 |
|
| 587 |
if __name__ == '__main__':
|
| 588 |
app.run(host='0.0.0.0', port=7860)
|
|
|
|
| 1 |
+
from flask import Flask, render_template_string, request, jsonify
|
| 2 |
import os, re, json
|
| 3 |
|
| 4 |
app = Flask(__name__)
|
|
|
|
| 76 |
"https://huggingface.co/spaces/VIDraft/FramePack_rotate_landscape",
|
| 77 |
"https://huggingface.co/spaces/fantaxy/Sound-AI-SFX",
|
| 78 |
"https://huggingface.co/spaces/ginigen/VoiceClone-TTS",
|
|
|
|
| 79 |
"https://huggingface.co/spaces/Heartsync/NSFW-image",
|
| 80 |
"https://huggingface.co/spaces/Heartsync/NSFW-detection",
|
| 81 |
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
|
|
|
|
| 83 |
"https://huggingface.co/spaces/ginigen/FLUX-Text-Tree-Image",
|
| 84 |
"https://huggingface.co/spaces/ginigen/text3d-r1",
|
| 85 |
"https://huggingface.co/spaces/VIDraft/stable-diffusion-3.5-large-turboX",
|
|
|
|
| 86 |
],
|
| 87 |
"BEST": [
|
| 88 |
"https://huggingface.co/spaces/Heartsync/Novel-NSFW",
|
|
|
|
| 177 |
"https://huggingface.co/spaces/seawolf2357/flxloraexp",
|
| 178 |
"https://huggingface.co/spaces/ginipick/Time-Stream",
|
| 179 |
"https://huggingface.co/spaces/fantos/textcutobject",
|
|
|
|
|
|
|
| 180 |
],
|
| 181 |
"NEW": [
|
| 182 |
"https://huggingface.co/spaces/ginigen/Family",
|
|
|
|
| 233 |
"https://huggingface.co/spaces/openfree/Cycle-Navigator",
|
| 234 |
"https://huggingface.co/spaces/openfree/DreamO-video",
|
| 235 |
"https://huggingface.co/spaces/Heartsync/NSFW-detection",
|
|
|
|
|
|
|
| 236 |
],
|
| 237 |
"Productivity": [
|
| 238 |
"https://huggingface.co/spaces/ginigen/Markets",
|
|
|
|
| 367 |
def screenshot_url(url):
|
| 368 |
return f"https://image.thum.io/get/fullpage/{url}"
|
| 369 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 370 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโ 4. API ROUTES โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 371 |
@app.route('/api/category')
|
| 372 |
def api_category():
|
|
|
|
| 400 |
})
|
| 401 |
|
| 402 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโ 5. MAIN ROUTES โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 403 |
+
HTML_TEMPLATE = r'''<!DOCTYPE html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
<html>
|
| 405 |
<head>
|
| 406 |
<meta charset="utf-8">
|
|
|
|
| 418 |
.tab.best.active{background:#38d9a9;color:white;}
|
| 419 |
.tab.new{background:#ffe066;color:#1a202c;}
|
| 420 |
.tab.new.active{background:#ffd43b;color:#1a202c;}
|
|
|
|
| 421 |
.grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px;padding:0 16px 60px;max-width:1200px;margin:0 auto;}
|
| 422 |
@media(max-width:800px){.grid{grid-template-columns:1fr;}}
|
|
|
|
| 423 |
.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;}
|
| 424 |
.frame{flex:1;position:relative;overflow:hidden;}
|
| 425 |
.frame iframe{position:absolute;width:166.667%;height:166.667%;transform:scale(.6);transform-origin:top left;border:0;}
|
|
|
|
| 446 |
<div class="tabs" id="tabs"></div>
|
| 447 |
<div id="content"></div>
|
| 448 |
<script>
|
| 449 |
+
const cats = {{ cats|tojson }};
|
|
|
|
| 450 |
const tabs = document.getElementById('tabs');
|
| 451 |
const content = document.getElementById('content');
|
| 452 |
let active = "";
|
| 453 |
let currentPage = 1;
|
|
|
|
| 454 |
function makeRequest(url, method, data, callback) {
|
| 455 |
const xhr = new XMLHttpRequest();
|
| 456 |
xhr.open(method, url, true);
|
|
|
|
| 470 |
b.classList.toggle('active', b.dataset.c === active);
|
| 471 |
});
|
| 472 |
}
|
|
|
|
| 473 |
function loadCategory(cat, page) {
|
| 474 |
if(cat === active && currentPage === page) return;
|
| 475 |
active = cat;
|
|
|
|
| 501 |
|
| 502 |
html += '</div>';
|
| 503 |
|
|
|
|
| 504 |
html += `
|
| 505 |
<div class="pagination">
|
| 506 |
<button ${currentPage <= 1 ? 'disabled' : ''} onclick="loadCategory('${cat}', ${currentPage-1})">ยซ Previous</button>
|
|
|
|
| 512 |
content.innerHTML = html;
|
| 513 |
});
|
| 514 |
}
|
|
|
|
|
|
|
| 515 |
['Popular', 'BEST', 'NEW'].forEach(specialCat => {
|
| 516 |
const b = document.createElement('button');
|
| 517 |
b.className = 'tab ' + specialCat.toLowerCase();
|
|
|
|
| 520 |
b.onclick = function() { loadCategory(specialCat, 1); };
|
| 521 |
tabs.appendChild(b);
|
| 522 |
});
|
|
|
|
| 523 |
cats.forEach(c => {
|
| 524 |
if (!['Popular', 'BEST', 'NEW'].includes(c)) {
|
| 525 |
const b = document.createElement('button');
|
|
|
|
| 530 |
tabs.appendChild(b);
|
| 531 |
}
|
| 532 |
});
|
|
|
|
| 533 |
loadCategory('Popular', 1);
|
| 534 |
</script>
|
| 535 |
</body>
|
| 536 |
+
</html>'''
|
| 537 |
+
|
| 538 |
+
@app.route('/')
|
| 539 |
+
def home():
|
| 540 |
+
return render_template_string(HTML_TEMPLATE, cats=list(CATEGORIES.keys()))
|
| 541 |
|
| 542 |
if __name__ == '__main__':
|
| 543 |
app.run(host='0.0.0.0', port=7860)
|