diff --git a/blueprints/risdeveau/__init__.py b/blueprints/risdeveau/__init__.py index 9b991cb..3c73da9 100644 --- a/blueprints/risdeveau/__init__.py +++ b/blueprints/risdeveau/__init__.py @@ -2,6 +2,7 @@ import os import magic from pathlib import Path from htmlmin import minify +from time import time from datetime import datetime, timedelta from musicbrainzngs import get_image_front from flask import ( @@ -14,6 +15,30 @@ from flask import ( ) from .modules.api.lb import listens, listening +from .modules.api.steam import recent, owned + +def tmsmp(sec: int) -> str: + if sec == 0: + return 0 + elif sec < 60: + return f"{sec} s" + elif sec < 60*60: + minutes = round(sec / 60, 1) + return f"{minutes:.0f} m" if minutes.is_integer() else f"{minutes:.1f} m" + elif sec < 60*60*24: + hours = round(sec / 3600, 1) + return f"{hours:.0f} h" if hours.is_integer() else f"{hours:.1f} h" + else: + days = round(sec / 86400, 1) + return f"{days:.0f} d" if days.is_integer() else f"{days:.1f} d" + +def utmsmp(unix: int) -> str: + return datetime \ + .utcfromtimestamp(unix) \ + .strftime('%Y-%m-%d %H:%M:%S') + +def rtmsmp(unix: int) -> str: + return tmsmp(int(time() - unix)) bp = Blueprint( "risdeveau", @@ -47,6 +72,16 @@ def mb_cover(mbid): .strftime('%a, %d %b %Y %H:%M:%S GMT') return r +args = { + "lb": listens, + "lb_now": listening, + "recent": recent, + "owned": owned, + "tmsmp": tmsmp, + "utmsmp": utmsmp, + "rtmsmp": rtmsmp +} + @bp.route("/") def index(): - return render_tmpl('index.html', lb=listens, lb_now=listening) + return render_tmpl('index.html', **args) diff --git a/blueprints/risdeveau/modules/api/steam.py b/blueprints/risdeveau/modules/api/steam.py new file mode 100644 index 0000000..6f45c77 --- /dev/null +++ b/blueprints/risdeveau/modules/api/steam.py @@ -0,0 +1,71 @@ +from os import environ +from flask import Flask, jsonify +from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.interval import IntervalTrigger +import requests +from time import time +import atexit +import re +from urllib.parse import urlparse, parse_qs + +TOKEN = environ.get("STEAM_TOKEN") +MY_ID = 76561198826355942 + +recent = {} +owned = {} + +def modify_game_list(json: dict) -> dict: + if 'games' in json.keys(): + apps = (3301060, 404790, 1281930, 1920960, 1325960, 431960) + new_games = [] + for i, g in enumerate(json['games']): + if g['appid'] not in apps: + json['games'][i]['h_cover'] = f"https://shared.fastly.steamstatic.com/store_item_assets//steam/apps/{g['appid']}/header.jpg" + json['games'][i]['v_cover'] = f"https://shared.fastly.steamstatic.com/store_item_assets//steam/apps/{g['appid']}/library_600x900.jpg" + new_games.append(json['games'][i]) + json['games'] = new_games + return json + +def steam_request(interface: str, method: str, v: int = 1, **kwargs) -> requests.Response: + return requests.get( + f"https://api.steampowered.com/{interface}/{method}/v{v:04}/", + params=dict({"key": TOKEN}, **kwargs), + timeout=10 + ) + +def api_request(cache, *args, **kwargs): + try: + response = steam_request(*args, **kwargs) + if response.status_code == 200: + cache.update({ + 'data': modify_game_list(response.json().get("response")), + 'last_updated': time(), + 'status': 'success' + }) + else: + cache['status'] = f'error: {response.status_code}' + except Exception as e: + cache['status'] = f'error: {str(e)}' + +if TOKEN: + scheduler = BackgroundScheduler() + scheduler.add_job( + func=lambda: api_request(recent, "IPlayerService", "GetRecentlyPlayedGames", steamid=76561198826355942), + trigger=IntervalTrigger(minutes=15), + id='risdeveau.steam.recent', + replace_existing=True + ) + scheduler.add_job( + func=lambda: api_request(owned, "IPlayerService", "GetOwnedGames", steamid=76561198826355942, include_appinfo=1, include_played_free_games=1), + trigger=IntervalTrigger(minutes=60), + id='risdeveau.steam.owned', + replace_existing=True + ) + scheduler.start() + + api_request(recent, "IPlayerService", "GetRecentlyPlayedGames", steamid=76561198826355942) + api_request(owned, "IPlayerService", "GetOwnedGames", steamid=76561198826355942, include_appinfo=1, include_played_free_games=1) + + atexit.register(lambda: scheduler.shutdown()) +else: + print("STEAM_TOKEN is not defined") diff --git a/blueprints/risdeveau/static/style/risdeveau.scss b/blueprints/risdeveau/static/style/risdeveau.scss index cec5142..47a4fe4 100644 --- a/blueprints/risdeveau/static/style/risdeveau.scss +++ b/blueprints/risdeveau/static/style/risdeveau.scss @@ -48,6 +48,21 @@ h3 { } } +.steam { + .block { + display: flex; + + img { + height: 7rem; + margin-right: .5rem; + } + + p { + margin: .5rem 0; + } + } +} + table, tbody { vertical-align: baseline; border-collapse: collapse; diff --git a/blueprints/risdeveau/templates/index.html b/blueprints/risdeveau/templates/index.html index f7b839c..6598001 100644 --- a/blueprints/risdeveau/templates/index.html +++ b/blueprints/risdeveau/templates/index.html @@ -23,6 +23,7 @@ 'info', 'contacts', 'listenbrainz', + 'steam', 'donate', '88x31' ) %} diff --git a/blueprints/risdeveau/templates/steam.htm b/blueprints/risdeveau/templates/steam.htm new file mode 100644 index 0000000..fae1b24 --- /dev/null +++ b/blueprints/risdeveau/templates/steam.htm @@ -0,0 +1,54 @@ +
+

Steam

+ + {% if recent.data.games %} +

Recently played:

+ {% for g in recent.data.games %} + + + + + + +
+ {{ g.name }} +

Played last 2 weeks: {{ tmsmp(g.playtime_2weeks*60) }} +

+ Total played: + {{ tmsmp(g.playtime_linux_forever*60) }} (L) + + {{ tmsmp(g.playtime_windows_forever*60) }} (W) = + {{ tmsmp(g.playtime_forever*60) }} (T) +

+
+
+ {% endfor %} +

Last updated: {{ rtmsmp(recent.last_updated) }} ago

+ {% endif %} + + {% if owned.data.games %} +

Top played games:

+ {% set owned_games = owned.data.games | sort(attribute="playtime_forever", reverse=true) %} + {% for g in owned_games[:5] %} + + + + + + +
+ {{ g.name }} +

+ Total played: + {{ tmsmp(g.playtime_linux_forever*60) }} (L) + + {{ tmsmp(g.playtime_windows_forever*60) }} (W) = + {{ tmsmp(g.playtime_forever*60) }} (T) +

+ {% if g.rtime_last_played != 0 %} +

Last played: {{ utmsmp(g.rtime_last_played) }}

+ {% endif %} +
+
+ {% endfor %} +

Last updated: {{ rtmsmp(owned.last_updated) }} ago

+ {% endif %} +