Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
import requests | |
import gradio as gr | |
from datetime import datetime | |
import random | |
USERNAME = "openfree" | |
def format_timestamp(timestamp): | |
if not timestamp: | |
return 'N/A' | |
try: | |
# ๋ฌธ์์ด์ธ ๊ฒฝ์ฐ | |
if isinstance(timestamp, str): | |
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00')) | |
# ์ ์(๋ฐ๋ฆฌ์ด)์ธ ๊ฒฝ์ฐ | |
elif isinstance(timestamp, (int, float)): | |
dt = datetime.fromtimestamp(timestamp / 1000) # ๋ฐ๋ฆฌ์ด๋ฅผ ์ด๋ก ๋ณํ | |
else: | |
return 'N/A' | |
return dt.strftime('%Y-%m-%d %H:%M') | |
except Exception as e: | |
print(f"Timestamp conversion error: {str(e)} for timestamp: {timestamp}") | |
return 'N/A' | |
def should_exclude_space(space_name): | |
"""ํน์ ์คํ์ด์ค๋ฅผ ์ ์ธํ๋ ํํฐ ํจ์""" | |
exclude_keywords = [ | |
'mixgen3', 'ginid', 'mouse', 'flxtrainlora', | |
'vidslicegpu', 'stickimg', 'ultpixgen', 'SORA', | |
'badassgi', 'newsplus', 'chargen', 'news', | |
'testhtml' | |
] | |
return any(keyword.lower() in space_name.lower() for keyword in exclude_keywords) | |
def get_pastel_color(index): | |
"""Generate unique pastel colors based on index""" | |
pastel_colors = [ | |
'#FFE6E6', # ์ฐํ ๋ถํ | |
'#FFE6FF', # ์ฐํ ๋ณด๋ผ | |
'#E6E6FF', # ์ฐํ ํ๋ | |
'#E6FFFF', # ์ฐํ ํ๋ | |
'#E6FFE6', # ์ฐํ ์ด๋ก | |
'#FFFFE6', # ์ฐํ ๋ ธ๋ | |
'#FFF0E6', # ์ฐํ ์ฃผํฉ | |
'#F0E6FF', # ์ฐํ ๋ผ๋ฒค๋ | |
'#FFE6F0', # ์ฐํ ๋ก์ฆ | |
'#E6FFF0', # ์ฐํ ๋ฏผํธ | |
'#F0FFE6', # ์ฐํ ๋ผ์ | |
'#FFE6EB', # ์ฐํ ์ฝ๋ | |
'#E6EBFF', # ์ฐํ ํผํ๋ธ๋ฃจ | |
'#FFE6F5', # ์ฐํ ํํฌ | |
'#E6FFF5', # ์ฐํ ํฐ์ฝ์ด์ฆ | |
'#F5E6FF', # ์ฐํ ๋ชจ๋ธ | |
'#FFE6EC', # ์ฐํ ์ด๋ชฌ | |
'#E6FFEC', # ์ฐํ ์คํ๋ง๊ทธ๋ฆฐ | |
'#ECE6FF', # ์ฐํ ํ๋ฆฌ์ํด | |
'#FFE6F7', # ์ฐํ ๋งค๊ทธ๋๋ฆฌ์ | |
] | |
return pastel_colors[index % len(pastel_colors)] | |
def get_space_card(space, index): | |
"""Generate HTML card for a space with colorful design and lots of emojis""" | |
space_id = space.get('id', '') | |
space_name = space_id.split('/')[-1] | |
likes = space.get('likes', 0) | |
created_at = format_timestamp(space.get('createdAt')) | |
sdk = space.get('sdk', 'N/A') | |
# SDK๋ณ ์ด๋ชจ์ง ๋ฐ ๊ด๋ จ ์ด๋ชจ์ง ์ธํธ | |
sdk_emoji_sets = { | |
'gradio': { | |
'main': '๐จ', | |
'related': ['๐ผ๏ธ', '๐ญ', '๐ช', '๐ ', '๐ก', '๐ข', '๐ฏ', '๐ฒ', '๐ฐ', '๐ณ'] | |
}, | |
'streamlit': { | |
'main': 'โก', | |
'related': ['๐ซ', 'โจ', 'โญ', '๐', '๐ฅ', 'โก', '๐ฅ', '๐', '๐', '๐'] | |
}, | |
'docker': { | |
'main': '๐ณ', | |
'related': ['๐', '๐', '๐', '๐ข', 'โด๏ธ', '๐ฅ๏ธ', '๐ ', '๐ก', '๐ฆ', '๐ฌ'] | |
}, | |
'static': { | |
'main': '๐', | |
'related': ['๐', '๐ฐ', '๐', '๐๏ธ', '๐', '๐', '๐', '๐', '๐', '๐'] | |
}, | |
'panel': { | |
'main': '๐', | |
'related': ['๐', '๐', '๐น', '๐', '๐', '๐', '๐บ๏ธ', '๐ฏ', '๐', '๐'] | |
}, | |
'N/A': { | |
'main': '๐ง', | |
'related': ['๐จ', 'โ๏ธ', '๐ ๏ธ', 'โ๏ธ', '๐ฉ', 'โ๏ธ', 'โก', '๐', '๐ก', '๐'] | |
} | |
} | |
# SDK์ ๋ฐ๋ฅธ ์ด๋ชจ์ง ์ ํ | |
sdk_lower = sdk.lower() | |
bg_color = get_pastel_color(index) # ์ธ๋ฑ์ค ๊ธฐ๋ฐ ์์ ์ ํ | |
emoji_set = sdk_emoji_sets.get(sdk_lower, sdk_emoji_sets['N/A']) | |
main_emoji = emoji_set['main'] | |
# ๋๋คํ๊ฒ 3๊ฐ์ ๊ด๋ จ ์ด๋ชจ์ง ์ ํ | |
decorative_emojis = random.sample(emoji_set['related'], 3) | |
# ์ถ๊ฐ ์ฅ์์ฉ ์ด๋ชจ์ง | |
general_emojis = ['๐', '๐ซ', 'โญ', '๐', 'โจ', '๐ฅ', '๐ฅ', '๐', '๐ฏ', '๐จ', | |
'๐ญ', '๐ช', '๐ข', '๐ก', '๐ ', '๐ช', '๐ญ', '๐จ', '๐ฏ', '๐ฒ'] | |
random_emojis = random.sample(general_emojis, 3) | |
# ์ข์์ ์์ ๋ฐ๋ฅธ ํํธ ์ด๋ชจ์ง | |
heart_emoji = 'โค๏ธ' if likes > 100 else '๐' if likes > 50 else '๐' if likes > 10 else '๐ค' | |
return f""" | |
<div style='border: none; | |
padding: 25px; | |
margin: 15px; | |
border-radius: 20px; | |
background-color: {bg_color}; | |
box-shadow: 0 4px 15px rgba(0,0,0,0.1); | |
transition: all 0.3s ease-in-out; | |
position: relative; | |
overflow: hidden;' | |
onmouseover='this.style.transform="translateY(-5px) scale(1.02)"; this.style.boxShadow="0 8px 25px rgba(0,0,0,0.15)"' | |
onmouseout='this.style.transform="translateY(0) scale(1)"; this.style.boxShadow="0 4px 15px rgba(0,0,0,0.1)"'> | |
<div style='position: absolute; top: -15px; right: -15px; font-size: 100px; opacity: 0.1;'> | |
{main_emoji} | |
</div> | |
<div style='position: absolute; top: 10px; right: 10px; font-size: 20px;'> | |
{decorative_emojis[0]} | |
</div> | |
<div style='position: absolute; bottom: 10px; left: 10px; font-size: 20px;'> | |
{decorative_emojis[1]} | |
</div> | |
<div style='position: absolute; top: 50%; right: 10px; font-size: 20px;'> | |
{decorative_emojis[2]} | |
</div> | |
<h3 style='color: #2d2d2d; | |
margin: 0 0 20px 0; | |
font-size: 1.4em; | |
display: flex; | |
align-items: center; | |
gap: 10px;'> | |
<span style='font-size: 1.3em'>{random_emojis[0]}</span> | |
<a href='https://huggingface.co/spaces/{space_id}' target='_blank' | |
style='text-decoration: none; color: #2d2d2d;'> | |
{space_name} | |
</a> | |
<span style='font-size: 1.3em'>{random_emojis[1]}</span> | |
</h3> | |
<div style='margin: 15px 0; color: #444; background: rgba(255,255,255,0.5); | |
padding: 15px; border-radius: 12px;'> | |
<p style='margin: 8px 0;'> | |
<strong>SDK:</strong> {main_emoji} {sdk} {decorative_emojis[0]} | |
</p> | |
<p style='margin: 8px 0;'> | |
<strong>Created:</strong> ๐ {created_at} โฐ | |
</p> | |
<p style='margin: 8px 0;'> | |
<strong>Likes:</strong> {heart_emoji} {likes} {random_emojis[2]} | |
</p> | |
</div> | |
<div style='margin-top: 20px; | |
display: flex; | |
justify-content: space-between; | |
align-items: center;'> | |
<a href='https://huggingface.co/spaces/{space_id}' target='_blank' | |
style='background: linear-gradient(45deg, #0084ff, #00a3ff); | |
color: white; | |
padding: 10px 20px; | |
border-radius: 15px; | |
text-decoration: none; | |
display: inline-flex; | |
align-items: center; | |
gap: 8px; | |
font-weight: 500; | |
transition: all 0.3s; | |
box-shadow: 0 2px 8px rgba(0,132,255,0.3);' | |
onmouseover='this.style.transform="scale(1.05)"; this.style.boxShadow="0 4px 12px rgba(0,132,255,0.4)"' | |
onmouseout='this.style.transform="scale(1)"; this.style.boxShadow="0 2px 8px rgba(0,132,255,0.3)"'> | |
<span>View Space</span> ๐ {random_emojis[0]} | |
</a> | |
<span style='color: #666; font-size: 0.9em; opacity: 0.7;'> | |
๐ {space_id} {decorative_emojis[2]} | |
</span> | |
</div> | |
</div> | |
""" | |
def get_vercel_deployments(): | |
"""Vercel API๋ฅผ ํตํด ๋ฐฐํฌ๋ ์๋น์ค ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ""" | |
token = "A8IFZmgW2cqA4yUNlLPnci0N" | |
url = "https://api.vercel.com/v6/deployments" | |
headers = { | |
"Authorization": f"Bearer {token}", | |
"Content-Type": "application/json" | |
} | |
try: | |
response = requests.get(url, headers=headers) | |
if response.status_code != 200: | |
print(f"Vercel API Error: {response.text}") | |
return [] | |
deployments = response.json().get('deployments', []) | |
# ์ํ๊ฐ 'READY'์ด๊ณ 'url'์ด ์๋ ๋ฐฐํฌ๋ง ํํฐ๋งํ๊ณ 'javis1' ์ ์ธ | |
active_deployments = [ | |
dep for dep in deployments | |
if dep.get('state') == 'READY' and | |
dep.get('url') and | |
'javis1' not in dep.get('name', '').lower() # javis1 ์ ์ธ | |
] | |
return active_deployments | |
except Exception as e: | |
print(f"Error fetching Vercel deployments: {str(e)}") | |
return [] | |
def get_vercel_card(deployment, index): | |
"""Generate HTML card for a Vercel deployment with like button""" | |
raw_url = deployment.get('url', '') | |
project_name = raw_url[:6] if len(raw_url) >= 6 else raw_url | |
url = f"{project_name}.vercel.app" | |
created = format_timestamp(deployment.get('created')) | |
name = deployment.get('name', 'Unnamed Project') | |
state = deployment.get('state', 'N/A') | |
# ๊ณ ์ ID ์์ฑ (์นด๋ ์๋ณ์ฉ) | |
card_id = f"vercel-card-{project_name}" | |
bg_color = get_pastel_color(index + 20) | |
tech_emojis = ['โก', '๐', '๐', 'โจ', '๐ซ', '๐ฅ', '๐', '๐ฏ', '๐จ', '๐ฎ'] | |
random_emojis = random.sample(tech_emojis, 3) | |
return f""" | |
<div id="{card_id}" class="vercel-card" | |
data-likes="0" | |
style='border: none; | |
padding: 25px; | |
margin: 15px; | |
border-radius: 20px; | |
background-color: {bg_color}; | |
box-shadow: 0 4px 15px rgba(0,0,0,0.1); | |
transition: all 0.3s ease-in-out; | |
position: relative; | |
overflow: hidden;' | |
onmouseover='this.style.transform="translateY(-5px) scale(1.02)"; this.style.boxShadow="0 8px 25px rgba(0,0,0,0.15)"' | |
onmouseout='this.style.transform="translateY(0) scale(1)"; this.style.boxShadow="0 4px 15px rgba(0,0,0,0.1)"'> | |
<div style='position: absolute; top: -15px; right: -15px; font-size: 100px; opacity: 0.1;'> | |
{random_emojis[0]} | |
</div> | |
<div style='position: absolute; top: 10px; right: 10px; font-size: 20px;'> | |
{random_emojis[1]} | |
</div> | |
<div style='position: absolute; bottom: 10px; left: 10px; font-size: 20px;'> | |
{random_emojis[2]} | |
</div> | |
<h3 style='color: #2d2d2d; | |
margin: 0 0 20px 0; | |
font-size: 1.4em; | |
display: flex; | |
align-items: center; | |
gap: 10px;'> | |
<span style='font-size: 1.3em'>{random_emojis[0]}</span> | |
<a href='https://{url}' target='_blank' | |
style='text-decoration: none; color: #2d2d2d;'> | |
{name} | |
</a> | |
<span style='font-size: 1.3em'>{random_emojis[1]}</span> | |
</h3> | |
<div style='margin: 15px 0; color: #444; background: rgba(255,255,255,0.5); | |
padding: 15px; border-radius: 12px;'> | |
<p style='margin: 8px 0;'> | |
<strong>Status:</strong> โ {state} | |
</p> | |
<p style='margin: 8px 0;'> | |
<strong>Created:</strong> ๐ {created} | |
</p> | |
<p style='margin: 8px 0;'> | |
<strong>URL:</strong> ๐ https://{url} | |
</p> | |
</div> | |
<div style='margin-top: 20px; display: flex; justify-content: space-between; align-items: center;'> | |
<div class="like-section" style="display: flex; align-items: center; gap: 10px;"> | |
<button onclick="toggleLike('{card_id}')" class="like-button" | |
style="background: none; border: none; cursor: pointer; font-size: 1.5em; padding: 5px 10px;"> | |
๐ค | |
</button> | |
<span class="like-count" style="font-size: 1.2em; color: #666;">0</span> | |
</div> | |
<a href='https://{url}' target='_blank' | |
style='background: linear-gradient(45deg, #0084ff, #00a3ff); | |
color: white; | |
padding: 10px 20px; | |
border-radius: 15px; | |
text-decoration: none; | |
display: inline-flex; | |
align-items: center; | |
gap: 8px; | |
font-weight: 500; | |
transition: all 0.3s; | |
box-shadow: 0 2px 8px rgba(0,132,255,0.3);' | |
onmouseover='this.style.transform="scale(1.05)"; this.style.boxShadow="0 4px 12px rgba(0,132,255,0.4)"' | |
onmouseout='this.style.transform="scale(1)"; this.style.boxShadow="0 2px 8px rgba(0,132,255,0.3)"'> | |
<span>View Deployment</span> ๐ {random_emojis[0]} | |
</a> | |
</div> | |
</div> | |
""" | |
# get_user_spaces ํจ์ ์์ | |
def get_user_spaces(): | |
# ๊ธฐ์กด Hugging Face ์คํ์ด์ค ๊ฐ์ ธ์ค๊ธฐ | |
url = f"https://huggingface.co/api/spaces?author={USERNAME}&limit=500" | |
headers = { | |
"Accept": "application/json", | |
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" | |
} | |
try: | |
# Hugging Face ์คํ์ด์ค ๊ฐ์ ธ์ค๊ธฐ | |
response = requests.get(url, headers=headers) | |
spaces_data = response.json() if response.status_code == 200 else [] | |
# ์ ์ธํ ์คํ์ด์ค ํํฐ๋ง | |
user_spaces = [ | |
space for space in spaces_data | |
if not should_exclude_space(space.get('id', '').split('/')[-1]) | |
] | |
# Vercel ๋ฐฐํฌ ๊ฐ์ ธ์ค๊ธฐ | |
vercel_deployments = get_vercel_deployments() | |
html_content = f""" | |
<div style='padding: 20px; background-color: #f5f5f5;'> | |
<div style='margin-bottom: 20px;'> | |
<h2 style='color: #333; margin: 0 0 5px 0;'>๊ณต๊ฐ ๊ฐค๋ฌ๋ฆฌ(์์ฑ Web/App) by MOUSE</h2> | |
<p style='color: #666; margin: 0 0 15px 0; font-size: 0.9em;'> | |
ํ๋กฌํํธ๋ง์ผ๋ก ๋๋ง์ ์น์๋น์ค๋ฅผ ์ฆ์ ์์ฑํ๋ MOUSE | |
<a href='https://openfree-mouse.hf.space' target='_blank' | |
style='color: #0084ff; text-decoration: none;'> | |
https://openfree-mouse.hf.space | |
</a> | |
</p> | |
<p style='color: #666; margin: 0;'> | |
Found {len(vercel_deployments)} Vercel deployments and {len(user_spaces)} Hugging Face spaces | |
</p> | |
</div> | |
<!-- Vercel Deployments --> | |
<h3 style='color: #333; margin: 20px 0;'>โก Vercel Deployments</h3> | |
<div id="vercel-container" style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'> | |
{"".join(get_vercel_card(dep, idx) for idx, dep in enumerate(vercel_deployments))} | |
</div> | |
<!-- Hugging Face Spaces --> | |
<h3 style='color: #333; margin: 20px 0;'>๐ค Hugging Face Spaces</h3> | |
<div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'> | |
{"".join(get_space_card(space, idx) for idx, space in enumerate(user_spaces))} | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() {{ | |
// ์ข์์ ์ํ ๋ก๋ | |
function loadLikes() {{ | |
const cards = document.querySelectorAll('.vercel-card'); | |
cards.forEach(card => {{ | |
const cardId = card.id; | |
const likes = localStorage.getItem(cardId) || 0; | |
card.querySelector('.like-count').textContent = likes; | |
card.dataset.likes = likes; | |
updateLikeButton(card, likes > 0); | |
}}); | |
sortCards(); | |
}} | |
// ์ข์์ ๋ฒํผ ํ ๊ธ | |
window.toggleLike = function(cardId) {{ | |
const card = document.getElementById(cardId); | |
const likeCount = parseInt(localStorage.getItem(cardId) || 0); | |
const newCount = likeCount > 0 ? 0 : 1; | |
localStorage.setItem(cardId, newCount); | |
card.querySelector('.like-count').textContent = newCount; | |
card.dataset.likes = newCount; | |
updateLikeButton(card, newCount > 0); | |
sortCards(); | |
}} | |
// ์ข์์ ๋ฒํผ ์ํ ์ ๋ฐ์ดํธ | |
function updateLikeButton(card, isLiked) {{ | |
const button = card.querySelector('.like-button'); | |
button.textContent = isLiked ? 'โค๏ธ' : '๐ค'; | |
}} | |
// ์นด๋ ์ ๋ ฌ | |
function sortCards() {{ | |
const container = document.getElementById('vercel-container'); | |
const cards = Array.from(container.children); | |
cards.sort((a, b) => {{ | |
return parseInt(b.dataset.likes) - parseInt(a.dataset.likes); | |
}}); | |
cards.forEach(card => container.appendChild(card)); | |
}} | |
// ์ด๊ธฐ ๋ก๋ | |
loadLikes(); | |
}}); | |
</script> | |
""" | |
return html_content | |
except Exception as e: | |
print(f"Error: {str(e)}") | |
return f""" | |
<div style='padding: 20px; text-align: center; color: #666;'> | |
<h2>Error occurred while fetching spaces</h2> | |
<p>Error details: {str(e)}</p> | |
<p>Please try again later.</p> | |
</div> | |
""" | |
# Creating the Gradio interface | |
demo = gr.Blocks() | |
with demo: | |
html_output = gr.HTML(value=get_user_spaces()) # ์ด๊ธฐ ๋ก๋ ์ ์ง์ ํจ์ ํธ์ถ | |
if __name__ == "__main__": | |
demo.launch() |