1
0
Fork 0
mirror of synced 2024-05-20 04:22:58 +12:00
Rare/rare/components/dialogs/login/browser_login.py
loathingKernel 88b6e91530 BrowserLogin: Add dedicated application mode to Rare for the webview login page
Add a sub-application to Rare to launch the webview for logging into EGS.
The sub-application operates similatly to the `laucher` sub-application and
it is autonomous. After a successful login in returns the exchange code
to the standard output to be parsed and used by the login dialog.

The reason this implementation was chosen is because when pywebview uses
pyqtwebengine as the GUI library, we cannot launch it through Rare as
it tries to spawn a QMainWindow inside an existing event loop, which is
prohibited by Qt.

Despite that, EGS login page doesn't work correctly with QtWebEngine,
so on linux default to the GTK backend for pywebview, and this change
helps keeping applications using different toolkits separate.

At this moment, spawning the sub-application blocks the execution of the
main application.

This change should make it easier to authenticate through Rare inside
a gamescope session, such as the steam deck.
2024-02-12 21:51:42 +02:00

106 lines
4 KiB
Python

import json
from logging import getLogger
from typing import Tuple
from PyQt5.QtCore import pyqtSignal, QUrl, QProcess, pyqtSlot
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QFrame, qApp, QFormLayout, QLineEdit
from legendary.utils import webview_login
from rare.lgndr.core import LegendaryCore
from rare.ui.components.dialogs.login.browser_login import Ui_BrowserLogin
from rare.utils.misc import icon
from rare.utils.paths import get_rare_executable
from rare.widgets.indicator_edit import IndicatorLineEdit, IndicatorReasonsCommon
logger = getLogger("BrowserLogin")
class BrowserLogin(QFrame):
success = pyqtSignal()
changed = pyqtSignal()
def __init__(self, core: LegendaryCore, parent=None):
super(BrowserLogin, self).__init__(parent=parent)
self.setFrameStyle(self.StyledPanel)
self.ui = Ui_BrowserLogin()
self.ui.setupUi(self)
self.core = core
self.login_url = self.core.egs.get_auth_url()
self.sid_edit = IndicatorLineEdit(
placeholder=self.tr("Insert authorizationCode here"), edit_func=self.text_changed, parent=self
)
self.sid_edit.line_edit.setEchoMode(QLineEdit.Password)
self.ui.link_text.setText(self.login_url)
self.ui.copy_button.setIcon(icon("mdi.content-copy", "fa.copy"))
self.ui.copy_button.clicked.connect(self.copy_link)
self.ui.form_layout.setWidget(
self.ui.form_layout.getWidgetPosition(self.ui.sid_label)[0],
QFormLayout.FieldRole, self.sid_edit
)
self.ui.open_button.clicked.connect(self.open_browser)
self.sid_edit.textChanged.connect(self.changed.emit)
@pyqtSlot()
def copy_link(self):
clipboard = qApp.clipboard()
clipboard.setText(self.login_url)
self.ui.status_label.setText(self.tr("Copied to clipboard"))
def is_valid(self):
return self.sid_edit.is_valid
@staticmethod
def text_changed(text) -> Tuple[bool, str, int]:
if text:
text = text.strip()
if text.startswith("{") and text.endswith("}"):
try:
text = json.loads(text).get("authorizationCode")
except json.JSONDecodeError:
return False, text, IndicatorReasonsCommon.WRONG_FORMAT
elif '"' in text:
text = text.strip('"')
return len(text) == 32, text, IndicatorReasonsCommon.WRONG_FORMAT
else:
return False, text, IndicatorReasonsCommon.VALID
def do_login(self):
self.ui.status_label.setText(self.tr("Logging in..."))
auth_code = self.sid_edit.text()
try:
if self.core.auth_code(auth_code):
logger.info("Successfully logged in as %s", self.core.lgd.userdata['displayName'])
self.success.emit()
else:
self.ui.status_label.setText(self.tr("Login failed."))
logger.warning("Failed to login through browser")
except Exception as e:
logger.warning(e)
@pyqtSlot()
def open_browser(self):
if not webview_login.webview_available:
logger.warning("You don't have webengine installed, you will need to manually copy the authorizationCode.")
QDesktopServices.openUrl(QUrl(self.login_url))
else:
cmd = get_rare_executable() + ["login", self.core.get_egl_version()]
proc = QProcess(self)
proc.start(cmd[0], cmd[1:])
proc.waitForFinished(-1)
out, err = (
proc.readAllStandardOutput().data().decode("utf-8", "ignore"),
proc.readAllStandardError().data().decode("utf-8", "ignore")
)
proc.deleteLater()
if out:
self.core.auth_ex_token(out)
logger.info("Successfully logged in as %s", {self.core.lgd.userdata['displayName']})
self.success.emit()
else:
logger.warning("Failed to login through browser.")