diff --git a/rare/__main__.py b/rare/__main__.py
index a88e67ea..cbb387f8 100644
--- a/rare/__main__.py
+++ b/rare/__main__.py
@@ -7,9 +7,15 @@ from rare.utils import singleton
def main():
parser = ArgumentParser()
- parser.add_argument("-V", "--version", action="store_true")
- parser.add_argument("-S", "--silent", action="store_true")
- parser.add_argument("--offline", action="store_true")
+ parser.add_argument("-V", "--version", action="store_true", help="Shows version and exits")
+ parser.add_argument("-S", "--silent", action="store_true",
+ help="Launch Rare in background. Open it from System Tray Icon")
+ parser.add_argument("--offline", action="store_true", help="Launch Rare in offline mode")
+ if os.name != "nt":
+ parser.add_argument("--disable-protondb", action="store_true", dest="disable_protondb",
+ help="Do not download and check data from ProtonDB. Disable it, if you don't need grades")
+ parser.add_argument("--enable-protondb", action="store_true", dest="enable_protondb",
+ help="Enable ProtonDB data, after disabled")
subparsers = parser.add_subparsers(title="Commands", dest="subparser")
launch_parser = subparsers.add_parser("launch")
diff --git a/rare/app.py b/rare/app.py
index 4f6c65f1..5acebeb7 100644
--- a/rare/app.py
+++ b/rare/app.py
@@ -60,6 +60,11 @@ class App(QApplication):
self.setOrganizationName("Rare")
settings = QSettings()
+ if args.disable_protondb:
+ settings.setValue("disable_protondb", True)
+ if args.enable_protondb:
+ settings.remove("disable_protondb")
+
# Translator
self.translator = QTranslator()
lang = settings.value("language", get_lang(), type=str)
diff --git a/rare/components/dialogs/launch_dialog.py b/rare/components/dialogs/launch_dialog.py
index d5c2fa89..6842c851 100644
--- a/rare/components/dialogs/launch_dialog.py
+++ b/rare/components/dialogs/launch_dialog.py
@@ -1,31 +1,69 @@
+import json
+import os
from logging import getLogger
-from PyQt5.QtCore import QThread, pyqtSignal
-from PyQt5.QtWidgets import QDialog, QLabel, QProgressBar, QVBoxLayout
+from PyQt5.QtCore import QThread, pyqtSignal, QSettings
+from PyQt5.QtWidgets import QDialog
from requests.exceptions import ConnectionError
from custom_legendary.core import LegendaryCore
from rare.components.dialogs.login import LoginDialog
-from rare.utils.utils import download_images, check_grades
+from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
+from rare.utils import steam_grades
+from rare.utils.utils import download_images
logger = getLogger("Login")
-class LaunchThread(QThread):
+class ImageThread(QThread):
download_progess = pyqtSignal(int)
- action = pyqtSignal(str)
def __init__(self, core: LegendaryCore, parent=None):
- super(LaunchThread, self).__init__(parent)
+ super(ImageThread, self).__init__(parent)
self.core = core
def run(self):
- self.action.emit("Login")
- self.action.emit(self.tr("Downloading Images"))
download_images(self.download_progess, self.core)
- #self.action.emit(self.tr("Read data from ProtonDB"))
- #check_grades(self.core, self.download_progess)
- self.action.emit("finish")
+ self.download_progess.emit(100)
+
+
+class SteamThread(QThread):
+ progress = pyqtSignal(int)
+ action = pyqtSignal(str)
+
+ def __init__(self, core: LegendaryCore, parent):
+ super(SteamThread, self).__init__(parent)
+ self.core = core
+
+ def run(self) -> None:
+ gamelist = self.core.get_game_list(True)
+ if not os.path.exists(os.path.expanduser("~/.cache/rare/game_list.json")):
+ self.action.emit(self.tr("Getting data from ProtonDB"))
+ steam_grades.upgrade_all([(i.app_title, i.app_name) for i in gamelist], self.progress)
+ self.progress.emit(99)
+
+ self.action.emit(self.tr("Checking Games for data"))
+ grades = json.load(open(os.path.expanduser("~/.cache/rare/game_list.json")))
+ ids = json.load(open(os.path.expanduser("~/.cache/rare/steam_ids.json")))
+ for game in gamelist:
+ if not grades.get(game.app_name):
+ steam_id = steam_grades.get_steam_id(game.app_title, ids)
+ grade = steam_grades.get_grade(steam_id)
+ grades[game.app_name] = {
+ "steam_id": steam_id,
+ "grade": grade
+ }
+ if not grades[game.app_name].get("steam_id"):
+ grades[game.app_name]["steam_id"] = steam_grades.get_steam_id(game.app_title)
+ if not grades[game.app_name].get("grade"):
+ grades[game.app_name]["grade"] = steam_grades.get_grade(game.app_title)
+
+ with open(os.path.expanduser("~/.cache/rare/game_list.json"), "w") as f:
+ f.write(json.dumps(grades))
+ f.close()
+
+ self.action.emit("Ready")
+ self.progress.emit(100)
class LoginThread(QThread):
@@ -52,11 +90,17 @@ class LoginThread(QThread):
self.start_app.emit(True)
-class LaunchDialog(QDialog):
+class LaunchDialog(QDialog, Ui_LaunchDialog):
start_app = pyqtSignal(bool)
+ finished = False
def __init__(self, core: LegendaryCore, offline):
super(LaunchDialog, self).__init__()
+ self.setupUi(self)
+ if os.name == "nt":
+ self.finished = True
+ self.steam_info.setVisible(False)
+ self.steam_prog_bar.setVisible(False)
self.core = core
if not offline:
self.login_thread = LoginThread(core)
@@ -64,18 +108,7 @@ class LaunchDialog(QDialog):
self.login_thread.start_app.connect(self.launch)
self.login_thread.start()
- self.title = QLabel("
" + self.tr("Launching Rare") + "
")
- self.info_pb = QProgressBar()
- self.info_text = QLabel(self.tr("Logging in"))
- self.layout = QVBoxLayout()
-
- self.layout.addWidget(self.title)
- self.layout.addWidget(self.info_pb)
- self.layout.addWidget(self.info_text)
-
- self.setLayout(self.layout)
-
- if offline:
+ else:
self.launch(offline)
def login(self):
@@ -88,20 +121,40 @@ class LaunchDialog(QDialog):
def launch(self, offline=False):
# self.core = core
+ if not os.path.exists(p := os.path.expanduser("~/.cache/rare/images")):
+ os.makedirs(p)
self.offline = offline
- self.info_text.setText(self.tr("Downloading Images"))
- self.thread = LaunchThread(self.core, self)
- self.thread.download_progess.connect(self.update_pb)
- self.thread.action.connect(self.info)
- self.thread.start()
- def update_pb(self, i: int):
- self.info_pb.setValue(i)
+ if not offline:
- def info(self, text: str):
- if text == "finish":
- self.info_text.setText(self.tr("Starting..."))
- self.info_pb.setValue(100)
+ self.image_info.setText(self.tr("Downloading Images"))
+ self.img_thread = ImageThread(self.core, self)
+ self.img_thread.download_progess.connect(self.update_image_progbar)
+ self.img_thread.finished.connect(self.finish)
+ self.img_thread.start()
+ # not disabled and not windows
+ if (not QSettings().value("disable_protondb", False, bool)) and (not os.name == "nt"):
+ self.steam_thread = SteamThread(self.core, self)
+ self.steam_thread.progress.connect(self.update_steam_prog_bar)
+ self.steam_thread.action.connect(lambda x: self.steam_info.setText(x))
+ self.steam_thread.finished.connect(self.finish)
+ self.steam_thread.start()
+ else:
+ self.finished = True
+ self.steam_info.setVisible(False)
+ self.steam_prog_bar.setVisible(False)
+
+ def update_steam_prog_bar(self, value):
+ self.steam_prog_bar.setValue(value)
+
+ def update_image_progbar(self, i: int):
+ self.image_prog_bar.setValue(i)
+
+ def finish(self):
+ if self.finished:
+ self.image_info.setText(self.tr("Starting..."))
+ self.image_prog_bar.setValue(100)
+ self.steam_prog_bar.setValue(100)
self.start_app.emit(self.offline)
else:
- self.info_text.setText(text)
+ self.finished = True
diff --git a/rare/components/tabs/games/game_info/__init__.py b/rare/components/tabs/games/game_info/__init__.py
index 626dce74..e658e1c0 100644
--- a/rare/components/tabs/games/game_info/__init__.py
+++ b/rare/components/tabs/games/game_info/__init__.py
@@ -63,8 +63,6 @@ class GameInfo(QWidget, Ui_GameInfo):
verify_game = pyqtSignal(str)
verify_threads = {}
- grade_table = json.load(open(os.path.expanduser("~/.cache/rare/game_list.json")))
-
def __init__(self, core: LegendaryCore, parent):
super(GameInfo, self).__init__(parent=parent)
self.setupUi(self)
@@ -74,7 +72,10 @@ class GameInfo(QWidget, Ui_GameInfo):
"bronze": self.tr("Bronze"),
"fail": self.tr("Could not get grade from ProtonDB"),
"pending": "Not enough reports"}
-
+ if os.path.exists(p := os.path.expanduser("~/.cache/rare/game_list.json")):
+ self.grade_table = json.load(open(p))
+ else:
+ self.grade_table = {}
self.widget = QWidget()
self.core = core
if os.name == "nt":
@@ -155,7 +156,7 @@ class GameInfo(QWidget, Ui_GameInfo):
self.install_size.setText(get_size(self.igame.install_size))
self.install_path.setText(self.igame.install_path)
- if os.name != "nt":
+ if os.name != "nt" and self.grade_table:
try:
grade = self.ratings.get(self.grade_table[app_name].get("grade"))
except KeyError:
diff --git a/rare/components/tabs/games/game_info/uninstalled_info.py b/rare/components/tabs/games/game_info/uninstalled_info.py
index 15ff62e1..fcb5b781 100644
--- a/rare/components/tabs/games/game_info/uninstalled_info.py
+++ b/rare/components/tabs/games/game_info/uninstalled_info.py
@@ -56,7 +56,11 @@ class UninstalledInfo(QWidget):
def __init__(self, core: LegendaryCore, parent):
super(UninstalledInfo, self).__init__(parent=parent)
self.layout = QVBoxLayout()
- self.grade_table = json.load(open(os.path.expanduser("~/.cache/rare/game_list.json")))
+
+ if os.path.exists(p := os.path.expanduser("~/.cache/rare/game_list.json")):
+ self.grade_table = json.load(open(p))
+ else:
+ self.grade_table = {}
self.ratings = {"platinum": self.tr("Platinum"),
"gold": self.tr("Gold"),
@@ -82,9 +86,9 @@ class UninstalledInfo(QWidget):
self.app_name = QLabel("Error")
self.right_layout.addWidget(self.app_name)
-
- self.rating = QLabel("Rating: Error")
- self.right_layout.addWidget(self.rating)
+ if os.name != "nt":
+ self.rating = QLabel("Rating: Error")
+ self.right_layout.addWidget(self.rating)
self.install_button = QPushButton(self.tr("Install"))
self.install_button.setFixedWidth(300)
@@ -124,11 +128,12 @@ class UninstalledInfo(QWidget):
self.image.setPixmap(pixmap)
self.version.setText(self.game.asset_info.build_version)
- try:
- rating = self.grade_table[app_name]["grade"]
- except KeyError:
- rating = "fail"
- if rating not in ["fail", "pending"]:
- self.rating.setText(self.tr("Rating from ProtonDB: ") + self.ratings[rating])
- else:
- self.rating.setText(self.ratings[rating])
+ if self.grade_table and (not os.name == "nt"):
+ try:
+ rating = self.grade_table[app_name]["grade"]
+ except KeyError:
+ rating = "fail"
+ if rating not in ["fail", "pending"]:
+ self.rating.setText(self.tr("Rating from ProtonDB: ") + self.ratings[rating])
+ else:
+ self.rating.setText(self.ratings[rating])
diff --git a/rare/utils/id.py b/rare/utils/id.py
deleted file mode 100644
index 65bd69ea..00000000
--- a/rare/utils/id.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import logging
-import os
-
-import requests
-import json
-from datetime import date
-
-
-replace_chars = ",;.:-_ "
-
-file = os.path.expanduser("~/.cache/rare/game_list.json")
-url = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"
-
-
-def get_id(game_name):
- global file
-
- if check_time() == 1:
- upgrade_content()
-
- text = open(file, 'r')
- game_list = json.load(text)
-
- return game_list[game_name.lower()]
-
-
-def download_ids():
- response = requests.get(url)
- with open(os.path.expanduser("~/.cache/rare/steam_ids.json"), "w") as f:
- f.write(response.text)
- f.close()
-
-
-def upgrade_content(games: list, status_signal): # this function uploads the ids database, aka game_list.json
- global url
- global file
- response = requests.get(url)
- with open(os.path.expanduser("~/.cache/rare/steam_ids.json"), "w") as f:
- f.write(response.text)
- f.close()
-
- content = json.loads(response.text)
- game_list = {} # {CrabEA: {id: 1234, grade: platinum}, ..}
-
- steam_games = {}
- for i in content["applist"]["apps"]:
- name: str = i["name"].lower()
- for c in replace_chars:
- name = name.replace(c, "")
- name = name.encode("ascii", "ignore").decode("ascii", "ignore")
- steam_games[name] = i["appid"]
-
- for i in games:
- if i.app_title.lower() in steam_games.keys():
- game_list[i.app_name] = {}
- game_list[i.app_name]["id"] = steam_games[i.app_title.lower()]
- continue
- else:
- app_title = i.app_title.lower()
- app_title = app_title.encode("ascii", "ignore").decode("ascii", "ignore")
- for c in replace_chars:
- app_title = app_title.replace(c, "")
- if app_title in steam_games.keys():
- game_list[i.app_name] = {}
- game_list[i.app_name]["id"] = steam_games[app_title]
- else:
- for game in steam_games:
- if app_title.startswith(game):
- game_list[i.app_name] = {}
- game_list[i.app_name]["id"] = steam_games[game]
-
- for i, game in enumerate(game_list):
- try:
- grade = get_grade(game_list[game]["id"])
- except json.decoder.JSONDecodeError as e:
- logging.error(str(e))
- game_list[game]["grade"] = "fail"
- print(game) # debug
- else:
- game_list[game]["grade"] = grade
- status_signal.emit(50 + i/len(game_list)*50)
-
- # print(game_list)
-
- # for game in content['applist']['apps']:
- # game_list[game['name'].lower()] = game['appid']
-
- # uploding date on json
- today = date.today()
- game_list['data'] = {}
- for i in "ymd":
- game_list["data"][i] = today.strftime('%' + i)
-
- table = open(file, 'w')
-
- json.dump(game_list, table)
- table.close()
-
-
-def check_time(): # this function check if it's time to update
- global file
- text = open(file, 'r')
- json_table = json.load(text)
- text.close()
-
- 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
-
-
-# you should iniciate the module with the game's steam code
-def get_grade(steam_code):
- steam_code = str(steam_code)
- url = 'https://www.protondb.com/api/v1/reports/summaries/'
- res = requests.get(url + steam_code + '.json')
- text = res.text
- lista = json.loads(text)
- # print(lista['tier']) # just for debug pourpouses!!!
-
- return lista['tier']
-
-
-def id(game_name):
- return get_grade(get_id(game_name))
diff --git a/rare/utils/steam_grades.py b/rare/utils/steam_grades.py
new file mode 100644
index 00000000..49feda5a
--- /dev/null
+++ b/rare/utils/steam_grades.py
@@ -0,0 +1,116 @@
+import difflib
+import json
+import os
+from datetime import date
+
+import requests
+from PyQt5.QtCore import pyqtSignal
+
+replace_chars = ",;.:-_ "
+
+file = os.path.expanduser("~/.cache/rare/game_list.json")
+url = "https://api.steampowered.com/ISteamApps/GetAppList/v2/"
+
+
+# you should iniciate the module with the game's steam code
+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(url + steam_code + '.json')
+ try:
+ lista = json.loads(res.text)
+ except json.decoder.JSONDecodeError:
+ return "fail"
+
+ return lista['tier']
+
+
+def load_json() -> dict:
+ if not os.path.exists(p := os.path.expanduser("~/.cache/rare/steam_ids.json")):
+ response = requests.get(url)
+ steam_ids = json.loads(response.text)["applist"]["apps"]
+ ids = {}
+ for game in steam_ids:
+ ids[game["name"]] = game["appid"]
+
+ with open(os.path.expanduser(p), "w") as f:
+ f.write(json.dumps(ids))
+ f.close()
+ return ids
+ else:
+ return json.loads(open(os.path.expanduser("~/.cache/rare/steam_ids.json"), "r").read())
+
+
+def upgrade_all(games, progress: pyqtSignal = None):
+ ids = load_json()
+ data = {}
+ for i, (title, app_name) in enumerate(games):
+ title = title.replace("Early Access", "").replace("Experimental", "").strip()
+ data[app_name] = {}
+
+ steam_id = get_steam_id(title, ids)
+
+ data[app_name] = {
+ "steam_id": steam_id,
+ "grade": get_grade(steam_id)}
+
+ if progress:
+ progress.emit(int(i / len(games) * 100))
+
+ with open(os.path.expanduser("~/.cache/rare/game_list.json"), "w") as f:
+ f.write(json.dumps(data))
+ f.close()
+
+
+def get_steam_id(title: str, json_data=None):
+ title = title.replace("Early Access", "").replace("Experimental", "").strip()
+ if not json_data:
+ if not os.path.exists(p := os.path.expanduser("~/.cache/rare/steam_ids.json")):
+ response = requests.get(url)
+ ids = {}
+ steam_ids = json.loads(response.text)["applist"]["apps"]
+ for game in steam_ids:
+ ids[game["name"]] = game["appid"]
+
+ with open(os.path.expanduser(p), "w") as f:
+ f.write(json.dumps(steam_ids))
+ f.close()
+ else:
+ ids = json.loads(open(os.path.expanduser("~/.cache/rare/steam_ids.json"), "r").read())
+ else:
+ ids = json_data
+ steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
+ if steam_name:
+ return ids[steam_name[0]]
+ else:
+ return 0
+ # print(x)
+
+ # for game in steam_ids:
+ # num = difflib.SequenceMatcher(None, game["name"], title).ratio()
+ # if num > most_similar[2] and num > 0.5:
+ # most_similar = (game["appid"], game["name"], num)
+ # print(time.time()-t)
+ # name = difflib.get_close_matches(steam_ids.keys(), title)
+ # return most_similar
+
+
+def check_time(): # this function check if it's time to update
+ global file
+ text = open(file, 'r')
+ json_table = json.load(text)
+ text.close()
+
+ 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
diff --git a/rare/utils/utils.py b/rare/utils/utils.py
index e2cf9c9f..5c2a5fbc 100644
--- a/rare/utils/utils.py
+++ b/rare/utils/utils.py
@@ -8,15 +8,12 @@ import requests
from PIL import Image, UnidentifiedImageError
from PyQt5.QtCore import pyqtSignal, QLocale, QSettings
from PyQt5.QtGui import QPalette, QColor
-from rare import style_path
-
-from rare.utils.id import upgrade_content, check_time, download_ids
# Windows
if os.name == "nt":
from win32com.client import Dispatch
-from rare import lang_path, __version__, style_path
+from rare import lang_path, style_path
from custom_legendary.core import LegendaryCore
logger = getLogger("Utils")
@@ -42,22 +39,7 @@ def download_images(signal: pyqtSignal, core: LegendaryCore):
except json.decoder.JSONDecodeError:
shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}")
download_image(game)
- signal.emit(i/len(game_list)*50)
-
-
-def check_grades(core: LegendaryCore, signal):
- games = core.get_game_list(True)
- # upgrade_content(games, signal)
- if not os.path.exists("~/.cache/rare/steam_ids.json"):
- download_ids()
-
- game_table = json.loads(open(os.path.expanduser("~/.cache/rare/game_list.json")).read())
- for game in games:
- if game.app_name not in game_table.keys():
- # TODO: Find id from method; Not upgrade all
- game_table[game.app_name] = {"grade": "pending", "id": "1234"}
-
- json.dump(game_table, open(os.path.expanduser("~/.cache/rare/game_list.json"), "w"))
+ signal.emit(i / len(game_list) * 100)
def download_image(game, force=False):
@@ -88,7 +70,7 @@ def download_image(game, force=False):
f.write(requests.get(url).content)
try:
img = Image.open(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png")
- img = img.resize((200, int(200*4/3)))
+ img = img.resize((200, int(200 * 4 / 3)))
img.save(f"{IMAGE_DIR}/{game.app_name}/{image['type']}.png")
except UnidentifiedImageError as e:
logger.warning(e)
@@ -103,7 +85,7 @@ def download_image(game, force=False):
bg = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxTall.png")
uninstalledArt = bg.convert('L')
- uninstalledArt = uninstalledArt.resize((200, int(200*4/3)))
+ uninstalledArt = uninstalledArt.resize((200, int(200 * 4 / 3)))
uninstalledArt.save(f'{IMAGE_DIR}/{game.app_name}/UninstalledArt.png')
elif os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png"):
bg: Image.Image = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo.png")
@@ -292,9 +274,9 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
shortcut.WorkingDirectory = os.getcwd()
# Icon
- if not os.path.exists(icon+".ico"):
- img = Image.open(icon+".png")
- img.save(icon+".ico")
+ if not os.path.exists(icon + ".ico"):
+ img = Image.open(icon + ".png")
+ img.save(icon + ".ico")
logger.info("Create Icon")
shortcut.IconLocation = os.path.join(icon + ".ico")
shortcut.save()