Spaces:
Running
Running
import gradio as gr | |
from datetime import datetime | |
import json | |
# Load video links and news data | |
with open('assets/game_video_link.json', 'r') as f: | |
VIDEO_LINKS = json.load(f) | |
with open('assets/news.json', 'r') as f: | |
NEWS_DATA = json.load(f) | |
def create_video_gallery(): | |
"""Create a custom HTML/JS component for video gallery""" | |
# Extract video IDs | |
mario_id = VIDEO_LINKS["super_mario"].split("?v=")[1] | |
sokoban_id = VIDEO_LINKS["sokoban"].split("?v=")[1] | |
game_2048_id = VIDEO_LINKS["2048"].split("?v=")[1] | |
candy_id = VIDEO_LINKS["candy"].split("?v=")[1] | |
ace_attorney_id = VIDEO_LINKS["ace_attorney"].split("?v=")[1] | |
# Get the latest video from news data | |
latest_news = NEWS_DATA["news"][0] # First item is the latest | |
latest_video_id = latest_news["video_link"].split("?v=")[1] | |
latest_date = datetime.strptime(latest_news["date"], "%Y-%m-%d") | |
formatted_latest_date = latest_date.strftime("%B %d, %Y") | |
# Generate news HTML | |
news_items = [] | |
for item in NEWS_DATA["news"]: | |
video_id = item["video_link"].split("?v=")[1] | |
date_obj = datetime.strptime(item["date"], "%Y-%m-%d") | |
formatted_date = date_obj.strftime("%B %d, %Y") | |
news_items.append(f''' | |
<div class="news-item"> | |
<div class="news-date">{formatted_date}</div> | |
<div class="news-content"> | |
<div class="news-video"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{video_id}"></iframe> | |
</div> | |
</div> | |
<div class="news-text"> | |
<a href="{item["twitter_link"]}" target="_blank" class="twitter-link"> | |
<span class="twitter-icon">๐ข</span> | |
{item["twitter_text"]} | |
</a> | |
</div> | |
</div> | |
</div> | |
''') | |
news_html = '\n'.join(news_items) | |
gallery_html = f''' | |
<div class="video-gallery-container"> | |
<style> | |
.video-gallery-container {{ | |
width: 100%; | |
max-width: 1400px; | |
margin: 0 auto; | |
padding: 20px; | |
}} | |
.highlight-section {{ | |
margin-bottom: 40px; | |
}} | |
.highlight-card {{ | |
background: #ffffff; | |
border-radius: 10px; | |
box-shadow: 0 4px 20px rgba(0,0,0,0.15); | |
overflow: hidden; | |
transition: transform 0.3s; | |
border: 2px solid #2196F3; | |
}} | |
.highlight-card:hover {{ | |
transform: translateY(-5px); | |
}} | |
.highlight-header {{ | |
background: #2196F3; | |
color: white; | |
padding: 15px 20px; | |
font-size: 1.2em; | |
font-weight: bold; | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
}} | |
.highlight-date {{ | |
font-size: 0.9em; | |
opacity: 0.9; | |
}} | |
.highlight-content {{ | |
padding: 20px; | |
}} | |
.video-grid {{ | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
gap: 20px; | |
margin-top: 20px; | |
margin-bottom: 40px; | |
}} | |
.video-card {{ | |
background: var(--card-bg, #ffffff); | |
border-radius: 10px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
overflow: hidden; | |
transition: transform 0.2s; | |
}} | |
.video-card:hover {{ | |
transform: translateY(-5px); | |
}} | |
.video-wrapper {{ | |
position: relative; | |
padding-bottom: 56.25%; | |
height: 0; | |
overflow: hidden; | |
}} | |
.video-wrapper iframe {{ | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
border: none; | |
}} | |
.video-title {{ | |
padding: 15px; | |
font-size: 1.2em; | |
font-weight: bold; | |
color: var(--title-text, #2c3e50); | |
text-align: center; | |
background: var(--title-bg, #f8f9fa); | |
border-top: 1px solid var(--border-color, #eee); | |
}} | |
.news-section {{ | |
margin-top: 40px; | |
border-top: 2px solid #e9ecef; | |
padding-top: 20px; | |
}} | |
.news-section-title {{ | |
font-size: 1.8em; | |
font-weight: bold; | |
color: #2c3e50; | |
margin-bottom: 20px; | |
text-align: center; | |
}} | |
.news-item {{ | |
background: #ffffff; | |
border-radius: 10px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
margin-bottom: 20px; | |
overflow: hidden; | |
}} | |
.news-date {{ | |
padding: 10px 20px; | |
background: #f8f9fa; | |
color: #666; | |
font-size: 0.9em; | |
border-bottom: 1px solid #eee; | |
}} | |
.news-content {{ | |
display: flex; | |
padding: 20px; | |
align-items: center; | |
gap: 30px; | |
}} | |
.news-video {{ | |
flex: 0 0 300px; | |
}} | |
.news-text {{ | |
flex: 1; | |
display: flex; | |
align-items: center; | |
min-height: 169px; | |
}} | |
.twitter-link {{ | |
color: #2c3e50; | |
text-decoration: none; | |
display: flex; | |
align-items: center; | |
gap: 15px; | |
font-size: 1.4em; | |
font-weight: 600; | |
line-height: 1.4; | |
}} | |
.twitter-link:hover {{ | |
color: #1da1f2; | |
}} | |
.twitter-icon {{ | |
font-size: 1.5em; | |
color: #1da1f2; | |
}} | |
/* Dark mode specific styles */ | |
.dark .video-card {{ | |
--card-bg: #2d3748; | |
--title-bg: #1a202c; | |
--title-text: #e2e8f0; | |
--border-color: #4a5568; | |
}} | |
/* Light mode specific styles */ | |
.light .video-card {{ | |
--card-bg: #ffffff; | |
--title-bg: #f8f9fa; | |
--title-text: #2c3e50; | |
--border-color: #eee; | |
}} | |
</style> | |
<!-- Highlight Section --> | |
<div class="highlight-section"> | |
<div class="highlight-card"> | |
<div class="highlight-header"> | |
<span>๐ Latest Update</span> | |
<span class="highlight-date">{formatted_latest_date}</span> | |
</div> | |
<div class="highlight-content"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{latest_video_id}"></iframe> | |
</div> | |
<div class="video-title"> | |
<a href="{latest_news["twitter_link"]}" target="_blank" class="twitter-link"> | |
<span class="twitter-icon">๐ข</span> | |
{latest_news["twitter_text"]} | |
</a> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Regular Video Grid --> | |
<div class="video-grid"> | |
<div class="video-card"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{mario_id}"></iframe> | |
</div> | |
<div class="video-title">๐ฎ Super Mario Bros</div> | |
</div> | |
<div class="video-card"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{sokoban_id}"></iframe> | |
</div> | |
<div class="video-title">๐ฆ Sokoban</div> | |
</div> | |
<div class="video-card"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{game_2048_id}"></iframe> | |
</div> | |
<div class="video-title">๐ข 2048</div> | |
</div> | |
<div class="video-card"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{candy_id}"></iframe> | |
</div> | |
<div class="video-title">๐ฌ Candy Crush</div> | |
</div> | |
<div class="video-card"> | |
<div class="video-wrapper"> | |
<iframe src="https://www.youtube.com/embed/{ace_attorney_id}"></iframe> | |
</div> | |
<div class="video-title">โ๏ธ Ace Attorney</div> | |
</div> | |
</div> | |
<!-- News Section --> | |
<div class="news-section"> | |
<div class="news-section-title">๐ฐ Latest News</div> | |
{news_html} | |
</div> | |
</div> | |
''' | |
return gr.HTML(gallery_html) | |
def create_gallery_tab(): | |
"""Create and return the gallery tab component""" | |
with gr.Tab("๐ฅ Gallery") as gallery_tab: | |
video_gallery = create_video_gallery() | |
return gallery_tab |