1
0
Fork 0
mirror of synced 2024-06-25 09:40:31 +12:00

Merge branch 'main' into dev

This commit is contained in:
Dummerle 2021-05-21 13:20:58 +02:00 committed by GitHub
commit 9edfd302bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 478 additions and 188 deletions

View file

@ -46,7 +46,7 @@ jobs:
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message: Update AUR package
pyinstaller-windows:
cx-freeze-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
@ -55,25 +55,18 @@ jobs:
python-version: '3.8'
- name: Install python deps
run: |
pip3 install pyinstaller setuptools wheel
pip3 install cx_Freeze setuptools wheel
pip3 install -r requirements.txt
- name: Prepare
run: cp rare/__main__.py ./
- name: Build
run: pyinstaller
--icon=rare/styles/Logo.ico
--onefile
--name Rare
--add-data="Rare/languages/*;Rare/languages"
--add-data="Rare/Styles/*;Rare/Styles"
--windowed
__main__.py
run: python3 freeze.py bdist_msi
- name: Copy File
run: cp dist/*.msi Rare.msi
- name: Upload files to GitHub
uses: svenstaro/upload-release-action@2.2.1
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: dist/Rare.exe
asset_name: Rare.exe
file: Rare.msi
asset_name: Rare.msi
tag: ${{ github.ref }}
overwrite: true

4
.gitignore vendored
View file

@ -6,5 +6,7 @@ __pycache__
/.vscode
/build
/dist
/deb_dist
*.tar.gz
/Rare.egg-info/
/venv
/venv

View file

@ -41,12 +41,14 @@ Install via `pip`.
## Run from source
1. Run `pip install -r requirements.txt` to get dependencies. If you use `pacman` you can run `sudo pacman --needed -S python-wheel python-setuptools python-pyqt5 python-qtawesome python-requests python-pillow`
2. For unix operating systems run `sh start.sh`. For windows run `set PYTHONPATH=%CD% && python Rare`
2. For unix operating systems run `sh start.sh`. For windows run `set PYTHONPATH=%CD% && python rare`
## Why Rare?
- Rare uses much less RAM than electron based apps such as [HeroicGL](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) and EpicGL which allows the games to run better.
- Rare supports all major platforms (Windows, Linux, macOS) unlike the alternatives.
- Rare only uses ~50MB of RAM which is much less than the electron based [HeroicGamesLauncher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) uses.
- Rare supports all major platforms (Windows, Linux, Mac) unlike the alternatives.
**Note** Mac should work too, but I have no Mac and I can't test it.
## Features
@ -60,7 +62,7 @@ Install via `pip`.
## Planned Features
- More Translations (Need help)
- More Information about Games
More planned features are in projects
More planned features are in [projects](https://github.com/Dummerle/Rare/projects/1)
## Contributing
There are more options to contribute.

74
freeze.py Normal file
View file

@ -0,0 +1,74 @@
import sys
from cx_Freeze import setup, Executable
from rare import __version__
# Packages to include
python_packages = []
# Modules to include
python_modules = []
base = None
name = None
build_options = {}
build_exe_options = {}
shortcutName = None
shortcutDir = None
bdist_msi_options = None
src_files = []
external_so_files = []
if sys.platform == 'win32':
base = 'Win32GUI'
name = 'Rare.exe'
shortcut_table = [
('DesktopShortcut', # Shortcut
'DesktopFolder', # Directory
'Rare', # Name
'TARGETDIR', # Component
'[TARGETDIR]'+name, # Target
None, # Arguments
'A gui for Legendary.', # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR' # Working Directory
)]
msi_data = {"Shortcut": shortcut_table}
bdist_msi_options = {'data': msi_data, "all_users": True}
build_options["bdist_msi"] = bdist_msi_options
else:
name = 'Rare'
src_files += [
'LICENSE',
'README.md',
'rare/styles/Logo.ico',
]
# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options["packages"] = python_packages
build_exe_options["include_files"] = src_files + external_so_files
build_exe_options["includes"] = python_modules
build_exe_options["excludes"] = ["setuptools", "tkinter", "pkg_resources"]
# Set options
build_options["build_exe"] = build_exe_options
setup(name = 'Rare',
version = __version__,
description = 'A gui for Legendary.',
options = build_options,
executables = [
Executable('rare/__main__.py',
targetName=name,
icon='rare/styles/Logo.ico',
base=base,
shortcutName=shortcutName,
shortcutDir=shortcutDir,
),
],
)

View file

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

View file

@ -1,7 +1,6 @@
import configparser
import logging
import os
import shutil
import sys
import time
@ -16,7 +15,7 @@ from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
from rare.utils.utils import get_lang, load_color_scheme
start_time = time.strftime('%y-%m-%d--%H:%M') # year-month-day-hour-minute
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")
if not os.path.exists(os.path.dirname(file_name)):
os.makedirs(os.path.dirname(file_name))

View file

@ -3,9 +3,14 @@ import webbrowser
from PyQt5.QtCore import QSize, pyqtSignal
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction
from qtawesome import icon
from rare.utils import legendary_utils
from custom_legendary.core import LegendaryCore
from rare.components.dialogs.install_dialog import InstallDialog
from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.tab_utils import TabBar, TabButtonWidget
from rare.components.tabs.account import MiniWidget
from rare.components.tabs.cloud_saves import SyncSaves
@ -64,6 +69,12 @@ class TabWidget(QTabWidget):
# open download tab
self.games_tab.default_widget.game_list.update_game.connect(lambda: self.setCurrentIndex(1))
# uninstall
self.games_tab.game_info.info.uninstall_game.connect(self.uninstall_game)
# imported
self.games_tab.import_widget.update_list.connect(self.game_imported)
if not offline:
# Download finished
self.downloadTab.finished.connect(self.dl_finished)
@ -87,6 +98,7 @@ class TabWidget(QTabWidget):
self.tabBarClicked.connect(lambda x: self.games_tab.layout.setCurrentIndex(0) if x == 0 else None)
self.setIconSize(QSize(25, 25))
def install_game(self, app_name, disable_path=False):
infos = InstallDialog(app_name, self.core, disable_path).get_information()
@ -102,16 +114,38 @@ class TabWidget(QTabWidget):
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.downloadTab.install_game(options)
def game_imported(self, app_name: str):
igame = self.core.get_installed_game(app_name)
if self.core.get_asset(app_name, True).build_version != igame.version:
self.downloadTab.add_update(igame)
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys())
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.games_tab.default_widget.game_list.update_list(app_name)
self.games_tab.layout.setCurrentIndex(0)
# Sync game and delete dc rpc
def game_finished(self, app_name):
self.delete_presence.emit()
if self.core.get_game(app_name).supports_cloud_saves:
self.cloud_saves.sync_game(app_name, True)
def uninstall_game(self, app_name):
game = self.core.get_game(app_name)
infos = UninstallDialog(game).get_information()
if infos == 0:
return
legendary_utils.uninstall(game.app_name, self.core, infos)
if app_name in self.downloadTab.update_widgets.keys():
self.downloadTab.update_layout.removeWidget(self.downloadTab.update_widgets[app_name])
self.downloadTab.update_widgets.pop(app_name)
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys())
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.downloadTab.update_text.setVisible(len(self.downloadTab.update_widgets) == 0)
# Update gamelist and set text of Downlaods to "Downloads"
def dl_finished(self, update_list):
if update_list:
self.games_tab.default_widget.game_list.update_list()
if update_list[0]:
self.games_tab.default_widget.game_list.update_list(update_list[1])
downloads = len(self.downloadTab.dl_queue) + len(self.downloadTab.update_widgets.keys())
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))

View file

@ -1,7 +1,7 @@
import os
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QSettings
from PyQt5.QtWidgets import QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QGroupBox
from custom_legendary.core import LegendaryCore
@ -167,6 +167,11 @@ class SyncWidget(QGroupBox):
self.layout.addStretch(1)
self.setLayout(self.layout)
if self.res == SaveGameStatus.REMOTE_NEWER:
settings = QSettings()
if settings.value(f"{igame.app_name}/auto_sync_cloud", False, bool):
self.download()
def change_path(self):
path = PathInputDialog("Select directory", "Select savepath. Warning: Do not change if you are not sure",
self.igame.save_path).get_path()

View file

@ -20,7 +20,7 @@ logger = getLogger("Download")
class DownloadTab(QWidget):
finished = pyqtSignal(bool)
finished = pyqtSignal(tuple)
thread: QThread
dl_queue = []
@ -75,18 +75,22 @@ class DownloadTab(QWidget):
self.update_text.setVisible(len(updates) == 0)
for igame in updates:
widget = UpdateWidget(core, igame, self)
self.update_layout.addWidget(widget)
self.update_widgets[igame.app_name] = widget
widget.update_signal.connect(self.update_game)
if QSettings().value("auto_update", False, bool):
self.update_game(igame.app_name, True)
widget.update_button.setDisabled(True)
self.add_update(igame)
self.layout.addStretch(1)
self.setLayout(self.layout)
def add_update(self, igame: InstalledGame):
widget = UpdateWidget(self.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)
def update_dl_queue(self, dl_queue):
self.dl_queue = dl_queue
@ -135,7 +139,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)
self.finished.emit(False, None)
return
if self.active_game is None:
@ -213,23 +217,25 @@ class DownloadTab(QWidget):
# QMessageBox.information(self, "Info", "Download finished")
logger.info("Download finished: " + self.active_game.app_title)
app_name = self.active_game.app_name
self.active_game = None
if self.dl_queue:
if self.dl_queue[0][1] == self.active_game.app_name:
if self.dl_queue[0][1] == app_name:
self.dl_queue.pop(0)
self.queue_widget.update_queue(self.dl_queue)
if self.active_game.app_name in self.update_widgets.keys():
self.update_widgets[self.active_game.app_name].setVisible(False)
self.update_widgets.pop(self.active_game.app_name)
if app_name in self.update_widgets.keys():
self.update_widgets[app_name].setVisible(False)
self.update_widgets.pop(app_name)
if len(self.update_widgets) == 0:
self.update_text.setVisible(True)
self.active_game = None
for i in self.update_widgets.values():
i.update_button.setDisabled(False)
self.finished.emit(True)
self.finished.emit((True, app_name))
self.reset_infos()
if len(self.dl_queue) != 0:
@ -243,7 +249,7 @@ class DownloadTab(QWidget):
elif text == "stop":
self.reset_infos()
self.active_game = None
self.finished.emit(False)
self.finished.emit((False, None))
if self.dl_queue:
self.start_installation(*self.dl_queue[0])
@ -302,6 +308,7 @@ class UpdateWidget(QWidget):
self.update_with_settings.clicked.connect(lambda: self.update_game(False))
self.layout.addWidget(self.update_button)
self.layout.addWidget(self.update_with_settings)
self.layout.addWidget(QLabel(self.tr("Version: ") + self.game.version + " -> " + self.core.get_asset(self.game.app_name, True).build_version))
self.setLayout(self.layout)

View file

@ -1,5 +1,6 @@
from PyQt5.QtCore import QSettings, QSize
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QPushButton, QStackedLayout, QLabel
from PyQt5.QtCore import QSettings, QSize, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QStackedLayout, \
QLabel, QComboBox
from qtawesome import icon
from rare.components.tabs.games.game_info import InfoTabs
@ -36,8 +37,8 @@ class GameTab(QWidget):
self.setLayout(self.layout)
def update_list(self):
self.default_widget.game_list.update_list(self.default_widget.head_bar.view.isChecked())
def update_list(self, app_name=None):
self.default_widget.game_list.update_list(app_name)
self.layout.setCurrentIndex(0)
def show_uninstalled(self, app_name):
@ -62,11 +63,10 @@ class Games(QWidget):
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()))
lambda: self.game_list.search(self.head_bar.search_bar.text()))
self.head_bar.filter_changed_signal.connect(self.game_list.filter)
self.head_bar.installed_only.stateChanged.connect(lambda:
self.game_list.installed_only(
self.head_bar.installed_only.isChecked()))
self.layout.addWidget(self.head_bar)
self.layout.addWidget(self.game_list)
# self.layout.addStretch(1)
@ -81,14 +81,23 @@ class Games(QWidget):
class GameListHeadBar(QWidget):
filter_changed_signal = pyqtSignal(str)
def __init__(self, parent):
super(GameListHeadBar, self).__init__(parent=parent)
self.layout = QHBoxLayout()
self.installed_only = QCheckBox(self.tr("Installed only"))
# self.installed_only = QCheckBox(self.tr("Installed only"))
self.settings = QSettings()
self.installed_only.setChecked(self.settings.value("installed_only", False, bool))
self.layout.addWidget(self.installed_only)
# self.installed_only.setChecked(self.settings.value("installed_only", False, bool))
# self.layout.addWidget(self.installed_only)
self.filter = QComboBox()
self.filter.addItems([self.tr("All"),
self.tr("Installed only"),
self.tr("Offline Games"),
self.tr("32 Bit Games")])
self.filter.currentIndexChanged.connect(self.filter_changed)
self.layout.addWidget(self.filter)
self.layout.addStretch(1)
self.import_game = QPushButton(icon("mdi.import", color="white"), self.tr("Import Game"))
@ -118,3 +127,6 @@ class GameListHeadBar(QWidget):
self.layout.addWidget(self.refresh_list)
self.setLayout(self.layout)
def filter_changed(self, i):
self.filter_changed_signal.emit(["", "installed", "offline", "32bit"][i])

View file

@ -59,7 +59,8 @@ class InfoTabs(QTabWidget):
class GameInfo(QWidget, Ui_GameInfo):
igame: InstalledGame
game: Game
update_list = pyqtSignal()
uninstall_game = pyqtSignal(str)
update_list = pyqtSignal(str)
verify_game = pyqtSignal(str)
verify_threads = {}
@ -87,12 +88,8 @@ class GameInfo(QWidget, Ui_GameInfo):
self.repair_button.clicked.connect(self.repair)
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()
self.uninstall_game.emit(self.game.app_name)
self.update_list.emit(self.game.app_name)
def repair(self):
repair_file = os.path.join(self.core.lgd.get_tmp_path(), f'{self.game.app_name}.repair')

View file

@ -4,9 +4,10 @@ from logging import getLogger
import psutil
from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from PyQt5.QtWidgets import QScrollArea, QWidget, QLabel, QVBoxLayout, QStackedWidget
from custom_legendary.core import LegendaryCore
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from rare.components.tabs.games.game_widgets.installed_icon_widget import GameWidgetInstalled
from rare.components.tabs.games.game_widgets.installed_list_widget import InstalledListWidget
from rare.components.tabs.games.game_widgets.uninstalled_icon_widget import IconWidgetUninstalled
@ -63,93 +64,36 @@ class GameList(QStackedWidget):
self.list_layout = QVBoxLayout()
self.list_layout.addWidget(QLabel(self.info_text))
IMAGE_DIR = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images"), str)
self.IMAGE_DIR = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images"), str)
self.updates = []
self.widgets = {}
self.bit32 = [i.app_name for i in self.core.get_game_and_dlc_list(True, "Win32")[0]]
self.mac_games = [i.app_name for i in self.core.get_game_and_dlc_list(True, "Mac")[0]]
self.installed = sorted(self.core.get_installed_list(), key=lambda x: x.title)
# Installed Games
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"):
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
elif os.path.exists(f"{IMAGE_DIR}/{igame.app_name}/DieselGameBoxLogo.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/DieselGameBoxLogo.png")
else:
logger.warning(f"No Image found: {igame.title}")
pixmap = None
if pixmap.isNull():
logger.info(igame.title + " has a corrupt image.")
download_image(igame, force=True)
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
icon_widget = GameWidgetInstalled(igame, self.core, pixmap, self.offline)
icon_widget, list_widget = self.add_installed_widget(igame)
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)
icon_widget.launch_signal.connect(self.launch)
icon_widget.finish_signal.connect(self.finished)
icon_widget.update_list.connect(lambda: self.update_list(self.settings.value("icon_view", True, bool)))
list_widget.launch_signal.connect(self.launch)
list_widget.finish_signal.connect(self.finished)
list_widget.update_list.connect(lambda: self.update_list(self.settings.value("icon_view", True, bool)))
if icon_widget.update_available:
self.updates.append(igame)
self.widgets[igame.app_name] = (icon_widget, list_widget)
active, pid = self.check_for_active_game(igame)
if active:
# Only one game works: Workaround for Satisfactory EA and Exp.
self.launch(igame.app_name)
icon_widget.game_running = True
list_widget.game_running = True
self.active_game = (igame.app_name, pid)
self.timer = QTimer()
self.timer.timeout.connect(self.is_finished)
self.timer.start(10000)
self.uninstalled_games = []
self.uninstalled_names = []
installed = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
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)
self.uninstalled_names.append(game)
# add 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")
if pixmap.isNull():
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)
pixmap = QPixmap(f"{IMAGE_DIR}/{igame.app_name}/UninstalledArt.png")
icon_widget = IconWidgetUninstalled(igame, self.core, pixmap)
icon_widget.show_uninstalled_info.connect(self.install)
list_widget = ListWidgetUninstalled(self.core, igame, pixmap)
list_widget.show_uninstalled_info.connect(self.install)
for game in self.uninstalled_games:
icon_widget, list_widget = self.add_uninstalled_widget(game)
self.icon_layout.addWidget(icon_widget)
self.list_layout.addWidget(list_widget)
self.widgets[igame.app_name] = (icon_widget, list_widget)
self.icon_parent_layout.addLayout(self.icon_layout)
self.icon_parent_layout.addStretch(1)
self.list_layout.addStretch(1)
@ -165,8 +109,81 @@ class GameList(QStackedWidget):
if not icon_view:
self.setCurrentIndex(1)
if self.settings.value("installed_only", False, bool):
self.installed_only(True)
if filter_games := self.settings.value("filter", "", str):
self.filter(filter_games)
def add_uninstalled_widget(self, game):
if os.path.exists(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png")
if pixmap.isNull():
logger.info(game.app_title + " has a corrupt image.")
download_image(game, force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png")
else:
logger.warning(f"No Image found: {game.app_title}")
download_image(game, force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{game.app_name}/UninstalledArt.png")
icon_widget = IconWidgetUninstalled(game, self.core, pixmap)
icon_widget.install_game.connect(self.install)
list_widget = ListWidgetUninstalled(self.core, game, pixmap)
list_widget.install_game.connect(self.install)
self.widgets[game.app_name] = (icon_widget, list_widget)
return icon_widget, list_widget
def add_installed_widget(self, igame):
if os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/FinalArt.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/FinalArt.png")
elif os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
elif os.path.exists(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxLogo.png"):
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxLogo.png")
else:
logger.warning(f"No Image found: {igame.title}")
pixmap = None
if pixmap.isNull():
logger.info(igame.title + " has a corrupt image.")
download_image(igame, force=True)
pixmap = QPixmap(f"{self.IMAGE_DIR}/{igame.app_name}/DieselGameBoxTall.png")
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)
self.widgets[igame.app_name] = (icon_widget, list_widget)
icon_widget.show_info.connect(self.show_game_info.emit)
list_widget.show_info.connect(self.show_game_info.emit)
icon_widget.launch_signal.connect(self.launch)
icon_widget.finish_signal.connect(self.finished)
icon_widget.update_list.connect(self.update_list)
list_widget.launch_signal.connect(self.launch)
list_widget.finish_signal.connect(self.finished)
list_widget.update_list.connect(self.update_list)
if icon_widget.update_available:
self.updates.append(igame)
active, pid = self.check_for_active_game(igame)
if active:
# Only one game works: Workaround for Satisfactory EA and Exp.
self.launch(igame.app_name)
icon_widget.game_running = True
list_widget.game_running = True
self.active_game = (igame.app_name, pid)
self.timer = QTimer()
self.timer.timeout.connect(self.is_finished)
self.timer.start(10000)
return icon_widget, list_widget
def is_finished(self):
if psutil.pid_exists(self.active_game[1]):
@ -226,7 +243,7 @@ class GameList(QStackedWidget):
self.widgets[app_name][1].launch_button.setDisabled(True)
self.widgets[app_name][1].launch_button.setText(self.tr("Game running"))
def filter(self, text: str):
def search(self, text: str):
for t in self.widgets.values():
for w in t:
if text.lower() in w.game.app_title.lower() + w.game.app_name.lower():
@ -234,31 +251,151 @@ class GameList(QStackedWidget):
else:
w.setVisible(False)
def installed_only(self, i_o: bool):
def filter(self, filter="installed"):
for t in self.widgets.values():
for w in t:
w.setVisible(not (not self.core.is_installed(w.game.app_name) and i_o))
self.settings.setValue("installed_only", i_o)
if filter == "installed":
w.setVisible(self.core.is_installed(w.game.app_name))
elif filter == "offline":
if self.core.is_installed(w.game.app_name):
w.setVisible(w.igame.can_run_offline)
else:
w.setVisible(False)
elif filter == "32bit":
w.setVisible(w.game.app_name in self.bit32)
elif filter == "mac":
w.setVisible(w.game.app_name in self.mac_games)
else:
# All visible
w.setVisible(True)
self.settings.setValue("filter", filter)
def update_list(self, icon_view=True):
self.settings.setValue("icon_view", icon_view)
def update_list(self, app_name=None):
# self.settings.setValue("icon_view", icon_view)
if app_name:
if widgets := self.widgets.get(app_name):
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)
# from update
if self.core.is_installed(widgets[0].game.app_name) and isinstance(widgets[0], BaseInstalledWidget):
igame = self.core.get_installed_game(app_name)
for w in widgets:
w.igame = igame
w.update_available = self.core.get_asset(w.game.app_name, True).build_version != igame.version
widgets[0].info_label.setText("")
widgets[0].info_text = ""
# new installed
elif self.core.is_installed(widgets[0].game.app_name) and not isinstance(widgets[0], BaseInstalledWidget):
self.widgets.pop(widgets[0].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]))
# QWidget().setLayout(self.icon_layout)
icon_layout = FlowLayout()
# QWidget().setLayout(self.list_layout)
list_layout = QVBoxLayout()
igame = self.core.get_installed_game(app_name)
self.add_installed_widget(igame)
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title):
i_widget, l_widget = self.widgets[igame.app_name]
icon_layout.addWidget(i_widget)
list_layout.addWidget(l_widget)
self.uninstalled_names = []
installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
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_names:
self.uninstalled_names.append(game)
for game in self.uninstalled_names:
i_widget, list_widget = self.widgets[game.app_name]
icon_layout.addWidget(i_widget)
list_layout.addWidget(list_widget)
QWidget().setLayout(self.icon_layout)
QWidget().setLayout(self.list_layout)
self.icon_widget = QWidget()
self.list_widget = QWidget()
self.icon_widget.setLayout(icon_layout)
self.list_widget.setLayout(list_layout)
self.list_scrollarea.setWidget(QWidget())
self.icon_scrollarea.setWidget(QWidget())
self.icon_scrollarea.setWidget(self.icon_widget)
self.list_scrollarea.setWidget(self.list_widget)
self.icon_layout = icon_layout
self.list_layout = list_layout
self.icon_widget.setLayout(self.icon_layout)
self.list_widget.setLayout(self.list_layout)
self.update()
# uninstalled
elif not self.core.is_installed(widgets[0].game.app_name) and isinstance(widgets[0], BaseInstalledWidget):
self.list_layout.removeWidget(widgets[1])
self.icon_layout.removeWidget(widgets[0])
self.widgets.pop(app_name)
game = self.core.get_game(app_name, True)
self.add_uninstalled_widget(game)
self.icon_layout.addWidget(self.widgets[app_name][0])
self.list_layout.addWidget(self.widgets[app_name][1])
else:
installed_names = [i.app_name for i in self.core.get_installed_list()]
# get Uninstalled games
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_names:
uninstalled_games.append(game.app_name)
new_installed_games = list(set(installed_names) - 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_names]))
if (not new_uninstalled_games) and (not new_installed_games):
return
if new_installed_games:
for name in new_installed_games:
self.icon_layout.removeWidget(self.widgets[app_name][0])
self.list_layout.removeWidget(self.widgets[app_name][1])
self.widgets.pop(name)
igame = self.core.get_installed_game(name)
self.add_installed_widget(igame)
for name in new_uninstalled_games:
self.icon_layout.removeWidget(self.widgets[app_name][0])
self.list_layout.removeWidget(self.widgets[app_name][1])
self.widgets.pop(name)
game = self.core.get_game(name, True)
self.add_uninstalled_widget(game)
for igame in sorted(self.core.get_installed_list(), key=lambda x: x.title):
i_widget, list_widget = self.widgets[igame.app_name]
self.icon_layout.addWidget(i_widget)
self.list_layout.addWidget(list_widget)
# get Uninstalled games
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_names:
self.uninstalled_names.append(game)
for name in uninstalled_games:
i_widget, list_widget = self.widgets[name]
self.icon_layout.addWidget(i_widget)
self.list_layout.addWidget(list_widget)
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)
self.update()

View file

@ -147,4 +147,4 @@ class BaseInstalledWidget(QGroupBox):
print("Cancel Uninstall")
return
legendary_utils.uninstall(self.game.app_name, self.core, infos)
self.update_list.emit()
self.update_list.emit(self.game.app_name)

View file

@ -14,7 +14,7 @@ logger = getLogger("GameWidgetInstalled")
class GameWidgetInstalled(BaseInstalledWidget):
update_list = pyqtSignal()
update_list = pyqtSignal(str)
show_info = pyqtSignal(str)
update_game = pyqtSignal()

View file

@ -16,7 +16,7 @@ logger = getLogger("Import")
class ImportWidget(QWidget):
update_list = pyqtSignal()
update_list = pyqtSignal(str)
def __init__(self, core: LegendaryCore, parent):
super(ImportWidget, self).__init__(parent=parent)
@ -119,7 +119,7 @@ class ImportWidget(QWidget):
self.core.get_installed_game(app_name).title))
self.app_name_input.setText("")
self.update_list.emit()
self.update_list.emit(app_name)
else:
logger.warning("Failed to import" + app_name)
self.info_label.setText(self.tr("Failed to import {}").format(app_name))

View file

@ -1362,82 +1362,83 @@ Installationsgröße: {} GB</translation>
<context>
<name>RareSettings</name>
<message>
<location filename="../components/tabs/settings/rare.py" line="25"/>
<location filename="../components/tabs/settings/rare.py" line="27"/>
<source>Rare settings</source>
<translation type="obsolete">Rare Einstellungen</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="48"/>
<location filename="../components/tabs/settings/rare.py" line="51"/>
<source>Save</source>
<translation type="obsolete">Speichern</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="50"/>
<location filename="../components/tabs/settings/rare.py" line="53"/>
<source>Image Directory</source>
<translation type="obsolete">Ordner für Bilder</translation>
</message>
<message>
<location filename="../ui/components/tabs/settings/rare.py" line="119"/>
<source>Language</source>
<translation>Sprache</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="87"/>
<location filename="../components/tabs/settings/rare.py" line="102"/>
<source>Restart Application to activate changes</source>
<translation type="obsolete">Starte die App neu um die Änderungen zu aktivieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="33"/>
<location filename="../components/tabs/settings/rare.py" line="36"/>
<source>Confirm launch of game</source>
<translation type="obsolete">Start des Spiels bestätigen</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="30"/>
<location filename="../components/tabs/settings/rare.py" line="33"/>
<source>Exit to System Tray Icon</source>
<translation type="obsolete">Beim verlassen auf das System Tray Icon minimieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="30"/>
<location filename="../components/tabs/settings/rare.py" line="33"/>
<source>Hide to System Tray Icon</source>
<translation type="obsolete">In das System Tray Icon minimieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="35"/>
<location filename="../components/tabs/settings/rare.py" line="38"/>
<source>Auto sync with cloud</source>
<translation type="obsolete">Speicherstände automatisch mit der Cloud synchronisieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="35"/>
<location filename="../components/tabs/settings/rare.py" line="38"/>
<source>Sync with cloud</source>
<translation type="obsolete">Automatisch Synchronisieren</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="38"/>
<location filename="../components/tabs/settings/rare.py" line="41"/>
<source>Save size</source>
<translation type="obsolete">Größe Speichern</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="38"/>
<location filename="../components/tabs/settings/rare.py" line="41"/>
<source>Save size of window after restart</source>
<translation type="obsolete">Die Fenstergröße nach dem Beenden speichern</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="31"/>
<location filename="../components/tabs/settings/rare.py" line="34"/>
<source>Automatically update Games on startup</source>
<translation type="obsolete">Spiele automatisch updaten</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="31"/>
<location filename="../components/tabs/settings/rare.py" line="34"/>
<source>Auto updates</source>
<translation type="obsolete">Automatische Updates</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="36"/>
<location filename="../components/tabs/settings/rare.py" line="39"/>
<source>Show Notifications after Downloads</source>
<translation type="obsolete">Benachrichtigung nach Abschluss des Downloads anzeigen</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="36"/>
<location filename="../components/tabs/settings/rare.py" line="39"/>
<source>Show notification</source>
<translation type="obsolete">Benachrichtigung anzeigen</translation>
</message>
@ -1526,6 +1527,11 @@ Installationsgröße: {} GB</translation>
<source>Behavior</source>
<translation>Optionen</translation>
</message>
<message>
<location filename="../components/tabs/settings/rare.py" line="76"/>
<source>Open Log directory</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SyncSaves</name>
@ -1643,22 +1649,22 @@ Installationsgröße: {} GB</translation>
<translation>Pfad ändern</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="181"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="186"/>
<source>Uploading...</source>
<translation>Hochladen...</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="189"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="194"/>
<source>Upload finished</source>
<translation>Hochladen abgeschlossen</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="199"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="204"/>
<source>Downloading...</source>
<translation>Runterladen...</translation>
</message>
<message>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="205"/>
<location filename="../components/tabs/cloud_saves/sync_widget.py" line="210"/>
<source>Download finished</source>
<translation>Download abgeschlossen</translation>
</message>

View file

@ -61,6 +61,12 @@ def uninstall(app_name: str, core: LegendaryCore, options=None):
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"))
elif os.name == "nt":
if os.path.exists(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk")):
os.remove(os.path.expanduser(f"~/Desktop/{igame.title.split(':')[0]}.lnk"))
elif os.path.exists(os.path.expandvars(f"%appdata%/Microsoft/Windows/Start Menu/{igame.title.split(':')[0]}.lnk")):
os.remove(os.path.expandvars(f"%appdata%/Microsoft/Windows/Start Menu/{igame.title.split(':')[0]}.lnk"))
try:
# Remove DLC first so directory is empty when game uninstall runs
dlcs = core.get_dlc_for_game(app_name)

View file

@ -14,12 +14,13 @@ if os.name == "nt":
from win32com.client import Dispatch
from rare import lang_path, style_path
# Mac not supported
from custom_legendary.core import LegendaryCore
logger = getLogger("Utils")
s = QSettings("Rare", "Rare")
IMAGE_DIR = s.value("img_dir", os.path.expanduser("~/.cache/rare/images"), type=str)
logger.info("IMAGE DIRECTORY: " + IMAGE_DIR)
def download_images(signal: pyqtSignal, core: LegendaryCore):
@ -246,7 +247,8 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
"Terminal=false\n"
"StartupWMClass=rare-game\n"
)
os.chmod(os.path.expanduser(f"~/Desktop/{igame.title}.desktop"), 0o755)
desktop_file.close()
os.chmod(os.path.expanduser(f"{path}{igame.title}.desktop"), 0o755)
# Windows
elif os.name == "nt":
@ -261,7 +263,12 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
target = os.path.abspath(sys.argv[0])
# Name of link file
linkName = igame.title + '.lnk'
linkName = igame.title
for c in r'<>?":|\/*':
linkName.replace(c, "")
linkName = linkName.strip() + '.lnk'
# Path to location of link file
pathLink = os.path.join(target_folder, linkName)
@ -279,4 +286,5 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop"):
img.save(icon + ".ico")
logger.info("Create Icon")
shortcut.IconLocation = os.path.join(icon + ".ico")
shortcut.save()

View file

@ -4,4 +4,5 @@ PyQt5
QtAwesome
notify-py
psutil
pypresence
pypresence
pywin32; platform_system == "Windows"

View file

@ -1,3 +1,5 @@
import os
import setuptools
from rare import __version__ as version
@ -5,6 +7,21 @@ from rare import __version__ as version
with open("README.md", "r") as fh:
long_description = fh.read()
requirements = [
"requests<3.0",
"pillow",
"setuptools",
"wheel",
"PyQt5",
"QtAwesome",
"notify-py",
"psutil",
"pypresence"
]
if os.name == "nt":
requirements.append("pywin32")
setuptools.setup(
name="Rare",
version=version,
@ -25,15 +42,5 @@ setuptools.setup(
],
python_requires=">=3.8",
entry_points=dict(console_scripts=["rare=rare.__main__:main"]),
install_requires=[
"requests<3.0",
"pillow",
"setuptools",
"wheel",
"PyQt5",
"QtAwesome",
"notify-py",
"psutil",
"pypresence"
],
install_requires=requirements,
)