The addition of download_only option broke the download queue due to different positional arguments and tuple indices. Since using indices is awkward, models to represent the download and each item in the queue itself were added.
395 lines
17 KiB
Python
395 lines
17 KiB
Python
import os
|
|
from logging import getLogger
|
|
|
|
import psutil
|
|
from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer
|
|
from PyQt5.QtGui import QPixmap
|
|
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
|
|
from rare.components.tabs.games.game_widgets.uninstalled_list_widget import ListWidgetUninstalled
|
|
from rare.utils.extra_widgets import FlowLayout
|
|
from rare.utils.models import InstallOptionsModel
|
|
from rare.utils.utils import download_image
|
|
|
|
logger = getLogger("Game list")
|
|
|
|
|
|
class GameList(QStackedWidget):
|
|
install_game = pyqtSignal(InstallOptionsModel)
|
|
show_game_info = pyqtSignal(str)
|
|
update_game = pyqtSignal()
|
|
game_exited = pyqtSignal(str)
|
|
game_started = pyqtSignal(str)
|
|
show_uninstalled_info = pyqtSignal(str)
|
|
|
|
running_games = []
|
|
active_game = ("", 0)
|
|
|
|
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)
|
|
self.procs = [(proc.name(), proc.pid) for proc in psutil.process_iter()]
|
|
self.init_ui(icon_view)
|
|
|
|
def init_ui(self, icon_view=True):
|
|
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)
|
|
|
|
self.list_scrollarea.setWidgetResizable(True)
|
|
self.list_scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
|
|
self.info_text = self.tr("Installed Games: {} Available Games: {}").format(
|
|
len(self.core.get_installed_list()),
|
|
len(self.core.get_game_list(update_assets=True)))
|
|
|
|
self.icon_parent_layout = QVBoxLayout()
|
|
self.icon_parent_layout.addWidget(QLabel(self.info_text))
|
|
|
|
self.icon_layout = FlowLayout()
|
|
self.list_layout = QVBoxLayout()
|
|
self.list_layout.addWidget(QLabel(self.info_text))
|
|
|
|
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:
|
|
icon_widget, list_widget = self.add_installed_widget(igame)
|
|
self.icon_layout.addWidget(icon_widget)
|
|
self.list_layout.addWidget(list_widget)
|
|
|
|
self.uninstalled_games = []
|
|
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)
|
|
|
|
# add uninstalled games
|
|
|
|
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.icon_parent_layout.addLayout(self.icon_layout)
|
|
self.icon_parent_layout.addStretch(1)
|
|
self.list_layout.addStretch(1)
|
|
self.icon_widget.setLayout(self.icon_parent_layout)
|
|
self.list_widget.setLayout(self.list_layout)
|
|
|
|
self.icon_scrollarea.setWidget(self.icon_widget)
|
|
self.list_scrollarea.setWidget(self.list_widget)
|
|
|
|
self.addWidget(self.icon_scrollarea)
|
|
self.addWidget(self.list_scrollarea)
|
|
|
|
if not icon_view:
|
|
self.setCurrentIndex(1)
|
|
|
|
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.show_uninstalled_info.connect(self.show_install_info)
|
|
|
|
list_widget = ListWidgetUninstalled(self.core, game, pixmap)
|
|
list_widget.show_uninstalled_info.connect(self.show_install_info)
|
|
|
|
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]):
|
|
self.timer.start(10000)
|
|
else:
|
|
self.finished(self.active_game[0])
|
|
del self.timer
|
|
|
|
def check_for_active_game(self, igame):
|
|
executable = igame.executable.split("/")[-1].split("\\")[-1]
|
|
if executable.endswith(".exe"):
|
|
executable = executable[:-4] # remove .exe
|
|
|
|
for i, pid in self.procs:
|
|
if executable in i:
|
|
# 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()):
|
|
return False, 0
|
|
return True, pid
|
|
return False, 0
|
|
|
|
def show_install_info(self, app_name):
|
|
self.show_uninstalled_info.emit(app_name)
|
|
|
|
def finished(self, app_name):
|
|
self.running_games.remove(app_name)
|
|
self.active_game = ("", 0)
|
|
self.widgets[app_name][0].info_text = ""
|
|
self.widgets[app_name][0].info_label.setText("")
|
|
self.widgets[app_name][1].launch_button.setDisabled(False)
|
|
self.widgets[app_name][1].launch_button.setText(self.tr("Launch"))
|
|
if self.widgets[app_name][0].game.supports_cloud_saves:
|
|
if not self.settings.value(f"{app_name}/auto_sync_cloud", True, bool) \
|
|
and not self.settings.value("auto_sync_cloud", True, bool):
|
|
logger.info("Auto saves disabled")
|
|
return
|
|
|
|
self.widgets[app_name][0].info_text = self.tr("Sync CLoud saves")
|
|
self.widgets[app_name][0].info_label.setText(self.tr("Sync CLoud saves"))
|
|
self.widgets[app_name][1].info_label.setText(self.tr("Sync CLoud saves"))
|
|
self.game_exited.emit(app_name)
|
|
|
|
def launch(self, app_name):
|
|
self.running_games.append(app_name)
|
|
self.game_started.emit(app_name)
|
|
self.widgets[app_name][0].info_text = self.tr("Game running")
|
|
self.widgets[app_name][0].info_label.setText(self.tr("Game running"))
|
|
self.widgets[app_name][1].launch_button.setDisabled(True)
|
|
self.widgets[app_name][1].launch_button.setText(self.tr("Game running"))
|
|
|
|
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():
|
|
w.setVisible(True)
|
|
else:
|
|
w.setVisible(False)
|
|
|
|
def filter(self, filter="installed"):
|
|
for t in self.widgets.values():
|
|
for w in t:
|
|
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, app_name=None):
|
|
# self.settings.setValue("icon_view", icon_view)
|
|
if app_name:
|
|
if widgets := self.widgets.get(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)
|
|
|
|
# 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)
|