From eb8ce3016905bfbe4090ba0db4d8cfa1d46bb09d Mon Sep 17 00:00:00 2001 From: Dummerle <44114474+Dummerle@users.noreply.github.com> Date: Sun, 2 Jan 2022 22:52:43 +0100 Subject: [PATCH] Save last played and add quick launch for tray icon --- rare/components/tabs/games/game_utils.py | 51 +++++++++++-------- rare/components/tray_icon.py | 35 ++++++++++++- rare/utils/meta.py | 64 ++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 rare/utils/meta.py diff --git a/rare/components/tabs/games/game_utils.py b/rare/components/tabs/games/game_utils.py index 8868ab31..c97559e7 100644 --- a/rare/components/tabs/games/game_utils.py +++ b/rare/components/tabs/games/game_utils.py @@ -1,3 +1,4 @@ +import datetime import os import platform import shutil @@ -14,6 +15,7 @@ from rare.components.dialogs.uninstall_dialog import UninstallDialog from rare.components.extra.console import ConsoleWindow from rare.components.tabs.games import CloudSaveUtils from rare.utils import legendary_utils +from rare.utils.meta import RareGameMeta logger = getLogger("GameUtils") @@ -50,6 +52,7 @@ class GameUtils(QObject): self.console = ConsoleWindow() self.cloud_save_utils = CloudSaveUtils() self.cloud_save_utils.sync_finished.connect(self.sync_finished) + self.game_meta = RareGameMeta() def uninstall_game(self, app_name) -> bool: # returns if uninstalled @@ -57,13 +60,13 @@ class GameUtils(QObject): igame = self.core.get_installed_game(app_name) if not os.path.exists(igame.install_path): if QMessageBox.Yes == QMessageBox.question( - None, - "Uninstall", - self.tr( - "Game files of {} do not exist. Remove it from installed games?" - ).format(igame.title), - QMessageBox.Yes | QMessageBox.No, - QMessageBox.Yes, + None, + "Uninstall", + self.tr( + "Game files of {} do not exist. Remove it from installed games?" + ).format(igame.title), + QMessageBox.Yes | QMessageBox.No, + QMessageBox.Yes, ): self.core.lgd.remove_installed_game(app_name) return True @@ -78,7 +81,7 @@ class GameUtils(QObject): return True def prepare_launch( - self, app_name, offline: bool = False, skip_update_check: bool = False + self, app_name, offline: bool = False, skip_update_check: bool = False ): game = self.core.get_game(app_name) dont_sync_after_finish = False @@ -102,19 +105,23 @@ class GameUtils(QObject): ) def launch_game( - self, - app_name: str, - offline: bool = False, - skip_update_check: bool = False, - wine_bin: str = None, - wine_pfx: str = None, - ask_always_sync: bool = False, + self, + app_name: str, + offline: bool = False, + skip_update_check: bool = False, + wine_bin: str = None, + wine_pfx: str = None, + ask_always_sync: bool = False, ): if shared.args.offline: offline = True game = self.core.get_game(app_name) igame = self.core.get_installed_game(app_name) + meta_data = self.game_meta.get_game(app_name) + meta_data.last_played = datetime.datetime.now() + self.game_meta.set_game(app_name, meta_data) + if not game: logger.error(f"{app_name} not found") self.finished.emit(app_name, self.tr("Game not found in available games")) @@ -122,13 +129,13 @@ class GameUtils(QObject): if QSettings().value("confirm_start", False, bool): if ( - not QMessageBox.question( - None, - "Launch", - self.tr("Do you want to launch {}").format(game.app_title), - QMessageBox.Yes | QMessageBox.No, - ) - == QMessageBox.Yes + not QMessageBox.question( + None, + "Launch", + self.tr("Do you want to launch {}").format(game.app_title), + QMessageBox.Yes | QMessageBox.No, + ) + == QMessageBox.Yes ): logger.info("Cancel Startup") self.finished.emit(app_name, "") diff --git a/rare/components/tray_icon.py b/rare/components/tray_icon.py index 9413e081..119f4288 100644 --- a/rare/components/tray_icon.py +++ b/rare/components/tray_icon.py @@ -1,6 +1,11 @@ +from typing import List + from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction +from rare import shared +from rare.utils.meta import GameMeta + class TrayIcon(QSystemTrayIcon): def __init__(self, parent): @@ -14,8 +19,34 @@ class TrayIcon(QSystemTrayIcon): self.start_rare = QAction("Rare") self.menu.addAction(self.start_rare) - self.exit_action = QAction(self.tr("Exit")) self.menu.addSeparator() - self.menu.addAction(self.exit_action) + self.text_action = QAction("Quick launch") + self.text_action.setEnabled(False) + self.menu.addAction(self.text_action) + if len(installed := shared.core.get_installed_list()) < 5: + last_played = [GameMeta(i.app_name) for i in sorted(installed, key=lambda x: x.title)] + elif games := sorted( + parent.mainwindow.tab_widget.games_tab.game_utils.game_meta.get_games(), + key=lambda x: x.last_played, reverse=True): + last_played: List[GameMeta] = games[0:5] + else: + last_played = [GameMeta(i.app_name) for i in sorted(installed, key=lambda x: x.title)][0:5] + + self.game_actions = [] + + for game in last_played: + a = QAction(shared.core.get_game(game.app_name).app_title) + a.setProperty("app_name", game.app_name) + self.game_actions.append(a) + a.triggered.connect( + lambda: parent.mainwindow.tab_widget.games_tab.game_utils.prepare_launch( + self.sender().property("app_name")) + ) + + self.menu.addActions(self.game_actions) + self.menu.addSeparator() + + self.exit_action = QAction(self.tr("Exit")) + self.menu.addAction(self.exit_action) self.setContextMenu(self.menu) diff --git a/rare/utils/meta.py b/rare/utils/meta.py new file mode 100644 index 00000000..66c668bd --- /dev/null +++ b/rare/utils/meta.py @@ -0,0 +1,64 @@ +import json +import os +from dataclasses import dataclass +from datetime import datetime +from logging import getLogger +from typing import Dict + +from rare import data_dir + +logger = getLogger("GameMeta") + + +@dataclass +class GameMeta: + app_name: str + last_played: datetime = None + + @classmethod + def from_json(cls, data): + return cls( + app_name=data["app_name"], + last_played=datetime.strptime(data.get("last_played", "None"), '%Y-%m-%dT%H:%M:%S.%f') + ) + + def __dict__(self): + return dict( + app_name=self.app_name, + last_played=self.last_played.strftime("%Y-%m-%dT%H:%M:%S.%f") + ) + + +class RareGameMeta: + _meta: Dict[str, GameMeta] = {} + + def __init__(self): + meta_data = {} + if os.path.exists(p := os.path.join(data_dir, "game_meta.json")): + try: + meta_data = json.load(open(p)) + except json.JSONDecodeError: + logger.warning("Game meta json file corrupt") + else: + with open(p, "w") as file: + file.write("{}") + + for app_name, data in meta_data.items(): + self._meta[app_name] = GameMeta.from_json(data) + + def get_games(self): + return list(self._meta.values()) + + def get_game(self, app_name): + return self._meta.get(app_name, GameMeta(app_name)) + + def set_game(self, app_name: str, game: GameMeta): + self._meta[app_name] = game + self.save_file() + + def save_file(self): + json.dump( + {app_name: data.__dict__() for app_name, data in self._meta.items()}, + open(os.path.join(data_dir, "game_meta.json"), "w"), + indent=4 + )