1
0
Fork 0
mirror of synced 2024-06-17 10:04:43 +12:00
This commit is contained in:
Dummerle 2020-11-06 17:29:57 +01:00
parent 7f13810d92
commit 868ecf3a03
5 changed files with 59 additions and 509 deletions

488
Rare.py
View file

@ -1,488 +0,0 @@
#!/usr/bin/env python
import configparser
import json
import math
import os
import subprocess
import requests
from PIL import Image
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
print('Rare v1 starting up...')
class MainWindow(QScrollArea):
def __init__(self, allGames, installedGames):
super().__init__()
self.allGames = allGames
self.installedGames = installedGames
self.initUI()
def initUI(self):
self.widget = QWidget()
self.layout = QGridLayout()
self.gamesPerRow = int((ScreenInfo().screenWidth * .7) / 250)
j = 0
for game in self.installedGames:
self.addImage('images/' + game + '/FinalArt.png', self.layout,
math.floor(j / self.gamesPerRow), j % self.gamesPerRow)
j += 1
for game in self.allGames:
if not game["app_name"] in self.installedGames:
self.addImage('images/' + game["app_name"] + '/UninstalledArt.png', self.layout,
math.floor(j / self.gamesPerRow), j % self.gamesPerRow)
j += 1
self.widget.setLayout(self.layout)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.resize(250 * self.gamesPerRow, int(.7 * ScreenInfo().screenHeight))
center(self)
self.setWindowTitle('Rare v1')
self.setWidget(self.widget)
self.show()
def addImage(self, pathToImage, layout, x, y):
self.label = ExtendedQLabel()
self.label.setPixmap(QPixmap(pathToImage).scaled(240, 320))
self.label.clicked.connect(lambda: self.clickedGame(pathToImage.split('/', 2)[1]))
self.label.rightClicked.connect(lambda: self.gameSettings(pathToImage.split('/', 2)[1]))
layout.addWidget(self.label, x, y)
def clickedGame(self, game):
self.game = game
if self.game in self.installedGames:
self.launchGame(self.game)
else:
self.installGame(self.game)
def launchGame(self, game):
self.window = LaunchWindow(game)
self.window.show()
self.window.outputWindow.appendPlainText('> legendary launch ' + game + '\n')
self.window.reader.start('legendary', ['launch', game])
def installGame(self, game):
self.window = InstallWindow(game)
self.window.show()
def gameSettings(self, game):
self.window = GameSettingsWindow(game)
self.window.show()
class LaunchWindow(QMainWindow):
def __init__(self, game):
super().__init__()
self.game = game
self.initUI()
def initUI(self):
self.widget = QWidget()
self.layout = QVBoxLayout()
self.outputWindow = TerminalOutput()
self.reader = ProcessOutputReader()
self.reader.produce_output.connect(self.outputWindow.append_output)
self.layout.addWidget(self.outputWindow)
self.widget.setLayout(self.layout)
self.setWindowTitle('Launching ' + self.game)
self.setCentralWidget(self.widget)
self.resize(int(.3 * ScreenInfo().screenWidth), int(.3 * ScreenInfo().screenHeight))
center(self)
class InstallWindow(QMainWindow):
def __init__(self, game):
super().__init__()
self.game = game
self.initUI()
def initUI(self):
self.widget = QWidget()
self.layout = QVBoxLayout()
self.buttonLayout = QHBoxLayout()
self.installConfirmation = QLabel()
self.installConfirmation.setText('Do you really want to install ' + self.game + '?')
self.layout.addWidget(self.installConfirmation)
self.layout.setAlignment(Qt.AlignCenter)
self.yesButton = QPushButton()
self.yesButton.setText('Yes')
self.yesButton.clicked.connect(self.installGame)
self.noButton = QPushButton()
self.noButton.setText('No')
self.noButton.clicked.connect(self.close)
self.buttonLayout.addWidget(self.yesButton)
self.buttonLayout.addWidget(self.noButton)
self.layout.addLayout(self.buttonLayout)
self.widget.setLayout(self.layout)
self.setWindowTitle('Install ' + self.game + '?')
self.setCentralWidget(self.widget)
self.resize(int(.3 * ScreenInfo().screenWidth), int(.3 * ScreenInfo().screenHeight))
center(self)
def installGame(self):
self.outputWindow = TerminalOutput()
self.reader = ProcessOutputReader()
self.reader.produce_output.connect(self.outputWindow.append_output)
self.reader.start('legendary', ['-y', 'install', self.game])
self.progressWindowCancel = QPushButton()
self.progressWindowCancel.setText('Cancel')
self.progressWindowCancel.pressed.connect(lambda: (self.reader.close(), self.progressWindow.close()))
self.progressWindow = QMainWindow()
self.progressWindowWidget = QWidget()
self.progressWindowLayout = QVBoxLayout()
self.progressWindowLayout.addWidget(self.outputWindow)
self.progressWindowLayout.addWidget(self.progressWindowCancel)
self.progressWindowWidget.setLayout(self.progressWindowLayout)
self.progressWindow.setCentralWidget(self.progressWindowWidget)
self.progressWindow.setWindowTitle('Installing ' + self.game)
self.progressWindow.resize(int(.3 * ScreenInfo().screenWidth), int(.3 * ScreenInfo().screenHeight))
center(self.progressWindow)
self.progressWindow.show()
self.close()
class GameSettingsWindow(QMainWindow):
def __init__(self, game):
super().__init__()
self.game = game
self.initUI()
def initUI(self):
self.widget = QWidget()
self.layout = QGridLayout()
self.config = configparser.ConfigParser()
self.config.optionxform = lambda option: option
self.config.read(os.path.expanduser("~") + '/.config/legendary/config.ini')
self.wineSetting_label = QLabel()
self.wineSetting_label.setText('Wine executable: ')
self.wineSetting = QLineEdit()
self.wrapperSetting_label = QLabel()
self.wrapperSetting_label.setText('Wrapper: ')
self.wrapperSetting = QLineEdit()
self.noWineSetting_label = QLabel()
self.noWineSetting_label.setText('Don\'t use \'wine_executable\'')
self.noWineSetting = QCheckBox()
self.OKButton = QPushButton()
self.OKButton.setText('OK')
self.OKButton.clicked.connect(lambda: (self.writeSettings(self.config), self.close()))
self.cancelButton = QPushButton()
self.cancelButton.setText('Cancel')
self.cancelButton.clicked.connect(self.close)
self.applyButton = QPushButton()
self.applyButton.setText('Apply')
self.applyButton.clicked.connect(lambda: self.writeSettings(self.config))
self.buttonLayout = QHBoxLayout()
self.buttonLayout.addWidget(self.OKButton)
self.buttonLayout.addWidget(self.cancelButton)
self.buttonLayout.addWidget(self.applyButton)
if self.game not in self.config:
self.config[self.game] = {}
self.gameSettings = self.config[self.game]
self.wineSetting.setText(self.gameSettings.get('wine_executable', ''))
self.wineSetting.setPlaceholderText('Unset, [default] value: \'' +
self.config['default'].get('wine_executable', '') + '\'')
self.wrapperSetting.setText(self.gameSettings.get('wrapper', ''))
self.wrapperSetting.setPlaceholderText('Unset, not using default value '
'(not supported for \'wrapper\' and \'no_wine\'')
self.noWineSetting.setChecked(self.gameSettings.getboolean('no_wine', False))
self.layout.addWidget(self.wineSetting_label, 0, 0)
self.layout.addWidget(self.wineSetting, 0, 1)
self.layout.addWidget(self.wrapperSetting_label, 1, 0)
self.layout.addWidget(self.wrapperSetting, 1, 1)
self.layout.addWidget(self.noWineSetting_label, 2, 0)
self.layout.addWidget(self.noWineSetting, 2, 1)
self.layout.addLayout(self.buttonLayout, 3, 1)
self.widget.setLayout(self.layout)
self.setWindowTitle('Settings for ' + self.game)
self.setCentralWidget(self.widget)
self.resize(int(.4 * ScreenInfo().screenWidth), int(.3 * ScreenInfo().screenHeight))
center(self)
def writeSettings(self, configToSave):
if not self.wineSetting.text() == '':
self.config.set(self.game, 'wine_executable', self.wineSetting.text())
if not self.wrapperSetting.text() == '':
self.config.set(self.game, 'wrapper', self.wrapperSetting.text())
self.config.set(self.game, 'no_wine', str(self.noWineSetting.isChecked()))
with open(os.path.expanduser("~") + '/.config/legendary/config.ini', 'w') as configfile:
configToSave.write(configfile)
class LoginWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.widget = QWidget()
self.layout = QVBoxLayout()
self.loginMessage = QLabel()
self.loginMessage.setText('You\'re currently not logged in.'
' Click "Open login window" to open a browser window to login.\n'
'After you do, copy the "sid" value into the Text Input field below'
' and click "Submit".')
self.layout.addWidget(self.loginMessage)
self.loginOpenButton = QPushButton()
self.loginOpenButton.setText('Open login window')
self.layout.addWidget(self.loginOpenButton)
self.loginOpenButton.pressed.connect(lambda: QDesktopServices.openUrl(QUrl(
'https://www.epicgames.com/id/login?redirectUrl=https%3A%2F%2Fwww.epicgames.com%2Fid%2Fapi%2Fredirect')))
self.loginInputField = QLineEdit()
self.loginInputField.setPlaceholderText('Paste the "sid" value here')
self.layout.addWidget(self.loginInputField)
self.loginSubmitButton = QPushButton()
self.loginSubmitButton.setText('Submit')
self.loginSubmitButton.pressed.connect(lambda: self.tryLogin(self.loginInputField.text()))
self.layout.addWidget(self.loginSubmitButton)
self.widget.setLayout(self.layout)
self.setWindowTitle('Not logged in')
self.setCentralWidget(self.widget)
self.resize(int(.4 * ScreenInfo().screenWidth), int(.3 * ScreenInfo().screenHeight))
center(self)
self.show()
def tryLogin(self, sid):
legendaryProcess = subprocess.Popen('legendary auth --sid ' + sid, shell=True, stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
try:
legendaryProcess.wait(20)
except subprocess.TimeoutExpired:
pass
if os.path.isfile(os.path.expanduser("~") + '/.config/legendary/user.json'):
self.close()
# Run list-games once to get game list + info for newly logged in account
installedGames = subprocess.Popen('legendary list-games', shell=True,
stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
installedGames.wait()
startMainProcess(True)
else:
self.errorWindow = QMainWindow()
self.errorWindowWidget = QWidget()
self.errorWindowLayout = QVBoxLayout()
self.errorWindow.setCentralWidget(self.errorWindowWidget)
self.errorWindowWidget.setLayout(self.errorWindowLayout)
self.errorText = QLabel()
self.errorText.setText('There was an error logging in.\n'
'Please double-check you typed in the "sid" value.')
self.errorWindowLayout.addWidget(self.errorText)
self.errorButton = QPushButton()
self.errorButton.setText('OK')
self.errorButton.pressed.connect(self.errorWindow.close)
self.errorWindowLayout.addWidget(self.errorButton)
self.errorWindow.resize(int(.2 * ScreenInfo().screenWidth), int(.15 * ScreenInfo().screenHeight))
center(self.errorWindow)
self.errorWindow.show()
class ProcessOutputReader(QProcess):
produce_output = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__()
# merge stderr channel into stdout channel
self.setProcessChannelMode(QProcess.MergedChannels)
# prepare decoding process' output to Unicode
codec = QTextCodec.codecForLocale()
self._decoder_stdout = codec.makeDecoder()
self.readyReadStandardOutput.connect(self._ready_read_standard_output)
@pyqtSlot()
def _ready_read_standard_output(self):
raw_bytes = self.readAllStandardOutput()
text = self._decoder_stdout.toUnicode(raw_bytes)
self.produce_output.emit(text)
class TerminalOutput(QPlainTextEdit):
def __init__(self):
super().__init__()
self.setReadOnly(True)
self._cursor_output = self.textCursor()
@pyqtSlot(str)
def append_output(self, text):
self._cursor_output.insertText(text)
self.scroll_to_last_line()
def scroll_to_last_line(self):
cursor = self.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.movePosition(QTextCursor.Up if cursor.atBlockStart() else
QTextCursor.StartOfLine)
self.setTextCursor(cursor)
class ExtendedQLabel(QLabel):
clicked = pyqtSignal()
rightClicked = pyqtSignal()
def __init__(self, parent=None):
QLabel.__init__(self, parent)
def mousePressEvent(self, ev):
if ev.button() == Qt.LeftButton:
self.clicked.emit()
if ev.button() == Qt.RightButton:
self.rightClicked.emit()
class ScreenInfo():
def __init__(self):
self.screenWidth = QDesktopWidget().screenGeometry(-1).width()
self.screenHeight = QDesktopWidget().screenGeometry(-1).height()
def center(self):
qr = self.frameGeometry()
qr.moveCenter(QDesktopWidget().availableGeometry().center())
self.move(qr.topLeft())
def main():
# Check if Legendary is installed
try:
subprocess.Popen('legendary', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
except FileNotFoundError:
print('Legendary does not appear to be installed. Install it using\'pip install legendary-gl\'')
exit(1)
if os.path.isfile(os.path.expanduser("~") + '/.config/legendary/user.json'):
startMainProcess()
else:
notLoggedIn()
def startMainProcess(loggedIn=False):
print('Creating directorys')
try:
os.mkdir('images')
except OSError:
pass
print('Parsing game metadata')
allGames = []
for filename in os.listdir(os.path.expanduser("~") + '/.config/legendary/metadata/'):
with open(os.path.expanduser("~") + '/.config/legendary/metadata/' + filename, 'r') as f:
game = json.load(f)
allGames.append(game)
try:
os.mkdir('images/' + game["app_name"])
except OSError:
pass
print('Parsing installed games')
installedGames = []
for line in subprocess.Popen('legendary list-installed --csv | tail -n +2', shell=True,
stdout=subprocess.PIPE, universal_newlines=True).stdout:
installedGames.append(line.split(',', 1)[0])
print('Downloading and scaling cover images, this could take a little on first boot')
for game in allGames:
for image in game["metadata"]["keyImages"]:
# Check if the game's type is either the background or the logo, since that's the only two images we need
if image["type"] == "DieselGameBoxTall" or image["type"] == "DieselGameBoxLogo":
# If a cover was already downloaded, we don't have to download it again
if not os.path.isfile('images/' + game["app_name"] + '/' + image["type"] + '.png'):
print('Downloading ' + image["type"] + ' for ' + game["app_name"])
url = image["url"].replace('"', '')
with open('images/' + game["app_name"] + '/' + image["type"] + '.png', 'wb') as f:
f.write(requests.get(url).content)
# Since some games have the background and logo split into two files,
# we have to do some extra logic to combine those
if not os.path.isfile('images/' + game["app_name"] + '/FinalArt.png'):
try:
print('Scaling cover for ' + game["app_name"])
# First off, check if the extra logo file is even there
if os.path.isfile('images/' + game["app_name"] + '/DieselGameBoxLogo.png'):
# Load in the two images that need to be combined
bg = Image.open('images/' + game["app_name"] + '/DieselGameBoxTall.png')
# To make sure the background is actually horizontal (3/4) (looking at you Celeste), resize the image
bg = bg.resize((int(bg.size[1] * 3 / 4), bg.size[1]))
# Since the logo is transparent, we have to convert it to RGBA
logo = Image.open('images/' + game["app_name"] + '/DieselGameBoxLogo.png').convert('RGBA')
# Resize the logo to be ~ 3/4 as wide as the background (EGL does something like this)
wpercent = ((bg.size[0] * (3 / 4)) / float(logo.size[0]))
hsize = int((float(logo.size[1]) * float(wpercent)))
logo = logo.resize((int(bg.size[0] * (3 / 4)), hsize), Image.ANTIALIAS)
# Calculate where the image has to be placed
pasteX = int((bg.size[0] - logo.size[0]) / 2)
pasteY = int((bg.size[1] - logo.size[1]) / 2)
# And finally copy the background and paste in the image
finalArt = bg.copy()
finalArt.paste(logo, (pasteX, pasteY), logo)
# Write out the file
finalArt.save('images/' + game["app_name"] + '/FinalArt.png')
# And we have to do part of that again
# since the cover for an uninstalled game has the logo half-transparent
logoCopy = logo.copy()
logoCopy.putalpha(int(256 * 3 / 4))
logo.paste(logoCopy, logo)
uninstalledArt = bg.copy()
uninstalledArt.paste(logo, (pasteX, pasteY), logo)
uninstalledArt = uninstalledArt.convert('L')
uninstalledArt.save('images/' + game["app_name"] + '/UninstalledArt.png')
# And if the logo and background aren't split
else:
# We just open up the background and save that as the final image
finalArt = Image.open('images/' + game["app_name"] + '/DieselGameBoxTall.png')
finalArt.save('images/' + game["app_name"] + '/FinalArt.png')
# And same with the grayscale one
uninstalledArt = finalArt.convert('L')
uninstalledArt.save('images/' + game["app_name"] + '/UninstalledArt.png')
except:
pass
# If the user had to login first, the QApplication will already be running, so we don't have to start it again
if not loggedIn:
# Start GUI stuff
app = QApplication([])
mainWindow = MainWindow(allGames, installedGames)
app.exec_()
else:
mainWindow = MainWindow(allGames, installedGames)
mainWindow.show()
def notLoggedIn():
app = QApplication([])
window = LoginWindow()
app.exec_()
if __name__ == '__main__':
main()

View file

@ -1,11 +1,35 @@
import subprocess
from logging import getLogger
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QStyle
from Rare.Dialogs import InstallDialog
from Rare.utils import legendaryUtils
logger = getLogger("Game")
class Thread(QThread):
signal = pyqtSignal()
def __init__(self, proc):
super(Thread, self).__init__()
self.proc: subprocess.Popen = proc
def run(self):
self.sleep(3)
logger.info("Running ")
while True:
if not self.proc.poll():
self.sleep(3)
else:
self.signal.emit()
self.quit()
logger.info("Kill")
break
class GameWidget(QWidget):
proc: subprocess.Popen
@ -53,14 +77,25 @@ class GameWidget(QWidget):
def launch(self):
if not self.game_running:
print(f"launch {self.title}")
self.launch_button.setText("Kill")
self.proc = legendaryUtils.launch_game(self.app_name)
self.game_running = True
logger.info(f"launching {self.title}")
resp = legendaryUtils.launch_game(self.app_name)
if resp != 1:
self.proc = resp
self.thread = Thread(self.proc)
self.thread.signal.connect(self.kill)
self.thread.start()
self.launch_button.setText("Kill")
self.game_running = True
else:
logger.warning("Error")
else:
self.proc.kill()
self.launch_button.setText("Launch")
self.game_running = False
self.kill()
def kill(self):
self.proc.kill()
self.launch_button.setText("Launch3")
self.game_running = False
def get_rating(self) -> str:
return "gold" # TODO
@ -100,11 +135,11 @@ class UninstalledGameWidget(QWidget):
self.setLayout(self.layout)
def install(self):
print("install " + self.title)
logger.info("install " + self.title)
dia = InstallDialog(self.game)
data = dia.get_data()
if data != 0:
path = data.get("install_path")
# TODO
print(f"install {self.app_name} in path {path}")
logger.info(f"install {self.app_name} in path {path}")
legendaryUtils.install(self.app_name)

View file

@ -13,6 +13,7 @@ from Rare.utils import legendaryUtils
logging.basicConfig(
format='[%(name)s] %(levelname)s: %(message)s',
level=logging.INFO
)
logger = logging.getLogger("Rare")
@ -50,10 +51,13 @@ class TabWidget(QTabWidget):
def main():
app = QApplication(sys.argv)
if os.path.isfile(os.path.expanduser("~") + '/.config/legendary/user.json'):
logger.info("Launching Rare")
download_images()
if legendaryUtils.core.login():
logger.info("Login credentials found")
else:
logger.info("No login data found")
dia = LoginDialog()
code = dia.get_login()
if code == 1:
@ -62,7 +66,7 @@ def main():
exit(0)
elif code == 0:
logger.info("Login successfully")
download_images()
window = MainWindow()
app.exec_()
@ -89,7 +93,7 @@ def download_images():
if not os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/FinalArt.png"):
logger.info("Scaling cover for " + game.app_name)
if os.path.isfile(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo"):
bg: Image.Image = Image.open()
bg: Image.Image = Image.open(f"{IMAGE_DIR}/{game.app_name}/DieselGameBoxLogo")
bg = bg.resize((int(bg.size[1] * 3 / 4), bg.size[1]))
logo = Image.open('images/' + game["app_name"] + '/DieselGameBoxLogo.png').convert('RGBA')
wpercent = ((bg.size[0] * (3 / 4)) / float(logo.size[0]))

View file

@ -25,7 +25,7 @@ class Settings(QWidget):
self.layout.addWidget(self.logout_button)
self.info_label = QLabel("<h2>Credits</h2>")
self.infotext = QLabel("""Developers : Dummerle, CommandMC\nLicence: GPL v3""")
self.infotext = QLabel("""Developers : Dummerle, CommandMC\nLegendary Dev: Derrod\nLicence: GPL v3""")
self.layout.addWidget(self.info_label)
self.layout.addWidget(self.infotext)

View file

@ -55,10 +55,13 @@ def launch_game(app_name: str, offline: bool = False, skip_version_check: bool =
no_wine: bool = os.name == "nt", extra: [] = None):
game = core.get_installed_game(app_name)
if not game:
print("Game not found")
return 1
if game.is_dlc:
print("Game is dlc")
return 1
if not os.path.exists(game.install_path):
print("Game doesn't exist")
return 1
if not offline:
@ -84,11 +87,6 @@ def launch_game(app_name: str, offline: bool = False, skip_version_check: bool =
return subprocess.Popen(params, cwd=cwd, env=env)
def auth(sid: str):
# cli.auth(Namespace(session_id=sid))
pass
def auth_import(lutris: bool = False, wine_prefix: str = None) -> bool:
'''
import Data from existing Egl installation
@ -159,5 +157,6 @@ def login(sid):
else:
return False
def get_name():
return core.lgd.userdata["displayName"]
return core.lgd.userdata["displayName"]