1
0
Fork 0
mirror of synced 2024-05-17 02:52:50 +12:00

Download Games, update List

This commit is contained in:
Dummerle 2021-02-17 17:46:03 +01:00
parent a566e836e5
commit 75b39cb189
8 changed files with 289 additions and 49 deletions

View file

@ -2,4 +2,4 @@
GUI for legendary. An Epic Games Launcher open source alternative
## This is a new version with better Styles. It has not many features, but i work on it.
It will take some time, because I want to reinmplement some features on a smoother way.
It will take some time, because I want to reimplement some features on a smoother way.

View file

@ -1,8 +1,10 @@
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QTabWidget, QTabBar, QWidget, QToolButton, QWidgetAction, QMenu
from Rare import style_path
from Rare.Components.Tabs.Account.AccountWidget import MiniWidget
from Rare.Components.Tabs.Games.Games import Games
from Rare.Components.Tabs.Downloads.DownloadTab import DownloadTab
from Rare.Components.Tabs.Games.GamesTab import Games
class TabWidget(QTabWidget):
@ -11,12 +13,12 @@ class TabWidget(QTabWidget):
self.setTabBar(TabBar(2))
self.game_list = Games(core)
self.addTab(self.game_list, self.tr("Games"))
self.downloads = QWidget()
self.addTab(self.downloads, "Downloads")
self.downloadTab = DownloadTab(core)
self.addTab(self.downloadTab, "Downloads")
self.downloadTab.finished.connect(self.game_list.game_list.update_list)
self.game_list.game_list.install_game.connect(lambda x: self.downloadTab.install_game(x))
# Space Tab
self.addTab(QWidget(), "")
self.setTabEnabled(2, False)
@ -35,9 +37,6 @@ class TabWidget(QTabWidget):
self.tabBar().setMinimumWidth(self.width())
super(TabWidget, self).resizeEvent(event)
def download(self):
self.downloads.download()
class TabBar(QTabBar):
def __init__(self, expanded):
@ -59,7 +58,7 @@ class TabButtonWidget(QToolButton):
super(TabButtonWidget, self).__init__()
self.setText("Icon")
self.setPopupMode(QToolButton.InstantPopup)
self.setIcon(QIcon(style_path+"/Icons/account.png"))
self.setIcon(QIcon(style_path + "/Icons/account.png"))
self.setToolTip("Account")
self.setMenu(QMenu())
action = QWidgetAction(self)

View file

@ -1,6 +1,152 @@
from PyQt5.QtWidgets import QWidget
import os
import subprocess
import time
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QMessageBox, QVBoxLayout, QLabel, QGridLayout, QProgressBar
from legendary.core import LegendaryCore
from Rare.utils.Dialogs.InstallDialog import InstallInfoDialog
logger = getLogger("Download")
class DownloadThread(QThread):
status = pyqtSignal(str)
def __init__(self, dlm, core: LegendaryCore, igame):
super(DownloadThread, self).__init__()
self.dlm = dlm
self.core = core
self.igame = igame
def run(self):
start_time = time.time()
try:
self.dlm.start()
self.dlm.join()
except:
logger.error(f"Installation failed after{time.time() - start_time:.02f} seconds.")
self.status.emit("error")
return
else:
self.status.emit("dl_finished")
end_t = time.time()
game = self.core.get_game(self.igame.app_name)
postinstall = self.core.install_game(self.igame)
if postinstall:
self._handle_postinstall(postinstall, self.igame)
dlcs = self.core.get_dlc_for_game(self.igame.app_name)
if dlcs:
print('The following DLCs are available for this game:')
for dlc in dlcs:
print(f' - {dlc.app_title} (App name: {dlc.app_name}, version: {dlc.app_version})')
print('Manually installing DLCs works the same; just use the DLC app name instead.')
# install_dlcs = QMessageBox.question(self, "", "Do you want to install the prequisites", QMessageBox.Yes|QMessageBox.No) == QMessageBox.Yes
# TODO
if game.supports_cloud_saves and not game.is_dlc:
logger.info('This game supports cloud saves, syncing is handled by the "sync-saves" command.')
logger.info(f'To download saves for this game run "legendary sync-saves {game.app_name}"')
self.status.emit("finish")
def _handle_postinstall(self, postinstall, igame):
print('This game lists the following prequisites to be installed:')
print(f'- {postinstall["name"]}: {" ".join((postinstall["path"], postinstall["args"]))}')
if os.name == 'nt':
if QMessageBox.question(self, "", "Do you want to install the prequisites",
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
self.core.prereq_installed(igame.app_name)
req_path, req_exec = os.path.split(postinstall['path'])
work_dir = os.path.join(igame.install_path, req_path)
fullpath = os.path.join(work_dir, req_exec)
subprocess.call([fullpath, postinstall['args']], cwd=work_dir)
else:
self.core.prereq_installed(self.igame.app_name)
else:
logger.info('Automatic installation not available on Linux.')
class DownloadTab(QWidget):
def __init__(self):
finished = pyqtSignal()
thread: QThread
def __init__(self, core: LegendaryCore):
super(DownloadTab, self).__init__()
self.core = core
self.layout = QVBoxLayout()
self.installing_game = QLabel("Installing Game: None")
self.dl_speed = QLabel("Download speed: 0MB/s")
self.cache_used = QLabel("Cache used: 0MB")
self.downloaded = QLabel("Downloaded: 0MB")
self.info_layout = QGridLayout()
self.info_layout.addWidget(self.installing_game, 0, 0)
self.info_layout.addWidget(self.dl_speed, 0, 1)
self.info_layout.addWidget(self.cache_used, 1,0)
self.info_layout.addWidget(self.downloaded, 1,1)
self.layout.addLayout(self.info_layout)
self.prog_bar = QProgressBar()
self.layout.addWidget(self.prog_bar)
self.layout.addWidget(QLabel("WARNING: This feature is not implemented. It is normal, if there is no progress. The progress is in console"))
self.installing_game_widget = QLabel("No active Download")
self.layout.addWidget(self.installing_game_widget)
self.layout.addStretch(1)
self.setLayout(self.layout)
def install_game(self, options: {}):
game = self.core.get_game(options["app_name"])
dlm, analysis, igame = self.core.prepare_download(
game=game,
base_path=options["options"]["path"],
max_workers=options["options"]["max_workers"])
if not analysis.dl_size:
QMessageBox.information(self, "Warning", "Download size is 0")
return
# Information
if not InstallInfoDialog(dl_size=analysis.dl_size, install_size=analysis.install_size).get_accept():
return
self.installing_game_widget.setText("")
self.installing_game.setText("Installing Game: "+ game.app_title)
res = self.core.check_installation_conditions(analysis=analysis, install=igame, game=game,
updating=self.core.is_installed(options["app_name"]),
)
if res.warnings:
for w in sorted(res.warnings):
logger.warning(w)
if res.failures:
for msg in sorted(res.failures):
logger.error(msg)
logger.error('Installation cannot proceed, exiting.')
QMessageBox.warning(self, "Installation failed", "Installation failed. See logs for more information")
return
self.thread = DownloadThread(dlm, self.core, igame)
self.thread.status.connect(self.status)
self.thread.start()
def status(self, text):
if text == "dl_finished":
pass
elif text == "finish":
QMessageBox.information(self, "Info", "Download finished")
self.finished.emit()
self.installing_game.setText("Installing Game: No running download")
elif text == "error":
QMessageBox.warning(self, "warn", "Download error")
def update_game(self, app_name: str):
print("Update ", app_name)

View file

@ -1,4 +1,4 @@
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import *
from legendary.core import LegendaryCore
@ -8,30 +8,45 @@ from Rare.utils.QtExtensions import FlowLayout
class GameList(QScrollArea):
install_game = pyqtSignal(dict)
def __init__(self, core: LegendaryCore):
super(GameList, self).__init__()
self.core = core
self.widget = QWidget()
self .setObjectName("list_widget")
self.widgets = []
self.setObjectName("list_widget")
self.setWidgetResizable(True)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.init_ui()
def init_ui(self):
self.widget = QWidget()
self.widgets=[]
self.layout = FlowLayout()
for i in self.core.get_installed_list():
# Installed Games
for game in self.core.get_installed_list():
# continue
self.layout.addWidget(GameWidgetInstalled(core, i))
widget = GameWidgetInstalled(self.core, game)
self.layout.addWidget(widget)
widget.update_list.connect(self.update_list)
uninstalled_games = []
installed = [i.app_name for i in core.get_installed_list()]
for game in sorted(core.get_game_list(), key=lambda x: x.app_title):
installed = []
installed = [i.app_name for i in self.core.get_installed_list()]
print(len(installed))
# get Uninstalled games
print(len(self.core.get_game_list()))
for game in sorted(self.core.get_game_list(), key=lambda x: x.app_title):
if not game.app_name in installed:
uninstalled_games.append(game)
self.widgets = []
for i in uninstalled_games:
widget = GameWidgetUninstalled(core, i)
# add uninstalled to gui
print(len(uninstalled_games))
for game in uninstalled_games:
widget = GameWidgetUninstalled(self.core, game)
widget.install_game.connect(lambda options: self.install_game.emit(options))
self.layout.addWidget(widget)
self.widgets.append(widget)
@ -48,4 +63,11 @@ class GameList(QScrollArea):
def installed_only(self, i_o: bool):
# TODO save state
for w in self.widgets:
w.setVisible(not i_o)
w.setVisible(not i_o)
def update_list(self):
print("Updating List")
self.setWidget(QWidget())
self.core.login()
self.init_ui()
self.update()

View file

@ -1,8 +1,8 @@
import os
from logging import getLogger
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QPixmap, QIcon, QMouseEvent
from PyQt5.QtCore import QEvent, pyqtSignal
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import *
from legendary.core import LegendaryCore
from legendary.models.game import InstalledGame
@ -16,6 +16,8 @@ logger = getLogger("GameWidgetInstalled")
class GameWidgetInstalled(QWidget):
update_list = pyqtSignal()
def __init__(self, core: LegendaryCore, game: InstalledGame):
super(GameWidgetInstalled, self).__init__()
self.setObjectName("game_widget_parent")
@ -26,7 +28,7 @@ class GameWidgetInstalled(QWidget):
self.update_available = self.core.get_asset(self.game.app_name, True).build_version != game.version
if self.update_available:
logger.info("Update available for game: "+ self.game.app_name)
logger.info("Update available for game: " + self.game.app_name)
if os.path.exists(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png"):
pixmap = QPixmap(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png")
@ -40,7 +42,6 @@ class GameWidgetInstalled(QWidget):
if pixmap:
w = 200
pixmap = pixmap.scaled(w, int(w * 4 / 3))
self.image = ClickableLabel()
self.image.setObjectName("game_widget")
self.image.setPixmap(pixmap)
@ -53,11 +54,13 @@ class GameWidgetInstalled(QWidget):
self.title_label.setObjectName("game_widget")
minilayout.addWidget(self.title_label)
# minilayout.addStretch(1)
self.menu = QPushButton(QIcon(style_path + "/Icons/menu.png"), "")
self.menu.setMenu(Menu())
self.menu.setObjectName("menu")
self.menu.setFixedWidth(10)
minilayout.addWidget(self.menu)
self.menu_btn = QPushButton(QIcon(style_path + "/Icons/menu.png"), "")
self.menu = Menu()
self.menu.action.connect(self.menu_action)
self.menu_btn.setMenu(self.menu)
self.menu_btn.setObjectName("menu")
self.menu_btn.setFixedWidth(10)
minilayout.addWidget(self.menu_btn)
minilayout.addStretch(1)
self.layout.addLayout(minilayout)
@ -93,14 +96,25 @@ class GameWidgetInstalled(QWidget):
self.info_label.setText("")
self.running = False
def menu_action(self, action: str):
if action == "uninstall":
if QMessageBox.question(self, "Uninstall", f"Do you want to uninstall {self.game.title}",
QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
logger.info("Uninstalling " + self.game.title)
self.core.uninstall_game(self.game)
self.update_list.emit()
class Menu(QMenu):
action = pyqtSignal(str)
def __init__(self):
super(Menu, self).__init__()
self.addAction("Game info", self.info)
self.addAction("Uninstall", self.uninstall)
self.addAction("Game info", lambda: self.action.emit("info"))
self.addAction("Uninstall", lambda: self.action.emit("uninstall"))
def info(self):
pass
def uninstall(self):
pass
pass

View file

@ -1,8 +1,10 @@
import os
from logging import getLogger
from multiprocessing import Queue as MPQueue
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QInputDialog
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
from legendary.core import LegendaryCore
from legendary.models.game import Game
@ -14,6 +16,8 @@ logger = getLogger("Uninstalled")
class GameWidgetUninstalled(QWidget):
install_game = pyqtSignal(dict)
def __init__(self, core: LegendaryCore, game: Game):
super(GameWidgetUninstalled, self).__init__()
self.layout = QVBoxLayout()
@ -57,7 +61,16 @@ class GameWidgetUninstalled(QWidget):
def install(self):
logger.info("Install " + self.game.app_title)
infos = InstallDialog().get_information()
if infos != 0:
path, max_workers = infos
print(path, max_workers)
self.install_game.emit({
"app_name": self.game.app_name,
"options": {
"path": path,
"max_workers": max_workers
}
})
# wait for update of legendary to get progress

View file

@ -1,4 +1,4 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QLabel
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QCheckBox, QLineEdit, QLabel, QPushButton, QStyle
from Rare.Components.Tabs.Games.GameList import GameList
@ -12,13 +12,13 @@ class Games(QWidget):
self.head_bar.setObjectName("head_bar")
self.game_list = GameList(core)
self.head_bar.search_bar.textChanged.connect(
lambda: self.game_list.filter(self.head_bar.search_bar.text()))
self.head_bar.installed_only.stateChanged.connect(lambda :
self.game_list.installed_only(self.head_bar.installed_only.isChecked()))
self.head_bar.installed_only.stateChanged.connect(lambda:
self.game_list.installed_only(
self.head_bar.installed_only.isChecked()))
self.head_bar.refresh_list.clicked.connect(lambda: self.game_list.update_list())
self.layout.addWidget(self.head_bar)
self.layout.addWidget(self.game_list)
# self.layout.addStretch(1)
@ -45,4 +45,8 @@ class GameListHeadBar(QWidget):
self.view = QCheckBox("Icon view")
self.layout.addWidget(self.view)
self.refresh_list = QPushButton()
self.refresh_list.setIcon(self.style().standardIcon(getattr(QStyle, "SP_BrowserReload"))) # Reload icon
self.layout.addWidget(self.refresh_list)
self.setLayout(self.layout)

View file

@ -6,27 +6,31 @@ from Rare.utils.QtExtensions import PathEdit
class InstallDialog(QDialog):
infos = 0
def __init__(self):
super(InstallDialog, self).__init__()
self.layout = QVBoxLayout()
self.form = QFormLayout()
default_path = os.path.expanduser("~/legendary")
#TODO read from config
# TODO read from config
self.install_path_field = PathEdit(text=default_path, type_of_file=QFileDialog.DirectoryOnly)
self.form.addRow(QLabel("Install directory"), self.install_path_field)
self.max_workes = QSpinBox()
self.form.addRow(QLabel("Max workers"), self.max_workes)
self.form.addRow(QLabel("Max workers (0: Default)"), self.max_workes)
self.layout.addLayout(self.form)
self.ok = QPushButton("Install")
self.ok_btn = QPushButton("Get Download infos")
self.ok_btn.clicked.connect(self.ok)
self.cancel = QPushButton("Cancel")
self.cancel.clicked.connect(lambda: self.close())
self.button_layout = QHBoxLayout()
self.button_layout.addStretch(1)
self.button_layout.addWidget(self.ok)
self.button_layout.addWidget(self.ok_btn)
self.button_layout.addWidget(self.cancel)
self.layout.addLayout(self.button_layout)
@ -35,4 +39,42 @@ class InstallDialog(QDialog):
def get_information(self):
self.exec_()
return self.install_path_field.text(), self.max_workes.value()
return self.infos
def ok(self):
self.infos = self.install_path_field.text(), self.max_workes.value()
self.close()
class InstallInfoDialog(QDialog):
accept: bool = False
def __init__(self, dl_size, install_size):
super(InstallInfoDialog, self).__init__()
self.layout = QVBoxLayout()
self.infos = QLabel(f"Download size: {(dl_size/1024**3):.02f}GB\nInstall size: {(install_size/1024**3):.02f}GB")
self.layout.addWidget(self.infos)
self.btn_layout = QHBoxLayout()
self.install_btn = QPushButton("Install")
self.install_btn.clicked.connect(self.install)
self.cancel_button = QPushButton("Cancel")
self.cancel_button.clicked.connect(self.cancel)
self.btn_layout.addStretch(1)
self.btn_layout.addWidget(self.install_btn)
self.btn_layout.addWidget(self.cancel_button)
self.layout.addLayout(self.btn_layout)
self.setLayout(self.layout)
def get_accept(self):
self.exec_()
return self.accept
def install(self):
self.accept = True
self.close()
def cancel(self):
self.accept = False
self.close()