Skip to content

Commit

Permalink
Update card script
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicconike committed Jun 2, 2024
1 parent e6f31e1 commit f90f2e3
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 139 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,6 @@ vercel.txt
Pipfile
Pipfile.lock
.vercel

#Chromium
Chromium
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
[![Visitor Badge](https://badges.pufler.dev/visits/nicconike/steam-stats)](https://badges.pufler.dev)

<!-- Steam-Stats start -->
![Steam Summary](https://github.com/Nicconike/Steam-Stats/blob/master/assets/steam_summary.png)
![Steam Games Stats](https://github.com/Nicconike/Steam-Stats/blob/master/assets/recently_played_games.png)
<!-- Steam-Stats end -->

<!-- Steam-Workshop start -->
![Steam Workshop Stats](https://github.com/Nicconike/Steam-Stats/blob/master/assets/steam_workshop_stats.svg?sanitize=true)
<!-- Steam-Workshop end -->
234 changes: 208 additions & 26 deletions api/card.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,98 @@
"""Generate Cards for Steam Stats"""
import datetime
from html2image import Html2Image
import math
import os
import asyncio
import time
import zipfile
from pyppeteer import launch
import requests
from steam_stats import get_player_summaries, get_recently_played_games
from steam_workshop import fetch_workshop_item_links, fetch_all_workshop_stats
from dotenv import load_dotenv

load_dotenv()

def format_unix_time(unix_time):
"""Convert Unix time to human-readable format"""
return datetime.datetime.fromtimestamp(unix_time).strftime("%d/%m/%Y")
# Secrets Configuration
STEAM_API_KEY = os.getenv("STEAM_API_KEY")
STEAM_ID = os.getenv("STEAM_ID")
STEAM_CUSTOM_ID = os.getenv("STEAM_CUSTOM_ID")
REQUEST_TIMEOUT = (10, 15)

CHROMIUM_ZIP_URL = "https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win_x64/1309077/chrome-win.zip"
CHROMIUM_DIR = os.path.join(os.getcwd(), 'chromium')
CHROMIUM_EXECUTABLE = os.path.join(CHROMIUM_DIR, 'chrome-win', 'chrome.exe')
MARGIN = 5


def download_and_extract_chromium():
"""Download and extract Chromium from the provided URL"""
if not os.path.exists(CHROMIUM_DIR):
os.makedirs(CHROMIUM_DIR)
zip_path = os.path.join(CHROMIUM_DIR, 'chrome-win.zip')
if not os.path.exists(zip_path):
print("Downloading Chromium...")
response = requests.get(
CHROMIUM_ZIP_URL, stream=True, timeout=REQUEST_TIMEOUT)
with open(zip_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=128):
file.write(chunk)
print("Chromium downloaded successfully.")
if not os.path.exists(CHROMIUM_EXECUTABLE):
print("Extracting Chromium...")
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(CHROMIUM_DIR)
print("Chromium extracted successfully.")


async def get_element_bounding_box(html_file, selector):
"""Get the bounding box of the specified element using pyppeteer"""
browser = await launch(headless=True, executablePath=CHROMIUM_EXECUTABLE)
page = await browser.newPage()
await page.goto(f'file://{os.path.abspath(html_file)}')
bounding_box = await page.evaluate(f'''() => {{
var element = document.querySelector('{selector}');
var rect = element.getBoundingClientRect();
return {{x: rect.x, y: rect.y, width: rect.width, height: rect.height}};
}}''')
await browser.close()
# Add margin to the bounding box
bounding_box['x'] = max(bounding_box['x'] - MARGIN, 0)
bounding_box['y'] = max(bounding_box['y'] - MARGIN, 0)
bounding_box['width'] += 2 * MARGIN
bounding_box['height'] += 2 * MARGIN
return bounding_box


def convert_html_to_png(html_file, output_dir, output_file):
"""Convert HTML file to PNG image using html2image"""
# Initialize Html2Image
html2png = Html2Image(output_path=output_dir)
async def html_to_png(html_file, output_file, selector):
"""Convert HTML file to PNG using pyppeteer with clipping"""
bounding_box = await get_element_bounding_box(html_file, selector)
browser = await launch(headless=True, executablePath=CHROMIUM_EXECUTABLE)
page = await browser.newPage()
await page.goto(f'file://{os.path.abspath(html_file)}')
await page.screenshot({
'path': output_file,
'clip': {
'x': bounding_box['x'],
'y': bounding_box['y'],
'width': bounding_box['width'],
'height': bounding_box['height']
}
})
await browser.close()

# Convert HTML to PNG
html2png.screenshot(html_file=html_file, save_as=output_file)

def convert_html_to_png(html_file, output_file, selector):
"""Convert HTML file to PNG using pyppeteer with clipping"""
download_and_extract_chromium()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(html_to_png(html_file, output_file, selector))


def format_unix_time(unix_time):
"""Convert Unix time to human-readable format"""
return datetime.datetime.fromtimestamp(unix_time).strftime("%d/%m/%Y")


def generate_card_for_player_summary(player_data):
Expand Down Expand Up @@ -51,7 +129,7 @@ def generate_card_for_player_summary(player_data):
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive SVG Card</title>
<title>Steam Player Summary</title>
<style>
.card {{
width: 100%;
Expand Down Expand Up @@ -98,13 +176,11 @@ def generate_card_for_player_summary(player_data):
</body>
</html>
"""
with open("assets/steam_summary.html", "w", encoding="utf-8") as file:
with open("../assets/steam_summary.html", "w", encoding="utf-8") as file:
file.write(html_content)

html_file = "assets/steam_summary.html"
output_dir = "assets"
output_file = "steam_summary.png"
convert_html_to_png(html_file, output_dir, output_file)
convert_html_to_png("../assets/steam_summary.html",
"../assets/steam_summary.png", ".card")

return (
"![Steam Summary]"
Expand All @@ -113,7 +189,7 @@ def generate_card_for_player_summary(player_data):


def generate_card_for_played_games(games_data):
"""Generate HTML content for recently played games with a horizontal bar graph"""
"""Generate HTML Card for recently played games in last 2 weeks"""
if not games_data:
return None

Expand All @@ -128,13 +204,17 @@ def generate_card_for_played_games(games_data):
img_icon_url = f"https://media.steampowered.com/steamcommunity/public/images/apps/{
game["appid"]}/{game["img_icon_url"]}.jpg"
normalized_playtime = (playtime / max_playtime) * 100
normalized_playtime = math.log1p(playtime) / math.log1p(
max(game["playtime_2weeks"] for game in games_data["response"]["games"])) * 100

normalized_playtime = round(normalized_playtime)
display_time = f"{playtime} mins" if playtime < 60 else f"{
playtime / 60:.2f} hrs"
progress_bars += f"""
<div class="bar-container">
<img src="{img_icon_url}" alt="{name}" class="game-icon">
<progress id="p{i}" value="{normalized_playtime}" max="100"
class="bar-{(i % 6) + 1}"></progress>
<progress class="progress-style-{(i % 6) + 1}" value="{normalized_playtime}"
max="100"></progress>
<span class="game-info"><b>{name} ({display_time})</b></span>
</div>
"""
Expand All @@ -146,7 +226,7 @@ def generate_card_for_played_games(games_data):
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Recently Played Games in Last 2 Weeks</title>
<link rel="stylesheet" href="assets/style.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="card">
Expand All @@ -159,15 +239,117 @@ def generate_card_for_played_games(games_data):
</html>
"""

with open("assets/recently_played_games.html", "w", encoding="utf-8") as file:
with open("../assets/recently_played_games.html", "w", encoding="utf-8") as file:
file.write(html_content)

html_file = "assets/recently_played_games.html"
output_dir = "assets"
output_file = "recently_played_games.png"
convert_html_to_png(html_file, output_dir, output_file)
convert_html_to_png("../assets/recently_played_games.html",
"../assets/recently_played_games.png", ".card")

return (
"![Steam Games Stats]"
"![Steam Summary]"
"(https://github.com/Nicconike/Steam-Stats/blob/master/assets/recently_played_games.png)"
)


def generate_card_for_steam_workshop(workshop_stats):
"""Generates HTML content for retrieved Workshop Data"""
html_content = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Steam Workshop Stats</title>
<style>
body {{
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0;
margin: 0;
}}
.card {{
width: 100%;
max-width: 400px;
margin: auto;
border: 2px solid #000;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 10px;
background-color: #fff;
}}
table {{
width: 100%;
border-collapse: collapse;
}}
th, td {{
border: 1px solid #ddd;
padding: 8px;
text-align: center;
}}
th {{
background-color: #4CAF50;
color: white;
}}
tr:nth-child(even) {{
background-color: #f2f2f2;
}}
</style>
</head>
<body>
<div class="card">
<h2>Steam Workshop Stats</h2>
<table>
<thead>
<tr>
<th>Workshop Stats</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Unique Visitors</td>
<td>{workshop_stats["total_unique_visitors"]}</td>
</tr>
<tr>
<td>Current Subscribers</td>
<td>{workshop_stats["total_current_subscribers"]}</td>
</tr>
<tr>
<td>Current Favorites</td>
<td>{workshop_stats["total_current_favorites"]}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
"""
with open("../assets/steam_workshop_stats.html", "w", encoding="utf-8") as file:
file.write(html_content)

convert_html_to_png("../assets/steam_workshop_stats.html",
"../assets/steam_workshop_stats.png", ".card")

return (
"![Steam Summary]"
"(https://github.com/Nicconike/Steam-Stats/blob/master/assets/steam_workshop_stats.png"
"?sanitize=true)\n"
)


if __name__ == "__main__":
start_time = time.time()
player_summary = get_player_summaries()
recently_played_games = get_recently_played_games()
links = fetch_workshop_item_links(STEAM_CUSTOM_ID)
workshop_data = fetch_all_workshop_stats(links)
generate_card_for_player_summary(player_summary)
generate_card_for_played_games(recently_played_games)
generate_card_for_steam_workshop(workshop_data)
end_time = time.time() # End the timer
total_time = end_time-start_time
total_time = round(total_time, 3) # Total time
print(f"Total Execution Time: {total_time} seconds")
Loading

1 comment on commit f90f2e3

@vercel
Copy link

@vercel vercel bot commented on f90f2e3 Jun 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.