-
-
Notifications
You must be signed in to change notification settings - Fork 288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fetch SteamGridDB assets when adding game to Steam #3662
base: main
Are you sure you want to change the base?
Changes from all commits
861c9d8
80dae8a
7071850
72d3eae
24eec6b
c5d186b
83d7965
3d483f2
045e8b5
8b7acbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,13 +20,17 @@ | |
import shlex | ||
import shutil | ||
import uuid | ||
from binascii import crc32 | ||
from datetime import datetime | ||
from functools import lru_cache | ||
from glob import glob | ||
from pathlib import Path | ||
from typing import Union, Dict, Optional | ||
|
||
from requests.exceptions import HTTPError, RequestException | ||
|
||
from bottles.backend.globals import Paths | ||
from bottles.backend.managers.steamgriddb import SteamGridDBManager | ||
from bottles.backend.models.config import BottleConfig | ||
from bottles.backend.models.result import Result | ||
from bottles.backend.models.samples import Samples | ||
|
@@ -520,22 +524,69 @@ def launch_app(prefix: str): | |
SignalManager.send(Signals.GShowUri, Result(data=uri)) | ||
|
||
def add_shortcut(self, program_name: str, program_path: str): | ||
def __add_to_user_conf(conf: str) -> bool: | ||
logging.info(f"Searching SteamGridDB for {program_name} assets…") | ||
asset_suffixes = { | ||
"grids": "p", | ||
"hgrids": "", | ||
"heroes": "_hero", | ||
"logos": "_logo", | ||
"icons": "_icon", | ||
} | ||
for asset_type, suffix in asset_suffixes.items(): | ||
base_filename = f"{appid}{suffix}" | ||
asset_path = os.path.join(conf, "grid", base_filename) | ||
try: | ||
filename = SteamGridDBManager.get_steam_game_asset( | ||
program_name, asset_path, asset_type, reraise_exceptions=True | ||
) | ||
except HTTPError: | ||
# Usually missing asset (404), keep trying for the rest | ||
continue | ||
except: | ||
# Unreachable host or issue saving files, nothing we can do | ||
break | ||
Comment on lines
+546
to
+548
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it'll be better to specify which exception(s). |
||
|
||
if asset_type == "icons": | ||
shortcut["icon"] = os.path.join(conf, "grid", filename) | ||
|
||
s = asset_type[:-1] if asset_type != "heroes" else "hero" | ||
logging.info(f"Added {s.capitalize()} asset ({filename})") | ||
|
||
try: | ||
with open(os.path.join(conf, "shortcuts.vdf"), "rb") as f: | ||
_existing = vdf.binary_loads(f.read()).get("shortcuts", {}) | ||
|
||
_all = list(_existing.values()) + [shortcut] | ||
_shortcuts = {"shortcuts": {str(i): s for i, s in enumerate(_all)}} | ||
|
||
with open(os.path.join(conf, "shortcuts.vdf"), "wb") as f: | ||
f.write(vdf.binary_dumps(_shortcuts)) | ||
|
||
except (OSError, IOError) as e: | ||
logging.error(e) | ||
return False | ||
|
||
return True | ||
|
||
logging.info(f"Adding shortcut for {program_name}") | ||
cmd = "xdg-open" | ||
args = "bottles:run/'{0}'/'{1}'" | ||
args = f"bottles:run/'{self.config.Name}'/'{program_name}'" | ||
appid = crc32(str.encode(self.config.Name + program_name)) | 0x80000000 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if self.userdata_path is None: | ||
logging.warning("Userdata path is not set") | ||
return Result(False) | ||
|
||
confs = glob(os.path.join(self.userdata_path, "*/config/")) | ||
shortcut = { | ||
"appid": appid - 0x100000000, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
"AppName": program_name, | ||
"Exe": cmd, | ||
"StartDir": ManagerUtils.get_bottle_path(self.config), | ||
"icon": ManagerUtils.extract_icon(self.config, program_name, program_path), | ||
"ShortcutPath": "", | ||
"LaunchOptions": args.format(self.config.Name, program_name), | ||
"LaunchOptions": args, | ||
"IsHidden": 0, | ||
"AllowDesktopConfig": 1, | ||
"AllowOverlay": 1, | ||
|
@@ -547,22 +598,11 @@ def add_shortcut(self, program_name: str, program_path: str): | |
"tags": {"0": "Bottles"}, | ||
} | ||
|
||
for c in confs: | ||
_shortcuts = {} | ||
_existing = {} | ||
|
||
if os.path.exists(os.path.join(c, "shortcuts.vdf")): | ||
with open(os.path.join(c, "shortcuts.vdf"), "rb") as f: | ||
try: | ||
_existing = vdf.binary_loads(f.read()).get("shortcuts", {}) | ||
except: | ||
continue | ||
|
||
_all = list(_existing.values()) + [shortcut] | ||
_shortcuts = {"shortcuts": {str(i): s for i, s in enumerate(_all)}} | ||
ok = False | ||
for conf in confs: | ||
ok |= __add_to_user_conf(conf) | ||
|
||
with open(os.path.join(c, "shortcuts.vdf"), "wb") as f: | ||
f.write(vdf.binary_dumps(_shortcuts)) | ||
if ok: | ||
logging.info(f"Added shortcut for {program_name}") | ||
|
||
logging.info(f"Added shortcut for {program_name}") | ||
return Result(True) | ||
return Result(ok) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,8 +16,10 @@ | |
# | ||
|
||
import os | ||
import uuid | ||
from typing import Optional | ||
|
||
import requests | ||
from requests.exceptions import HTTPError, RequestException | ||
|
||
from bottles.backend.logger import Logger | ||
from bottles.backend.models.config import BottleConfig | ||
|
@@ -27,31 +29,42 @@ | |
|
||
|
||
class SteamGridDBManager: | ||
@staticmethod | ||
def get_game_grid(name: str, config: BottleConfig): | ||
def get_steam_game_asset( | ||
program_name: str, | ||
asset_path: str, | ||
asset_type: Optional[str] = None, | ||
reraise_exceptions: bool = False, | ||
) -> Optional[str]: | ||
try: | ||
res = requests.get(f"https://steamgrid.usebottles.com/api/search/{name}") | ||
except: | ||
return | ||
# url = f"https://steamgrid.usebottles.com/api/search/{program_name}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason why this is commented? |
||
url = f"http://127.0.0.1:8000/api/search/{program_name}" | ||
if asset_type: | ||
url = f"{url}/{asset_type}" | ||
res = requests.get(url, timeout=5) | ||
res.raise_for_status() | ||
filename = SteamGridDBManager.__save_asset_to_steam(res.json(), asset_path) | ||
|
||
if res.status_code == 200: | ||
return SteamGridDBManager.__save_grid(res.json(), config) | ||
except Exception as e: | ||
if isinstance(e, HTTPError): | ||
logging.warning(str(e)) | ||
else: | ||
logging.error(str(e)) | ||
if reraise_exceptions: | ||
raise | ||
|
||
@staticmethod | ||
def __save_grid(url: str, config: BottleConfig): | ||
grids_path = os.path.join(ManagerUtils.get_bottle_path(config), "grids") | ||
if not os.path.exists(grids_path): | ||
os.makedirs(grids_path) | ||
return filename | ||
|
||
ext = url.split(".")[-1] | ||
filename = str(uuid.uuid4()) + "." + ext | ||
path = os.path.join(grids_path, filename) | ||
@staticmethod | ||
def __save_asset_to_steam(url: str, asset_path: str) -> str: | ||
asset_dir = os.path.dirname(asset_path) | ||
if not os.path.exists(asset_dir): | ||
os.makedirs(asset_dir) | ||
|
||
try: | ||
r = requests.get(url) | ||
with open(path, "wb") as f: | ||
f.write(r.content) | ||
except Exception: | ||
return | ||
res = requests.get(url) | ||
res.raise_for_status() | ||
ext = os.path.splitext(url)[-1] | ||
asset_path += ext | ||
with open(asset_path, "wb") as img: | ||
img.write(res.content) | ||
Comment on lines
+63
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For increased readability, I'd avoid abbreviating variable names, i.e. use |
||
|
||
return f"grid:{filename}" | ||
return os.path.basename(asset_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment can be removed