Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,95 +1,4 @@
|
|
1 |
-
|
2 |
-
const textarea = document.getElementById('batch-urls');
|
3 |
-
const text = textarea.value.trim();
|
4 |
-
|
5 |
-
if (!text) {
|
6 |
-
showStatus('add-status', 'Please enter at least one URL', false);
|
7 |
-
return;
|
8 |
-
}
|
9 |
-
|
10 |
-
// Split by newlines and filter out empty lines
|
11 |
-
let urls = text.split(/\r?\n/).filter(url => url.trim() !== '');
|
12 |
-
|
13 |
-
// Limit to 100 URLs
|
14 |
-
if (urls.length > 100) {
|
15 |
-
showStatus('add-status', 'Too many URLs. Limited to 100 at once.', false);
|
16 |
-
urls = urls.slice(0, 100);
|
17 |
-
}
|
18 |
-
|
19 |
-
if (urls.length === 0) {
|
20 |
-
showStatus('add-status', 'No valid URLs found', false);
|
21 |
-
return;
|
22 |
-
}
|
23 |
-
|
24 |
-
// Show progress bar
|
25 |
-
const progressBar = document.getElementById('progress-bar');
|
26 |
-
const progressFill = document.getElementById('progress-fill');
|
27 |
-
const progressText = document.getElementById('progress-text');
|
28 |
-
progressBar.style.display = 'block';
|
29 |
-
progressFill.style.width = '0%';
|
30 |
-
progressText.textContent = '0%';
|
31 |
-
|
32 |
-
// Add URLs one by one
|
33 |
-
let processed = 0;
|
34 |
-
let succeeded = 0;
|
35 |
-
|
36 |
-
function updateProgress() {
|
37 |
-
const percentage = Math.round((processed / urls.length) * 100);
|
38 |
-
progressFill.style.width = percentage + '%';
|
39 |
-
progressText.textContent = `${processed}/${urls.length} (${percentage}%)`;
|
40 |
-
}
|
41 |
-
|
42 |
-
function addNextUrl(index) {
|
43 |
-
if (index >= urls.length) {
|
44 |
-
// All done
|
45 |
-
setTimeout(() => {
|
46 |
-
progressBar.style.display = 'none';
|
47 |
-
textarea.value = '';
|
48 |
-
showStatus('add-status', `Added ${succeeded} of ${urls.length} URLs successfully`, true);
|
49 |
-
|
50 |
-
// Reload URL list and favorites
|
51 |
-
loadUrlList();
|
52 |
-
if (active === 'Favorites') {
|
53 |
-
loadFavorites(currentPage);
|
54 |
-
}
|
55 |
-
}, 500);
|
56 |
-
return;
|
57 |
-
}
|
58 |
-
|
59 |
-
const url = urls[index].trim();
|
60 |
-
if (!url) {
|
61 |
-
// Skip empty URLs
|
62 |
-
processed++;
|
63 |
-
updateProgress();
|
64 |
-
addNextUrl(index + 1);
|
65 |
-
return;
|
66 |
-
}
|
67 |
-
|
68 |
-
const formData = new FormData();
|
69 |
-
formData.append('url', url);
|
70 |
-
|
71 |
-
makeRequest('/api/url/add', 'POST', formData, function(data) {
|
72 |
-
processed++;
|
73 |
-
|
74 |
-
if (data.success) {
|
75 |
-
succeeded++;
|
76 |
-
|
77 |
-
// Update localStorage
|
78 |
-
const localUrls = loadFromLocalStorage() || [];
|
79 |
-
if (!localUrls.includes(url)) {
|
80 |
-
localUrls.unshift(url);
|
81 |
-
saveToLocalStorage(localUrls);
|
82 |
-
}
|
83 |
-
}
|
84 |
-
|
85 |
-
updateProgress();
|
86 |
-
addNextUrl(index + 1);
|
87 |
-
});
|
88 |
-
}
|
89 |
-
|
90 |
-
// Start adding URLs
|
91 |
-
addNextUrl(0);
|
92 |
-
}from flask import Flask, render_template, request, jsonify
|
93 |
import os, re, json, sqlite3, logging
|
94 |
|
95 |
app = Flask(__name__)
|
@@ -113,7 +22,7 @@ BLOCKED_DOMAINS = [
|
|
113 |
|
114 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโ 2. CURATED CATEGORIES โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
115 |
CATEGORIES = {
|
116 |
-
"Productivity": [
|
117 |
"https://huggingface.co/spaces/ginigen/perflexity-clone",
|
118 |
"https://huggingface.co/spaces/ginipick/IDEA-DESIGN",
|
119 |
"https://huggingface.co/spaces/VIDraft/mouse-webgen",
|
@@ -124,7 +33,7 @@ CATEGORIES = {
|
|
124 |
"https://huggingface.co/spaces/fantaxy/Space-Leaderboard",
|
125 |
"https://huggingface.co/spaces/openfree/Korean-Leaderboard",
|
126 |
],
|
127 |
-
"Multimodal": [
|
128 |
"https://huggingface.co/spaces/openfree/DreamO-video",
|
129 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
|
130 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
|
@@ -134,7 +43,7 @@ CATEGORIES = {
|
|
134 |
"https://huggingface.co/spaces/aiqcamp/MCP-kokoro",
|
135 |
"https://huggingface.co/spaces/aiqcamp/ENGLISH-Speaking-Scoring",
|
136 |
],
|
137 |
-
"Professional": [
|
138 |
"https://huggingface.co/spaces/ginigen/blogger",
|
139 |
"https://huggingface.co/spaces/VIDraft/money-radar",
|
140 |
"https://huggingface.co/spaces/immunobiotech/drug-discovery",
|
@@ -144,7 +53,7 @@ CATEGORIES = {
|
|
144 |
"https://huggingface.co/spaces/ginipick/AgentX-Papers",
|
145 |
"https://huggingface.co/spaces/openfree/Cycle-Navigator",
|
146 |
],
|
147 |
-
"Image": [
|
148 |
"https://huggingface.co/spaces/ginigen/interior-design",
|
149 |
"https://huggingface.co/spaces/ginigen/Workflow-Canvas",
|
150 |
"https://huggingface.co/spaces/ginigen/Multi-LoRAgen",
|
@@ -158,7 +67,7 @@ CATEGORIES = {
|
|
158 |
"https://huggingface.co/spaces/VIDraft/Open-Meme-Studio",
|
159 |
"https://huggingface.co/spaces/ginigen/3D-LLAMA",
|
160 |
],
|
161 |
-
"LLM / VLM": [
|
162 |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-4B",
|
163 |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-12B",
|
164 |
"https://huggingface.co/spaces/ginigen/Mistral-Perflexity",
|
@@ -921,7 +830,6 @@ function loadUrlList() {
|
|
921 |
urlList.innerHTML = html;
|
922 |
});
|
923 |
}
|
924 |
-
}
|
925 |
|
926 |
function addUrl() {
|
927 |
const url = document.getElementById('new-url').value.trim();
|
@@ -955,6 +863,99 @@ function addUrl() {
|
|
955 |
});
|
956 |
}
|
957 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
958 |
function editUrl(url) {
|
959 |
// Decode URL if it was previously escaped
|
960 |
const decodedUrl = url.replace(/\\'/g, "'");
|
|
|
1 |
+
from flask import Flask, render_template, request, jsonify
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
import os, re, json, sqlite3, logging
|
3 |
|
4 |
app = Flask(__name__)
|
|
|
22 |
|
23 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโ 2. CURATED CATEGORIES โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
24 |
CATEGORIES = {
|
25 |
+
"Free AI: Productivity": [
|
26 |
"https://huggingface.co/spaces/ginigen/perflexity-clone",
|
27 |
"https://huggingface.co/spaces/ginipick/IDEA-DESIGN",
|
28 |
"https://huggingface.co/spaces/VIDraft/mouse-webgen",
|
|
|
33 |
"https://huggingface.co/spaces/fantaxy/Space-Leaderboard",
|
34 |
"https://huggingface.co/spaces/openfree/Korean-Leaderboard",
|
35 |
],
|
36 |
+
"Free AI: Multimodal": [
|
37 |
"https://huggingface.co/spaces/openfree/DreamO-video",
|
38 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored-photo",
|
39 |
"https://huggingface.co/spaces/Heartsync/NSFW-Uncensored",
|
|
|
43 |
"https://huggingface.co/spaces/aiqcamp/MCP-kokoro",
|
44 |
"https://huggingface.co/spaces/aiqcamp/ENGLISH-Speaking-Scoring",
|
45 |
],
|
46 |
+
"Free AI: Professional": [
|
47 |
"https://huggingface.co/spaces/ginigen/blogger",
|
48 |
"https://huggingface.co/spaces/VIDraft/money-radar",
|
49 |
"https://huggingface.co/spaces/immunobiotech/drug-discovery",
|
|
|
53 |
"https://huggingface.co/spaces/ginipick/AgentX-Papers",
|
54 |
"https://huggingface.co/spaces/openfree/Cycle-Navigator",
|
55 |
],
|
56 |
+
"Free AI: Image": [
|
57 |
"https://huggingface.co/spaces/ginigen/interior-design",
|
58 |
"https://huggingface.co/spaces/ginigen/Workflow-Canvas",
|
59 |
"https://huggingface.co/spaces/ginigen/Multi-LoRAgen",
|
|
|
67 |
"https://huggingface.co/spaces/VIDraft/Open-Meme-Studio",
|
68 |
"https://huggingface.co/spaces/ginigen/3D-LLAMA",
|
69 |
],
|
70 |
+
"Free AI: LLM / VLM": [
|
71 |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-4B",
|
72 |
"https://huggingface.co/spaces/VIDraft/Gemma-3-R1984-12B",
|
73 |
"https://huggingface.co/spaces/ginigen/Mistral-Perflexity",
|
|
|
830 |
urlList.innerHTML = html;
|
831 |
});
|
832 |
}
|
|
|
833 |
|
834 |
function addUrl() {
|
835 |
const url = document.getElementById('new-url').value.trim();
|
|
|
863 |
});
|
864 |
}
|
865 |
|
866 |
+
function addBatchUrls() {
|
867 |
+
const textarea = document.getElementById('batch-urls');
|
868 |
+
const text = textarea.value.trim();
|
869 |
+
|
870 |
+
if (!text) {
|
871 |
+
showStatus('add-status', 'Please enter at least one URL', false);
|
872 |
+
return;
|
873 |
+
}
|
874 |
+
|
875 |
+
// Split by newlines and filter out empty lines
|
876 |
+
let urls = text.split(/\r?\n/).filter(url => url.trim() !== '');
|
877 |
+
|
878 |
+
// Limit to 100 URLs
|
879 |
+
if (urls.length > 100) {
|
880 |
+
showStatus('add-status', 'Too many URLs. Limited to 100 at once.', false);
|
881 |
+
urls = urls.slice(0, 100);
|
882 |
+
}
|
883 |
+
|
884 |
+
if (urls.length === 0) {
|
885 |
+
showStatus('add-status', 'No valid URLs found', false);
|
886 |
+
return;
|
887 |
+
}
|
888 |
+
|
889 |
+
// Show progress bar
|
890 |
+
const progressBar = document.getElementById('progress-bar');
|
891 |
+
const progressFill = document.getElementById('progress-fill');
|
892 |
+
const progressText = document.getElementById('progress-text');
|
893 |
+
progressBar.style.display = 'block';
|
894 |
+
progressFill.style.width = '0%';
|
895 |
+
progressText.textContent = '0%';
|
896 |
+
|
897 |
+
// Add URLs one by one
|
898 |
+
let processed = 0;
|
899 |
+
let succeeded = 0;
|
900 |
+
|
901 |
+
function updateProgress() {
|
902 |
+
const percentage = Math.round((processed / urls.length) * 100);
|
903 |
+
progressFill.style.width = percentage + '%';
|
904 |
+
progressText.textContent = `${processed}/${urls.length} (${percentage}%)`;
|
905 |
+
}
|
906 |
+
|
907 |
+
function addNextUrl(index) {
|
908 |
+
if (index >= urls.length) {
|
909 |
+
// All done
|
910 |
+
setTimeout(() => {
|
911 |
+
progressBar.style.display = 'none';
|
912 |
+
textarea.value = '';
|
913 |
+
showStatus('add-status', `Added ${succeeded} of ${urls.length} URLs successfully`, true);
|
914 |
+
|
915 |
+
// Reload URL list and favorites
|
916 |
+
loadUrlList();
|
917 |
+
if (active === 'Favorites') {
|
918 |
+
loadFavorites(currentPage);
|
919 |
+
}
|
920 |
+
}, 500);
|
921 |
+
return;
|
922 |
+
}
|
923 |
+
|
924 |
+
const url = urls[index].trim();
|
925 |
+
if (!url) {
|
926 |
+
// Skip empty URLs
|
927 |
+
processed++;
|
928 |
+
updateProgress();
|
929 |
+
addNextUrl(index + 1);
|
930 |
+
return;
|
931 |
+
}
|
932 |
+
|
933 |
+
const formData = new FormData();
|
934 |
+
formData.append('url', url);
|
935 |
+
|
936 |
+
makeRequest('/api/url/add', 'POST', formData, function(data) {
|
937 |
+
processed++;
|
938 |
+
|
939 |
+
if (data.success) {
|
940 |
+
succeeded++;
|
941 |
+
|
942 |
+
// Update localStorage
|
943 |
+
const localUrls = loadFromLocalStorage() || [];
|
944 |
+
if (!localUrls.includes(url)) {
|
945 |
+
localUrls.unshift(url);
|
946 |
+
saveToLocalStorage(localUrls);
|
947 |
+
}
|
948 |
+
}
|
949 |
+
|
950 |
+
updateProgress();
|
951 |
+
addNextUrl(index + 1);
|
952 |
+
});
|
953 |
+
}
|
954 |
+
|
955 |
+
// Start adding URLs
|
956 |
+
addNextUrl(0);
|
957 |
+
}
|
958 |
+
|
959 |
function editUrl(url) {
|
960 |
// Decode URL if it was previously escaped
|
961 |
const decodedUrl = url.replace(/\\'/g, "'");
|