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]}

View Space 🚀 {random_emojis[0]} 🆔 {space_id} {decorative_emojis[2]}
""" def get_vercel_deployments(): """Vercel API를 통해 모든 배포된 서비스 정보 가져오기 (페이지네이션 적용)""" token = "A8IFZmgW2cqA4yUNlLPnci0N" base_url = "https://api.vercel.com/v6/deployments" all_deployments = [] has_next = True page = 1 until = None # 첫 요청에서는 until 파라미터 없음 headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } try: while has_next: # URL 구성 (페이지네이션 파라미터 포함) url = f"{base_url}?limit=100" if until: url += f"&until={until}" print(f"Fetching page {page}... URL: {url}") # 디버깅용 response = requests.get(url, headers=headers) if response.status_code != 200: print(f"Vercel API Error: {response.text}") break data = response.json() current_deployments = data.get('deployments', []) if not current_deployments: # 더 이상 데이터가 없으면 종료 break all_deployments.extend(current_deployments) # 다음 페이지를 위한 until 값 설정 pagination = data.get('pagination', {}) until = pagination.get('next') has_next = bool(until) # until 값이 있으면 다음 페이지 존재 print(f"Page {page} fetched. Got {len(current_deployments)} deployments") # 디버깅용 page += 1 print(f"Total deployments fetched: {len(all_deployments)}") # 디버깅용 # 상태가 'READY'이고 'url'이 있는 배포만 필터링하고 'javis1' 제외 active_deployments = [ dep for dep in all_deployments if dep.get('state') == 'READY' and dep.get('url') and 'javis1' not in dep.get('name', '').lower() ] print(f"Active deployments after filtering: {len(active_deployments)}") # 디버깅용 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}

View Deployment 🚀 {random_emojis[0]}
""" # 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]) ] # TOP_BEST_URLS 항목 수 top_best_count = len(TOP_BEST_URLS) # Vercel API를 통한 실제 배포 수 (디버깅을 위한 출력 추가) vercel_deployments = get_vercel_deployments() print(f"Debug - Vercel API response: {vercel_deployments}") # 디버깅 로그 actual_vercel_count = len(vercel_deployments) if vercel_deployments else 0 html_content = f"""

공개 갤러리(생성 Web/App) by MOUSE

프롬프트만으로 나만의 웹서비스를 즉시 생성하는 MOUSE https://openfree-mouse.hf.space

Found {actual_vercel_count} Vercel deployments and {len(user_spaces)} Hugging Face spaces
(Plus {top_best_count} featured items in Top Best section)

🏆 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))}
{f'''

⚡ Vercel Deployments

{"".join(get_vercel_card(dep, idx) for idx, dep in enumerate(vercel_deployments))}
''' if vercel_deployments else ''}

🤗 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()