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);
|