Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -2,7 +2,7 @@ from flask import Flask, render_template, request, redirect, url_for, jsonify, s
|
|
2 |
import requests
|
3 |
import os
|
4 |
from datetime import timedelta
|
5 |
-
from urllib.parse import urlparse
|
6 |
|
7 |
app = Flask(__name__)
|
8 |
app.secret_key = os.urandom(24) # Session encryption key
|
@@ -55,6 +55,15 @@ def extract_model_info(url):
|
|
55 |
|
56 |
return None
|
57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
# Extract title from the last part of URL
|
59 |
def extract_title(url):
|
60 |
parts = url.split("/")
|
@@ -83,40 +92,6 @@ def validate_token(token):
|
|
83 |
|
84 |
return False, None
|
85 |
|
86 |
-
# Proxy route to bypass X-Frame-Options
|
87 |
-
@app.route('/proxy/<path:url>')
|
88 |
-
def proxy(url):
|
89 |
-
# Authorization header if user is logged in
|
90 |
-
headers = {}
|
91 |
-
if 'token' in session:
|
92 |
-
headers["Authorization"] = f"Bearer {session['token']}"
|
93 |
-
|
94 |
-
try:
|
95 |
-
# Parse URL to ensure it's safe
|
96 |
-
parsed_url = urlparse(url)
|
97 |
-
if not parsed_url.netloc.endswith('huggingface.co'):
|
98 |
-
return "Only Huggingface URLs are allowed", 403
|
99 |
-
|
100 |
-
# Make request to the target URL
|
101 |
-
response = requests.get(url, headers=headers, stream=True)
|
102 |
-
|
103 |
-
# Create response
|
104 |
-
resp = Response(
|
105 |
-
response.iter_content(chunk_size=10*1024),
|
106 |
-
content_type=response.headers.get('Content-Type')
|
107 |
-
)
|
108 |
-
|
109 |
-
# Remove headers that prevent iframe embedding
|
110 |
-
if 'X-Frame-Options' in resp.headers:
|
111 |
-
resp.headers.remove('X-Frame-Options')
|
112 |
-
if 'Content-Security-Policy' in resp.headers:
|
113 |
-
resp.headers.remove('Content-Security-Policy')
|
114 |
-
|
115 |
-
return resp
|
116 |
-
except Exception as e:
|
117 |
-
print(f"Proxy error: {e}")
|
118 |
-
return f"Error: {str(e)}", 500
|
119 |
-
|
120 |
# Homepage route
|
121 |
@app.route('/')
|
122 |
def home():
|
@@ -128,12 +103,12 @@ def login():
|
|
128 |
token = request.form.get('token', '')
|
129 |
|
130 |
if not token:
|
131 |
-
return jsonify({'success': False, 'message': '
|
132 |
|
133 |
is_valid, user_info = validate_token(token)
|
134 |
|
135 |
if not is_valid or not user_info:
|
136 |
-
return jsonify({'success': False, 'message': '
|
137 |
|
138 |
# Find username
|
139 |
username = None
|
@@ -144,7 +119,7 @@ def login():
|
|
144 |
elif 'username' in user_info:
|
145 |
username = user_info['username']
|
146 |
else:
|
147 |
-
username = '
|
148 |
|
149 |
# Save to session
|
150 |
session['token'] = token
|
@@ -180,6 +155,7 @@ def get_urls():
|
|
180 |
|
181 |
results.append({
|
182 |
'url': url,
|
|
|
183 |
'title': title,
|
184 |
'model_info': model_info
|
185 |
})
|
@@ -206,7 +182,7 @@ if __name__ == '__main__':
|
|
206 |
<head>
|
207 |
<meta charset="UTF-8">
|
208 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
209 |
-
<title
|
210 |
<style>
|
211 |
body {
|
212 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
@@ -296,7 +272,7 @@ if __name__ == '__main__':
|
|
296 |
|
297 |
.grid-container {
|
298 |
display: grid;
|
299 |
-
grid-template-columns: repeat(auto-fill, minmax(
|
300 |
gap: 1.5rem;
|
301 |
}
|
302 |
|
@@ -307,7 +283,7 @@ if __name__ == '__main__':
|
|
307 |
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
308 |
display: flex;
|
309 |
flex-direction: column;
|
310 |
-
height:
|
311 |
}
|
312 |
|
313 |
.grid-header {
|
@@ -500,7 +476,7 @@ if __name__ == '__main__':
|
|
500 |
async function handleApiResponse(response) {
|
501 |
if (!response.ok) {
|
502 |
const errorText = await response.text();
|
503 |
-
throw new Error(`API
|
504 |
}
|
505 |
return response.json();
|
506 |
}
|
@@ -521,14 +497,14 @@ if __name__ == '__main__':
|
|
521 |
loadUrls();
|
522 |
}
|
523 |
} catch (error) {
|
524 |
-
console.error('
|
525 |
}
|
526 |
}
|
527 |
|
528 |
// Login process
|
529 |
async function login(token) {
|
530 |
if (!token.trim()) {
|
531 |
-
showMessage('
|
532 |
return;
|
533 |
}
|
534 |
|
@@ -552,16 +528,16 @@ if __name__ == '__main__':
|
|
552 |
elements.loginSection.style.display = 'none';
|
553 |
elements.loggedInSection.style.display = 'block';
|
554 |
|
555 |
-
showMessage(
|
556 |
|
557 |
// Load URL list
|
558 |
loadUrls();
|
559 |
} else {
|
560 |
-
showMessage(data.message || '
|
561 |
}
|
562 |
} catch (error) {
|
563 |
-
console.error('
|
564 |
-
showMessage(
|
565 |
} finally {
|
566 |
setLoading(false);
|
567 |
}
|
@@ -586,14 +562,14 @@ if __name__ == '__main__':
|
|
586 |
elements.loginSection.style.display = 'block';
|
587 |
elements.loggedInSection.style.display = 'none';
|
588 |
|
589 |
-
showMessage('
|
590 |
|
591 |
// Clear grid
|
592 |
elements.gridContainer.innerHTML = '';
|
593 |
}
|
594 |
} catch (error) {
|
595 |
-
console.error('
|
596 |
-
showMessage(
|
597 |
} finally {
|
598 |
setLoading(false);
|
599 |
}
|
@@ -611,8 +587,8 @@ if __name__ == '__main__':
|
|
611 |
|
612 |
renderGrid(urls);
|
613 |
} catch (error) {
|
614 |
-
console.error('URL
|
615 |
-
showMessage(`URL
|
616 |
} finally {
|
617 |
setLoading(false);
|
618 |
}
|
@@ -632,10 +608,7 @@ if __name__ == '__main__':
|
|
632 |
}
|
633 |
|
634 |
urls.forEach(item => {
|
635 |
-
const { url, title } = item;
|
636 |
-
|
637 |
-
// Create proxy URL
|
638 |
-
const proxyUrl = `/proxy/${encodeURIComponent(url)}`;
|
639 |
|
640 |
// Create grid item
|
641 |
const gridItem = document.createElement('div');
|
@@ -663,12 +636,13 @@ if __name__ == '__main__':
|
|
663 |
const content = document.createElement('div');
|
664 |
content.className = 'grid-content';
|
665 |
|
666 |
-
// Create iframe to display the
|
667 |
const iframe = document.createElement('iframe');
|
668 |
-
iframe.src =
|
669 |
iframe.title = title;
|
670 |
-
iframe.sandbox = 'allow-same-origin allow-scripts allow-popups allow-forms';
|
671 |
iframe.allow = 'accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi';
|
|
|
|
|
672 |
iframe.loading = 'lazy'; // Lazy load iframes for better performance
|
673 |
|
674 |
content.appendChild(iframe);
|
|
|
2 |
import requests
|
3 |
import os
|
4 |
from datetime import timedelta
|
5 |
+
from urllib.parse import urlparse, urljoin
|
6 |
|
7 |
app = Flask(__name__)
|
8 |
app.secret_key = os.urandom(24) # Session encryption key
|
|
|
55 |
|
56 |
return None
|
57 |
|
58 |
+
# Extract direct embed URL
|
59 |
+
def get_embed_url(url):
|
60 |
+
model_info = extract_model_info(url)
|
61 |
+
if not model_info or model_info['type'] != 'spaces':
|
62 |
+
return url
|
63 |
+
|
64 |
+
# For spaces, use the embedded version
|
65 |
+
return f"https://huggingface.co/spaces/{model_info['owner']}/{model_info['repo']}/embed"
|
66 |
+
|
67 |
# Extract title from the last part of URL
|
68 |
def extract_title(url):
|
69 |
parts = url.split("/")
|
|
|
92 |
|
93 |
return False, None
|
94 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
# Homepage route
|
96 |
@app.route('/')
|
97 |
def home():
|
|
|
103 |
token = request.form.get('token', '')
|
104 |
|
105 |
if not token:
|
106 |
+
return jsonify({'success': False, 'message': 'ํ ํฐ์ ์
๋ ฅํด์ฃผ์ธ์.'})
|
107 |
|
108 |
is_valid, user_info = validate_token(token)
|
109 |
|
110 |
if not is_valid or not user_info:
|
111 |
+
return jsonify({'success': False, 'message': '์ ํจํ์ง ์์ ํ ํฐ์
๋๋ค.'})
|
112 |
|
113 |
# Find username
|
114 |
username = None
|
|
|
119 |
elif 'username' in user_info:
|
120 |
username = user_info['username']
|
121 |
else:
|
122 |
+
username = '์ธ์ฆ๋ ์ฌ์ฉ์'
|
123 |
|
124 |
# Save to session
|
125 |
session['token'] = token
|
|
|
155 |
|
156 |
results.append({
|
157 |
'url': url,
|
158 |
+
'embedUrl': get_embed_url(url),
|
159 |
'title': title,
|
160 |
'model_info': model_info
|
161 |
})
|
|
|
182 |
<head>
|
183 |
<meta charset="UTF-8">
|
184 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
185 |
+
<title>ํ๊น
ํ์ด์ค ์คํ์ด์ค ๊ทธ๋ฆฌ๋</title>
|
186 |
<style>
|
187 |
body {
|
188 |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
|
272 |
|
273 |
.grid-container {
|
274 |
display: grid;
|
275 |
+
grid-template-columns: repeat(auto-fill, minmax(480px, 1fr));
|
276 |
gap: 1.5rem;
|
277 |
}
|
278 |
|
|
|
283 |
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
284 |
display: flex;
|
285 |
flex-direction: column;
|
286 |
+
height: 650px;
|
287 |
}
|
288 |
|
289 |
.grid-header {
|
|
|
476 |
async function handleApiResponse(response) {
|
477 |
if (!response.ok) {
|
478 |
const errorText = await response.text();
|
479 |
+
throw new Error(`API ์ค๋ฅ (${response.status}): ${errorText}`);
|
480 |
}
|
481 |
return response.json();
|
482 |
}
|
|
|
497 |
loadUrls();
|
498 |
}
|
499 |
} catch (error) {
|
500 |
+
console.error('์ธ์
์ํ ํ์ธ ์ค๋ฅ:', error);
|
501 |
}
|
502 |
}
|
503 |
|
504 |
// Login process
|
505 |
async function login(token) {
|
506 |
if (!token.trim()) {
|
507 |
+
showMessage('ํ ํฐ์ ์
๋ ฅํด์ฃผ์ธ์.', true);
|
508 |
return;
|
509 |
}
|
510 |
|
|
|
528 |
elements.loginSection.style.display = 'none';
|
529 |
elements.loggedInSection.style.display = 'block';
|
530 |
|
531 |
+
showMessage(`${state.username}๋์ผ๋ก ๋ก๊ทธ์ธ๋์์ต๋๋ค.`);
|
532 |
|
533 |
// Load URL list
|
534 |
loadUrls();
|
535 |
} else {
|
536 |
+
showMessage(data.message || '๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค.', true);
|
537 |
}
|
538 |
} catch (error) {
|
539 |
+
console.error('๋ก๊ทธ์ธ ์ค๋ฅ:', error);
|
540 |
+
showMessage(`๋ก๊ทธ์ธ ์ค๋ฅ: ${error.message}`, true);
|
541 |
} finally {
|
542 |
setLoading(false);
|
543 |
}
|
|
|
562 |
elements.loginSection.style.display = 'block';
|
563 |
elements.loggedInSection.style.display = 'none';
|
564 |
|
565 |
+
showMessage('๋ก๊ทธ์์๋์์ต๋๋ค.');
|
566 |
|
567 |
// Clear grid
|
568 |
elements.gridContainer.innerHTML = '';
|
569 |
}
|
570 |
} catch (error) {
|
571 |
+
console.error('๋ก๊ทธ์์ ์ค๋ฅ:', error);
|
572 |
+
showMessage(`๋ก๊ทธ์์ ์ค๋ฅ: ${error.message}`, true);
|
573 |
} finally {
|
574 |
setLoading(false);
|
575 |
}
|
|
|
587 |
|
588 |
renderGrid(urls);
|
589 |
} catch (error) {
|
590 |
+
console.error('URL ๋ชฉ๋ก ๋ก๋ ์ค๋ฅ:', error);
|
591 |
+
showMessage(`URL ๋ก๋ ์ค๋ฅ: ${error.message}`, true);
|
592 |
} finally {
|
593 |
setLoading(false);
|
594 |
}
|
|
|
608 |
}
|
609 |
|
610 |
urls.forEach(item => {
|
611 |
+
const { url, embedUrl, title } = item;
|
|
|
|
|
|
|
612 |
|
613 |
// Create grid item
|
614 |
const gridItem = document.createElement('div');
|
|
|
636 |
const content = document.createElement('div');
|
637 |
content.className = 'grid-content';
|
638 |
|
639 |
+
// Create iframe to display the content
|
640 |
const iframe = document.createElement('iframe');
|
641 |
+
iframe.src = embedUrl; // Use the embed URL directly
|
642 |
iframe.title = title;
|
|
|
643 |
iframe.allow = 'accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi';
|
644 |
+
iframe.setAttribute('allowfullscreen', '');
|
645 |
+
iframe.setAttribute('frameborder', '0');
|
646 |
iframe.loading = 'lazy'; // Lazy load iframes for better performance
|
647 |
|
648 |
content.appendChild(iframe);
|