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"""
{main_emoji}
{decorative_emojis[0]}
{decorative_emojis[1]}
{decorative_emojis[2]}
{random_emojis[0]}
{space_name}
{random_emojis[1]}
SDK: {main_emoji} {sdk} {decorative_emojis[0]}
Created: 📅 {created_at} ⏰
Likes: {heart_emoji} {likes} {random_emojis[2]}
"""
def get_vercel_deployments():
"""Vercel API를 통해 배포된 서비스 정보 가져오기"""
token = "A8IFZmgW2cqA4yUNlLPnci0N"
url = "https://api.vercel.com/v6/deployments?limit=100" # limit 파라미터 추가
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', '')
# URL 처리 로직 수정
if raw_url.startswith('http'):
url = raw_url # 전체 URL이 제공된 경우 그대로 사용
else:
project_name = raw_url[:6] if len(raw_url) >= 6 else raw_url
url = f"{project_name}.vercel.app"
# Hugging Face 스페이스 URL인 경우 직접 사용
if 'huggingface.co' in url:
final_url = url
else:
final_url = f"https://{url}" if not url.startswith('http') else url
created = format_timestamp(deployment.get('created'))
name = deployment.get('name', 'Unnamed Project')
state = deployment.get('state', 'N/A')
# 고유 ID 생성 (카드 식별용)
card_id = f"vercel-card-{url.replace('.', '-').replace('/', '-')}"
bg_color = get_pastel_color(index + 20)
tech_emojis = ['⚡', '🚀', '🌟', '✨', '💫', '🔥', '🌈', '🎯', '🎨', '🔮']
random_emojis = random.sample(tech_emojis, 3)
return f"""
{random_emojis[0]}
{name}
{random_emojis[1]}
Status: ✅ {state}
Created: 📅 {created}
URL: 🔗 https://{url}
"""
# Top Best URLs 정의
TOP_BEST_URLS = [
{
"url": "dekvxz.vercel.app",
"name": "[게임] 다이어트 헌터",
"created": "2024-11-20 00:00",
"state": "READY"
},
{
"url": "jtufui.vercel.app",
"name": "[게임] 테러리스트",
"created": "2024-11-20 00:00",
"state": "READY"
},
{
"url": "https://huggingface.co/spaces/openfree/ggumim",
"name": "[MOUSE-II] 이미지에 한글 출력",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "xabtnc.vercel.app",
"name": "[ChatGPT] 나만의 LLM",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "https://huggingface.co/spaces/openfree/ifbhdc",
"name": "[게임] 보석 팡팡",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "nxhquk.vercel.app",
"name": "[게임] 테트리스",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "bydcnd.vercel.app",
"name": "[모델] 3D 분자 모형",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "ijhama.vercel.app",
"name": "투자 포트폴리오 분석",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "oschnl.vercel.app",
"name": "로또 번호 분석/추천",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "rzwzrq.vercel.app",
"name": "엑셀/CSV 데이터 분석",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "twkqre.vercel.app",
"name": "[운세] 타로카드",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "htwymz.vercel.app",
"name": "[게임] 소방헬기",
"created": "2024-11-20 00:00",
"state": "READY"
},
{
"url": "mktmbn.vercel.app",
"name": "[게임] 우주전쟁",
"created": "2024-11-19 00:00",
"state": "READY"
},
{
"url": "euguwt.vercel.app",
"name": "[게임] 포세이돈",
"created": "2024-11-19 00:00",
"state": "READY"
},
{
"url": "qmdzoh.vercel.app",
"name": "[게임] 하늘을 지켜라",
"created": "2024-11-19 00:00",
"state": "READY"
},
{
"url": "kofaqo.vercel.app",
"name": "[게임] 운석 충돌!",
"created": "2024-11-19 00:00",
"state": "READY"
},
{
"url": "qoqqkq.vercel.app",
"name": "[게임] 두더쥐 잡기",
"created": "2024-11-19 00:00",
"state": "READY"
},
{
"url": "nmznel.vercel.app",
"name": "[게임] 고양이 전용",
"created": "2024-11-19 00:00",
"state": "READY"
},
{
"url": "psrrtp.vercel.app",
"name": "[대시보드] 세계 인구",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "xxloav.vercel.app",
"name": "[게임] 벽돌 깨기",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "https://huggingface.co/spaces/openfree/edpaje",
"name": "[게임] 기억력 카드",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "https://huggingface.co/spaces/openfree/ixtidb",
"name": "AI 요리사",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "cnlzji.vercel.app",
"name": "국가 정보 비교",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "fazely.vercel.app",
"name": "Wikipedia 지식 분석",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "pkzhbo.vercel.app",
"name": "세계 국가별 시간대",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "pammgl.vercel.app",
"name": "보도자료 배포 서비스",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "https://ktduhm.vercel.app/",
"name": "수학을 그래프로 이해",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "vjmfoy.vercel.app",
"name": "[게임] 3D 벽돌쌓기",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "aodakf.vercel.app",
"name": "[버추얼] 3D 가상현실",
"created": "2024-11-18 00:00",
"state": "READY"
},
{
"url": "mxoeue.vercel.app",
"name": "음성 생성(TTS),조정",
"created": "2024-11-18 00:00",
"state": "READY"
}
]
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()
# 순수 Vercel 배포 수만 카운트
vercel_count = len(vercel_deployments) if vercel_deployments else 0
html_content = f"""
공개 갤러리(생성 Web/App) by MOUSE
프롬프트만으로 나만의 웹서비스를 즉시 생성하는 MOUSE
https://openfree-mouse.hf.space
Found {vercel_count} Vercel deployments and {len(user_spaces)} Hugging Face spaces
🏆 Top Best
{"".join(get_vercel_card({"url": url["url"], "created": url["created"], "name": url["name"], "state": url["state"]}, idx)
for idx, url in enumerate(TOP_BEST_URLS))}
⚡ Vercel Deployments
{"".join(get_vercel_card(dep, idx) for idx, dep in enumerate(vercel_deployments))}
🤗 Hugging Face Spaces
{"".join(get_space_card(space, idx) for idx, space in enumerate(user_spaces))}
"""
return html_content
except Exception as e:
print(f"Error: {str(e)}")
return f"""
Error occurred while fetching spaces
Error details: {str(e)}
Please try again later.
"""
# Creating the Gradio interface
demo = gr.Blocks()
with demo:
html_output = gr.HTML(value=get_user_spaces()) # 초기 로드 시 직접 함수 호출
if __name__ == "__main__":
demo.launch()