1
0
Fork 0
mirror of synced 2024-05-14 17:42:41 +12:00

Merge branch 'main' into windows_desktop_shortcut

This commit is contained in:
ChemicalXandco 2021-05-09 15:07:13 +01:00 committed by GitHub
commit de408cc534
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 841 additions and 354 deletions

View file

@ -90,7 +90,7 @@ jobs:
uses: svenstaro/upload-release-action@2.2.1
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: rare.deb
file: Rare.deb
asset_name: Rare.deb
tag: ${{ github.ref }}
overwrite: true

View file

@ -4,10 +4,12 @@
### Add translations
1. Execute ```pylupdate5 $(find -name "*.py") -ts Rare/languages/{your lang (two letters)}.ts``` in project directory
1. Execute ```pylupdate5 $(find -name "*.py") -ts Rare/languages/de.ts``` in source directory. Replace *de* with your language code
2. Modify the .ts file manually or in Qt Linguist
3. Compile the file with ```lrelease Rare/languages/{lang}.ts```
If compilation fails, just push ts file. Then I will compile it
### Add Stylesheets
For this you can create a .qss file in Rare/Styles/ directory or modify the existing RareStyle.qss file. Here are some
@ -16,11 +18,11 @@ examples:
### Add features
Select one Card of the project and implement it or make other changes
Select one Card of the project and implement it, or if you want to add another feature ask me on Discord, or create an issue on GitHub
## Git crash-course
To contribute fork the repository and clone **your** repo. Then make your changes, add it to git with `git add File.xy` and upload it to GitHub with `git commit -m "message"` and `git push`.
Some IDEs can do this automatically.
If you uploaded your changes, create a pull request to dev-branch
If you uploaded your changes, create a pull request

View file

@ -21,7 +21,7 @@ Execute `pip install Rare` for all users Or `pip install Rare --user` for only o
Download Rare.exe from the [releases page](https://github.com/Dummerle/Rare/releases) and execute it.
**Note:**
Using the exe file could cause errors with Windows Defender or other Anti Virus
Using the exe file could cause errors with Windows Defender or other Anti Virus. Sometimes it is not possible to download games and sometimes the app crashes. In this case please use pip
### Linux
@ -30,7 +30,6 @@ Using the exe file could cause errors with Windows Defender or other Anti Virus
There are some AUR packages available:
- [rare](https://aur.archlinux.org/packages/rare) - for stable releases
- [rare-git](https://aur.archlinux.org/packages/rare-git) - for the latest features, which are not in a stable release
- [rare-dev](https://aur.archlinux.org/packages/rare-dev) - dev branch: latest development features. This could be unstable
#### Debian based
@ -59,7 +58,6 @@ Install via `pip`.
- Translations (English, German and French)
## Planned Features
- Offline mode
- More Translations (Need help)
- More Information about Games
More planned features are in projects
@ -70,8 +68,6 @@ There are more options to contribute.
- If you are a designer, you can add Stylesheets or create a logo or a banner
- You can translate the application in your language
**Note:** Pull Requests please to "dev" branch
More Information is in CONTRIBUTING.md
## Images
@ -79,4 +75,4 @@ More Information is in CONTRIBUTING.md
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/Rare.png?raw=true)
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/GameInfo.png?raw=true)
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareSettings.png?raw=true)
![alt text](https://github.com/Dummerle/Rare/blob/main/Screenshots/RareDownloads.png?raw=true)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 KiB

After

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -1,5 +1,5 @@
import os
__version__ = "1.1.1"
__version__ = "1.3.0"
style_path = os.path.join(os.path.dirname(__file__), "styles/")
lang_path = os.path.join(os.path.dirname(__file__), "languages/")

View file

@ -1,4 +1,6 @@
import os
from argparse import ArgumentParser
from rare import __version__
from rare.utils import singleton
@ -7,6 +9,7 @@ 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")
subparsers = parser.add_subparsers(title="Commands", dest="subparser")
launch_parser = subparsers.add_parser("launch")
@ -22,8 +25,19 @@ def main():
me = singleton.SingleInstance()
except singleton.SingleInstanceException:
print("Rare is already running")
with open(os.path.expanduser("~/.cache/rare/lockfile"), "w") as file:
if args.subparser == "launch":
file.write("launch " + args.app_name)
else:
file.write("start")
file.close()
exit(0)
if args.subparser == "launch":
args.silent = True
from rare.app import start
start(args)

View file

@ -1,6 +1,7 @@
import configparser
import logging
import os
import shutil
import sys
import time
@ -8,12 +9,12 @@ from PyQt5.QtCore import QSettings, QTranslator
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon
from custom_legendary.core import LegendaryCore
from rare import lang_path, style_path
from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
from rare.utils.utils import get_lang
from custom_legendary.core import LegendaryCore
start_time = time.strftime('%y-%m-%d--%H:%M') # year-month-day-hour-minute
file_name = os.path.expanduser(f"~/.cache/rare/logs/Rare_{start_time}.log")
@ -24,16 +25,14 @@ logging.basicConfig(
format='[%(name)s] %(levelname)s: %(message)s',
level=logging.INFO,
filename=file_name,
filemode="w"
)
)
logger = logging.getLogger("Rare")
class App(QApplication):
def __init__(self, args):
super(App, self).__init__(sys.argv)
self.args = args
# add some options
self.args = args # add some options
# init Legendary
try:
@ -62,7 +61,7 @@ class App(QApplication):
lang = settings.value("language", get_lang(), type=str)
if os.path.exists(lang_path + lang + ".qm"):
self.translator.load(lang_path + lang + ".qm")
logger.info("Your language is supported")
logger.info("Your language is supported: " + lang)
elif not lang == "en":
logger.info("Your language is not supported")
self.installTranslator(self.translator)
@ -72,20 +71,27 @@ class App(QApplication):
self.setWindowIcon(QIcon(style_path + "Logo.png"))
# launch app
self.launch_dialog = LaunchDialog(self.core)
self.launch_dialog = LaunchDialog(self.core, args.offline)
self.launch_dialog.start_app.connect(self.start_app)
self.launch_dialog.show()
def start_app(self):
if not args.silent or args.subparser == "launch":
self.launch_dialog.show()
def start_app(self, offline=False):
self.args.offline = offline
self.mainwindow = MainWindow(self.core, self.args)
self.launch_dialog.close()
self.tray_icon = TrayIcon(self)
self.tray_icon.exit_action.triggered.connect(lambda: exit(0))
self.tray_icon.start_rare.triggered.connect(self.mainwindow.show)
self.tray_icon.activated.connect(self.tray)
self.mainwindow.tab_widget.downloadTab.finished.connect(lambda: self.tray_icon.showMessage(
self.tr("Download finished"), self.tr("Download finished. Game is playable now"),
QSystemTrayIcon.Information, 4000))
self.launch_dialog.close()
if not offline:
self.mainwindow.tab_widget.downloadTab.finished.connect(lambda update: self.tray_icon.showMessage(
self.tr("Download finished"), self.tr("Download finished. Game is playable now"),
QSystemTrayIcon.Information, 4000) if update else None)
if not self.args.silent:
self.mainwindow.show()
def tray(self, reason):
if reason == QSystemTrayIcon.DoubleClick:
@ -101,4 +107,4 @@ def start(args):
if exit_code != -133742:
break
# restart app
del app
del app

View file

@ -3,8 +3,8 @@ import os
from PyQt5.QtWidgets import QDialog, QFormLayout, QVBoxLayout, QSpinBox, QFileDialog, QLabel, QPushButton, QHBoxLayout, \
QCheckBox
from rare.utils.extra_widgets import PathEdit
from custom_legendary.core import LegendaryCore
from rare.utils.extra_widgets import PathEdit
class InstallDialog(QDialog):
@ -18,14 +18,24 @@ class InstallDialog(QDialog):
self.form = QFormLayout()
self.update_game = update
self.layout.addWidget(QLabel(self.tr("<h3>Install {}</h3>").format(self.game.app_title)))
default_path = os.path.expanduser("~/legendary")
# TODO read from config
if self.core.lgd.config.has_option("Legendary", "install_dir"):
default_path = self.core.lgd.config.get("Legendary", "install_dir")
else:
default_path = os.path.expanduser("~/legendary")
if not default_path:
default_path = os.path.expanduser("~/legendary")
if not update:
self.install_path_field = PathEdit(text=default_path, type_of_file=QFileDialog.DirectoryOnly)
self.form.addRow(QLabel("Install directory"), self.install_path_field)
if self.core.lgd.config.has_option("Legendary", "max_workers"):
max_workers = self.core.lgd.config.get("Legendary", "max_workers")
else:
max_workers = 0
self.max_workes = QSpinBox()
self.max_workes.setValue(int(max_workers))
self.form.addRow(QLabel(self.tr("Max workers (0: Default)")), self.max_workes)
self.force = QCheckBox()
@ -52,7 +62,9 @@ class InstallDialog(QDialog):
self.setLayout(self.layout)
def get_information(self):
def get_information(self, path=None):
if path:
self.install_path_field.text_edit.setText(path)
self.exec_()
return self.infos
@ -95,4 +107,3 @@ class InstallInfoDialog(QDialog):
def cancel(self):
self.accept = False
self.close()

View file

@ -2,10 +2,11 @@ from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QDialog, QLabel, QProgressBar, QVBoxLayout
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
from custom_legendary.core import LegendaryCore
logger = getLogger("Login")
@ -27,7 +28,7 @@ class LaunchThread(QThread):
class LoginThread(QThread):
login = pyqtSignal()
start_app = pyqtSignal()
start_app = pyqtSignal(bool) # offline
def __init__(self, core: LegendaryCore):
super(LoginThread, self).__init__()
@ -38,24 +39,28 @@ class LoginThread(QThread):
try:
if self.core.login():
logger.info("You are logged in")
self.start_app.emit()
self.start_app.emit(False)
else:
self.run()
except ValueError:
logger.info("You are not logged in. Open Login Window")
self.login.emit()
except ConnectionError as e:
logger.warning(e)
self.start_app.emit(True)
class LaunchDialog(QDialog):
start_app = pyqtSignal()
start_app = pyqtSignal(bool)
def __init__(self, core: LegendaryCore):
def __init__(self, core: LegendaryCore, offline):
super(LaunchDialog, self).__init__()
self.core = core
self.login_thread = LoginThread(core)
self.login_thread.login.connect(self.login)
self.login_thread.start_app.connect(self.launch)
self.login_thread.start()
if not offline:
self.login_thread = LoginThread(core)
self.login_thread.login.connect(self.login)
self.login_thread.start_app.connect(self.launch)
self.login_thread.start()
self.title = QLabel("<h3>" + self.tr("Launching Rare") + "</h3>")
self.info_pb = QProgressBar()
@ -68,6 +73,9 @@ class LaunchDialog(QDialog):
self.setLayout(self.layout)
if offline:
self.launch(offline)
def login(self):
self.hide()
if LoginDialog(core=self.core).login():
@ -76,9 +84,9 @@ class LaunchDialog(QDialog):
else:
exit(0)
def launch(self):
def launch(self, offline=False):
# self.core = core
self.pb_size = len(self.core.get_game_list())
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)
@ -86,12 +94,12 @@ class LaunchDialog(QDialog):
self.thread.start()
def update_pb(self, i: int):
self.info_pb.setValue(i / self.pb_size * 100)
self.info_pb.setValue(i)
def info(self, text: str):
if text == "finish":
self.info_text.setText(self.tr("Starting..."))
self.info_pb.setValue(100)
self.start_app.emit()
self.start_app.emit(self.offline)
else:
self.info_text.setText(text)

View file

@ -1,9 +1,9 @@
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QStackedLayout, QWidget, QPushButton
from custom_legendary.core import LegendaryCore
# Login Opportunities: Browser, Import
from rare.components.dialogs.login.browser_login import BrowserLogin
from rare.components.dialogs.login.import_widget import ImportWidget
from custom_legendary.core import LegendaryCore
class LoginDialog(QDialog):

View file

@ -1,8 +1,9 @@
import os
from logging import getLogger
from PyQt5.QtCore import QSettings
from PyQt5.QtCore import QSettings, QTimer
from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QMainWindow, QMessageBox
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication
from custom_legendary.core import LegendaryCore
from rare.components.tab_widget import TabWidget
@ -17,30 +18,32 @@ class MainWindow(QMainWindow):
super(MainWindow, self).__init__()
self.settings = QSettings()
self.core = core
self.rpc = DiscordRPC(core)
self.offline = args.offline
width, height = 1200, 800
if self.settings.value("save_size", False):
width, height = self.settings.value("window_size", (1200, 800), tuple)
self.setGeometry(0, 0, width, height)
self.setWindowTitle("Rare - GUI for legendary")
self.tab_widget = TabWidget(core)
self.setCentralWidget(self.tab_widget)
desktop = QApplication.desktop()
self.setGeometry((desktop.width() - width) / 2, (desktop.height() - height) / 2, width, height)
# Discord RPC on game launch
self.tab_widget.games_tab.default_widget.game_list.game_started.connect(
lambda: self.rpc.set_discord_rpc(self.tab_widget.games_tab.default_widget.game_list.running_games[0]))
# Remove RPC
self.tab_widget.delete_presence.connect(self.rpc.set_discord_rpc)
# Show RPC on changed rare_settings
self.tab_widget.settings.rare_settings.rpc.update_settings.connect(
lambda: self.rpc.changed_settings(self.tab_widget.games_tab.default_widget.game_list.running_games))
self.setWindowTitle("Rare - GUI for legendary")
self.tab_widget = TabWidget(core, self, args.offline)
self.setCentralWidget(self.tab_widget)
if not args.offline:
self.rpc = DiscordRPC(core)
# Discord RPC on game launch
self.tab_widget.games_tab.default_widget.game_list.game_started.connect(
lambda: self.rpc.set_discord_rpc(self.tab_widget.games_tab.default_widget.game_list.running_games[0]))
# Remove RPC
self.tab_widget.delete_presence.connect(self.rpc.set_discord_rpc)
# Show RPC on changed rare_settings
self.tab_widget.settings.rare_settings.rpc.update_settings.connect(
lambda: self.rpc.changed_settings(self.tab_widget.games_tab.default_widget.game_list.running_games))
game = self.tab_widget.games_tab.default_widget.game_list.active_game
if game != ("", 0):
self.set_discord_rpc(game[0]) # Appname
if game != ("", 0) and not args.offline:
self.rpc.set_discord_rpc(game[0]) # Appname
self.show()
if args.subparser == "launch":
logger.info("Launching " + self.core.get_installed_game(args.app_name).title)
if args.app_name in self.tab_widget.games_tab.default_widget.game_list.widgets.keys():
@ -48,11 +51,35 @@ class MainWindow(QMainWindow):
else:
logger.info(f"Could not find {args.app_name} in Games")
self.timer = QTimer()
self.timer.timeout.connect(self.timer_finished)
self.timer.start(1000)
def timer_finished(self):
file_path = os.path.expanduser("~/.cache/rare/lockfile")
if os.path.exists(file_path):
file = open(file_path, "r")
action = file.read()
file.close()
if action.startswith("launch"):
game = action.replace("launch ", "").replace("\n", "")
if game in self.tab_widget.games_tab.default_widget.game_list.widgets.keys():
self.tab_widget.games_tab.default_widget.game_list.widgets[game][1].launch()
else:
logger.info(f"Could not find {game} in Games")
elif action.startswith("start"):
self.show()
os.remove(file_path)
self.timer.start(1000)
def closeEvent(self, e: QCloseEvent):
if self.settings.value("sys_tray", True, bool):
self.hide()
e.ignore()
return
elif self.offline:
pass
elif self.tab_widget.downloadTab.active_game is not None:
if not QMessageBox.question(self, "Close",
self.tr("There is a download active. Do you really want to exit app?"),

View file

@ -17,31 +17,33 @@ from rare.utils.models import InstallOptions
class TabWidget(QTabWidget):
delete_presence = pyqtSignal()
def __init__(self, core: LegendaryCore):
super(TabWidget, self).__init__()
disabled_tab = 3
def __init__(self, core: LegendaryCore, parent, offline):
super(TabWidget, self).__init__(parent=parent)
disabled_tab = 3 if not offline else 1
self.core = core
self.setTabBar(TabBar(disabled_tab))
# Generate Tabs
self.games_tab = GameTab(core)
updates = self.games_tab.default_widget.game_list.updates
self.downloadTab = DownloadTab(core, updates)
self.cloud_saves = SyncSaves(core)
self.settings = SettingsTab(core)
# add tabs
self.games_tab = GameTab(core, self, offline)
self.addTab(self.games_tab, self.tr("Games"))
self.addTab(self.downloadTab, "Downloads" + (" (" + str(len(updates)) + ")" if len(updates) != 0 else ""))
self.addTab(self.cloud_saves, "Cloud Saves")
if not offline:
updates = self.games_tab.default_widget.game_list.updates
self.downloadTab = DownloadTab(core, updates, self)
self.addTab(self.downloadTab, "Downloads" + (" (" + str(len(updates)) + ")" if len(updates) != 0 else ""))
self.cloud_saves = SyncSaves(core, self)
self.addTab(self.cloud_saves, "Cloud Saves")
self.settings = SettingsTab(core, self)
# Space Tab
self.addTab(QWidget(), "")
self.setTabEnabled(disabled_tab, False)
# Buttons
store_button = TabButtonWidget(core, 'fa.shopping-cart', 'Epic Games Store')
store_button.pressed.connect(lambda: webbrowser.open("https://www.epicgames.com/store"))
self.tabBar().setTabButton(3, self.tabBar().RightSide, store_button)
self.tabBar().setTabButton(disabled_tab, self.tabBar().RightSide, store_button)
self.account = QWidget()
self.addTab(self.account, "")
@ -52,7 +54,7 @@ class TabWidget(QTabWidget):
account_button = TabButtonWidget(core, 'mdi.account-circle', 'Account')
account_button.setMenu(QMenu())
account_button.menu().addAction(account_action)
self.tabBar().setTabButton(4, self.tabBar().RightSide, account_button)
self.tabBar().setTabButton(disabled_tab + 1, self.tabBar().RightSide, account_button)
self.addTab(self.settings, icon("fa.gear", color='white'),
"(!)" if self.settings.about.update_available else "")
@ -61,23 +63,25 @@ class TabWidget(QTabWidget):
# open download tab
self.games_tab.default_widget.game_list.update_game.connect(lambda: self.setCurrentIndex(1))
# Download finished
self.downloadTab.finished.connect(self.dl_finished)
# start download
self.games_tab.default_widget.game_list.install_game.connect(self.start_download)
if not offline:
# Download finished
self.downloadTab.finished.connect(self.dl_finished)
# start download
self.games_tab.default_widget.game_list.install_game.connect(self.start_download)
# install dlc
self.games_tab.game_info.dlc_tab.install_dlc.connect(self.start_download)
# repair game
self.games_tab.game_info.info.verify_game.connect(lambda app_name: self.downloadTab.install_game(
InstallOptions(app_name, core.get_installed_game(app_name).install_path, repair=True)))
# repair game
self.games_tab.game_info.info.verify_game.connect(lambda app_name: self.downloadTab.install_game(
InstallOptions(app_name, core.get_installed_game(app_name).install_path, repair=True)))
# Finished sync
self.cloud_saves.finished.connect(self.finished_sync)
# Game finished
self.games_tab.default_widget.game_list.game_exited.connect(self.game_finished)
# Open game list on click on Games tab button
self.tabBarClicked.connect(lambda x: self.games_tab.layout.setCurrentIndex(0) if x == 0 else None)
# Finished sync
self.cloud_saves.finished.connect(self.finished_sync)
self.games_tab.default_widget.game_list.game_exited.connect(self.game_finished)
self.setIconSize(QSize(25, 25))
# Sync game and delete dc rpc
@ -93,10 +97,10 @@ class TabWidget(QTabWidget):
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys())
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
def start_download(self, app_name):
self.downloadTab.install_game(app_name)
def start_download(self, options):
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys()) + 1
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.downloadTab.install_game(options)
def resizeEvent(self, event):
self.tabBar().setMinimumWidth(self.width())

View file

@ -14,8 +14,11 @@ class MiniWidget(QWidget):
self.layout.addWidget(QLabel("Account"))
username = self.core.lgd.userdata.get("display_name")
if not username:
self.core.login()
username = self.core.lgd.userdata.get("display_name")
try:
self.core.login()
username = self.core.lgd.userdata.get("display_name")
except Exception:
username = "Offline"
self.layout.addWidget(QLabel(self.tr("Logged in as ") + str(username)))

View file

@ -27,8 +27,8 @@ class LoadThread(QThread):
class SyncSaves(QScrollArea):
finished = pyqtSignal(str)
def __init__(self, core: LegendaryCore):
super(SyncSaves, self).__init__()
def __init__(self, core: LegendaryCore, parent):
super(SyncSaves, self).__init__(parent=parent)
self.core = core
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.load_saves()

View file

@ -4,9 +4,17 @@ from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QGroupBox
from rare.components.dialogs.path_input_dialog import PathInputDialog
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame, SaveGameStatus
from rare.components.dialogs.path_input_dialog import PathInputDialog
logger = getLogger("Sync")
def get_raw_save_path(app_name, core):
game = core.lgd.get_game_meta(app_name)
save_path = game.metadata['customAttributes'].get('CloudSaveFolder', {}).get('value')
return save_path
class _UploadThread(QThread):
@ -20,7 +28,10 @@ class _UploadThread(QThread):
self.save_path = save_path
def run(self) -> None:
self.core.upload_save(self.app_name, self.save_path, self.date_time)
try:
self.core.upload_save(self.app_name, self.save_path, self.date_time)
except Exception as e:
logger.error(e)
class _DownloadThread(QThread):
@ -34,7 +45,10 @@ class _DownloadThread(QThread):
self.save_path = save_path
def run(self) -> None:
self.core.download_saves(self.app_name, self.latest_save.manifest_name, self.save_path, clean_dir=True)
try:
self.core.download_saves(self.app_name, self.latest_save.manifest_name, self.save_path, clean_dir=True)
except Exception as e:
logger.error(e)
class SyncWidget(QGroupBox):
@ -131,6 +145,11 @@ class SyncWidget(QGroupBox):
self.layout.addWidget(cloud_save_date)
save_path_layout = QHBoxLayout()
self.raw_path = QLabel("Raw path: " + get_raw_save_path(self.game.app_name, self.core))
self.raw_path.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.layout.addWidget(self.raw_path)
self.save_path_text = QLabel(igame.save_path)
self.save_path_text.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.save_path_text.setWordWrap(True)

View file

@ -6,14 +6,14 @@ from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar, QPushButton, QDialog, \
QListWidget, QHBoxLayout, QGroupBox
from rare.components.dialogs.install_dialog import InstallInfoDialog, InstallDialog
from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget
from rare.components.tabs.downloads.download_thread import DownloadThread
from rare.utils.models import InstallOptions
from custom_legendary.core import LegendaryCore
from custom_legendary.models.downloading import UIUpdate
from custom_legendary.models.game import Game, InstalledGame
from custom_legendary.utils.selective_dl import games
from rare.components.dialogs.install_dialog import InstallInfoDialog, InstallDialog
from rare.components.tabs.downloads.dl_queue_widget import DlQueueWidget
from rare.components.tabs.downloads.download_thread import DownloadThread
from rare.utils.models import InstallOptions
from rare.utils.utils import get_size
logger = getLogger("Download")
@ -24,36 +24,36 @@ class DownloadTab(QWidget):
thread: QThread
dl_queue = []
def __init__(self, core: LegendaryCore, updates: list):
super(DownloadTab, self).__init__()
def __init__(self, core: LegendaryCore, updates: list, parent):
super(DownloadTab, self).__init__(parent=parent)
self.core = core
self.layout = QVBoxLayout()
self.active_game: Game = None
self.installing_game = QLabel(self.tr("No active Download"))
self.dl_speed = QLabel()
self.cache_used = QLabel()
self.downloaded = QLabel()
self.time_left = QLabel()
self.info_layout = QGridLayout()
self.installing_game = QLabel(self.tr("No active Download"))
self.info_layout.addWidget(self.installing_game, 0, 0)
self.dl_speed = QLabel()
self.info_layout.addWidget(self.dl_speed, 0, 1)
self.cache_used = QLabel()
self.info_layout.addWidget(self.cache_used, 1, 0)
self.downloaded = QLabel()
self.info_layout.addWidget(self.downloaded, 1, 1)
self.time_left = QLabel()
self.info_layout.addWidget(self.time_left, 2, 0)
self.layout.addLayout(self.info_layout)
self.mini_layout = QHBoxLayout()
self.prog_bar = QProgressBar()
self.prog_bar.setMaximum(100)
self.mini_layout.addWidget(self.prog_bar)
self.prog_bar.setMaximum(100)
self.kill_button = QPushButton(self.tr("Stop Download"))
self.mini_layout.addWidget(self.kill_button)
self.kill_button.setDisabled(True)
self.kill_button.clicked.connect(self.stop_download)
self.mini_layout.addWidget(self.kill_button)
self.layout.addLayout(self.mini_layout)
@ -62,24 +62,27 @@ class DownloadTab(QWidget):
self.queue_widget.update_list.connect(self.update_dl_queue)
self.updates = QGroupBox(self.tr("Updates"))
self.updates.setObjectName("group")
self.layout.addWidget(self.updates)
self.update_layout = QVBoxLayout()
self.updates.setLayout(self.update_layout)
self.updates.setObjectName("group")
self.update_widgets = {}
self.update_text = QLabel(self.tr("No updates available"))
self.update_text.setVisible(len(updates) == 0)
self.update_layout.addWidget(self.update_text)
self.update_text.setVisible(len(updates) == 0)
for igame in updates:
widget = UpdateWidget(core, igame)
self.update_widgets[igame.app_name] = widget
widget = UpdateWidget(core, igame, self)
self.update_layout.addWidget(widget)
self.update_widgets[igame.app_name] = widget
widget.update.connect(self.update_game)
if QSettings().value("auto_update", False, bool):
self.update_game(igame.app_name, True)
widget.update_button.setDisabled(True)
self.updates.setLayout(self.update_layout)
self.layout.addWidget(self.updates)
self.layout.addStretch(1)
self.setLayout(self.layout)
@ -132,6 +135,7 @@ class DownloadTab(QWidget):
# Information
if not from_update:
if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept():
self.finished.emit(False)
return
if self.active_game is None:
@ -235,7 +239,7 @@ class DownloadTab(QWidget):
self.queue_widget.update_queue(self.dl_queue)
elif text[:5] == "error":
QMessageBox.warning(self, "warn", "Download error: "+text[6:])
QMessageBox.warning(self, "warn", "Download error: " + text[6:])
elif text == "stop":
self.reset_infos()
@ -258,7 +262,8 @@ class DownloadTab(QWidget):
self.prog_bar.setValue(ui_update.progress)
self.dl_speed.setText(self.tr("Download speed") + f": {ui_update.download_speed / 1024 / 1024:.02f}MB/s")
self.cache_used.setText(self.tr("Cache used") + f": {ui_update.cache_usage / 1024 / 1024:.02f}MB")
self.downloaded.setText(self.tr("Downloaded") + f": {get_size(ui_update.total_downloaded)} / {get_size(self.analysis.dl_size)}")
self.downloaded.setText(
self.tr("Downloaded") + f": {get_size(ui_update.total_downloaded)} / {get_size(self.analysis.dl_size)}")
self.time_left.setText(self.tr("Time left: ") + self.get_time(ui_update.estimated_time_left))
def get_time(self, seconds: int) -> str:
@ -280,8 +285,8 @@ class DownloadTab(QWidget):
class UpdateWidget(QWidget):
update = pyqtSignal(str)
def __init__(self, core: LegendaryCore, game: InstalledGame):
super(UpdateWidget, self).__init__()
def __init__(self, core: LegendaryCore, game: InstalledGame, parent):
super(UpdateWidget, self).__init__(parent=parent)
self.core = core
self.game = game
@ -297,4 +302,4 @@ class UpdateWidget(QWidget):
def update_game(self):
self.update_button.setDisabled(True)
self.update.emit(self.game.app_name)
self.update.emit(self.game.app_name)

View file

@ -63,7 +63,8 @@ class DownloadThread(QThread):
# clean up all the queues, otherwise this process won't terminate properly
for name, q in zip(('Download jobs', 'Writer jobs', 'Download results', 'Writer results'),
(self.dlm.dl_worker_queue, self.dlm.writer_queue, self.dlm.dl_result_q, self.dlm.writer_result_q)):
(self.dlm.dl_worker_queue, self.dlm.writer_queue, self.dlm.dl_result_q,
self.dlm.writer_result_q)):
logger.debug(f'Cleaning up queue "{name}"')
try:
while True:
@ -111,7 +112,7 @@ class DownloadThread(QThread):
except Exception as e:
logger.error(f"Installation failed after {time.time() - start_time:.02f} seconds: {e}")
self.status.emit("error "+str(e))
self.status.emit("error " + str(e))
return
else:
@ -172,4 +173,3 @@ class DownloadThread(QThread):
def kill(self):
self._kill = True

View file

@ -9,20 +9,22 @@ from rare.utils.extra_widgets import SelectViewWidget
class GameTab(QWidget):
def __init__(self, core):
super(GameTab, self).__init__()
def __init__(self, core, parent, offline):
super(GameTab, self).__init__(parent=parent)
self.layout = QStackedLayout()
self.default_widget = Games(core)
self.default_widget = Games(core, self, offline)
# Signal to show info
self.default_widget.game_list.show_game_info.connect(self.show_info)
self.default_widget.head_bar.import_game.clicked.connect(lambda: self.layout.setCurrentIndex(2))
self.layout.addWidget(self.default_widget)
self.game_info = InfoTabs(core)
self.game_info = InfoTabs(core, self)
self.game_info.info.update_list.connect(self.update_list)
self.layout.addWidget(self.game_info)
self.default_widget.head_bar.refresh_list.clicked.connect(self.update_list)
self.import_widget = ImportWidget(core)
self.import_widget = ImportWidget(core, self)
self.layout.addWidget(self.import_widget)
self.import_widget.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0))
self.import_widget.update_list.connect(self.update_list)
@ -33,20 +35,20 @@ class GameTab(QWidget):
self.layout.setCurrentIndex(0)
def show_info(self, app_name):
self.game_info.update_game(app_name)
self.game_info.update_game(app_name, self.default_widget.game_list.dlcs)
self.game_info.setCurrentIndex(1)
self.layout.setCurrentIndex(1)
class Games(QWidget):
def __init__(self, core):
super(Games, self).__init__()
def __init__(self, core, parent, offline):
super(Games, self).__init__(parent=parent)
self.layout = QVBoxLayout()
self.head_bar = GameListHeadBar()
self.head_bar = GameListHeadBar(self)
self.head_bar.setObjectName("head_bar")
self.game_list = GameList(core)
self.game_list = GameList(core, self, offline)
self.head_bar.search_bar.textChanged.connect(
lambda: self.game_list.filter(self.head_bar.search_bar.text()))
@ -68,8 +70,8 @@ class Games(QWidget):
class GameListHeadBar(QWidget):
def __init__(self):
super(GameListHeadBar, self).__init__()
def __init__(self, parent):
super(GameListHeadBar, self).__init__(parent=parent)
self.layout = QHBoxLayout()
self.installed_only = QCheckBox(self.tr("Installed only"))
self.settings = QSettings()

View file

@ -6,18 +6,20 @@ from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QLabel, QHBoxLayo
QProgressBar, QStackedWidget, QGroupBox, QScrollArea
from qtawesome import icon
from rare import utils
from rare.components.tabs.games.game_info.game_settings import GameSettings
from rare.utils.legendary_utils import VerifyThread
from rare.utils.extra_widgets import SideTabBar
from rare.utils.utils import IMAGE_DIR, get_size, create_desktop_link
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame, Game
from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.tabs.games.game_info.dlcs import DlcTab
from rare.components.tabs.games.game_info.game_settings import GameSettings
from rare.utils import legendary_utils
from rare.utils.extra_widgets import SideTabBar
from rare.utils.legendary_utils import VerifyThread
from rare.utils.utils import IMAGE_DIR, get_size
class InfoTabs(QTabWidget):
def __init__(self, core):
super(InfoTabs, self).__init__()
def __init__(self, core, parent):
super(InfoTabs, self).__init__(parent=parent)
self.app_name = ""
self.core = core
self.setTabBar(SideTabBar())
@ -26,16 +28,28 @@ class InfoTabs(QTabWidget):
self.addTab(QWidget(), icon("mdi.keyboard-backspace", color="white"), self.tr("Back"))
self.tabBarClicked.connect(lambda x: self.parent().layout.setCurrentIndex(0) if x == 0 else None)
self.info = GameInfo(core)
self.info = GameInfo(core, self)
self.addTab(self.info, self.tr("Game Info"))
self.settings = GameSettings(core)
self.settings = GameSettings(core, self)
self.addTab(self.settings, self.tr("Settings"))
self.tabBar().setCurrentIndex(1)
def update_game(self, app_name):
self.dlc_tab = DlcTab(core, self)
self.addTab(self.dlc_tab, self.tr("DLCs"))
def update_game(self, app_name, dlcs: list):
self.info.update_game(app_name)
self.settings.update_game(app_name)
# DLC Tab: Disable if no dlcs available
if len(dlcs[self.core.get_game(app_name).asset_info.catalog_item_id]) == 0:
self.setTabEnabled(3, False)
else:
self.setTabEnabled(3, True)
self.dlc_tab.update_dlcs(app_name, dlcs)
def keyPressEvent(self, e: QKeyEvent):
if e.key() == Qt.Key_Escape:
self.parent().layout.setCurrentIndex(0)
@ -47,10 +61,9 @@ class GameInfo(QScrollArea):
update_list = pyqtSignal()
verify_game = pyqtSignal(str)
verify_threads = {}
action = pyqtSignal(str)
def __init__(self, core: LegendaryCore):
super(GameInfo, self).__init__()
def __init__(self, core: LegendaryCore, parent):
super(GameInfo, self).__init__(parent=parent)
self.widget = QWidget()
self.core = core
self.layout = QVBoxLayout()
@ -84,13 +97,14 @@ class GameInfo(QScrollArea):
self.install_path = QLabel("Error")
self.install_path.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.install_path.setWordWrap(True)
right_layout.addWidget(self.install_path)
top_layout.addLayout(right_layout)
top_layout.addStretch()
self.game_actions = GameActions()
self.game_actions.uninstall_button.clicked.connect(lambda: self.action.emit("uninstall"))
self.game_actions.uninstall_button.clicked.connect(self.uninstall)
self.game_actions.verify_button.clicked.connect(self.verify)
self.game_actions.repair_button.clicked.connect(self.repair)
@ -100,6 +114,14 @@ class GameInfo(QScrollArea):
self.widget.setLayout(self.layout)
self.setWidget(self.widget)
def uninstall(self):
infos = UninstallDialog(self.game).get_information()
if infos == 0:
print("Cancel Uninstall")
return
legendary_utils.uninstall(self.game.app_name, self.core, infos)
self.update_list.emit()
def repair(self):
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.game.app_name}.repair')
if not os.path.exists(repair_file):

View file

@ -0,0 +1,147 @@
import os
from PyQt5.QtCore import pyqtSignal, QSettings
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QVBoxLayout, QScrollArea, QLabel, QPushButton, QMessageBox
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare.components.dialogs.install_dialog import InstallDialog
from rare.utils.models import InstallOptions
from rare.utils.utils import download_image
class DlcTab(QScrollArea):
install_dlc = pyqtSignal(InstallOptions)
game: Game
def __init__(self, core: LegendaryCore, parent):
super(DlcTab, self).__init__(parent=parent)
self.core = core
self.widget = QGroupBox("DLCs")
self.widget.setObjectName("group")
self.setWidgetResizable(True)
self.layout = QVBoxLayout()
self.installed_dlcs = [i.app_name for i in self.core.get_installed_dlc_list()]
self.installed_dlc_widget = QGroupBox(self.tr("Installed DLCs"))
self.installed_layout = QVBoxLayout()
self.available_dlcs = QGroupBox(self.tr("Available DLCs"))
self.available_dlcs_layout = QVBoxLayout()
self.installed = []
self.available = []
def update_dlcs(self, app_name, dlcs: list):
self.installed_dlcs = [i.app_name for i in self.core.get_installed_dlc_list()]
self.installed_dlc_widget = QGroupBox(self.tr("Installed DLCs"))
self.installed_layout = QVBoxLayout()
self.available_dlcs = QGroupBox(self.tr("Available DLCs"))
self.available_dlcs_layout = QVBoxLayout()
self.installed = []
self.available = []
QVBoxLayout().addWidget(self.widget)
self.game = self.core.get_game(app_name)
for dlc in sorted(dlcs[self.game.asset_info.catalog_item_id], key=lambda x: x.app_title):
if dlc.app_name in self.installed_dlcs:
w = DLCWidget(dlc, True)
self.installed_layout.addWidget(w)
self.installed.append(dlc)
else:
w = DLCWidget(dlc, False)
w.install.connect(self.install)
self.available_dlcs_layout.addWidget(w)
self.available.append(dlc)
if len(self.installed) == 0:
self.installed_layout.addWidget(QLabel(self.tr("No DLCs are installed")))
if len(self.available) == 0:
self.available_dlcs_layout.addWidget(QLabel(self.tr("No DLCs are available")))
self.widget = QGroupBox("DLCs")
self.layout = QVBoxLayout()
self.installed_dlc_widget.setLayout(self.installed_layout)
self.available_dlcs.setLayout(self.available_dlcs_layout)
self.layout.addWidget(self.installed_dlc_widget)
self.layout.addWidget(self.available_dlcs)
self.layout.addStretch(1)
self.widget.setLayout(self.layout)
self.setWidget(self.widget)
def install(self, app_name):
if not self.core.is_installed(self.game.app_name):
QMessageBox.warning(self, "Error", self.tr("Base Game is not installed. Please install {} first").format(
self.game.app_title))
return
infos = InstallDialog(self.game.app_name, self.core, True).get_information()
if infos != 0:
path, max_workers, force, ignore_free_space = infos
self.install_dlc.emit(
InstallOptions(app_name=app_name, max_workers=max_workers, path=path, force=force,
ignore_free_space=ignore_free_space))
class DLCWidget(QGroupBox):
install = pyqtSignal(str) # Appname
def __init__(self, dlc: Game, installed: bool):
super(DLCWidget, self).__init__()
self.main_layout = QHBoxLayout()
self.dlc = dlc
IMAGE_DIR = QSettings().value("img_dir", os.path.expanduser("~/.cache/rare/images"))
if installed:
if os.path.exists(os.path.join(IMAGE_DIR, dlc.app_name, "FinalArt.png")):
pixmap = QPixmap(os.path.join(IMAGE_DIR, dlc.app_name, "FinalArt.png"))
elif os.path.exists(os.path.join(IMAGE_DIR, dlc.app_name, "DieselGameBoxTall.png")):
pixmap = QPixmap(os.path.join(IMAGE_DIR, dlc.app_name, "DieselGameBoxTall.png"))
elif os.path.exists(os.path.join(IMAGE_DIR, dlc.app_name, "DieselGameBoxLogo.png")):
pixmap = QPixmap(os.path.join(IMAGE_DIR, dlc.app_name, "DieselGameBoxLogo.png"))
else:
print(f"No Image found: {dlc.app_title}")
pixmap = None
if not pixmap or pixmap.isNull():
print(dlc.app_title + " has corrupt Image")
download_image(dlc, force=True)
pixmap = QPixmap(f"{IMAGE_DIR}/{dlc.app_name}/UninstalledArt.png")
else:
if os.path.exists(f"{IMAGE_DIR}/{dlc.app_name}/UninstalledArt.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{dlc.app_name}/DieselGameBoxTall.png")
else:
pixmap = None
if not pixmap or pixmap.isNull():
print(dlc.app_title + " has corrupt Image")
download_image(dlc, force=True)
pixmap = QPixmap(f"{IMAGE_DIR}/{dlc.app_name}/UninstalledArt.png")
image = QLabel()
image.setPixmap(pixmap)
self.main_layout.addWidget(image)
self.layout = QVBoxLayout()
self.layout.addWidget(QLabel(dlc.app_title))
self.layout.addWidget(QLabel("Version: " + str(dlc.app_version)))
self.layout.addWidget(QLabel("App Name: " + dlc.app_name))
if not installed:
self.install_button = QPushButton(self.tr("Install"))
self.layout.addWidget(self.install_button)
self.install_button.clicked.connect(lambda: self.install.emit(dlc.app_name))
else:
self.layout.addWidget(QLabel(self.tr("Installed. Uninstalling DLCs is not supported")))
self.main_layout.addLayout(self.layout)
self.setLayout(self.main_layout)
self.layout.addStretch(1)
def install_game(self):
self.install_button.setDisabled(True)
self.install_button.setText(self.tr("Installing"))
self.install.emit(self.dlc.app_name)

View file

@ -18,8 +18,8 @@ class GameSettings(QScrollArea):
# variable to no update when changing game
change = False
def __init__(self, core: LegendaryCore):
super(GameSettings, self).__init__()
def __init__(self, core: LegendaryCore, parent):
super(GameSettings, self).__init__(parent=parent)
self.core = core
self.widget = QWidget()
self.settings = QSettings()
@ -45,7 +45,8 @@ class GameSettings(QScrollArea):
self.launch_params_widget = SettingsWidget(self.tr("Launch parameters"), self.launch_params,
self.launch_params_accept_button)
self.layout.addWidget(self.launch_params_widget)
self.launch_params_accept_button.clicked.connect(lambda: self.save_line_edit("start_params", self.launch_params.text()))
self.launch_params_accept_button.clicked.connect(
lambda: self.save_line_edit("start_params", self.launch_params.text()))
self.cloud_sync = QCheckBox("Sync with cloud")
self.cloud_sync_widget = SettingsWidget(self.tr("Auto sync with cloud"), self.cloud_sync)

View file

@ -28,10 +28,11 @@ class GameList(QStackedWidget):
running_games = []
active_game = ("", 0)
def __init__(self, core: LegendaryCore):
super(GameList, self).__init__()
def __init__(self, core: LegendaryCore, parent, offline):
super(GameList, self).__init__(parent=parent)
self.core = core
self.setObjectName("list_widget")
self.offline = offline
self.settings = QSettings()
icon_view = self.settings.value("icon_view", True, bool)
@ -39,10 +40,10 @@ class GameList(QStackedWidget):
self.init_ui(icon_view)
def init_ui(self, icon_view=True):
self.icon_scrollarea = QScrollArea()
self.icon_widget = QWidget()
self.list_scrollarea = QScrollArea()
self.list_widget = QWidget()
self.icon_scrollarea = QScrollArea(parent=self)
self.icon_widget = QWidget(parent=self.icon_scrollarea)
self.list_scrollarea = QScrollArea(parent=self)
self.list_widget = QWidget(parent=self.list_scrollarea)
self.icon_scrollarea.setWidgetResizable(True)
self.icon_scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
@ -65,8 +66,10 @@ class GameList(QStackedWidget):
self.updates = []
self.widgets = {}
self.installed = sorted(self.core.get_installed_list(), key=lambda x: x.title)
# Installed Games
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title):
for igame in self.installed:
if os.path.exists(f"{IMAGE_DIR}/{igame.app_name}/FinalArt.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/FinalArt.png")
elif os.path.exists(f"{IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png"):
@ -82,8 +85,11 @@ class GameList(QStackedWidget):
download_image(igame, force=True)
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
icon_widget = GameWidgetInstalled(igame, self.core, pixmap)
list_widget = InstalledListWidget(igame, self.core, pixmap)
icon_widget = GameWidgetInstalled(igame, self.core, pixmap, self.offline)
self.icon_layout.addWidget(icon_widget)
list_widget = InstalledListWidget(igame, self.core, pixmap, self.offline)
self.list_layout.addWidget(list_widget)
icon_widget.show_info.connect(self.show_game_info.emit)
list_widget.show_info.connect(self.show_game_info.emit)
@ -95,9 +101,6 @@ class GameList(QStackedWidget):
list_widget.finish_signal.connect(self.finished)
list_widget.update_list.connect(lambda: self.update_list(self.settings.value("icon_view", True, bool)))
self.icon_layout.addWidget(icon_widget)
self.list_layout.addWidget(list_widget)
if icon_widget.update_available:
self.updates.append(igame)
@ -113,15 +116,16 @@ class GameList(QStackedWidget):
self.timer.timeout.connect(self.is_finished)
self.timer.start(10000)
uninstalled_games = []
self.uninstalled_games = []
installed = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
for igame in sorted(self.core.get_game_list(), key=lambda x: x.app_title):
if not igame.app_name in installed:
uninstalled_games.append(igame)
games, self.dlcs = self.core.get_game_and_dlc_list()
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed:
self.uninstalled_games.append(game)
# add uninstalled games
for igame in uninstalled_games:
for igame in self.uninstalled_games:
if os.path.exists(f"{IMAGE_DIR}/{igame.app_name}/UninstalledArt.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/UninstalledArt.png")
@ -129,7 +133,6 @@ class GameList(QStackedWidget):
logger.info(igame.app_title + " has a corrupt image.")
download_image(igame, force=True)
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/UninstalledArt.png")
else:
logger.warning(f"No Image found: {igame.app_title}")
download_image(igame, force=True)
@ -178,7 +181,7 @@ class GameList(QStackedWidget):
for i, pid in self.procs:
if executable in i:
# Workaround for Satisfactory
# Workaround for Satisfactory: Check Cmdline args
if igame.app_name in ["CrabEA", "CrabTest"]:
p = psutil.Process(pid)
if not igame.install_path.split("/")[-1].split("\\")[-1] in " ".join(p.cmdline()):
@ -236,6 +239,22 @@ class GameList(QStackedWidget):
def update_list(self, icon_view=True):
self.settings.setValue("icon_view", icon_view)
uninstalled_games = []
installed = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
games = self.core.get_game_list(True)
for game in sorted(games, key=lambda x: x.app_title):
if not game.app_name in installed:
uninstalled_games.append(game.app_name)
# get new uninstalled/ installed games that changed
new_installed_games = list(set(installed) - set([i.app_name for i in self.installed]))
new_uninstalled_games = list(set(uninstalled_games) - set([i.app_name for i in self.uninstalled_games]))
if not new_uninstalled_games and not new_installed_games:
return
self.removeWidget(self.icon_scrollarea)
self.removeWidget(self.list_scrollarea)
self.init_ui(icon_view)

View file

@ -1,7 +1,8 @@
import logging
import os
from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt
from PyQt5.QtCore import pyqtSignal, QProcess, QSettings, Qt, QByteArray
from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction
from custom_legendary.core import LegendaryCore
@ -20,13 +21,14 @@ class BaseInstalledWidget(QGroupBox):
update_list = pyqtSignal()
proc: QProcess()
def __init__(self, igame: InstalledGame, core: LegendaryCore, pixmap):
def __init__(self, igame: InstalledGame, core: LegendaryCore, pixmap, offline):
super(BaseInstalledWidget, self).__init__()
self.igame = igame
self.core = core
self.game = self.core.get_game(self.igame.app_name)
self.pixmap = pixmap
self.game_running = False
self.offline = offline
self.update_available = self.core.get_asset(self.game.app_name, True).build_version != igame.version
self.setContentsMargins(0, 0, 0, 0)
@ -36,7 +38,7 @@ class BaseInstalledWidget(QGroupBox):
launch.triggered.connect(self.launch)
self.addAction(launch)
if os.path.exists(os.path.expanduser(f"~/Desktop/{self.igame.title}.desktop"))\
if os.path.exists(os.path.expanduser(f"~/Desktop/{self.igame.title}.desktop")) \
or os.path.exists(os.path.expanduser(f"~/Desktop/{self.igame.title}.lnk")):
self.create_desktop = QAction(self.tr("Remove Desktop link"))
else:
@ -65,7 +67,7 @@ class BaseInstalledWidget(QGroupBox):
path = os.path.expanduser("~/.local/share/applications/")
else:
return
if not (os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.desktop"))\
if not (os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.desktop"))
or os.path.exists(os.path.expanduser(f"{path}{self.igame.title}.lnk"))):
create_desktop_link(self.igame.app_name, self.core, type_of_link)
if type_of_link == "desktop":
@ -90,17 +92,45 @@ class BaseInstalledWidget(QGroupBox):
logger.info("Cancel Startup")
return 1
logger.info("Launching " + self.igame.title)
self.proc, params = legendary_utils.launch_game(self.core, self.igame.app_name, offline,
skip_version_check=skip_version_check)
if offline or self.offline:
if not self.igame.can_run_offline:
QMessageBox.warning(self, "Offline",
self.tr("Game cannot run offline. Please start game in Online mode"))
return
try:
self.proc, params = legendary_utils.launch_game(self.core, self.igame.app_name, offline,
skip_version_check=skip_version_check)
except Exception as e:
logger.error(e)
QMessageBox.warning(self, "Error",
self.tr("An error occurred while starting game. Maybe game files are missing"))
return
if not self.proc:
logger.error("Could not start process")
return 1
self.game_logger = getLogger(self.game.app_name)
self.proc.finished.connect(self.finished)
self.proc.readyReadStandardOutput.connect(self.stdout)
self.proc.readyReadStandardError.connect(self.stderr)
self.proc.start(params[0], params[1:])
self.launch_signal.emit(self.igame.app_name)
self.game_running = True
self.data = QByteArray()
return 0
def stdout(self):
data = self.proc.readAllStandardOutput()
stdout = bytes(data).decode("utf-8")
self.game_logger.info(stdout)
def stderr(self):
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8")
self.game_logger.error(stderr)
QMessageBox.warning(self, "Warning", stderr + "\nSee ~/.cache/rare/logs/")
def finished(self, exit_code):
logger.info("Game exited with exit code: " + str(exit_code))
self.finish_signal.emit(self.game.app_name)

View file

@ -1,7 +1,7 @@
from logging import getLogger
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QGroupBox
from PyQt5.QtWidgets import QGroupBox
from rare.components.dialogs.install_dialog import InstallDialog
from rare.utils.models import InstallOptions
@ -25,4 +25,6 @@ class BaseUninstalledWidget(QGroupBox):
infos = InstallDialog(self.game.app_name, self.core).get_information()
if infos != 0:
path, max_workers, force, ignore_free_space = infos
self.install_game.emit(InstallOptions(app_name=self.game.app_name, max_workers=max_workers, path=path, force=force, ignore_free_space=ignore_free_space))
self.install_game.emit(
InstallOptions(app_name=self.game.app_name, max_workers=max_workers, path=path, force=force,
ignore_free_space=ignore_free_space))

View file

@ -1,16 +1,14 @@
import os
from logging import getLogger
from PyQt5.QtCore import QEvent, pyqtSignal, QSettings, QSize, Qt
from PyQt5.QtCore import QEvent, pyqtSignal, QSize, Qt
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import *
from qtawesome import icon
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from rare.utils import legendary_utils
from rare.utils.extra_widgets import ClickableLabel
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from rare.utils.extra_widgets import ClickableLabel
logger = getLogger("GameWidgetInstalled")
@ -20,8 +18,8 @@ class GameWidgetInstalled(BaseInstalledWidget):
show_info = pyqtSignal(str)
update_game = pyqtSignal()
def __init__(self, game: InstalledGame, core: LegendaryCore, pixmap):
super(GameWidgetInstalled, self).__init__(game, core, pixmap)
def __init__(self, game: InstalledGame, core: LegendaryCore, pixmap, offline):
super(GameWidgetInstalled, self).__init__(game, core, pixmap, offline)
self.setObjectName("game_widget_icon")
self.setContextMenuPolicy(Qt.ActionsContextMenu)
@ -112,4 +110,4 @@ class GameWidgetInstalled(BaseInstalledWidget):
desktop_link = menu.addAction("Add Desktop link")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == desktop_link:
print("LOL")"""
print("LOL")"""

View file

@ -2,12 +2,12 @@ import os
from logging import getLogger
from PyQt5.QtCore import QProcess, pyqtSignal, Qt
from PyQt5.QtWidgets import QHBoxLayout, QLabel, QPushButton, QStyle, QVBoxLayout, QAction
from PyQt5.QtWidgets import QHBoxLayout, QLabel, QPushButton, QVBoxLayout
from qtawesome import icon
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
logger = getLogger("GameWidget")
@ -17,14 +17,12 @@ class InstalledListWidget(BaseInstalledWidget):
signal = pyqtSignal(str)
update_game = pyqtSignal()
def __init__(self, game: InstalledGame, core: LegendaryCore, pixmap):
super(InstalledListWidget, self).__init__(game, core, pixmap)
def __init__(self, game: InstalledGame, core: LegendaryCore, pixmap, offline):
super(InstalledListWidget, self).__init__(game, core, pixmap, offline)
self.dev = core.get_game(self.igame.app_name).metadata["developer"]
self.size = game.install_size
self.launch_params = game.launch_parameters
self.layout = QHBoxLayout()
##Layout on the right
@ -59,7 +57,7 @@ class InstalledListWidget(BaseInstalledWidget):
self.childLayout.addWidget(self.app_name_label)
self.childLayout.addWidget(self.developer_label)
#if os.name != "nt":
# if os.name != "nt":
# self.childLayout.addWidget(self.wine_rating)
self.childLayout.addWidget(self.version_label)
self.childLayout.addWidget(self.size_label)

View file

@ -3,11 +3,11 @@ from logging import getLogger
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QVBoxLayout, QLabel
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import BaseUninstalledWidget
from rare.utils.models import InstallOptions
from rare.utils.extra_widgets import ClickableLabel
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import BaseUninstalledWidget
from rare.utils.extra_widgets import ClickableLabel
from rare.utils.models import InstallOptions
logger = getLogger("Uninstalled")

View file

@ -2,8 +2,8 @@ from logging import getLogger
from PyQt5.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QPushButton
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import BaseUninstalledWidget
from custom_legendary.core import LegendaryCore
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import BaseUninstalledWidget
logger = getLogger("Game")

View file

@ -8,9 +8,9 @@ from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QPushButton, QVBoxLayo
QGroupBox
from qtawesome import icon
from custom_legendary.core import LegendaryCore
from rare.utils import legendary_utils
from rare.utils.extra_widgets import PathEdit
from custom_legendary.core import LegendaryCore
logger = getLogger("Import")
@ -18,8 +18,8 @@ logger = getLogger("Import")
class ImportWidget(QWidget):
update_list = pyqtSignal()
def __init__(self, core: LegendaryCore):
super(ImportWidget, self).__init__()
def __init__(self, core: LegendaryCore, parent):
super(ImportWidget, self).__init__(parent=parent)
self.core = core
self.game_list = [i.app_name for i in self.core.get_game_list()]
@ -43,7 +43,8 @@ class ImportWidget(QWidget):
self.import_game_info = QLabel(self.tr("Select path to game"))
self.gb_layout.addWidget(self.import_game_info)
self.override_app_name_label = QLabel(self.tr("Override app name (Only if imported game from legendary or the app could not find the app name)"))
self.override_app_name_label = QLabel(
self.tr("Override app name (Only if imported game from legendary or the app could not find the app name)"))
self.override_app_name_label.setWordWrap(True)
self.app_name_input = QLineEdit()
self.app_name_input.setFixedHeight(32)
@ -86,9 +87,9 @@ class ImportWidget(QWidget):
def app_name_changed(self, text):
if text in self.game_list:
self.indicator_label.setPixmap(icon("ei.ok-sign", color="green").pixmap(16,16))
self.indicator_label.setPixmap(icon("ei.ok-sign", color="green").pixmap(16, 16))
else:
self.indicator_label.setPixmap(icon("ei.remove-sign", color="red").pixmap(16,16))
self.indicator_label.setPixmap(icon("ei.remove-sign", color="red").pixmap(16, 16))
def path_changed(self, path):
if os.path.exists(path):
@ -120,7 +121,8 @@ class ImportWidget(QWidget):
return
if legendary_utils.import_game(self.core, app_name=app_name, path=path):
self.info_label.setText(self.tr("Successfully imported {}. Reload library").format(self.core.get_installed_game(app_name).title))
self.info_label.setText(self.tr("Successfully imported {}. Reload library").format(
self.core.get_installed_game(app_name).title))
self.app_name_input.setText("")
self.update_list.emit()
@ -165,7 +167,8 @@ class ImportWidget(QWidget):
for wine_prefix in possible_wineprefixes:
imported += self.auto_import_games(os.path.join(wine_prefix, "drive_c/Program Files/Epic Games/"))
if imported > 0:
QMessageBox.information(self, "Imported Games", self.tr("Successfully imported {} Games. Reloading Library").format(imported))
QMessageBox.information(self, "Imported Games",
self.tr("Successfully imported {} Games. Reloading Library").format(imported))
self.update_list.emit()
else:
QMessageBox.information(self, "Imported Games", self.tr("No Games were found"))

View file

@ -10,8 +10,8 @@ from rare.utils.extra_widgets import SideTabBar
class SettingsTab(QTabWidget):
def __init__(self, core):
super(SettingsTab, self).__init__()
def __init__(self, core, parent):
super(SettingsTab, self).__init__(parent=parent)
self.core = core
self.setTabBar(SideTabBar())
self.setTabPosition(QTabWidget.West)

View file

@ -2,12 +2,13 @@ from logging import getLogger
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIntValidator
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QFileDialog, QPushButton, QLineEdit, QGroupBox, QMessageBox, \
from PyQt5.QtWidgets import QVBoxLayout, QFileDialog, QPushButton, QLineEdit, QGroupBox, QMessageBox, \
QScrollArea
from custom_legendary.core import LegendaryCore
from rare.components.tabs.settings.settings_widget import SettingsWidget
from rare.utils.extra_widgets import PathEdit
from custom_legendary.core import LegendaryCore
from rare.utils.utils import get_size
logger = getLogger("LegendarySettings")
@ -41,7 +42,7 @@ class LegendarySettings(QScrollArea):
self.max_worker_select)
self.layout.addWidget(self.max_worker_widget)
#cleanup
# cleanup
self.clean_layout = QVBoxLayout()
self.cleanup_widget = QGroupBox(self.tr("Cleanup"))
self.clean_button = QPushButton(self.tr("Remove everything"))
@ -96,7 +97,8 @@ class LegendarySettings(QScrollArea):
after = self.core.lgd.get_dir_size()
logger.info(f'Cleanup complete! Removed {(before - after) / 1024 / 1024:.02f} MiB.')
if cleaned := (before-after) > 0:
QMessageBox.information(self, "Cleanup", self.tr("Cleanup complete! Successfully removed {} MB").format(round(cleaned / 1024 / 1024, 3)))
if (before - after) > 0:
QMessageBox.information(self, "Cleanup", self.tr("Cleanup complete! Successfully removed {}").format(
get_size(before - after)))
else:
QMessageBox.information(self, "Cleanup", "Nothing to clean")

View file

@ -1,11 +1,11 @@
from logging import getLogger
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog, QLineEdit, QGroupBox
from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QFileDialog, QLineEdit, QGroupBox
from custom_legendary.core import LegendaryCore
from rare.components.tabs.settings.dxvk import DxvkWidget
from rare.components.tabs.settings.settings_widget import SettingsWidget
from rare.utils.extra_widgets import PathEdit
from custom_legendary.core import LegendaryCore
logger = getLogger("LinuxSettings")

View file

@ -1,5 +1,7 @@
import os
import shutil
import subprocess
import sys
from logging import getLogger
from PyQt5.QtCore import QSettings, Qt
@ -27,13 +29,17 @@ class RareSettings(QScrollArea):
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setWidgetResizable(True)
# (option_name, group_text, checkbox_text, default
self.checkboxes = [("sys_tray", self.tr("Hide to System Tray Icon"), self.tr("Exit to System Tray Icon"), True),
("auto_update", self.tr("Automatically update Games on startup"), self.tr("Auto updates"), False),
("confirm_start", self.tr("Confirm launch of game"), self.tr("Confirm launch of game"), False),
("auto_sync_cloud", self.tr("Auto sync with cloud"), self.tr("Sync with cloud"), True),
("notification", self.tr("Show Notifications after Downloads"), self.tr("Show notification"), True),
("save_size", self.tr("Save size of window after restart"), self.tr("Save size"), False)
]
self.checkboxes = [
("sys_tray", self.tr("Hide to System Tray Icon"), self.tr("Exit to System Tray Icon"), True),
("auto_update", self.tr("Automatically update Games on startup"), self.tr("Auto updates"),
False),
("confirm_start", self.tr("Confirm launch of game"), self.tr("Confirm launch of game"),
False),
("auto_sync_cloud", self.tr("Auto sync with cloud"), self.tr("Sync with cloud"), True),
("notification", self.tr("Show Notifications after Downloads"), self.tr("Show notification"),
True),
("save_size", self.tr("Save size of window after restart"), self.tr("Save size"), False)
]
self.layout = QVBoxLayout()
self.settings = QSettings()
@ -67,10 +73,22 @@ class RareSettings(QScrollArea):
settings_widget = SettingsWidget(head_text, checkbox)
self.layout.addWidget(settings_widget)
self.open_log_dir = QPushButton(self.tr("Open Log directory"))
self.layout.addWidget(self.open_log_dir)
self.open_log_dir.clicked.connect(self.open_dir)
self.layout.addStretch()
self.widget.setLayout(self.layout)
self.setWidget(self.widget)
def open_dir(self):
logdir = os.path.expanduser("~/.cache/rare/logs")
if os.name == "nt":
os.startfile(logdir)
else:
opener = "open" if sys.platform == "darwin" else "xdg-open"
subprocess.Popen([opener, logdir])
def save_window_size(self):
self.settings.setValue("save_size", self.save_size.isChecked())
self.settings.remove("window_size")

View file

@ -38,4 +38,4 @@ class RPCSettings(QGroupBox):
def changed(self, i):
self.settings.setValue("rpc_enable", i)
self.update_settings.emit()
self.update_settings.emit()

View file

@ -1,12 +1,13 @@
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu, QAction
from rare import style_path
class TrayIcon(QSystemTrayIcon):
def __init__(self, parent):
super(TrayIcon, self).__init__(parent)
self.setIcon(QIcon(style_path+"Logo.png"))
self.setIcon(QIcon(style_path + "Logo.png"))
self.setVisible(True)
self.setToolTip("Rare")

Binary file not shown.

View file

@ -37,12 +37,12 @@
<context>
<name>App</name>
<message>
<location filename="../app.py" line="85"/>
<location filename="../app.py" line="89"/>
<source>Download finished</source>
<translation>Download abgeschlossen</translation>
</message>
<message>
<location filename="../app.py" line="85"/>
<location filename="../app.py" line="89"/>
<source>Download finished. Game is playable now</source>
<translation>Downlaod abgeschlossen. Spiel kann jetzt gespielt werden</translation>
</message>
@ -50,10 +50,60 @@
<context>
<name>BaseInstalledWidget</name>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="34"/>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="90"/>
<source>Do you want to launch {}</source>
<translation>Möchtest du {} starten</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="37"/>
<source>Launch</source>
<translation>Starten</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="74"/>
<source>Remove Desktop link</source>
<translation>Desktop Verknüpfung löschen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="84"/>
<source>Create Desktop link</source>
<translation>Desktopverknüpfung erstellen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="52"/>
<source>Remove start menu link</source>
<translation>Startmenüverknüpfung löschen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="54"/>
<source>Create start menu link</source>
<translation>Startmenüverknüpfung erstellen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="59"/>
<source>Uninstall</source>
<translation>Deinstallieren</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="76"/>
<source>Remove Start menu link</source>
<translation>Startmenüverknüpfung löschen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="86"/>
<source>Create Start menu link</source>
<translation>Startmenüverknüpfung erstellen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="106"/>
<source>An error occurred while starting game. Maybe game files are missing</source>
<translation>Ein Fehler ist passiert. Vielleicht fehlen die Spieldateien</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/base_installed_widget.py" line="97"/>
<source>Game cannot run offline. Please start game in Online mode</source>
<translation>Spiel kann nicht offline gestartet werden. Starte die App im Online Mode neu</translation>
</message>
</context>
<context>
<name>BrowserLogin</name>
@ -78,6 +128,24 @@
<translation>Laden...</translation>
</message>
</context>
<context>
<name>DLCWidget</name>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="131"/>
<source>Install</source>
<translation>Installieren</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="136"/>
<source>Installed. Uninstalling DLCs is not supported</source>
<translation>Installiert. Das Deinstallieren von DLCs wird nicht unterstützt</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="146"/>
<source>Installing</source>
<translation>Installieren</translation>
</message>
</context>
<context>
<name>DlQueueWidget</name>
<message>
@ -109,10 +177,38 @@
<translation>Download löschen</translation>
</message>
</context>
<context>
<name>DlcTab</name>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="37"/>
<source>Installed DLCs</source>
<translation>Installierte DLCs</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="39"/>
<source>Available DLCs</source>
<translation>Verfügbare DLCs</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="58"/>
<source>No DLCs are installed</source>
<translation>Keine DLCs sind installiert</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="60"/>
<source>No DLCs are available</source>
<translation>Keine weiteren DLCs verfügbar</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/dlcs.py" line="74"/>
<source>Base Game is not installed. Please install {} first</source>
<translation>Basisspiel ist nicht installiert. Bitte installiere zuerst {}</translation>
</message>
</context>
<context>
<name>DownloadTab</name>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="33"/>
<location filename="../components/tabs/downloads/__init__.py" line="35"/>
<source>No active Download</source>
<translation>Kein aktiver Download</translation>
</message>
@ -122,57 +218,57 @@
<translation>Download anhalten</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="69"/>
<location filename="../components/tabs/downloads/__init__.py" line="73"/>
<source>No updates available</source>
<translation>Keine Updates verfügbar</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="124"/>
<location filename="../components/tabs/downloads/__init__.py" line="127"/>
<source>Error preparing download</source>
<translation>Fehler beim Vorbereiten des Downloads</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="129"/>
<location filename="../components/tabs/downloads/__init__.py" line="132"/>
<source>Download size is 0. Game already exists</source>
<translation>Die Größe des Downloads ist 0. Spiel existiert bereits</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="207"/>
<location filename="../components/tabs/downloads/__init__.py" line="211"/>
<source>Installation finished</source>
<translation>Installation abgeschlossen</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="249"/>
<location filename="../components/tabs/downloads/__init__.py" line="253"/>
<source>Installing Game: No active download</source>
<translation>Installierendes Spiel: Kein aktiver Download</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="259"/>
<location filename="../components/tabs/downloads/__init__.py" line="263"/>
<source>Download speed</source>
<translation>Geschwindigkeit</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="260"/>
<location filename="../components/tabs/downloads/__init__.py" line="264"/>
<source>Cache used</source>
<translation>Benutzter Cache</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="261"/>
<location filename="../components/tabs/downloads/__init__.py" line="265"/>
<source>Downloaded</source>
<translation>Runtergeladen</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="262"/>
<location filename="../components/tabs/downloads/__init__.py" line="267"/>
<source>Time left: </source>
<translation>Zeit übrig: </translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="208"/>
<location filename="../components/tabs/downloads/__init__.py" line="212"/>
<source>Finished Download of game {}</source>
<translation>Download von {} abgeschlossen</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="199"/>
<location filename="../components/tabs/downloads/__init__.py" line="203"/>
<source>Download finished. Reload library</source>
<translation>Download abgeschlossen. Spiele neu laden</translation>
</message>
@ -192,7 +288,7 @@
<translation type="obsolete">Keine</translation>
</message>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="155"/>
<location filename="../components/tabs/downloads/__init__.py" line="159"/>
<source>Installing Game: </source>
<translation>Installierendes Spiel: </translation>
</message>
@ -243,32 +339,32 @@
<context>
<name>GameActions</name>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="183"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="203"/>
<source>Uninstall game</source>
<translation>Spiel deinstallieren</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="185"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="205"/>
<source>Uninstall</source>
<translation>Deinstallieren</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="191"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="211"/>
<source>Verify Game</source>
<translation>Spieldateien überprüfen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="196"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="216"/>
<source>Verify</source>
<translation>Überprüfen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="205"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="225"/>
<source>Repair Game</source>
<translation>Spiel reparieren</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="207"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="227"/>
<source>Repair</source>
<translation>Reparieren</translation>
</message>
@ -276,27 +372,27 @@
<context>
<name>GameInfo</name>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="107"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="128"/>
<source>Repair file does not exist or game does not need a repair. Please verify game first</source>
<translation>Reparationsdatei existiert nicht oder das Spiel braucht keine Reperatur. Bitte das spiel zuerst überprüfen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="132"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="153"/>
<source>Verification failed, {} file(s) corrupted, {} file(s) are missing. Do you want to repair them?</source>
<translation>Überprüfung fehlgeschlagen, {} Datei(en) fehlerhaft, {} Datei(en) fehlen. Willst du das Spiel reparieren?</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="161"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="182"/>
<source>Developer: </source>
<translation>Entwickler: </translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="162"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="183"/>
<source>Install size: </source>
<translation>Größe: </translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="164"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="185"/>
<source>Install path: </source>
<translation>Installationsordner: </translation>
</message>
@ -309,22 +405,22 @@
<context>
<name>GameList</name>
<message>
<location filename="../components/tabs/games/game_list.py" line="203"/>
<location filename="../components/tabs/games/game_list.py" line="206"/>
<source>Launch</source>
<translation>Starten</translation>
</message>
<message>
<location filename="../components/tabs/games/game_list.py" line="221"/>
<location filename="../components/tabs/games/game_list.py" line="224"/>
<source>Game running</source>
<translation>Spiel läuft</translation>
</message>
<message>
<location filename="../components/tabs/games/game_list.py" line="53"/>
<location filename="../components/tabs/games/game_list.py" line="54"/>
<source>Installed Games: {} Available Games: {}</source>
<translation>Installierte Spiele: {} Verfügbare Spiele: {}</translation>
</message>
<message>
<location filename="../components/tabs/games/game_list.py" line="213"/>
<location filename="../components/tabs/games/game_list.py" line="215"/>
<source>Sync CLoud saves</source>
<translation>Spielstand synchronisieren</translation>
</message>
@ -332,17 +428,17 @@
<context>
<name>GameListHeadBar</name>
<message>
<location filename="../components/tabs/games/__init__.py" line="74"/>
<location filename="../components/tabs/games/__init__.py" line="76"/>
<source>Installed only</source>
<translation>Nur Installierte</translation>
</message>
<message>
<location filename="../components/tabs/games/__init__.py" line="81"/>
<location filename="../components/tabs/games/__init__.py" line="83"/>
<source>Import Game</source>
<translation>Spiel importieren</translation>
</message>
<message>
<location filename="../components/tabs/games/__init__.py" line="93"/>
<location filename="../components/tabs/games/__init__.py" line="95"/>
<source>Search Game</source>
<translation>Spiel suchen</translation>
</message>
@ -360,37 +456,37 @@
<translation>Überprüfung nach Updates beim Start überspringen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="86"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="87"/>
<source>Save</source>
<translation>Speichern</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="62"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="63"/>
<source>Wrapper (e.g. optirun)</source>
<translation>Wrapper (z.B. optirun)</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="82"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="83"/>
<source>Proton Wrapper</source>
<translation>Proton Version</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="88"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="89"/>
<source>Proton prefix</source>
<translation>Protonprefix</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="178"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="179"/>
<source>No permission to create folder</source>
<translation>Keine Berechtigung den Ordner zu erstellen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="222"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="223"/>
<source>Please select path for proton prefix</source>
<translation>Bitte wähle den Pfad zum Protonprefix</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/game_settings.py" line="51"/>
<location filename="../components/tabs/games/game_info/game_settings.py" line="52"/>
<source>Auto sync with cloud</source>
<translation>Speicherstände automatisch mit der Cloud synchronisieren</translation>
</message>
@ -408,17 +504,17 @@
<context>
<name>GameWidgetInstalled</name>
<message>
<location filename="../components/tabs/games/game_widgets/installed_icon_widget.py" line="36"/>
<location filename="../components/tabs/games/game_widgets/installed_icon_widget.py" line="34"/>
<source>Update available</source>
<translation>Update verfügbar</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/installed_icon_widget.py" line="88"/>
<location filename="../components/tabs/games/game_widgets/installed_icon_widget.py" line="86"/>
<source>Start game without version check</source>
<translation>Ohne Updateüberprüfung starten</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/installed_icon_widget.py" line="94"/>
<location filename="../components/tabs/games/game_widgets/installed_icon_widget.py" line="92"/>
<source>Game running</source>
<translation>Spiel läuft</translation>
</message>
@ -479,12 +575,12 @@
<translation>Wähle den Pfad zum Spiel</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="66"/>
<location filename="../components/tabs/games/import_widget.py" line="67"/>
<source>Import Game</source>
<translation>Spiel importieren</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="78"/>
<location filename="../components/tabs/games/import_widget.py" line="79"/>
<source>Import all games from Epic Games Launcher</source>
<translation>Alle Spiele aus dem Epic Games Launcher importieren</translation>
</message>
@ -499,22 +595,22 @@
<translation>App Name überschreiben (Nur falls das Spiel von Legendary importiert wird oder der App Name nicht gefunden wird</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="119"/>
<location filename="../components/tabs/games/import_widget.py" line="120"/>
<source>Could not find app name</source>
<translation>Konnte den Appnamen nicht finden</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="123"/>
<location filename="../components/tabs/games/import_widget.py" line="124"/>
<source>Successfully imported {}. Reload library</source>
<translation>Erfolgreich {} importiert. Spiele neu laden</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="129"/>
<location filename="../components/tabs/games/import_widget.py" line="131"/>
<source>Failed to import {}</source>
<translation>{} Konnte nicht importiert werden</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="168"/>
<location filename="../components/tabs/games/import_widget.py" line="170"/>
<source>Successfully imported {} Games. Reloading Library</source>
<translation>Erfolgreich {} Spiele importiert. Spiele neu laden</translation>
</message>
@ -524,7 +620,7 @@
<translation>Ein bereits existierendes Spiel aus dem Epic Games Launcher importieren</translation>
</message>
<message>
<location filename="../components/tabs/games/import_widget.py" line="171"/>
<location filename="../components/tabs/games/import_widget.py" line="174"/>
<source>No Games were found</source>
<translation>Keine Spiele wurden gefunden</translation>
</message>
@ -532,25 +628,30 @@
<context>
<name>InfoTabs</name>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="27"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="28"/>
<source>Back</source>
<translation>Zurück</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="31"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="32"/>
<source>Game Info</source>
<translation>Spielinfo</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="33"/>
<location filename="../components/tabs/games/game_info/__init__.py" line="35"/>
<source>Settings</source>
<translation>Einstellungen</translation>
</message>
<message>
<location filename="../components/tabs/games/game_info/__init__.py" line="39"/>
<source>DLCs</source>
<translation>DLCs</translation>
</message>
</context>
<context>
<name>InstallDialog</name>
<message>
<location filename="../components/dialogs/install_dialog.py" line="29"/>
<location filename="../components/dialogs/install_dialog.py" line="39"/>
<source>Max workers (0: Default)</source>
<translation>Maximale Anzahl Downloadprozessen(Standard: 0)</translation>
</message>
@ -560,12 +661,12 @@
<translation>&lt;h3&gt;Installiere {}&lt;/h3&gt;</translation>
</message>
<message>
<location filename="../components/dialogs/install_dialog.py" line="33"/>
<location filename="../components/dialogs/install_dialog.py" line="43"/>
<source>Force download</source>
<translation>Download erzwingen</translation>
</message>
<message>
<location filename="../components/dialogs/install_dialog.py" line="37"/>
<location filename="../components/dialogs/install_dialog.py" line="47"/>
<source>Ignore free space (Warning!)</source>
<translation>Freien Speicherplatz ignorieren (Achtung!)</translation>
</message>
@ -573,19 +674,19 @@
<context>
<name>InstallInfoDialog</name>
<message>
<location filename="../components/dialogs/install_dialog.py" line="70"/>
<location filename="../components/dialogs/install_dialog.py" line="82"/>
<source>Download size: {}GB
Install size: {}GB</source>
<translation>Downloadgröße: {}GB
Installationsgröße: {} GB</translation>
</message>
<message>
<location filename="../components/dialogs/install_dialog.py" line="76"/>
<location filename="../components/dialogs/install_dialog.py" line="88"/>
<source>Install</source>
<translation>Installieren</translation>
</message>
<message>
<location filename="../components/dialogs/install_dialog.py" line="78"/>
<location filename="../components/dialogs/install_dialog.py" line="90"/>
<source>Cancel</source>
<translation>Abbruch</translation>
</message>
@ -593,40 +694,40 @@ Installationsgröße: {} GB</translation>
<context>
<name>InstalledListWidget</name>
<message>
<location filename="../components/tabs/games/game_widgets/installed_list_widget.py" line="49"/>
<location filename="../components/tabs/games/game_widgets/installed_list_widget.py" line="40"/>
<source>Launch</source>
<translation>Starten</translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/installed_list_widget.py" line="60"/>
<location filename="../components/tabs/games/game_widgets/installed_list_widget.py" line="51"/>
<source>Developer: </source>
<translation>Entwickler: </translation>
</message>
<message>
<location filename="../components/tabs/games/game_widgets/installed_list_widget.py" line="31"/>
<source>Uninstall</source>
<translation>Deinstallieren</translation>
<translation type="obsolete">Deinstallieren</translation>
</message>
</context>
<context>
<name>LaunchDialog</name>
<message>
<location filename="../components/dialogs/launch_dialog.py" line="60"/>
<location filename="../components/dialogs/launch_dialog.py" line="65"/>
<source>Launching Rare</source>
<translation>Starte Rare</translation>
</message>
<message>
<location filename="../components/dialogs/launch_dialog.py" line="62"/>
<location filename="../components/dialogs/launch_dialog.py" line="67"/>
<source>Logging in</source>
<translation>Einloggen</translation>
</message>
<message>
<location filename="../components/dialogs/launch_dialog.py" line="82"/>
<location filename="../components/dialogs/launch_dialog.py" line="90"/>
<source>Downloading Images</source>
<translation>Bilder runterladen</translation>
</message>
<message>
<location filename="../components/dialogs/launch_dialog.py" line="93"/>
<location filename="../components/dialogs/launch_dialog.py" line="101"/>
<source>Starting...</source>
<translation>Starten...</translation>
</message>
@ -634,7 +735,7 @@ Installationsgröße: {} GB</translation>
<context>
<name>LaunchThread</name>
<message>
<location filename="../components/dialogs/launch_dialog.py" line="23"/>
<location filename="../components/dialogs/launch_dialog.py" line="24"/>
<source>Downloading Images</source>
<translation>Bilder runterladen</translation>
</message>
@ -642,38 +743,43 @@ Installationsgröße: {} GB</translation>
<context>
<name>LegendarySettings</name>
<message>
<location filename="../components/tabs/settings/legendary.py" line="18"/>
<location filename="../components/tabs/settings/legendary.py" line="19"/>
<source>Legendary settings</source>
<translation>Legendary Einstellungen</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="31"/>
<location filename="../components/tabs/settings/legendary.py" line="32"/>
<source>Default installation directory</source>
<translation>Standardordner für Installationen</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="40"/>
<location filename="../components/tabs/settings/legendary.py" line="41"/>
<source>Max workers for Download (Less: slower download)(0: Default)</source>
<translation>Maximale Anzahl Downloadprozesse (Weniger: langsamer)(Standard: 0)</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="46"/>
<location filename="../components/tabs/settings/legendary.py" line="47"/>
<source>Cleanup</source>
<translation>Aufräumen</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="47"/>
<location filename="../components/tabs/settings/legendary.py" line="48"/>
<source>Remove everything</source>
<translation>Alles aufräumen</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="51"/>
<location filename="../components/tabs/settings/legendary.py" line="52"/>
<source>Clean, but keep manifests</source>
<translation>Aufräumen, aber Manifests behalten</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="100"/>
<source>Cleanup complete! Successfully removed {} MB</source>
<translation type="obsolete">Fertig! Es wurden {} MB entfernt</translation>
</message>
<message>
<location filename="../components/tabs/settings/legendary.py" line="101"/>
<source>Cleanup complete! Successfully removed {}</source>
<translation>Fertig! Es wurden {} MB entfernt</translation>
</message>
</context>
@ -724,30 +830,48 @@ Installationsgröße: {} GB</translation>
<context>
<name>MainWindow</name>
<message>
<location filename="../components/main_window.py" line="48"/>
<location filename="../components/main_window.py" line="82"/>
<source>There is a download active. Do you really want to exit app?</source>
<translation>Ein Download läuft noch. Möchtest du die App wirklich beenden?</translation>
</message>
</context>
<context>
<name>MangoSettings</name>
<message>
<location filename="../components/tabs/settings/mango_hud.py" line="7"/>
<source>MangoHUD settings</source>
<translation>MangoHUD Einstellungen</translation>
</message>
<message>
<location filename="../components/tabs/settings/mango_hud.py" line="11"/>
<source>CPU temperature</source>
<translation>CPU Temperatur</translation>
</message>
<message>
<location filename="../components/tabs/settings/mango_hud.py" line="12"/>
<source>GPU temperature</source>
<translation>GPU Temperatur</translation>
</message>
</context>
<context>
<name>MiniWidget</name>
<message>
<location filename="../components/tabs/account/__init__.py" line="20"/>
<location filename="../components/tabs/account/__init__.py" line="23"/>
<source>Logged in as </source>
<translation>Eingeloggt als </translation>
</message>
<message>
<location filename="../components/tabs/account/__init__.py" line="22"/>
<location filename="../components/tabs/account/__init__.py" line="25"/>
<source>Account settings</source>
<translation>Accounteinstellungen</translation>
</message>
<message>
<location filename="../components/tabs/account/__init__.py" line="27"/>
<location filename="../components/tabs/account/__init__.py" line="30"/>
<source>Logout</source>
<translation>Ausloggen</translation>
</message>
<message>
<location filename="../components/tabs/account/__init__.py" line="33"/>
<location filename="../components/tabs/account/__init__.py" line="36"/>
<source>Do you really want to logout</source>
<translation>Willst du dich wirklich abmelden</translation>
</message>
@ -819,27 +943,27 @@ Installationsgröße: {} GB</translation>
<translation>Rare Einstellungen</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="45"/>
<location filename="../components/tabs/settings/rare.py" line="48"/>
<source>Save</source>
<translation>Speichern</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="47"/>
<location filename="../components/tabs/settings/rare.py" line="50"/>
<source>Image Directory</source>
<translation>Ordner für Bilder</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="58"/>
<location filename="../components/tabs/settings/rare.py" line="61"/>
<source>Language</source>
<translation>Sprache</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="84"/>
<location filename="../components/tabs/settings/rare.py" line="87"/>
<source>Restart Application to activate changes</source>
<translation>Starte die App neu um die Änderungen zu aktivieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="32"/>
<location filename="../components/tabs/settings/rare.py" line="33"/>
<source>Confirm launch of game</source>
<translation>Start des Spiels bestätigen</translation>
</message>
@ -854,22 +978,22 @@ Installationsgröße: {} GB</translation>
<translation>In das System Tray Icon minimieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="33"/>
<location filename="../components/tabs/settings/rare.py" line="35"/>
<source>Auto sync with cloud</source>
<translation>Speicherstände automatisch mit der Cloud synchronisieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="33"/>
<location filename="../components/tabs/settings/rare.py" line="35"/>
<source>Sync with cloud</source>
<translation>Automatisch Synchronisieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="35"/>
<location filename="../components/tabs/settings/rare.py" line="38"/>
<source>Save size</source>
<translation>Größe Speichern</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="35"/>
<location filename="../components/tabs/settings/rare.py" line="38"/>
<source>Save size of window after restart</source>
<translation>Die Fenstergröße nach dem Beenden speichern</translation>
</message>
@ -884,12 +1008,12 @@ Installationsgröße: {} GB</translation>
<translation>Automatische Updates</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="34"/>
<location filename="../components/tabs/settings/rare.py" line="36"/>
<source>Show Notifications after Downloads</source>
<translation>Benachrichtigung nach Abschluss des Downloads anzeigen</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="34"/>
<location filename="../components/tabs/settings/rare.py" line="36"/>
<source>Show notification</source>
<translation>Benachrichtigung anzeigen</translation>
</message>
@ -935,97 +1059,97 @@ Installationsgröße: {} GB</translation>
<context>
<name>SyncWidget</name>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="62"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="76"/>
<source>Path not found</source>
<translation>Ordner nicht gefunden</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="78"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="92"/>
<source>Local Save date: </source>
<translation>Lokales Speicherdatum: </translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="81"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="95"/>
<source>No Local Save files</source>
<translation>Keine Lokalen Dateien</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="83"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="97"/>
<source>Cloud save date: </source>
<translation>Online Speicherdatum: </translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="85"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="99"/>
<source>No Cloud saves</source>
<translation>Keine Online Speicherstände</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="89"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="103"/>
<source>Game is up to date</source>
<translation>Spiel ist aktuell</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="90"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="104"/>
<source>Upload anyway</source>
<translation>Trotzdem hochladen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="91"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="105"/>
<source>Download anyway</source>
<translation>Trotzdem herunterladen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="93"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="107"/>
<source>Cloud save is newer</source>
<translation>Online Speicherstand ist aktueller</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="94"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="108"/>
<source>Download Cloud saves</source>
<translation>Online Speicherstand herunterladen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="98"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="112"/>
<source>Upload Saves</source>
<translation>Spielstände hochladen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="109"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="123"/>
<source>Local save is newer</source>
<translation>Lokaler Speicher ist aktueller</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="110"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="124"/>
<source>Upload saves</source>
<translation>Spielstände hochladen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="114"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="128"/>
<source>Download saves</source>
<translation>Spielstand herunterladen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="137"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="156"/>
<source>Change path</source>
<translation>Pfad ändern</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="162"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="181"/>
<source>Uploading...</source>
<translation>Hochladen...</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="170"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="189"/>
<source>Upload finished</source>
<translation>Hochladen abgeschlossen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="180"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="199"/>
<source>Downloading...</source>
<translation>Runterladen...</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="186"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="205"/>
<source>Download finished</source>
<translation>Download abgeschlossen</translation>
</message>
@ -1033,7 +1157,7 @@ Installationsgröße: {} GB</translation>
<context>
<name>TabWidget</name>
<message>
<location filename="../components/tab_widget.py" line="34"/>
<location filename="../components/tab_widget.py" line="28"/>
<source>Games</source>
<translation>Spiele</translation>
</message>
@ -1041,7 +1165,7 @@ Installationsgröße: {} GB</translation>
<context>
<name>TrayIcon</name>
<message>
<location filename="../components/tray_icon.py" line="18"/>
<location filename="../components/tray_icon.py" line="19"/>
<source>Exit</source>
<translation>Schließen</translation>
</message>
@ -1077,7 +1201,7 @@ Installationsgröße: {} GB</translation>
<context>
<name>UpdateWidget</name>
<message>
<location filename="../components/tabs/downloads/__init__.py" line="288"/>
<location filename="../components/tabs/downloads/__init__.py" line="297"/>
<source>Update Game</source>
<translation>Spiel updaten</translation>
</message>

View file

@ -36,13 +36,10 @@ def launch_game(core, app_name: str, offline: bool = False, skip_version_check:
if latest.build_version != game.version:
print("Please update game")
return None
try:
params, cwd, env = core.get_launch_parameters(app_name=app_name, offline=offline)
except Exception as e:
logger.error(e)
return 1
params, cwd, env = core.get_launch_parameters(app_name=app_name, offline=offline)
process = QProcess()
process.setProcessChannelMode(QProcess.MergedChannels)
process.setWorkingDirectory(cwd)
environment = QProcessEnvironment()
for e in env:
@ -52,10 +49,18 @@ def launch_game(core, app_name: str, offline: bool = False, skip_version_check:
return process, params
def uninstall(app_name: str, core, options=None):
def uninstall(app_name: str, core: LegendaryCore, options=None):
if not options:
options = {"keep_files": False}
igame = core.get_installed_game(app_name)
# remove shortcuts link
if os.name == "posix":
if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title}.desktop")):
os.remove(os.path.expanduser(f"~/Desktop/{igame.title}.desktop"))
if os.path.exists(os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop")):
os.remove(os.path.expanduser(f"~/.local/share/applications/{igame.title}.desktop"))
try:
# Remove DLC first so directory is empty when game uninstall runs
dlcs = core.get_dlc_for_game(app_name)

View file

@ -2,6 +2,7 @@ import platform
import time
from logging import getLogger
import pypresence.exceptions
from PyQt5.QtCore import QObject, QSettings
from PyQt5.QtWidgets import QMessageBox
from pypresence import Presence
@ -60,10 +61,22 @@ class DiscordRPC(QObject):
logger.warning("Discord is not active\n" + str(e))
self.RPC = None
return
except FileNotFoundError as e:
logger.warning("File not found error\n" + str(e))
self.RPC = None
return
except pypresence.exceptions.InvalidPipe as e:
logger.error("Is Discord running? \n" + str(e))
self.RPC = None
return
except Exception as e:
logger.error(str(e))
self.RPC = None
return
self.update_rpc(app_name)
def update_rpc(self, app_name=None):
if self.settings.value("rpc_enable", 0, int) == 2 or (app_name is None and self.settings.value("rpc_enable", 0) == 0):
if self.settings.value("rpc_enable", 0, int) == 2 or (app_name is None and self.settings.value("rpc_enable", 0, int) == 0):
self.remove_rpc()
return
title = None

View file

@ -28,18 +28,22 @@ def download_images(signal: pyqtSignal, core: LegendaryCore):
logger.info("Create Image dir")
# Download Images
for i, game in enumerate(sorted(core.get_game_list(), key=lambda x: x.app_title)):
games, dlcs = core.get_game_and_dlc_list()
dlc_list = []
for i in dlcs.values():
dlc_list.append(i[0])
game_list = games + dlc_list
for i, game in enumerate(game_list):
try:
download_image(game)
except json.decoder.JSONDecodeError:
shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}")
download_image(game)
signal.emit(i)
signal.emit(i/len(game_list)*100)
def download_image(game, force=False):
if force:
if force and os.path.exists(f"{IMAGE_DIR}/{game.app_name}"):
shutil.rmtree(f"{IMAGE_DIR}/{game.app_name}")
if not os.path.isdir(f"{IMAGE_DIR}/" + game.app_name):
os.mkdir(f"{IMAGE_DIR}/" + game.app_name)
@ -128,9 +132,12 @@ def get_possible_langs():
def get_latest_version():
resp = requests.get("https://api.github.com/repos/Dummerle/Rare/releases/latest")
tag = json.loads(resp.content.decode("utf-8"))["tag_name"]
return tag
try:
resp = requests.get("https://api.github.com/repos/Dummerle/Rare/releases/latest")
tag = json.loads(resp.content.decode("utf-8"))["tag_name"]
return tag
except requests.exceptions.ConnectionError:
return "0.0.0"
def get_size(b: int) -> str: