diff --git a/rare/components/tabs/games/game_info/game_info.py b/rare/components/tabs/games/game_info/game_info.py
index 05dc382f..c7f353e8 100644
--- a/rare/components/tabs/games/game_info/game_info.py
+++ b/rare/components/tabs/games/game_info/game_info.py
@@ -20,7 +20,7 @@ from rare.models.game import RareGame
from rare.shared import RareCore
from rare.shared.workers import VerifyWorker, MoveWorker
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
-from rare.utils.misc import format_size, icon
+from rare.utils.misc import format_size, icon, style_hyperlink
from rare.widgets.image_widget import ImageWidget, ImageSize
from rare.widgets.side_tab import SideTabContents
from rare.components.dialogs.move_dialog import MoveDialog, is_game_dir
@@ -302,7 +302,12 @@ class GameInfo(QWidget, SideTabContents):
self.ui.grade.setDisabled(
self.rgame.is_unreal or platform.system() == "Windows"
)
- self.ui.grade.setText(self.steam_grade_ratings[self.rgame.steam_grade()])
+ self.ui.grade.setText(
+ style_hyperlink(
+ f"https://www.protondb.com/app/{self.rgame.steam_appid}",
+ self.steam_grade_ratings[self.rgame.steam_grade()]
+ )
+ )
self.ui.install_button.setEnabled(
(not self.rgame.is_installed or self.rgame.is_non_asset) and self.rgame.is_idle
diff --git a/rare/models/game.py b/rare/models/game.py
index 2bcdccbb..721dd075 100644
--- a/rare/models/game.py
+++ b/rare/models/game.py
@@ -19,6 +19,7 @@ from rare.shared.game_process import GameProcess
from rare.shared.image_manager import ImageManager
from rare.utils.paths import data_dir, get_rare_executable
from rare.utils.steam_grades import get_rating
+from rare.utils.config_helper import add_envvar, remove_envvar
logger = getLogger("RareGame")
@@ -31,6 +32,7 @@ class RareGame(RareGameSlim):
queue_pos: Optional[int] = None
last_played: datetime = datetime.min
grant_date: Optional[datetime] = None
+ steam_appid: int = 0
steam_grade: Optional[str] = None
steam_date: datetime = datetime.min
tags: List[str] = field(default_factory=list)
@@ -43,6 +45,7 @@ class RareGame(RareGameSlim):
queue_pos=data.get("queue_pos", None),
last_played=datetime.fromisoformat(data["last_played"]) if data.get("last_played", None) else datetime.min,
grant_date=datetime.fromisoformat(data["grant_date"]) if data.get("grant_date", None) else None,
+ steam_appid=data.get("steam_appid", 0),
steam_grade=data.get("steam_grade", None),
steam_date=datetime.fromisoformat(data["steam_date"]) if data.get("steam_date", None) else datetime.min,
tags=data.get("tags", []),
@@ -55,6 +58,7 @@ class RareGame(RareGameSlim):
queue_pos=self.queue_pos,
last_played=self.last_played.isoformat() if self.last_played else datetime.min,
grant_date=self.grant_date.isoformat() if self.grant_date else None,
+ steam_appid=self.steam_appid,
steam_grade=self.steam_grade,
steam_date=self.steam_date.isoformat() if self.steam_date else datetime.min,
tags=self.tags,
@@ -77,6 +81,8 @@ class RareGame(RareGameSlim):
self.pixmap: QPixmap = QPixmap()
self.metadata: RareGame.Metadata = RareGame.Metadata()
self.__load_metadata()
+ if self.metadata.grant_date is None:
+ self.grant_date()
self.owned_dlcs: Set[RareGame] = set()
@@ -427,18 +433,29 @@ class RareGame(RareGameSlim):
if platform.system() == "Windows" or self.is_unreal:
return "na"
elapsed_time = abs(datetime.utcnow() - self.metadata.steam_date)
- if self.metadata.steam_grade is not None and elapsed_time.days < 3:
+ if (
+ self.metadata.steam_grade is not None
+ and self.metadata.steam_appid != 0
+ and elapsed_time.days < 3
+ ):
return self.metadata.steam_grade
def _set_steam_grade():
- rating = get_rating(self.core, self.app_name)
- self.set_steam_grade(rating)
+ appid, rating = get_rating(self.core, self.app_name)
+ self.set_steam_grade(appid, rating)
worker = QRunnable.create(_set_steam_grade)
QThreadPool.globalInstance().start(worker)
return "pending"
- def set_steam_grade(self, grade: str) -> None:
+ @property
+ def steam_appid(self) -> Optional[int]:
+ return self.metadata.steam_appid
+
+ def set_steam_grade(self, appid: int, grade: str) -> None:
+ if appid and not self.steam_appid:
+ add_envvar(self.app_name, "SteamAppId", str(appid))
+ self.metadata.steam_appid = appid
self.metadata.steam_grade = grade
self.metadata.steam_date = datetime.utcnow()
self.__save_metadata()
diff --git a/rare/ui/components/tabs/games/game_info/game_info.py b/rare/ui/components/tabs/games/game_info/game_info.py
index 3457e942..712f657a 100644
--- a/rare/ui/components/tabs/games/game_info/game_info.py
+++ b/rare/ui/components/tabs/games/game_info/game_info.py
@@ -133,6 +133,7 @@ class Ui_GameInfo(object):
self.info_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_grade)
self.grade = QtWidgets.QLabel(GameInfo)
self.grade.setText("error")
+ self.grade.setOpenExternalLinks(True)
self.grade.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.grade.setObjectName("grade")
self.info_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.grade)
diff --git a/rare/ui/components/tabs/games/game_info/game_info.ui b/rare/ui/components/tabs/games/game_info/game_info.ui
index 89115667..261ba5d0 100644
--- a/rare/ui/components/tabs/games/game_info/game_info.ui
+++ b/rare/ui/components/tabs/games/game_info/game_info.ui
@@ -232,6 +232,9 @@
error
+
+ true
+
Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
diff --git a/rare/utils/misc.py b/rare/utils/misc.py
index 3d0267b4..aa27a36a 100644
--- a/rare/utils/misc.py
+++ b/rare/utils/misc.py
@@ -204,3 +204,7 @@ def widget_object_name(widget: Union[QObject, wrappertype, Type], suffix: str) -
def elide_text(label: QLabel, text: str) -> str:
metrics = QFontMetrics(label.font())
return metrics.elidedText(text, Qt.ElideRight, label.sizeHint().width())
+
+
+def style_hyperlink(link: str, title: str) -> str:
+ return "{}".format(link, title)
diff --git a/rare/utils/steam_grades.py b/rare/utils/steam_grades.py
index 06a7a2b8..54e5e599 100644
--- a/rare/utils/steam_grades.py
+++ b/rare/utils/steam_grades.py
@@ -1,46 +1,34 @@
import difflib
-import orjson
import os
-from datetime import date
+from datetime import datetime
+from typing import Tuple
+import orjson
import requests
from rare.lgndr.core import LegendaryCore
-from rare.utils.paths import data_dir, cache_dir
+from rare.utils.paths import cache_dir
replace_chars = ",;.:-_ "
-url = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"
-
+steamapi_url = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"
+protondb_url = "https://www.protondb.com/api/v1/reports/summaries/"
__steam_ids_json = None
__grades_json = None
+__active_download = False
-def get_rating(core: LegendaryCore, app_name: str):
- global __grades_json
- if __grades_json is None:
- if os.path.exists(p := os.path.join(data_dir(), "steam_ids.json")):
- grades = orjson.loads(open(p).read())
- __grades_json = grades
- else:
- grades = {}
- __grades_json = grades
+def get_rating(core: LegendaryCore, app_name: str) -> Tuple[int, str]:
+ game = core.get_game(app_name)
+ try:
+ steam_id = get_steam_id(game.app_title)
+ if not steam_id:
+ raise Exception
+ grade = get_grade(steam_id)
+ except Exception as e:
+ return 0, "fail"
else:
- grades = __grades_json
-
- if not grades.get(app_name):
- game = core.get_game(app_name)
- try:
- steam_id = get_steam_id(game.app_title)
- grade = get_grade(steam_id)
- except:
- return "fail"
- grades[app_name] = {"steam_id": steam_id, "grade": grade}
- with open(os.path.join(data_dir(), "steam_ids.json"), "w") as f:
- f.write(orjson.dumps(grades).decode("utf-8"))
- return grade
- else:
- return grades[app_name].get("grade")
+ return steam_id, grade
# you should iniciate the module with the game's steam code
@@ -48,8 +36,7 @@ def get_grade(steam_code):
if steam_code == 0:
return "fail"
steam_code = str(steam_code)
- url = "https://www.protondb.com/api/v1/reports/summaries/"
- res = requests.get(f"{url}{steam_code}.json")
+ res = requests.get(f"{protondb_url}{steam_code}.json")
try:
lista = orjson.loads(res.text)
except orjson.JSONDecodeError:
@@ -60,13 +47,19 @@ def get_grade(steam_code):
def load_json() -> dict:
file = os.path.join(cache_dir(), "game_list.json")
- if not os.path.exists(file):
- response = requests.get(url)
+ mod_time = datetime.fromtimestamp(os.path.getmtime(file))
+ elapsed_time = abs(datetime.now() - mod_time)
+ global __active_download
+ if __active_download:
+ return {}
+ if not os.path.exists(file) or elapsed_time.days > 7:
+ __active_download = True
+ response = requests.get(steamapi_url)
+ __active_download = False
steam_ids = orjson.loads(response.text)["applist"]["apps"]
ids = {}
for game in steam_ids:
ids[game["name"]] = game["appid"]
-
with open(file, "w") as f:
f.write(orjson.dumps(ids).decode("utf-8"))
return ids
@@ -74,51 +67,22 @@ def load_json() -> dict:
return orjson.loads(open(file, "r").read())
-def get_steam_id(title: str):
- file = os.path.join(cache_dir(), "game_list.json")
+def get_steam_id(title: str) -> int:
# workarounds for satisfactory
+ # FIXME: This has to be made smarter.
title = title.replace("Early Access", "").replace("Experimental", "").strip()
+ # title = title.split(":")[0]
+ # title = title.split("-")[0]
global __steam_ids_json
if __steam_ids_json is None:
- if not os.path.exists(file):
- response = requests.get(url)
- ids = {}
- steam_ids = orjson.loads(response.text)["applist"]["apps"]
- for game in steam_ids:
- ids[game["name"]] = game["appid"]
- __steam_ids_json = ids
-
- with open(file, "w") as f:
- f.write(orjson.dumps(ids).decode("utf-8"))
- else:
- ids = orjson.loads(open(file, "r").read())
- __steam_ids_json = ids
- else:
- ids = __steam_ids_json
+ __steam_ids_json = load_json()
+ ids = __steam_ids_json
if title in ids.keys():
steam_name = [title]
-
else:
- steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
+ steam_name = difflib.get_close_matches(title, ids.keys(), n=1, cutoff=0.5)
if steam_name:
return ids[steam_name[0]]
else:
return 0
-
-
-def check_time(): # this function check if it's time to update
- file = os.path.join(cache_dir(), "game_list.json")
- json_table = orjson.loads(open(file, "r").read())
-
- today = date.today()
- day = 0 # it controls how many days it's necessary for an update
- for i in "ymd":
- if i == "d":
- day = 7
- else:
- day = 0
- if int(today.strftime("%" + i)) > int(json_table["data"][i]) + day:
- return 1
- else:
- return 0