import requests import gradio as gr from datetime import datetime import random from selenium import webdriver from selenium.common.exceptions import WebDriverException from PIL import Image from io import BytesIO import base64 def take_screenshot(url): """웹사이트 스크린샷 촬영 함수""" if not url.startswith('http'): url = f"https://{url}" options = webdriver.ChromeOptions() options.add_argument('--headless') options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') options.add_argument('--window-size=1080,720') try: driver = webdriver.Chrome(options=options) driver.get(url) driver.implicitly_wait(10) screenshot = driver.get_screenshot_as_png() img = Image.open(BytesIO(screenshot)) buffered = BytesIO() img.save(buffered, format="PNG") return base64.b64encode(buffered.getvalue()).decode() except WebDriverException as e: print(f"스크린샷 촬영 실패: {str(e)}") return None finally: if 'driver' in locals(): driver.quit() 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"""
""" 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, is_top_best=False): """Vercel 배포 카드 HTML 생성 함수""" raw_url = deployment.get('url', '') # URL 처리 if raw_url.startswith('http'): url = raw_url else: url = f"https://{raw_url}" name = deployment.get('name', '이름 없는 프로젝트') created = format_timestamp(deployment.get('created')) state = deployment.get('state', 'N/A') # 카드 ID 생성 card_id = f"vercel-card-{url.replace('.', '-').replace('/', '-')}" # Top Best 항목일 경우의 스크린샷 처리 screenshot_html = "" if is_top_best: try: screenshot_base64 = take_screenshot(raw_url) if screenshot_base64: screenshot_html = f""" """ except Exception as e: print(f"스크린샷 처리 오류: {str(e)}") bg_color = get_pastel_color(index + (20 if not is_top_best else 0)) tech_emojis = ['⚡', '🚀', '🌟', '✨', '💫', '🔥', '🌈', '🎯', '🎨', '🔮'] random_emojis = random.sample(tech_emojis, 3) # Top Best와 일반 카드의 내용을 다르게 구성 if is_top_best: info_section = f"""URL: 🔗 {url}
Status: ✅ {state}
Created: 📅 {created}
URL: 🔗 {url}
프롬프트만으로 나만의 웹서비스를 즉시 생성하는 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)
Error details: {str(e)}
Please try again later.