Spaces:
Paused
Paused
| import requests | |
| import gradio as gr | |
| from datetime import datetime | |
| import random | |
| from selenium import webdriver | |
| from selenium.webdriver.support.ui import WebDriverWait | |
| from selenium.webdriver.support import expected_conditions as EC | |
| from selenium.webdriver.common.by import By | |
| from selenium.common.exceptions import WebDriverException, TimeoutException | |
| from PIL import Image | |
| from io import BytesIO | |
| import base64 | |
| import time | |
| 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) | |
| # ๋ช ์์ ๋๊ธฐ: body ์์๊ฐ ๋ก๋๋ ๋๊น์ง ๋๊ธฐ (์ต๋ 10์ด) | |
| try: | |
| WebDriverWait(driver, 10).until( | |
| EC.presence_of_element_located((By.TAG_NAME, "body")) | |
| ) | |
| except TimeoutException: | |
| print(f"ํ์ด์ง ๋ก๋ฉ ํ์์์: {url}") | |
| # ์ถ๊ฐ ๋๊ธฐ ์๊ฐ (1์ด) | |
| time.sleep(1) | |
| # JavaScript ์คํ ์๋ฃ ๋๊ธฐ | |
| driver.execute_script("return document.readyState") == "complete" | |
| # ์คํฌ๋ฆฐ์ท ์ดฌ์ | |
| 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)} for URL: {url}") | |
| return None | |
| except Exception as e: | |
| print(f"์์์น ๋ชปํ ์ค๋ฅ: {str(e)} for URL: {url}") | |
| 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""" | |
| <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" | |
| 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', '์ด๋ฆ ์๋ ํ๋ก์ ํธ') | |
| # ์นด๋ ID ์์ฑ | |
| card_id = f"vercel-card-{url.replace('.', '-').replace('/', '-')}" | |
| # Top Best ํญ๋ชฉ์ผ ๊ฒฝ์ฐ์ ์คํฌ๋ฆฐ์ท ์ฒ๋ฆฌ | |
| screenshot_html = "" | |
| if is_top_best: | |
| try: | |
| print(f"์คํฌ๋ฆฐ์ท ์บก์ฒ ์๋: {url}") # ๋๋ฒ๊น ์ฉ ๋ก๊ทธ | |
| screenshot_base64 = take_screenshot(raw_url) | |
| if screenshot_base64: | |
| screenshot_html = f""" | |
| <div style="width: 100%; height: 200px; overflow: hidden; border-radius: 10px; margin-bottom: 15px;"> | |
| <img src="data:image/png;base64,{screenshot_base64}" | |
| style="width: 100%; height: 100%; object-fit: cover;" | |
| alt="{name} ์คํฌ๋ฆฐ์ท"/> | |
| </div> | |
| """ | |
| else: | |
| print(f"์คํฌ๋ฆฐ์ท ์บก์ฒ ์คํจ: {url}") # ๋๋ฒ๊น ์ฉ ๋ก๊ทธ | |
| except Exception as e: | |
| print(f"์คํฌ๋ฆฐ์ท ์ฒ๋ฆฌ ์ค๋ฅ: {str(e)} for URL: {url}") # ๋๋ฒ๊น ์ฉ ๋ก๊ทธ | |
| 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""" | |
| <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>URL:</strong> ๐ {url} | |
| </p> | |
| </div> | |
| """ | |
| else: | |
| info_section = f""" | |
| <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> โ {deployment.get('state', 'N/A')} | |
| </p> | |
| <p style='margin: 8px 0;'> | |
| <strong>Created:</strong> ๐ {format_timestamp(deployment.get('created'))} | |
| </p> | |
| <p style='margin: 8px 0;'> | |
| <strong>URL:</strong> ๐ {url} | |
| </p> | |
| </div> | |
| """ | |
| 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)"'> | |
| {screenshot_html} | |
| <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='{url}' target='_blank' | |
| style='text-decoration: none; color: #2d2d2d;'> | |
| {name} | |
| </a> | |
| <span style='font-size: 1.3em'>{random_emojis[1]}</span> | |
| </h3> | |
| {info_section} | |
| <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='{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> | |
| """ | |
| # 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() | |
| actual_vercel_count = len(vercel_deployments) if vercel_deployments else 0 | |
| html_content = f""" | |
| <div style=' | |
| min-height: 100vh; | |
| background: linear-gradient(135deg, #f6f8ff 0%, #f0f4ff 100%); | |
| background-image: url("data:image/svg+xml,%3Csvg width='100' height='20' viewBox='0 0 100 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M21.184 20c.357-.13.72-.264 1.088-.402l1.768-.661C33.64 15.347 39.647 14 50 14c10.271 0 15.362 1.222 24.629 4.928.955.383 1.869.74 2.75 1.072h6.225c-2.51-.73-5.139-1.691-8.233-2.928C65.888 13.278 60.562 12 50 12c-10.626 0-16.855 1.397-26.66 5.063l-1.767.662c-2.475.923-4.66 1.674-6.724 2.275h6.335zm0-20C13.258 2.892 8.077 4 0 4V2c5.744 0 9.951-.574 14.85-2h6.334zM77.38 0C85.239 2.966 90.502 4 100 4V2c-6.842 0-11.386-.542-16.396-2h-6.225zM0 14c8.44 0 13.718-1.21 22.272-4.402l1.768-.661C33.64 5.347 39.647 4 50 4c10.271 0 15.362 1.222 24.629 4.928C84.112 12.722 89.438 14 100 14v-2c-10.271 0-15.362-1.222-24.629-4.928C65.888 3.278 60.562 2 50 2 39.374 2 33.145 3.397 23.34 7.063l-1.767.662C13.223 10.84 8.163 12 0 12v2z' fill='%23f0f0f0' fill-opacity='0.2' fill-rule='evenodd'/%3E%3C/svg%3E"); | |
| padding: 40px; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;'> | |
| <!-- ๋ฉ์ธ ํค๋ --> | |
| <div style=' | |
| background: rgba(255, 255, 255, 0.8); | |
| border-radius: 20px; | |
| padding: 30px; | |
| margin-bottom: 40px; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.05); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255,255,255,0.8);'> | |
| <h2 style=' | |
| color: #2d2d2d; | |
| margin: 0 0 15px 0; | |
| font-size: 2em; | |
| background: linear-gradient(45deg, #2d2d2d, #0084ff); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent;'> | |
| ๊ณต๊ฐ ๊ฐค๋ฌ๋ฆฌ(์์ฑ Web/App) by MOUSE | |
| </h2> | |
| <div style=' | |
| background: linear-gradient(45deg, #0084ff, #00a3ff); | |
| border-radius: 10px; | |
| padding: 15px; | |
| margin: 20px 0;'> | |
| <a href='https://openfree-mouse.hf.space' | |
| target='_blank' | |
| style=' | |
| color: white; | |
| text-decoration: none; | |
| font-size: 1.1em; | |
| display: block; | |
| text-align: center;'> | |
| ๐ ํ๋กฌํํธ๋ง์ผ๋ก ๋๋ง์ ์น์๋น์ค๋ฅผ ์ฆ์ ์์ฑํ๋ MOUSE | |
| </a> | |
| </div> | |
| <p style=' | |
| color: #666; | |
| margin: 0; | |
| font-size: 0.9em; | |
| text-align: center; | |
| background: rgba(255,255,255,0.5); | |
| padding: 10px; | |
| border-radius: 10px;'> | |
| Found {actual_vercel_count} Vercel deployments and {len(user_spaces)} Hugging Face spaces<br> | |
| (Plus {top_best_count} featured items in Top Best section) | |
| </p> | |
| </div> | |
| <!-- Top Best ์น์ --> | |
| <div class="section-container" style=' | |
| background: rgba(255, 255, 255, 0.4); | |
| border-radius: 20px; | |
| padding: 30px; | |
| margin: 20px 0; | |
| backdrop-filter: blur(10px);'> | |
| <h3 style=' | |
| color: #2d2d2d; | |
| margin: 0 0 20px 0; | |
| padding: 15px 25px; | |
| background: rgba(255,255,255,0.7); | |
| border-radius: 15px; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.05); | |
| border-left: 5px solid #0084ff; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px;'> | |
| <span style='font-size: 1.5em;'>๐</span> | |
| Top Best | |
| </h3> | |
| <div style=' | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | |
| gap: 20px;'> | |
| {"".join(get_vercel_card( | |
| {"url": url["url"], "created": url["created"], "name": url["name"], "state": url["state"]}, | |
| idx, | |
| is_top_best=True | |
| ) for idx, url in enumerate(TOP_BEST_URLS))} | |
| </div> | |
| </div> | |
| <!-- Vercel Deployments ์น์ --> | |
| {f''' | |
| <div class="section-container" style=' | |
| background: rgba(255, 255, 255, 0.4); | |
| border-radius: 20px; | |
| padding: 30px; | |
| margin: 20px 0; | |
| backdrop-filter: blur(10px);'> | |
| <h3 style=' | |
| color: #2d2d2d; | |
| margin: 0 0 20px 0; | |
| padding: 15px 25px; | |
| background: rgba(255,255,255,0.7); | |
| border-radius: 15px; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.05); | |
| border-left: 5px solid #00a3ff; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px;'> | |
| <span style='font-size: 1.5em;'>โก</span> | |
| 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> | |
| </div> | |
| ''' if vercel_deployments else ''} | |
| <!-- Hugging Face Spaces ์น์ --> | |
| <div class="section-container" style=' | |
| background: rgba(255, 255, 255, 0.4); | |
| border-radius: 20px; | |
| padding: 30px; | |
| margin: 20px 0; | |
| backdrop-filter: blur(10px);'> | |
| <h3 style=' | |
| color: #2d2d2d; | |
| margin: 0 0 20px 0; | |
| padding: 15px 25px; | |
| background: rgba(255,255,255,0.7); | |
| border-radius: 15px; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.05); | |
| border-left: 5px solid #ff6b6b; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px;'> | |
| <span style='font-size: 1.5em;'>๐ค</span> | |
| 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> | |
| </div> | |
| <!-- ๊ธฐ์กด JavaScript ์ฝ๋๋ ๊ทธ๋๋ก ์ ์ง --> | |
| <script> | |
| // ... (๊ธฐ์กด JavaScript ์ฝ๋) | |
| </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() | |