diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml
index 5b55af69..78236951 100644
--- a/AppImageBuilder.yml
+++ b/AppImageBuilder.yml
@@ -22,27 +22,28 @@ AppDir:
version: 1.7.0
exec: usr/bin/python3
exec_args: $APPDIR/usr/src/__main__.py $@
- apt:d
- arch: amd64
- allow_unauthenticated: true
- sources:
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy main restricted
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates main restricted
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy universe
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates universe
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy multiverse
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates multiverse
- - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-backports main restricted
- universe multiverse
- - sourceline: deb http://security.ubuntu.com/ubuntu groovy-security main restricted
- - sourceline: deb http://security.ubuntu.com/ubuntu groovy-security universe
- - sourceline: deb http://security.ubuntu.com/ubuntu groovy-security multiverse
- include:
- - python3
- - python3-distutils
- - python3-pyqt5
- - python3-psutil
- - python3-requests
+ apt:
+ arch: amd64
+ allow_unauthenticated: true
+ sources:
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy main restricted
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates main restricted
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy universe
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates universe
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy multiverse
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-updates multiverse
+ - sourceline: deb http://de.archive.ubuntu.com/ubuntu/ groovy-backports main restricted
+ universe multiverse
+ - sourceline: deb http://security.ubuntu.com/ubuntu groovy-security main restricted
+ - sourceline: deb http://security.ubuntu.com/ubuntu groovy-security universe
+ - sourceline: deb http://security.ubuntu.com/ubuntu groovy-security multiverse
+
+ include:
+ - python3
+ - python3-distutils
+ - python3-pyqt5
+ - python3-psutil
+ - python3-requests
runtime:
env:
diff --git a/rare/components/tabs/settings/legendary.py b/rare/components/tabs/settings/legendary.py
index a9da54bc..dc3e4782 100644
--- a/rare/components/tabs/settings/legendary.py
+++ b/rare/components/tabs/settings/legendary.py
@@ -1,10 +1,15 @@
import re
+import webbrowser
from logging import getLogger
from typing import Tuple
-from PyQt5.QtWidgets import QSizePolicy, QWidget, QFileDialog, QMessageBox
+from PyQt5.QtCore import QThreadPool, QRunnable, QObject, pyqtSignal, QSize
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtWidgets import QSizePolicy, QWidget, QFileDialog, QMessageBox, QLabel, QPushButton, QHBoxLayout
+from qtawesome import icon
import rare.shared as shared
+from legendary.models.game import Game
from rare.ui.components.tabs.settings.legendary import Ui_LegendarySettings
from rare.utils.extra_widgets import PathEdit, IndicatorLineEdit
from rare.utils.utils import get_size
@@ -12,6 +17,102 @@ from rare.utils.utils import get_size
logger = getLogger("LegendarySettings")
+class Signals(QObject):
+ worker_finished = pyqtSignal(set, set, str)
+ connected = pyqtSignal(str)
+
+class UbiGetInfoWorker(QRunnable):
+ def __init__(self):
+ super(UbiGetInfoWorker, self).__init__()
+ self.signals = Signals()
+ self.setAutoDelete(True)
+
+ def run(self) -> None:
+ try:
+ external_auths = shared.core.egs.get_external_auths()
+ for ext_auth in external_auths:
+ if ext_auth['type'] != 'ubisoft':
+ continue
+ ubi_account_id = ext_auth['externalAuthId']
+ break
+ else:
+ self.signals.worker_finished.emit(set(), set(), "")
+ return
+
+ uplay_keys = shared.core.egs.store_get_uplay_codes()
+ key_list = uplay_keys['data']['PartnerIntegration']['accountUplayCodes']
+ redeemed = {k['gameId'] for k in key_list if k['redeemedOnUplay']}
+
+ entitlements = shared.core.egs.get_user_entitlements()
+ entitlements = {i['entitlementName'] for i in entitlements}
+ self.signals.worker_finished.emit(redeemed, entitlements, ubi_account_id)
+ except Exception as e:
+ logger.error(str(e))
+ self.signals.worker_finished.emit(set(), set(), "error")
+
+class UbiConnectWorker(QRunnable):
+ def __init__(self, ubi_account_id, partner_link_id):
+ super(UbiConnectWorker, self).__init__()
+ self.signals = Signals()
+ self.setAutoDelete(True)
+ self.ubi_account_id = ubi_account_id
+ self.partner_link_id = partner_link_id
+
+ def run(self) -> None:
+ try:
+ shared.core.egs.store_claim_uplay_code(self.ubi_account_id, self.partner_link_id)
+ shared.core.egs.store_redeem_uplay_codes(self.ubi_account_id)
+ except Exception as e:
+ self.signals.connected.emit(str(e))
+ return
+ else:
+ self.signals.connected.emit("")
+
+
+class UbiLinkWidget(QWidget):
+ def __init__(self, game: Game, ubi_account_id):
+ super(UbiLinkWidget, self).__init__()
+ self.setLayout(QHBoxLayout())
+ self.game = game
+ self.ubi_account_id = ubi_account_id
+
+ self.ok_indicator = QLabel()
+ self.ok_indicator.setVisible(False)
+ self.ok_indicator.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Preferred)
+ self.layout().addWidget(self.ok_indicator)
+
+ self.title_label = QLabel(game.app_title)
+ self.layout().addWidget(self.title_label)
+ if not shared.args.debug:
+ self.link_button = QPushButton(self.tr("Redeem to Ubisoft"))
+ self.layout().addWidget(self.link_button)
+ self.link_button.clicked.connect(self.activate)
+ else:
+ btn = QPushButton(self.tr("Test: error"))
+ self.layout().addWidget(btn)
+ btn.clicked.connect(lambda: self.worker_finished("Any Error"))
+
+ ok_button = QPushButton(self.tr("Test: Ok"))
+ self.layout().addWidget(ok_button)
+ ok_button.clicked.connect(lambda: self.worker_finished(""))
+
+ def activate(self):
+ if shared.args.debug:
+ self.worker_finished("Connection Error")
+ return
+ worker = UbiConnectWorker(self.ubi_account_id, self.game.partner_link_id)
+ worker.signals.worker_finished.connect(self.worker_finished)
+ QThreadPool.globalInstance().start(worker)
+
+ def worker_finished(self, error):
+ self.ok_indicator.setVisible(True)
+ if not error:
+ self.ok_indicator.setPixmap(icon("ei.ok-circle", color="green").pixmap(QSize(20, 20)))
+ else:
+ self.ok_indicator.setPixmap(icon("fa.info-circle", color="red").pixmap(QSize(20, 20)))
+ self.ok_indicator.setToolTip(error)
+
+
class LegendarySettings(QWidget, Ui_LegendarySettings):
def __init__(self, parent=None):
super(LegendarySettings, self).__init__(parent=parent)
@@ -59,6 +160,67 @@ class LegendarySettings(QWidget, Ui_LegendarySettings):
)
self.locale_layout.addWidget(self.locale_edit)
+ self.thread_pool = QThreadPool.globalInstance()
+ worker = UbiGetInfoWorker()
+ worker.signals.worker_finished.connect(self.show_ubi_games)
+ self.thread_pool.start(worker)
+
+ def show_ubi_games(self, redeemed: set, entitlements: set, ubi_account_id: str):
+ if not redeemed and ubi_account_id != "error":
+ logger.error('No linked ubisoft account found! Link your accounts via your browser and try again.')
+ self.ubisoft_gb.layout().addWidget(
+ QLabel(self.tr("Your account is not linked with Ubisoft. Please link your account first")))
+ open_browser_button = QPushButton(self.tr("Open link page"))
+ open_browser_button.clicked.connect(lambda: webbrowser.open("https://www.epicgames.com/id/link/ubisoft"))
+ self.ubisoft_gb.layout().addWidget(open_browser_button)
+ return
+ elif ubi_account_id == "error":
+ self.ubisoft_gb.layout().addWidget(QLabel(self.tr("An error occurred")))
+ return
+
+ games = self.core.get_game_list(False)
+ uplay_games = []
+ activated = 0
+ for game in games:
+ for dlc_data in game.metadata.get('dlcItemList', []):
+ if dlc_data['entitlementName'] not in entitlements:
+ continue
+
+ try:
+ app_name = dlc_data['releaseInfo'][0]['appId']
+ except (IndexError, KeyError):
+ app_name = 'unknown'
+
+ dlc_game = Game(app_name=app_name, app_title=dlc_data['title'], metadata=dlc_data)
+ if dlc_game.partner_link_type != 'ubisoft':
+ continue
+ if dlc_game.partner_link_id in redeemed:
+ continue
+ uplay_games.append(dlc_game)
+
+ if game.partner_link_type != "ubisoft":
+ continue
+ if game.partner_link_id in redeemed:
+ activated += 1
+ continue
+ uplay_games.append(game)
+
+ if not uplay_games:
+ if activated >= 1:
+ self.ubisoft_gb.layout().addWidget(QLabel(self.tr("All your Ubisoft games have already been activated")))
+ else:
+ self.ubisoft_gb.layout().addWidget(QLabel(self.tr("You don't own any Ubisoft games")))
+ if shared.args.debug:
+ widget = UbiLinkWidget(Game(app_name="Test", app_title="Test Game"), ubi_account_id)
+ self.ubisoft_gb.layout().addWidget(widget)
+ return
+ logger.info(f'Found {len(uplay_games)} game(s) to redeem')
+
+ for game in uplay_games:
+ widget = UbiLinkWidget(game, ubi_account_id)
+ self.ubisoft_gb.layout().addWidget(widget)
+
+
@staticmethod
def locale_edit_cb(text: str) -> Tuple[bool, str]:
if text:
diff --git a/rare/legendary b/rare/legendary
index ab7d209d..d371c0b3 160000
--- a/rare/legendary
+++ b/rare/legendary
@@ -1 +1 @@
-Subproject commit ab7d209d5318f862d20081724383f523b6de4c7b
+Subproject commit d371c0b3c48a422ff7e6c966af649a1502300c2d
diff --git a/rare/ui/components/tabs/settings/legendary.py b/rare/ui/components/tabs/settings/legendary.py
index 5a0d388e..30e57e98 100644
--- a/rare/ui/components/tabs/settings/legendary.py
+++ b/rare/ui/components/tabs/settings/legendary.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'rare/ui/components/tabs/settings/legendary.ui'
#
-# Created by: PyQt5 UI code generator 5.15.4
+# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
@@ -14,10 +14,39 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LegendarySettings(object):
def setupUi(self, LegendarySettings):
LegendarySettings.setObjectName("LegendarySettings")
- LegendarySettings.resize(552, 268)
+ LegendarySettings.resize(564, 374)
LegendarySettings.setWindowTitle("LegendarySettings")
- self.settings_layout = QtWidgets.QHBoxLayout(LegendarySettings)
- self.settings_layout.setObjectName("settings_layout")
+ self.gridLayout = QtWidgets.QGridLayout(LegendarySettings)
+ self.gridLayout.setObjectName("gridLayout")
+ self.right_layout = QtWidgets.QVBoxLayout()
+ self.right_layout.setObjectName("right_layout")
+ self.locale_group = QtWidgets.QGroupBox(LegendarySettings)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.locale_group.sizePolicy().hasHeightForWidth())
+ self.locale_group.setSizePolicy(sizePolicy)
+ self.locale_group.setObjectName("locale_group")
+ self.locale_layout = QtWidgets.QVBoxLayout(self.locale_group)
+ self.locale_layout.setObjectName("locale_layout")
+ self.right_layout.addWidget(self.locale_group)
+ self.cleanup_group = QtWidgets.QGroupBox(LegendarySettings)
+ self.cleanup_group.setObjectName("cleanup_group")
+ self.cleanup_layout = QtWidgets.QVBoxLayout(self.cleanup_group)
+ self.cleanup_layout.setObjectName("cleanup_layout")
+ self.clean_keep_manifests_button = QtWidgets.QPushButton(self.cleanup_group)
+ self.clean_keep_manifests_button.setObjectName("clean_keep_manifests_button")
+ self.cleanup_layout.addWidget(self.clean_keep_manifests_button)
+ self.clean_button = QtWidgets.QPushButton(self.cleanup_group)
+ self.clean_button.setObjectName("clean_button")
+ self.cleanup_layout.addWidget(self.clean_button)
+ self.right_layout.addWidget(self.cleanup_group)
+ self.gridLayout.addLayout(self.right_layout, 0, 1, 1, 1)
+ self.ubisoft_gb = QtWidgets.QGroupBox(LegendarySettings)
+ self.ubisoft_gb.setObjectName("ubisoft_gb")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.ubisoft_gb)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.gridLayout.addWidget(self.ubisoft_gb, 1, 0, 1, 2)
self.left_layout = QtWidgets.QVBoxLayout()
self.left_layout.setObjectName("left_layout")
self.install_dir_group = QtWidgets.QGroupBox(LegendarySettings)
@@ -96,41 +125,20 @@ class Ui_LegendarySettings(object):
self.disable_https_check.setObjectName("disable_https_check")
self.download_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.disable_https_check)
self.left_layout.addWidget(self.download_group)
+ self.gridLayout.addLayout(self.left_layout, 0, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.left_layout.addItem(spacerItem)
- self.settings_layout.addLayout(self.left_layout)
- self.right_layout = QtWidgets.QVBoxLayout()
- self.right_layout.setObjectName("right_layout")
- self.locale_group = QtWidgets.QGroupBox(LegendarySettings)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.locale_group.sizePolicy().hasHeightForWidth())
- self.locale_group.setSizePolicy(sizePolicy)
- self.locale_group.setObjectName("locale_group")
- self.locale_layout = QtWidgets.QVBoxLayout(self.locale_group)
- self.locale_layout.setObjectName("locale_layout")
- self.right_layout.addWidget(self.locale_group)
- self.cleanup_group = QtWidgets.QGroupBox(LegendarySettings)
- self.cleanup_group.setObjectName("cleanup_group")
- self.cleanup_layout = QtWidgets.QVBoxLayout(self.cleanup_group)
- self.cleanup_layout.setObjectName("cleanup_layout")
- self.clean_keep_manifests_button = QtWidgets.QPushButton(self.cleanup_group)
- self.clean_keep_manifests_button.setObjectName("clean_keep_manifests_button")
- self.cleanup_layout.addWidget(self.clean_keep_manifests_button)
- self.clean_button = QtWidgets.QPushButton(self.cleanup_group)
- self.clean_button.setObjectName("clean_button")
- self.cleanup_layout.addWidget(self.clean_button)
- self.right_layout.addWidget(self.cleanup_group)
- spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
- self.right_layout.addItem(spacerItem1)
- self.settings_layout.addLayout(self.right_layout)
+ self.gridLayout.addItem(spacerItem, 2, 0, 1, 2)
self.retranslateUi(LegendarySettings)
QtCore.QMetaObject.connectSlotsByName(LegendarySettings)
def retranslateUi(self, LegendarySettings):
_translate = QtCore.QCoreApplication.translate
+ self.locale_group.setTitle(_translate("LegendarySettings", "Locale"))
+ self.cleanup_group.setTitle(_translate("LegendarySettings", "Cleanup"))
+ self.clean_keep_manifests_button.setText(_translate("LegendarySettings", "Clean, but keep manifests"))
+ self.clean_button.setText(_translate("LegendarySettings", "Remove everything"))
+ self.ubisoft_gb.setTitle(_translate("LegendarySettings", "Link Ubisoft Games"))
self.install_dir_group.setTitle(_translate("LegendarySettings", "Default Installation Directory"))
self.download_group.setTitle(_translate("LegendarySettings", "Download Settings"))
self.max_workers_label.setText(_translate("LegendarySettings", "Max Workers"))
@@ -141,10 +149,6 @@ class Ui_LegendarySettings(object):
self.preferred_cdn_label.setText(_translate("LegendarySettings", "Preferred CDN"))
self.preferred_cdn_line.setPlaceholderText(_translate("LegendarySettings", "Default"))
self.disable_https_label.setText(_translate("LegendarySettings", "Disable HTTPS"))
- self.locale_group.setTitle(_translate("LegendarySettings", "Locale"))
- self.cleanup_group.setTitle(_translate("LegendarySettings", "Cleanup"))
- self.clean_keep_manifests_button.setText(_translate("LegendarySettings", "Clean, but keep manifests"))
- self.clean_button.setText(_translate("LegendarySettings", "Remove everything"))
if __name__ == "__main__":
diff --git a/rare/ui/components/tabs/settings/legendary.ui b/rare/ui/components/tabs/settings/legendary.ui
index d379885e..f604b949 100644
--- a/rare/ui/components/tabs/settings/legendary.ui
+++ b/rare/ui/components/tabs/settings/legendary.ui
@@ -6,15 +6,64 @@
0
0
- 552
- 268
+ 564
+ 374
LegendarySettings
-
- -
+
+
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Locale
+
+
+
+
+ -
+
+
+ Cleanup
+
+
+
-
+
+
+ Clean, but keep manifests
+
+
+
+ -
+
+
+ Remove everything
+
+
+
+
+
+
+
+
+ -
+
+
+ Link Ubisoft Games
+
+
+
+
+ -
-
@@ -160,74 +209,20 @@
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Locale
-
-
-
-
- -
-
-
- Cleanup
-
-
-
-
-
-
- Clean, but keep manifests
-
-
-
- -
-
-
- Remove everything
-
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
-
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+