Update app.py
Browse files
app.py
CHANGED
|
@@ -17,8 +17,13 @@ BLOCKED_DOMAINS = [
|
|
| 17 |
|
| 18 |
# ββββββββββββββββββββββββββ 2. CURATED CATEGORIES ββββββββββββββββββββββββββ
|
| 19 |
CATEGORIES = {
|
| 20 |
-
|
| 21 |
"Productivity": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
"https://huggingface.co/spaces/VIDraft/Robo-Beam",
|
| 23 |
"https://huggingface.co/spaces/VIDraft/voice-trans",
|
| 24 |
"https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB",
|
|
@@ -42,8 +47,14 @@ CATEGORIES = {
|
|
| 42 |
"https://huggingface.co/spaces/ginipick/Change-Hair",
|
| 43 |
],
|
| 44 |
"Multimodal": [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
"https://huggingface.co/spaces/ginigen/VEO3-Free",
|
| 46 |
-
"https://huggingface.co/spaces/ginigen/VEO3-
|
| 47 |
"https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX",
|
| 48 |
"https://huggingface.co/spaces/Heartsync/adult",
|
| 49 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
|
|
@@ -64,6 +75,9 @@ CATEGORIES = {
|
|
| 64 |
"https://huggingface.co/spaces/fantaxy/Remove-Video-Background",
|
| 65 |
],
|
| 66 |
"Professional": [
|
|
|
|
|
|
|
|
|
|
| 67 |
"https://huggingface.co/spaces/Heartsync/Novel-NSFW",
|
| 68 |
"https://huggingface.co/spaces/fantaxy/fantasy-novel",
|
| 69 |
"https://huggingface.co/spaces/VIDraft/money-radar",
|
|
@@ -78,6 +92,7 @@ CATEGORIES = {
|
|
| 78 |
"https://huggingface.co/spaces/ginigen/multimodal-chat-mbti-korea",
|
| 79 |
],
|
| 80 |
"Image": [
|
|
|
|
| 81 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL",
|
| 82 |
"https://huggingface.co/spaces/ginigen/FLUX-Ghibli-LoRA2",
|
| 83 |
"https://huggingface.co/spaces/aiqcamp/REMOVAL-TEXT-IMAGE",
|
|
@@ -111,7 +126,7 @@ CATEGORIES = {
|
|
| 111 |
"LLM / VLM": [
|
| 112 |
"https://huggingface.co/spaces/fantaxy/fantasy-novel",
|
| 113 |
"https://huggingface.co/spaces/ginigen/deepseek-r1-0528-API",
|
| 114 |
-
"https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API"
|
| 115 |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
|
| 116 |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528-qwen3-8b",
|
| 117 |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
|
|
@@ -496,13 +511,14 @@ body{margin:0;font-family:Nunito,sans-serif;background:#f6f8fb;}
|
|
| 496 |
</head>
|
| 497 |
<body>
|
| 498 |
<header style="text-align: center; padding: 20px; background: linear-gradient(135deg, #f6f8fb, #e2e8f0); border-bottom: 1px solid #ddd;">
|
| 499 |
-
<h1 style="margin-bottom: 10px;">π
|
| 500 |
<p>
|
| 501 |
<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>
|
| 502 |
</p>
|
| 503 |
</header>
|
| 504 |
<div class="tabs" id="tabs"></div>
|
| 505 |
<div id="content"></div>
|
|
|
|
| 506 |
<script>
|
| 507 |
// Basic configuration
|
| 508 |
const cats = {{cats|tojson}};
|
|
@@ -510,6 +526,7 @@ const tabs = document.getElementById('tabs');
|
|
| 510 |
const content = document.getElementById('content');
|
| 511 |
let active = "";
|
| 512 |
let currentPage = 1;
|
|
|
|
| 513 |
// Simple utility functions
|
| 514 |
function loadHTML(url, callback) {
|
| 515 |
const xhr = new XMLHttpRequest();
|
|
@@ -521,6 +538,7 @@ function loadHTML(url, callback) {
|
|
| 521 |
};
|
| 522 |
xhr.send();
|
| 523 |
}
|
|
|
|
| 524 |
function makeRequest(url, method, data, callback) {
|
| 525 |
const xhr = new XMLHttpRequest();
|
| 526 |
xhr.open(method, url, true);
|
|
@@ -535,11 +553,13 @@ function makeRequest(url, method, data, callback) {
|
|
| 535 |
xhr.send();
|
| 536 |
}
|
| 537 |
}
|
|
|
|
| 538 |
function updateTabs() {
|
| 539 |
Array.from(tabs.children).forEach(b => {
|
| 540 |
b.classList.toggle('active', b.dataset.c === active);
|
| 541 |
});
|
| 542 |
}
|
|
|
|
| 543 |
// Tab handlers
|
| 544 |
function loadCategory(cat, page) {
|
| 545 |
if(cat === active && currentPage === page) return;
|
|
@@ -584,6 +604,7 @@ function loadCategory(cat, page) {
|
|
| 584 |
content.innerHTML = html;
|
| 585 |
});
|
| 586 |
}
|
|
|
|
| 587 |
function loadFavorites(page) {
|
| 588 |
if(active === 'Favorites' && currentPage === page) return;
|
| 589 |
active = 'Favorites';
|
|
@@ -641,6 +662,7 @@ function loadFavorites(page) {
|
|
| 641 |
content.innerHTML = html;
|
| 642 |
});
|
| 643 |
}
|
|
|
|
| 644 |
function loadManage() {
|
| 645 |
if(active === 'Manage') return;
|
| 646 |
active = 'Manage';
|
|
@@ -663,6 +685,7 @@ function loadManage() {
|
|
| 663 |
|
| 664 |
loadUrlList();
|
| 665 |
}
|
|
|
|
| 666 |
// URL management functions
|
| 667 |
function loadUrlList() {
|
| 668 |
makeRequest('/api/favorites?per_page=100', 'GET', null, function(data) {
|
|
@@ -692,6 +715,7 @@ function loadUrlList() {
|
|
| 692 |
urlList.innerHTML = html;
|
| 693 |
});
|
| 694 |
}
|
|
|
|
| 695 |
function addUrl() {
|
| 696 |
const url = document.getElementById('new-url').value.trim();
|
| 697 |
|
|
@@ -715,6 +739,7 @@ function addUrl() {
|
|
| 715 |
}
|
| 716 |
});
|
| 717 |
}
|
|
|
|
| 718 |
function editUrl(url) {
|
| 719 |
// Decode URL if it was previously escaped
|
| 720 |
const decodedUrl = url.replace(/\\'/g, "'");
|
|
@@ -738,6 +763,7 @@ function editUrl(url) {
|
|
| 738 |
}
|
| 739 |
});
|
| 740 |
}
|
|
|
|
| 741 |
function deleteUrl(url) {
|
| 742 |
// Decode URL if it was previously escaped
|
| 743 |
const decodedUrl = url.replace(/\\'/g, "'");
|
|
@@ -758,6 +784,7 @@ function deleteUrl(url) {
|
|
| 758 |
}
|
| 759 |
});
|
| 760 |
}
|
|
|
|
| 761 |
function showStatus(id, message, success) {
|
| 762 |
const status = document.getElementById(id);
|
| 763 |
status.textContent = message;
|
|
@@ -766,6 +793,7 @@ function showStatus(id, message, success) {
|
|
| 766 |
status.className = 'status';
|
| 767 |
}, 3000);
|
| 768 |
}
|
|
|
|
| 769 |
// Create tabs
|
| 770 |
// Favorites tab first
|
| 771 |
const favTab = document.createElement('button');
|
|
@@ -774,6 +802,7 @@ favTab.textContent = 'Favorites';
|
|
| 774 |
favTab.dataset.c = 'Favorites';
|
| 775 |
favTab.onclick = function() { loadFavorites(1); };
|
| 776 |
tabs.appendChild(favTab);
|
|
|
|
| 777 |
// Category tabs
|
| 778 |
cats.forEach(c => {
|
| 779 |
const b = document.createElement('button');
|
|
@@ -783,6 +812,7 @@ cats.forEach(c => {
|
|
| 783 |
b.onclick = function() { loadCategory(c, 1); };
|
| 784 |
tabs.appendChild(b);
|
| 785 |
});
|
|
|
|
| 786 |
// Manage tab last
|
| 787 |
const manageTab = document.createElement('button');
|
| 788 |
manageTab.className = 'tab manage';
|
|
@@ -790,6 +820,7 @@ manageTab.textContent = 'Manage';
|
|
| 790 |
manageTab.dataset.c = 'Manage';
|
| 791 |
manageTab.onclick = function() { loadManage(); };
|
| 792 |
tabs.appendChild(manageTab);
|
|
|
|
| 793 |
// Start with Favorites tab
|
| 794 |
loadFavorites(1);
|
| 795 |
</script>
|
|
|
|
| 17 |
|
| 18 |
# ββββββββββββββββββββββββββ 2. CURATED CATEGORIES ββββββββββββββββββββββββββ
|
| 19 |
CATEGORIES = {
|
| 20 |
+
|
| 21 |
"Productivity": [
|
| 22 |
+
|
| 23 |
+
"https://huggingface.co/spaces/aiqtech/Heatmap-Leaderboard",
|
| 24 |
+
"https://huggingface.co/spaces/VIDraft/DNA-CASINO",
|
| 25 |
+
|
| 26 |
+
"https://huggingface.co/spaces/openfree/Open-GAMMA",
|
| 27 |
"https://huggingface.co/spaces/VIDraft/Robo-Beam",
|
| 28 |
"https://huggingface.co/spaces/VIDraft/voice-trans",
|
| 29 |
"https://huggingface.co/spaces/Heartsync/FREE-NSFW-HUB",
|
|
|
|
| 47 |
"https://huggingface.co/spaces/ginipick/Change-Hair",
|
| 48 |
],
|
| 49 |
"Multimodal": [
|
| 50 |
+
|
| 51 |
+
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
|
| 52 |
+
|
| 53 |
+
"https://huggingface.co/spaces/fantaxy/YTB-TEST",
|
| 54 |
+
"https://huggingface.co/spaces/ginigen/Seedance-Free",
|
| 55 |
+
"https://huggingface.co/spaces/Heartsync/VEO3-RealTime",
|
| 56 |
"https://huggingface.co/spaces/ginigen/VEO3-Free",
|
| 57 |
+
"https://huggingface.co/spaces/ginigen/VEO3-Directors",
|
| 58 |
"https://huggingface.co/spaces/Heartsync/WAN2-1-fast-T2V-FusioniX",
|
| 59 |
"https://huggingface.co/spaces/Heartsync/adult",
|
| 60 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
|
|
|
|
| 75 |
"https://huggingface.co/spaces/fantaxy/Remove-Video-Background",
|
| 76 |
],
|
| 77 |
"Professional": [
|
| 78 |
+
"https://huggingface.co/spaces/Heartsync/NSFW-novels",
|
| 79 |
+
"https://huggingface.co/spaces/aiqtech/SOMA-Oriental",
|
| 80 |
+
"https://huggingface.co/spaces/VIDraft/SOMA-AGI",
|
| 81 |
"https://huggingface.co/spaces/Heartsync/Novel-NSFW",
|
| 82 |
"https://huggingface.co/spaces/fantaxy/fantasy-novel",
|
| 83 |
"https://huggingface.co/spaces/VIDraft/money-radar",
|
|
|
|
| 92 |
"https://huggingface.co/spaces/ginigen/multimodal-chat-mbti-korea",
|
| 93 |
],
|
| 94 |
"Image": [
|
| 95 |
+
"https://huggingface.co/spaces/ginigen/Flux-Kontext-FaceLORA",
|
| 96 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-REAL",
|
| 97 |
"https://huggingface.co/spaces/ginigen/FLUX-Ghibli-LoRA2",
|
| 98 |
"https://huggingface.co/spaces/aiqcamp/REMOVAL-TEXT-IMAGE",
|
|
|
|
| 126 |
"LLM / VLM": [
|
| 127 |
"https://huggingface.co/spaces/fantaxy/fantasy-novel",
|
| 128 |
"https://huggingface.co/spaces/ginigen/deepseek-r1-0528-API",
|
| 129 |
+
"https://huggingface.co/spaces/aiqcamp/Mistral-Devstral-API",
|
| 130 |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
|
| 131 |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528-qwen3-8b",
|
| 132 |
"https://huggingface.co/spaces/aiqcamp/deepseek-r1-0528",
|
|
|
|
| 511 |
</head>
|
| 512 |
<body>
|
| 513 |
<header style="text-align: center; padding: 20px; background: linear-gradient(135deg, #f6f8fb, #e2e8f0); border-bottom: 1px solid #ddd;">
|
| 514 |
+
<h1 style="margin-bottom: 10px;">πAI Playground</h1>
|
| 515 |
<p>
|
| 516 |
<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>
|
| 517 |
</p>
|
| 518 |
</header>
|
| 519 |
<div class="tabs" id="tabs"></div>
|
| 520 |
<div id="content"></div>
|
| 521 |
+
|
| 522 |
<script>
|
| 523 |
// Basic configuration
|
| 524 |
const cats = {{cats|tojson}};
|
|
|
|
| 526 |
const content = document.getElementById('content');
|
| 527 |
let active = "";
|
| 528 |
let currentPage = 1;
|
| 529 |
+
|
| 530 |
// Simple utility functions
|
| 531 |
function loadHTML(url, callback) {
|
| 532 |
const xhr = new XMLHttpRequest();
|
|
|
|
| 538 |
};
|
| 539 |
xhr.send();
|
| 540 |
}
|
| 541 |
+
|
| 542 |
function makeRequest(url, method, data, callback) {
|
| 543 |
const xhr = new XMLHttpRequest();
|
| 544 |
xhr.open(method, url, true);
|
|
|
|
| 553 |
xhr.send();
|
| 554 |
}
|
| 555 |
}
|
| 556 |
+
|
| 557 |
function updateTabs() {
|
| 558 |
Array.from(tabs.children).forEach(b => {
|
| 559 |
b.classList.toggle('active', b.dataset.c === active);
|
| 560 |
});
|
| 561 |
}
|
| 562 |
+
|
| 563 |
// Tab handlers
|
| 564 |
function loadCategory(cat, page) {
|
| 565 |
if(cat === active && currentPage === page) return;
|
|
|
|
| 604 |
content.innerHTML = html;
|
| 605 |
});
|
| 606 |
}
|
| 607 |
+
|
| 608 |
function loadFavorites(page) {
|
| 609 |
if(active === 'Favorites' && currentPage === page) return;
|
| 610 |
active = 'Favorites';
|
|
|
|
| 662 |
content.innerHTML = html;
|
| 663 |
});
|
| 664 |
}
|
| 665 |
+
|
| 666 |
function loadManage() {
|
| 667 |
if(active === 'Manage') return;
|
| 668 |
active = 'Manage';
|
|
|
|
| 685 |
|
| 686 |
loadUrlList();
|
| 687 |
}
|
| 688 |
+
|
| 689 |
// URL management functions
|
| 690 |
function loadUrlList() {
|
| 691 |
makeRequest('/api/favorites?per_page=100', 'GET', null, function(data) {
|
|
|
|
| 715 |
urlList.innerHTML = html;
|
| 716 |
});
|
| 717 |
}
|
| 718 |
+
|
| 719 |
function addUrl() {
|
| 720 |
const url = document.getElementById('new-url').value.trim();
|
| 721 |
|
|
|
|
| 739 |
}
|
| 740 |
});
|
| 741 |
}
|
| 742 |
+
|
| 743 |
function editUrl(url) {
|
| 744 |
// Decode URL if it was previously escaped
|
| 745 |
const decodedUrl = url.replace(/\\'/g, "'");
|
|
|
|
| 763 |
}
|
| 764 |
});
|
| 765 |
}
|
| 766 |
+
|
| 767 |
function deleteUrl(url) {
|
| 768 |
// Decode URL if it was previously escaped
|
| 769 |
const decodedUrl = url.replace(/\\'/g, "'");
|
|
|
|
| 784 |
}
|
| 785 |
});
|
| 786 |
}
|
| 787 |
+
|
| 788 |
function showStatus(id, message, success) {
|
| 789 |
const status = document.getElementById(id);
|
| 790 |
status.textContent = message;
|
|
|
|
| 793 |
status.className = 'status';
|
| 794 |
}, 3000);
|
| 795 |
}
|
| 796 |
+
|
| 797 |
// Create tabs
|
| 798 |
// Favorites tab first
|
| 799 |
const favTab = document.createElement('button');
|
|
|
|
| 802 |
favTab.dataset.c = 'Favorites';
|
| 803 |
favTab.onclick = function() { loadFavorites(1); };
|
| 804 |
tabs.appendChild(favTab);
|
| 805 |
+
|
| 806 |
// Category tabs
|
| 807 |
cats.forEach(c => {
|
| 808 |
const b = document.createElement('button');
|
|
|
|
| 812 |
b.onclick = function() { loadCategory(c, 1); };
|
| 813 |
tabs.appendChild(b);
|
| 814 |
});
|
| 815 |
+
|
| 816 |
// Manage tab last
|
| 817 |
const manageTab = document.createElement('button');
|
| 818 |
manageTab.className = 'tab manage';
|
|
|
|
| 820 |
manageTab.dataset.c = 'Manage';
|
| 821 |
manageTab.onclick = function() { loadManage(); };
|
| 822 |
tabs.appendChild(manageTab);
|
| 823 |
+
|
| 824 |
// Start with Favorites tab
|
| 825 |
loadFavorites(1);
|
| 826 |
</script>
|