1
0
Fork 0
mirror of synced 2024-06-17 01:54:46 +12:00

Merge branch 'shop' into update_shop

This commit is contained in:
Dummerle 2021-08-22 22:43:08 +02:00 committed by GitHub
commit c212904cc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 2137 additions and 313 deletions

View file

@ -144,3 +144,39 @@ class VerifyResult(Enum):
HASH_MISMATCH = 1
FILE_MISSING = 2
OTHER_ERROR = 3
x = {'title': 'Frostpunk',
'id': 'b43c1e1e0ca14b6784b323c59c751136', 'namespace': 'd5241c76f178492ea1540fce45616757',
'description': 'Frostpunk', 'effectiveDate': '2099-01-01T00:00:00.000Z', 'offerType': 'OTHERS', 'expiryDate': None,
'status': 'ACTIVE', 'isCodeRedemptionOnly': True, 'keyImages': [{'type': 'VaultClosed',
'url': 'https://cdn1.epicgames.com/d5241c76f178492ea1540fce45616757/offer/EpicVault_Clean_OPEN_V10_LightsON-1920x1080-75e6d0636a6083944570a1c6f94ead4f.png'},
{'type': 'DieselStoreFrontWide',
'url': 'https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_Frostpunk_wide_2560x1440-ef2f4d458120af0839dde35b1a022828'},
{'type': 'DieselStoreFrontTall',
'url': 'https://cdn1.epicgames.com/salesEvent/salesEvent/EGS_Frostpunk_Tall_1200x1600-c71dc27cfe505c6c662c49011b36a0c5'}],
'seller': {'id': 'o-ufmrk5furrrxgsp5tdngefzt5rxdcn', 'name': 'Epic Dev Test Account'}, 'productSlug': 'frostpunk',
'urlSlug': 'free-games-06', 'url': None,
'items': [{'id': '8341d7c7e4534db7848cc428aa4cbe5a', 'namespace': 'd5241c76f178492ea1540fce45616757'}],
'customAttributes': [{'key': 'com.epicgames.app.freegames.vault.close', 'value': '[]'},
{'key': 'com.epicgames.app.blacklist', 'value': '[]'},
{'key': 'com.epicgames.app.freegames.vault.slug',
'value': 'news/the-epic-mega-sale-returns-for-2021'},
{'key': 'publisherName', 'value': '11 bit studios'}, {'key': 'dupe', 'value': '[]'},
{'key': 'com.epicgames.app.freegames.vault.open', 'value': '[]'},
{'key': 'developerName', 'value': '11 bit studios'},
{'key': 'com.epicgames.app.productSlug', 'value': 'frostpunk'}],
'categories': [{'path': 'freegames/vaulted'}, {'path': 'freegames'}, {'path': 'games'}, {'path': 'applications'}],
'tags': [], 'price': {'totalPrice': {'discountPrice': 0, 'originalPrice': 0, 'voucherDiscount': 0, 'discount': 0,
'currencyCode': 'USD', 'currencyInfo': {'decimals': 2},
'fmtPrice': {'originalPrice': '0', 'discountPrice': '0',
'intermediatePrice': '0'}},
'lineOffers': [{'appliedRules': []}]}, 'promotions': {'promotionalOffers': [],
'upcomingPromotionalOffers': [{
'promotionalOffers': [
{
'startDate': '2021-06-03T15:00:00.000Z',
'endDate': '2021-06-10T15:00:00.000Z',
'discountSetting': {
'discountType': 'PERCENTAGE',
'discountPercentage': 0}}]}]}}

View file

@ -12,6 +12,7 @@ elif os.name == "nt":
cache_dir = os.path.expandvars("%APPDATA%/rare/cache")
else:
cache_dir = os.path.expanduser("~/.cache/rare/")
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
@ -26,3 +27,4 @@ if not os.path.exists(data_dir):
os.makedirs(data_dir)
image_dir = os.path.join(data_dir, "images")

View file

@ -3,15 +3,17 @@
import os
from argparse import ArgumentParser
from rare import __version__
from rare import __version__, data_dir
from rare.utils import singleton, utils
def main():
# CLI Options
parser = ArgumentParser()
parser.add_argument("-V", "--version", action="store_true", help="Shows version and exits")
parser.add_argument("-S", "--silent", action="store_true",
help="Launch Rare in background. Open it from System Tray Icon")
parser.add_argument("--debug", action="store_true", help="Launch in debug mode")
parser.add_argument("--offline", action="store_true", help="Launch Rare in offline mode")
parser.add_argument("--desktop-shortcut", action="store_true", dest="desktop_shortcut",
@ -36,12 +38,12 @@ def main():
print(__version__)
exit(0)
try:
# this object only allows one instance pre machine
# this object only allows one instance per machine
me = singleton.SingleInstance()
except singleton.SingleInstanceException:
print("Rare is already running")
with open(os.path.expanduser("~/.cache/rare/lockfile"), "w") as file:
with open(os.path.join(data_dir, "lockfile"), "w") as file:
if args.subparser == "launch":
file.write("launch " + args.app_name)
else:
@ -53,6 +55,8 @@ def main():
if args.subparser == "launch":
args.silent = True
# start app
# Import start now, to not import everything
from rare.app import start
start(args)

View file

@ -10,21 +10,24 @@ from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QStyleFactory
from custom_legendary.core import LegendaryCore
from rare import languages_path, resources_path
from rare.components.dialogs.launch_dialog import LaunchDialog
from rare.components.main_window import MainWindow
from rare.components.tray_icon import TrayIcon
from rare.utils.utils import get_lang, load_color_scheme
start_time = time.strftime('%y-%m-%d--%H-%M') # year-month-day-hour-minute
file_name = os.path.expanduser(f"~/.cache/rare/logs/Rare_{start_time}.log")
file_name = os.path.join(cache_dir, f"logs/Rare_{start_time}.log")
if not os.path.exists(os.path.dirname(file_name)):
os.makedirs(os.path.dirname(file_name))
logging.basicConfig(
format='[%(name)s] %(levelname)s: %(message)s',
level=logging.INFO,
filename=file_name,
)
if "--debug" in sys.argv:
logging.basicConfig(format='[%(name)s] %(levelname)s: %(message)s', level=logging.INFO)
else:
logging.basicConfig(
format='[%(name)s] %(levelname)s: %(message)s',
level=logging.INFO,
filename=file_name,
)
logger = logging.getLogger("Rare")

View file

@ -7,6 +7,7 @@ from requests.exceptions import ConnectionError
from custom_legendary.core import LegendaryCore
from rare import image_dir
from rare.components.dialogs.login import LoginDialog
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
from rare.utils.utils import download_images
@ -26,6 +27,7 @@ class ImageThread(QThread):
self.download_progess.emit(100)
class LaunchDialog(QDialog, Ui_LaunchDialog):
quit_app = pyqtSignal(int)
start_app = pyqtSignal(bool)
@ -66,6 +68,7 @@ class LaunchDialog(QDialog, Ui_LaunchDialog):
if not os.path.exists(image_dir):
os.makedirs(image_dir)
if not self.offline:
self.image_info.setText(self.tr("Downloading Images"))
self.image_thread = ImageThread(self.core, self)

View file

@ -6,6 +6,7 @@ from PyQt5.QtGui import QCloseEvent
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QApplication
from custom_legendary.core import LegendaryCore
from rare import data_dir
from rare.components.tab_widget import TabWidget
from rare.utils.rpc import DiscordRPC
@ -59,7 +60,7 @@ class MainWindow(QMainWindow):
self.timer.start(1000)
def timer_finished(self):
file_path = os.path.expanduser("~/.cache/rare/lockfile")
file_path = os.path.join(data_dir, "lockfile")
if os.path.exists(file_path):
file = open(file_path, "r")
action = file.read()

View file

@ -1,5 +1,3 @@
import webbrowser
from PyQt5.QtCore import QSize, pyqtSignal
from PyQt5.QtWidgets import QMenu, QTabWidget, QWidget, QWidgetAction
from qtawesome import icon
@ -13,6 +11,7 @@ from rare.components.tabs.cloud_saves import SyncSaves
from rare.components.tabs.downloads import DownloadTab
from rare.components.tabs.games import GameTab
from rare.components.tabs.settings import SettingsTab
from rare.components.tabs.shop import Shop
from rare.utils import legendary_utils
from rare.utils.models import InstallQueueItemModel, InstallOptionsModel
@ -23,32 +22,26 @@ class TabWidget(QTabWidget):
def __init__(self, core: LegendaryCore, parent, offline):
super(TabWidget, self).__init__(parent=parent)
disabled_tab = 3 if not offline else 1
disabled_tab = 4 if not offline else 1
self.core = core
self.setTabBar(TabBar(disabled_tab))
# Generate Tabs
self.games_tab = GameTab(core, self, offline)
self.addTab(self.games_tab, self.tr("Games"))
if not offline:
updates = self.games_tab.default_widget.game_list.updates
self.downloadTab = DownloadTab(core, updates, self)
self.addTab(self.downloadTab, "Downloads" + (" (" + str(len(updates)) + ")" if len(updates) != 0 else ""))
self.cloud_saves = SyncSaves(core, self)
self.addTab(self.cloud_saves, "Cloud Saves")
self.store = Shop(self.core)
self.addTab(self.store, self.tr("Store (Beta)"))
self.settings = SettingsTab(core, self)
# Space Tab
self.addTab(QWidget(), "")
self.setTabEnabled(disabled_tab, False)
# Buttons
store_button = TabButtonWidget(core, 'fa.shopping-cart', 'Epic Games Store')
store_button.pressed.connect(lambda: webbrowser.open("https://www.epicgames.com/store"))
self.tabBar().setTabButton(disabled_tab, self.tabBar().RightSide, store_button)
# Button
self.account = QWidget()
self.addTab(self.account, "")
self.setTabEnabled(disabled_tab + 1, False)
@ -105,11 +98,17 @@ class TabWidget(QTabWidget):
self.games_tab.default_widget.game_list.game_exited.connect(self.game_finished)
# Open game list on click on Games tab button
self.tabBarClicked.connect(lambda x: self.games_tab.layout.setCurrentIndex(0) if x == 0 else None)
self.tabBarClicked.connect(self.mouse_clicked)
self.setIconSize(QSize(25, 25))
def mouse_clicked(self, tab_num):
if tab_num == 0:
self.games_tab.layout.setCurrentIndex(0)
if tab_num == 3:
self.store.load()
# TODO; maybe pass InstallOptionsModel only, not split arguments
def install_game(self, options: InstallOptionsModel, update=False, silent=False):
install_dialog = InstallDialog(self.core,
InstallQueueItemModel(options=options),
update=update, silent=silent, parent=self)
@ -155,8 +154,7 @@ class TabWidget(QTabWidget):
self.setTabText(1, "Downloads" + ((" (" + str(downloads) + ")") if downloads != 0 else ""))
self.downloadTab.update_text.setVisible(len(self.downloadTab.update_widgets) == 0)
# Update gamelist and set text of Downlaods to "Downloads"
# Update gamelist and set text of Downloads to "Downloads"
def dl_finished(self, update_list):
if update_list[0]:
self.games_tab.default_widget.game_list.update_list(update_list[1])

View file

@ -8,6 +8,7 @@ from qtawesome import icon
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game, InstalledGame
from rare import data_dir
from rare.components.tabs.games.game_info.dlcs import DlcTab
from rare.components.tabs.games.game_info.game_settings import GameSettings
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
@ -72,6 +73,18 @@ class GameInfo(QWidget, Ui_GameInfo):
self.setupUi(self)
self.core = core
self.ratings = {"platinum": self.tr("Platinum"),
"gold": self.tr("Gold"),
"silver": self.tr("Silver"),
"bronze": self.tr("Bronze"),
"fail": self.tr("Could not get grade"),
"pending": self.tr("Not enough reports")}
if os.path.exists(p := os.path.join(data_dir, "game_list.json")):
self.grade_table = json.load(open(p))
else:
self.grade_table = {}
if platform.system() == "Windows":
self.lbl_grade.setVisible(False)
self.grade.setVisible(False)

View file

@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QGroupBox, QHBoxLayout, QVBoxLayout, QScrollArea, QL
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare import data_dir
from rare.utils.utils import download_image
@ -82,7 +83,7 @@ class DLCWidget(QGroupBox):
super(DLCWidget, self).__init__()
self.main_layout = QHBoxLayout()
self.dlc = dlc
IMAGE_DIR = QSettings().value("img_dir", os.path.expanduser("~/.cache/rare/images"))
IMAGE_DIR = QSettings().value("img_dir", os.path.join(data_dir, "images"))
if installed:
if os.path.exists(os.path.join(IMAGE_DIR, dlc.app_name, "FinalArt.png")):

View file

@ -9,6 +9,7 @@ from qtawesome import icon
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare import data_dir
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
from rare.utils.extra_widgets import SideTabBar
from rare.utils.json_formatter import QJsonModel
@ -66,6 +67,7 @@ class UninstalledInfo(QWidget, Ui_GameInfo):
self.steam_worker.rating_signal.connect(self.grade.setText)
if platform.system() == "Windows":
self.lbl_grade.setVisible(False)
self.grade.setVisible(False)

View file

@ -5,6 +5,7 @@ from PyQt5.QtCore import Qt, pyqtSignal, QSettings, QTimer
from PyQt5.QtWidgets import QScrollArea, QWidget, QLabel, QVBoxLayout, QStackedWidget
from custom_legendary.core import LegendaryCore
from rare import data_dir
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

View file

@ -7,6 +7,7 @@ from PyQt5.QtWidgets import QGroupBox, QMessageBox, QAction
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame
from rare import cache_dir
from rare.components.dialogs.uninstall_dialog import UninstallDialog
from rare.components.extra.Console import ConsoleWindow
from rare.utils import legendary_utils
@ -150,8 +151,10 @@ class BaseInstalledWidget(QGroupBox):
def stderr(self):
stderr = bytes(self.proc.readAllStandardError()).decode("utf-8", errors="ignore")
print(stderr)
logger.error(stderr)
# QMessageBox.warning(self, "Warning", stderr + "\nSee ~/.cache/rare/logs/")
def finished(self, exit_code):
logger.info("Game exited with exit code: " + str(exit_code))
self.finish_signal.emit(self.game.app_name)

View file

@ -8,7 +8,6 @@ from qtawesome import icon
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import InstalledGame
from rare.components.tabs.games.game_widgets.base_installed_widget import BaseInstalledWidget
from rare.utils.extra_widgets import ClickableLabel
logger = getLogger("GameWidgetInstalled")
@ -36,7 +35,7 @@ class GameWidgetInstalled(BaseInstalledWidget):
if self.pixmap:
w = 200
self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3), transformMode=Qt.SmoothTransformation)
self.image = ClickableLabel()
self.image = QLabel()
self.image.setObjectName("game_widget")
self.image.setPixmap(self.pixmap)
self.layout.addWidget(self.image)

View file

@ -5,7 +5,6 @@ from PyQt5.QtWidgets import QVBoxLayout, QLabel
from custom_legendary.core import LegendaryCore
from custom_legendary.models.game import Game
from rare.components.tabs.games.game_widgets.base_uninstalled_widget import BaseUninstalledWidget
from rare.utils.extra_widgets import ClickableLabel
logger = getLogger("Uninstalled")
@ -19,7 +18,7 @@ class IconWidgetUninstalled(BaseUninstalledWidget):
if self.pixmap:
w = 200
self.pixmap = self.pixmap.scaled(w, int(w * 4 / 3))
self.image = ClickableLabel()
self.image = QLabel()
self.image.setPixmap(self.pixmap)
self.layout.addWidget(self.image)

View file

@ -6,12 +6,12 @@ import sys
from logging import getLogger
from PyQt5.QtCore import QSettings, Qt
from PyQt5.QtWidgets import QFileDialog, QWidget
from PyQt5.QtWidgets import QWidget
from rare import cache_dir, data_dir
from rare.components.tabs.settings.rpc_settings import RPCSettings
from rare.ui.components.tabs.settings.rare import Ui_RareSettings
from rare.utils import utils
from rare.utils.extra_widgets import PathEdit
from rare.utils.utils import get_lang, get_possible_langs, get_color_schemes, get_style_sheets
logger = getLogger("RareSettings")
@ -36,17 +36,18 @@ class RareSettings(QWidget, Ui_RareSettings):
(self.auto_sync_cloud, "auto_sync_cloud", True),
(self.notification, "notification", True),
(self.save_size, "save_size", False),
(self.log_games, "show_console", False)
(self.log_games, "show_console", False),
(self.image_cache, "cache_images", True)
]
self.settings = QSettings()
self.img_dir_path = self.settings.value("img_dir", os.path.expanduser("~/.cache/rare/images/"), type=str)
self.img_dir_path = self.settings.value("img_dir", os.path.join(data_dir, "images"), type=str)
language = self.settings.value("language", get_lang(), type=str)
self.logdir = os.path.expanduser("~/.cache/rare/logs")
self.logdir = os.path.join(cache_dir, "logs")
# Select Image directory
self.img_dir = PathEdit(self.img_dir_path, file_type=QFileDialog.DirectoryOnly, save_func=self.save_path)
self.img_dir_layout.addWidget(self.img_dir)
# self.img_dir = PathEdit(self.img_dir_path, file_type=QFileDialog.DirectoryOnly, save_func=self.save_path)
# self.img_dir_layout.addWidget(self.img_dir)
# Select lang
self.lang_select.addItems([i[1] for i in languages])
@ -90,6 +91,7 @@ class RareSettings(QWidget, Ui_RareSettings):
)
if platform.system() == "Linux":
self.desktop_file = os.path.expanduser("~/Desktop/Rare.desktop")
self.start_menu_link = os.path.expanduser("~/.local/share/applications/Rare.desktop")
elif platform.system() == "Windows":
@ -110,7 +112,7 @@ class RareSettings(QWidget, Ui_RareSettings):
self.log_dir_open_button.clicked.connect(self.open_dir)
self.log_dir_clean_button.clicked.connect(self.clean_logdir)
logdir = os.path.expanduser("~/.cache/rare/logs")
logdir = os.path.join(cache_dir, "logs")
# get size of logdir
size = 0
for i in os.listdir(logdir):
@ -121,8 +123,8 @@ class RareSettings(QWidget, Ui_RareSettings):
# self.log_dir_size_label.setVisible(False)
def clean_logdir(self):
for i in os.listdir(os.path.expanduser("~/.cache/rare/logs")):
os.remove(os.path.expanduser("~/.cache/rare/logs/") + i)
for i in os.listdir(os.path.join(cache_dir, "logs")):
os.remove(os.path.join(cache_dir, "logs/") + i)
self.log_dir_size_label.setText("0KB")
def create_start_menu_link(self):

View file

@ -0,0 +1,60 @@
from PyQt5.QtWidgets import QStackedWidget, QTabWidget
from custom_legendary.core import LegendaryCore
from rare import cache_dir
from rare.components.tabs.shop.browse_games import BrowseGames
from rare.components.tabs.shop.game_info import ShopGameInfo
from rare.components.tabs.shop.search_results import SearchResults
from rare.components.tabs.shop.shop_api_core import ShopApiCore
from rare.components.tabs.shop.shop_widget import ShopWidget
class Shop(QStackedWidget):
init = False
def __init__(self, core: LegendaryCore):
super(Shop, self).__init__()
self.core = core
self.shop_api = ShopApiCore(self.core.egs.session.headers["Authorization"])
self.shop = ShopWidget(cache_dir, core, self.shop_api)
self.browse_games = BrowseGames(cache_dir, self.shop_api)
self.store_tabs = QTabWidget()
self.store_tabs.addTab(self.shop, self.tr("Games"))
self.store_tabs.addTab(self.browse_games, self.tr("Browse"))
self.store_tabs.tabBarClicked.connect(lambda x: self.browse_games.load() if x == 1 else None)
self.addWidget(self.store_tabs)
self.search_results = SearchResults(self.shop_api)
self.addWidget(self.search_results)
self.search_results.show_info.connect(self.show_game_info)
self.info = ShopGameInfo([i.asset_info.namespace for i in self.core.get_game_list(True)], self.shop_api)
self.addWidget(self.info)
self.info.back_button.clicked.connect(lambda: self.setCurrentIndex(0))
self.search_results.back_button.clicked.connect(lambda: self.setCurrentIndex(0))
self.shop.show_info.connect(self.show_search_results)
self.shop.show_game.connect(self.show_game_info)
self.browse_games.show_game.connect(self.show_game_info)
self.shop_api.update_wishlist.connect(self.update_wishlist)
def update_wishlist(self):
self.shop.update_wishlist()
def load(self):
if not self.init:
self.init = True
self.shop.load()
def show_game_info(self, data):
self.info.update_game(data)
self.setCurrentIndex(2)
def show_search_results(self, text: str):
self.search_results.load_results(text)
self.setCurrentIndex(1)

View file

@ -0,0 +1,130 @@
import datetime
import logging
import random
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QWidget, QCheckBox, QVBoxLayout, QLabel
from rare.components.tabs.shop.constants import Constants
from rare.components.tabs.shop.game_widgets import GameWidget
from rare.components.tabs.shop.shop_models import BrowseModel
from rare.ui.components.tabs.store.browse_games import Ui_browse_games
from rare.utils.extra_widgets import FlowLayout, WaitingSpinner
from rare.utils.utils import get_lang
logger = logging.getLogger("BrowseGames")
class BrowseGames(QWidget, Ui_browse_games):
show_game = pyqtSignal(dict)
init = False
price = ""
tags = []
types = []
def __init__(self, path, api_core):
super(BrowseGames, self).__init__()
self.setupUi(self)
self.api_core = api_core
self.path = path
self.games_widget = QWidget()
self.games_widget.setLayout(FlowLayout())
self.games.setWidget(self.games_widget)
self.stack.addWidget(WaitingSpinner())
self.clear_price.toggled.connect(lambda: self.prepare_request("") if self.clear_price.isChecked() else None)
self.free_button.toggled.connect(lambda: self.prepare_request("free") if self.free_button.isChecked() else None)
self.under10.toggled.connect(
lambda: self.prepare_request("<price>[0, 1000)") if self.under10.isChecked() else None)
self.under20.toggled.connect(
lambda: self.prepare_request("<price>[0, 2000)") if self.under20.isChecked() else None)
self.under30.toggled.connect(
lambda: self.prepare_request("<price>[0, 3000)") if self.under30.isChecked() else None)
self.above.toggled.connect(lambda: self.prepare_request("<price>[1499,]") if self.above.isChecked() else None)
self.on_discount.toggled.connect(lambda: self.prepare_request("sale") if self.on_discount.isChecked() else None)
constants = Constants()
for groupbox, variables in [(self.genre_gb, constants.categories),
(self.platform_gb, constants.platforms),
(self.others_gb, constants.others)]:
for text, tag in variables:
checkbox = CheckBox(text, tag)
checkbox.activated.connect(lambda x: self.prepare_request(added_tag=x))
checkbox.deactivated.connect(lambda x: self.prepare_request(removed_tag=x))
groupbox.layout().addWidget(checkbox)
for text, tag in constants.types:
checkbox = CheckBox(text, tag)
checkbox.activated.connect(lambda x: self.prepare_request(added_type=x))
checkbox.deactivated.connect(lambda x: self.prepare_request(removed_type=x))
self.type_gb.layout().addWidget(checkbox)
def load(self):
if not self.init:
self.prepare_request()
self.init = True
def prepare_request(self, price: str = None, added_tag: int = 0, removed_tag: int = 0,
added_type: str = "", removed_type: str = ""):
if price is not None:
self.price = price
if added_tag != 0:
self.tags.append(added_tag)
if removed_tag != 0 and removed_tag in self.tags:
self.tags.remove(removed_tag)
if added_type:
self.types.append(added_type)
if removed_type and removed_type in self.types:
self.types.remove(removed_type)
locale = get_lang()
self.stack.setCurrentIndex(2)
date = f"[,{datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%dT%X')}.{str(random.randint(0, 999)).zfill(3)}Z]"
browse_model = BrowseModel(locale=locale, date=date, count=30, price=self.price)
browse_model.tag = "|".join(self.tags)
if self.types:
browse_model.category = "|".join(self.types)
self.api_core.browse_games(browse_model, self.show_games)
def show_games(self, data):
QWidget().setLayout(self.games_widget.layout())
if data:
self.games_widget.setLayout(FlowLayout())
for game in data:
w = GameWidget(self.path, game, 275)
self.games_widget.layout().addWidget(w)
w.show_info.connect(self.show_game.emit)
else:
self.games_widget.setLayout(QVBoxLayout())
self.games_widget.layout().addWidget(
QLabel(self.tr("Could not get games matching the filter")))
self.games_widget.layout().addStretch(1)
self.stack.setCurrentIndex(0)
class CheckBox(QCheckBox):
activated = pyqtSignal(str)
deactivated = pyqtSignal(str)
def __init__(self, text, tag):
super(CheckBox, self).__init__(text)
self.tag = tag
self.toggled.connect(self.handle_toggle)
def handle_toggle(self):
if self.isChecked():
self.activated.emit(self.tag)
else:
self.deactivated.emit(self.tag)

View file

@ -0,0 +1,109 @@
from PyQt5.QtCore import QObject
# Class to use QObject.tr()
class Constants(QObject):
def __init__(self):
super(Constants, self).__init__()
self.categories = sorted([
(self.tr("Action"), "1216"),
(self.tr("Adventure"), "1117"),
(self.tr("Puzzle"), "1298"),
(self.tr("Open world"), "1307"),
(self.tr("Racing"), "1212"),
(self.tr("RPG"), "1367"),
(self.tr("Shooter"), "1210"),
(self.tr("Strategy"), "1115"),
(self.tr("Survival"), "1080"),
(self.tr("First Person"), "1294"),
(self.tr("Indie"), "1263"),
(self.tr("Simulation"), "1393"),
(self.tr("Sport"), "1283")
], key=lambda x: x[0])
self.platforms = [
("MacOS", "9548"),
("Windows", "9547"),
]
self.others = [
(self.tr("Single player"), "1370"),
(self.tr("Multiplayer"), "1203"),
(self.tr("Controller"), "9549"),
(self.tr("Co-op"), "1264"),
]
self.types = [
(self.tr("Editor"), "editors"),
(self.tr("Game"), "games/edition/base"),
(self.tr("Bundle"), "bundles/games"),
(self.tr("Add-on"), "addons"),
(self.tr("Apps"), "software/edition/base")
]
game_query = "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, " \
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, " \
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean " \
"= false, $withPromotions: Boolean = false, $priceRange: String, $freeGame: Boolean, $onSale: Boolean, " \
"$effectiveDate: String) {\n Catalog {\n searchStore(\n allowCountries: $allowCountries\n " \
"category: $category\n count: $count\n country: $country\n keywords: $keywords\n " \
"locale: $locale\n namespace: $namespace\n itemNs: $itemNs\n sortBy: $sortBy\n " \
"sortDir: $sortDir\n releaseDate: $releaseDate\n start: $start\n tag: $tag\n " \
"priceRange: $priceRange\n freeGame: $freeGame\n onSale: $onSale\n effectiveDate: " \
"$effectiveDate\n ) {\n elements {\n title\n id\n namespace\n " \
"description\n effectiveDate\n keyImages {\n type\n url\n }\n " \
" currentPrice\n seller {\n id\n name\n }\n productSlug\n " \
" urlSlug\n url\n tags {\n id\n }\n items {\n id\n " \
" namespace\n }\n customAttributes {\n key\n value\n }\n " \
"categories {\n path\n }\n catalogNs @include(if: $withMapping) {\n " \
"mappings(pageType: \"productHome\") {\n pageSlug\n pageType\n }\n " \
"}\n offerMappings @include(if: $withMapping) {\n pageSlug\n pageType\n " \
"}\n price(country: $country) @include(if: $withPrice) {\n totalPrice {\n " \
"discountPrice\n originalPrice\n voucherDiscount\n discount\n " \
" currencyCode\n currencyInfo {\n decimals\n }\n fmtPrice(" \
"locale: $locale) {\n originalPrice\n discountPrice\n " \
"intermediatePrice\n }\n }\n lineOffers {\n appliedRules {\n " \
" id\n endDate\n discountSetting {\n discountType\n " \
" }\n }\n }\n }\n promotions(category: $category) @include(if: " \
"$withPromotions) {\n promotionalOffers {\n promotionalOffers {\n " \
"startDate\n endDate\n discountSetting {\n discountType\n " \
" discountPercentage\n }\n }\n }\n " \
"upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n " \
"endDate\n discountSetting {\n discountType\n " \
"discountPercentage\n }\n }\n }\n }\n }\n paging {\n " \
" count\n total\n }\n }\n }\n}\n "
search_query = "query searchStoreQuery($allowCountries: String, $category: String, $count: Int, $country: String!, " \
"$keywords: String, $locale: String, $namespace: String, $withMapping: Boolean = false, $itemNs: String, " \
"$sortBy: String, $sortDir: String, $start: Int, $tag: String, $releaseDate: String, $withPrice: Boolean = " \
"false, $withPromotions: Boolean = false, $priceRange: String, $freeGame: Boolean, $onSale: Boolean, " \
"$effectiveDate: String) {\n Catalog {\n searchStore(\n allowCountries: $allowCountries\n " \
"category: $category\n count: $count\n country: $country\n keywords: $keywords\n locale: " \
"$locale\n namespace: $namespace\n itemNs: $itemNs\n sortBy: $sortBy\n sortDir: " \
"$sortDir\n releaseDate: $releaseDate\n start: $start\n tag: $tag\n priceRange: " \
"$priceRange\n freeGame: $freeGame\n onSale: $onSale\n effectiveDate: $effectiveDate\n ) {" \
"\n elements {\n title\n id\n namespace\n description\n " \
"effectiveDate\n keyImages {\n type\n url\n }\n currentPrice\n " \
"seller {\n id\n name\n }\n productSlug\n urlSlug\n url\n " \
" tags {\n id\n }\n items {\n id\n namespace\n }\n " \
"customAttributes {\n key\n value\n }\n categories {\n path\n " \
"}\n catalogNs @include(if: $withMapping) {\n mappings(pageType: \"productHome\") {\n " \
" pageSlug\n pageType\n }\n }\n offerMappings @include(if: $withMapping) " \
"{\n pageSlug\n pageType\n }\n price(country: $country) @include(if: " \
"$withPrice) {\n totalPrice {\n discountPrice\n originalPrice\n " \
"voucherDiscount\n discount\n currencyCode\n currencyInfo {\n " \
"decimals\n }\n fmtPrice(locale: $locale) {\n originalPrice\n " \
"discountPrice\n intermediatePrice\n }\n }\n lineOffers {\n " \
" appliedRules {\n id\n endDate\n discountSetting {\n " \
"discountType\n }\n }\n }\n }\n promotions(category: " \
"$category) @include(if: $withPromotions) {\n promotionalOffers {\n promotionalOffers {\n " \
" startDate\n endDate\n discountSetting {\n " \
"discountType\n discountPercentage\n }\n }\n }\n " \
"upcomingPromotionalOffers {\n promotionalOffers {\n startDate\n " \
"endDate\n discountSetting {\n discountType\n discountPercentage\n " \
" }\n }\n }\n }\n }\n paging {\n count\n " \
"total\n }\n }\n }\n}\n "
wishlist_query = "\n query wishlistQuery($country:String!, $locale:String) {\n Wishlist {\n wishlistItems {\n elements {\n id\n order\n created\n offerId\n updated\n namespace\n \n offer {\n productSlug\n urlSlug\n title\n id\n namespace\n offerType\n expiryDate\n status\n isCodeRedemptionOnly\n description\n effectiveDate\n keyImages {\n type\n url\n }\n seller {\n id\n name\n }\n productSlug\n urlSlug\n items {\n id\n namespace\n }\n customAttributes {\n key\n value\n }\n catalogNs {\n mappings(pageType: \"productHome\") {\n pageSlug\n pageType\n }\n }\n offerMappings {\n pageSlug\n pageType\n }\n categories {\n path\n }\n price(country: $country) {\n totalPrice {\n discountPrice\n originalPrice\n voucherDiscount\n discount\n fmtPrice(locale: $locale) {\n originalPrice\n discountPrice\n intermediatePrice\n }\n currencyCode\n currencyInfo {\n decimals\n symbol\n }\n }\n lineOffers {\n appliedRules {\n id\n endDate\n }\n }\n }\n }\n\n }\n }\n }\n }\n"
add_to_wishlist_query = "\n mutation removeFromWishlistMutation($namespace: String!, $offerId: String!, $operation: RemoveOperation!) {\n Wishlist {\n removeFromWishlist(namespace: $namespace, offerId: $offerId, operation: $operation) {\n success\n }\n }\n }\n"
remove_from_wishlist_query = "\n mutation removeFromWishlistMutation($namespace: String!, $offerId: String!, $operation: RemoveOperation!) {\n Wishlist {\n removeFromWishlist(namespace: $namespace, offerId: $offerId, operation: $operation) {\n success\n }\n }\n }\n"

View file

@ -0,0 +1,147 @@
import logging
import webbrowser
from PyQt5.QtGui import QPixmap, QFont
from PyQt5.QtWidgets import QWidget, QLabel
from rare.components.tabs.shop.shop_models import ShopGame
from rare.ui.components.tabs.store.shop_game_info import Ui_shop_info
from rare.utils.extra_widgets import WaitingSpinner, ImageLabel
from rare.utils.utils import get_lang
logger = logging.getLogger("ShopInfo")
class ShopGameInfo(QWidget, Ui_shop_info):
game: ShopGame
data: dict
# TODO Design
def __init__(self, installed_titles: list, api_core):
super(ShopGameInfo, self).__init__()
self.setupUi(self)
self.api_core = api_core
self.installed = installed_titles
self.open_store_button.clicked.connect(self.button_clicked)
self.image = ImageLabel()
self.image_stack.addWidget(self.image)
self.image_stack.addWidget(WaitingSpinner())
self.locale = get_lang()
self.wishlist_button.clicked.connect(self.add_to_wishlist)
self.in_wishlist = False
self.wishlist = []
def handle_wishlist_update(self, data):
self.wishlist = [i["offer"]["title"] for i in data]
if self.title_str in self.wishlist:
self.in_wishlist = True
self.wishlist_button.setVisible(True)
self.wishlist_button.setText(self.tr("Remove from Wishlist"))
else:
self.in_wishlist = False
self.wishlist_button.setVisible(False)
def update_game(self, data: dict):
self.image_stack.setCurrentIndex(1)
self.title.setText(data["title"])
self.title_str = data["title"]
self.api_core.get_wishlist(self.handle_wishlist_update)
for i in reversed(range(self.req_group_box.layout().count())):
self.req_group_box.layout().itemAt(i).widget().setParent(None)
slug = data["productSlug"]
if "/home" in slug:
slug = slug.replace("/home", "")
self.slug = slug
if data["namespace"] in self.installed:
self.open_store_button.setText(self.tr("Show Game on Epic Page"))
self.owned_label.setVisible(True)
else:
self.open_store_button.setText(self.tr("Buy Game in Epic Games Store"))
self.owned_label.setVisible(False)
self.dev.setText(self.tr("Loading"))
self.price.setText(self.tr("Loading"))
# self.title.setText(self.tr("Loading"))
self.image.setPixmap(QPixmap())
self.data = data
is_bundle = False
for i in data["categories"]:
if "bundles" in i.get("path", ""):
is_bundle = True
# init API request
self.api_core.get_game(slug, is_bundle, self.data_received)
def add_to_wishlist(self):
if not self.in_wishlist:
return
self.api_core.add_to_wishlist(self.game.namespace, self.game.offer_id,
lambda success: self.wishlist_button.setText(self.tr("Remove from wishlist"))
if success else self.wishlist_button.setText("Something goes wrong"))
else:
self.api_core.remove_from_wishlist(self.game.namespace, self.game.offer_id,
lambda success: self.wishlist_button.setVisible(False)
if success else self.wishlist_button.setText("Something goes wrong"))
def data_received(self, game):
self.game = ShopGame.from_json(game, self.data)
self.title.setText(self.game.title)
self.price.setFont(QFont())
if self.game.price != "0":
self.price.setText(self.game.price)
else:
self.price.setText(self.tr("Free"))
if self.game.price != self.game.discount_price:
font = QFont()
font.setStrikeOut(True)
self.price.setFont(font)
self.discount_price.setText(
self.game.discount_price if self.game.discount_price != "0" else self.tr("Free"))
self.discount_price.setVisible(True)
else:
self.discount_price.setVisible(False)
# print(self.game.reqs)
bold_font = QFont()
bold_font.setBold(True)
min_label = QLabel(self.tr("Minimum"))
min_label.setFont(bold_font)
rec_label = QLabel(self.tr("Recommend"))
rec_label.setFont(bold_font)
if self.game.reqs:
self.req_group_box.layout().addWidget(min_label, 0, 1)
self.req_group_box.layout().addWidget(rec_label, 0, 2)
for i, (key, value) in enumerate(self.game.reqs.get("Windows", {}).items()):
self.req_group_box.layout().addWidget(QLabel(key), i + 1, 0)
min_label = QLabel(value[0])
min_label.setWordWrap(True)
self.req_group_box.layout().addWidget(min_label, i + 1, 1)
rec_label = QLabel(value[1])
rec_label.setWordWrap(True)
self.req_group_box.layout().addWidget(rec_label, i + 1, 2)
else:
self.req_group_box.layout().addWidget(QLabel(self.tr("Could not get requirements")))
self.image.update_image(self.game.image_urls.front_tall, self.game.title, (240, 320))
self.image_stack.setCurrentIndex(0)
try:
if isinstance(self.game.developer, list):
self.dev.setText(", ".join(self.game.developer))
else:
self.dev.setText(self.game.developer)
except KeyError:
pass
self.tags.setText(", ".join(self.game.tags))
self.price.setText(self.game.price)
def add_wishlist_items(self, wishlist):
wishlist = wishlist["data"]["Wishlist"]["wishlistItems"]["elements"]
for game in wishlist:
self.wishlist.append(game["offer"]["title"])
def button_clicked(self):
webbrowser.open("https://www.epicgames.com/store/de/p/" + self.slug)

View file

@ -0,0 +1,84 @@
import logging
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QFont
from PyQt5.QtNetwork import QNetworkAccessManager
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
from rare.utils.extra_widgets import ImageLabel
logger = logging.getLogger("GameWidgets")
class GameWidget(QWidget):
show_info = pyqtSignal(dict)
def __init__(self, path, json_info=None, width=300):
super(GameWidget, self).__init__()
self.manager = QNetworkAccessManager()
self.width = width
self.path = path
if json_info:
self.init_ui(json_info)
def init_ui(self, json_info):
self.layout = QVBoxLayout()
self.image = ImageLabel()
self.layout.addWidget(self.image)
self.title_label = QLabel(json_info.get("title"))
self.title_label.setWordWrap(True)
self.layout.addWidget(self.title_label)
for c in r'<>?":|\/*':
json_info["title"] = json_info["title"].replace(c, "")
self.json_info = json_info
self.slug = json_info["productSlug"]
self.title = json_info["title"]
for img in json_info["keyImages"]:
if img["type"] in ["DieselStoreFrontWide", "OfferImageWide", "VaultClosed"]:
if img["type"] == "VaultClosed" and self.title != "Mystery Game":
continue
self.image.update_image(img["url"], json_info["title"], (self.width, int(self.width * 9 / 16)))
break
else:
logger.info(", ".join([img["type"] for img in json_info["keyImages"]]))
# print(json_info["keyImages"])
self.setLayout(self.layout)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
self.show_info.emit(self.json_info)
@classmethod
def from_request(cls, name, path, shop_api):
c = cls(path)
shop_api.search_game(name, c.handle_response)
return c
def handle_response(self, data):
data = data["data"]["Catalog"]["searchStore"]["elements"][0]
self.init_ui(data)
class GameWidgetDiscount(GameWidget):
def __init__(self, *args, **kwargs):
super(GameWidgetDiscount, self).__init__(*args, **kwargs)
h_layout = QHBoxLayout()
self.layout.addLayout(h_layout)
price = args[1]['price']['totalPrice']['fmtPrice']['originalPrice']
discount_price = args[1]['price']['totalPrice']['fmtPrice']['discountPrice']
price_label = QLabel(price)
font = QFont()
font.setStrikeOut(True)
price_label.setFont(font)
h_layout.addWidget(QLabel(discount_price if discount_price != "0" else self.tr("Free")))
h_layout.addWidget(price_label)

View file

@ -0,0 +1,103 @@
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QScrollArea, QGroupBox, QPushButton, \
QStackedWidget
from rare.utils.extra_widgets import ImageLabel, FlowLayout, WaitingSpinner
class SearchResults(QStackedWidget):
show_info = pyqtSignal(dict)
def __init__(self, api_core):
super(SearchResults, self).__init__()
self.search_result_widget = QWidget()
self.api_core = api_core
self.addWidget(self.search_result_widget)
self.main_layout = QVBoxLayout()
self.back_button = QPushButton(self.tr("Back"))
self.main_layout.addWidget(self.back_button)
self.main_layout.addWidget(self.back_button)
self.result_area = QScrollArea()
self.widget = QWidget()
self.result_area.setWidgetResizable(True)
self.main_layout.addWidget(self.result_area)
self.result_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.result_area.setWidget(self.widget)
self.layout = FlowLayout()
self.widget.setLayout(self.layout)
self.search_result_widget.setLayout(self.main_layout)
self.addWidget(WaitingSpinner())
self.setCurrentIndex(1)
def load_results(self, text: str):
self.setCurrentIndex(1)
if text != "":
self.api_core.search_game(text, self.show_results)
def show_results(self, results: dict):
results = results["data"]["Catalog"]["searchStore"]["elements"]
QVBoxLayout().addWidget(self.widget)
self.widget = QWidget()
self.layout = FlowLayout()
if not results:
self.layout.addWidget(QLabel(self.tr("No results found")))
else:
for res in results:
w = _SearchResultItem(res)
w.show_info.connect(self.show_info.emit)
self.layout.addWidget(w)
self.widget.setLayout(self.layout)
self.result_area.setWidget(self.widget)
self.setCurrentIndex(0)
class _SearchResultItem(QGroupBox):
res: dict
show_info = pyqtSignal(dict)
def __init__(self, result: dict):
super(_SearchResultItem, self).__init__()
self.layout = QVBoxLayout()
self.image = ImageLabel()
for img in result["keyImages"]:
if img["type"] == "DieselStoreFrontTall":
width = 240
self.image.update_image(img["url"], result["title"], (width, 360))
break
else:
print("No image found")
self.layout.addWidget(self.image)
self.res = result
self.title = QLabel(self.res["title"])
title_font = QFont()
title_font.setPixelSize(15)
self.title.setFont(title_font)
self.title.setWordWrap(True)
self.layout.addWidget(self.title)
price = result['price']['totalPrice']['fmtPrice']['originalPrice']
discount_price = result['price']['totalPrice']['fmtPrice']['discountPrice']
price_layout = QHBoxLayout()
price_label = QLabel(price if price != "0" else self.tr("Free"))
price_layout.addWidget(price_label)
if price != discount_price:
font = QFont()
font.setStrikeOut(True)
price_label.setFont(font)
price_layout.addWidget(QLabel(discount_price))
# self.discount_price = QLabel(f"{self.tr('Discount price: ')}{discount_price}")
self.layout.addLayout(price_layout)
self.setLayout(self.layout)
self.setFixedWidth(260)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
if a0.button() == 1:
self.show_info.emit(self.res)

View file

@ -0,0 +1,137 @@
from logging import getLogger
from PyQt5.QtCore import pyqtSignal, QObject
from rare.components.tabs.shop.constants import wishlist_query, search_query, game_query, add_to_wishlist_query, \
remove_from_wishlist_query
from rare.components.tabs.shop.shop_models import BrowseModel
from rare.utils.qt_requests import QtRequestManager
from rare.utils.utils import get_lang
logger = getLogger("ShopAPICore")
graphql_url = "https://www.epicgames.com/graphql"
class ShopApiCore(QObject):
update_wishlist = pyqtSignal()
def __init__(self, auth_token):
super(ShopApiCore, self).__init__()
self.token = auth_token
self.manager = QtRequestManager()
self.auth_manager = QtRequestManager(authorization_token=auth_token)
self.locale = get_lang()
self.browse_active = False
self.next_browse_request = tuple(())
def get_free_games(self, handle_func: callable):
url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions"
self.manager.get(url, lambda data: self._handle_free_games(data, handle_func))
def _handle_free_games(self, data, handle_func):
try:
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
except KeyError as e:
logger.error(str(e))
def get_wishlist(self, handle_func):
self.auth_manager.post(graphql_url, {
"query": wishlist_query,
"variables": {
"country": self.locale.upper(),
"locale": self.locale
}
}, lambda data: self._handle_wishlist(data, handle_func))
def _handle_wishlist(self, data, handle_func):
handle_func(data["data"]["Wishlist"]["wishlistItems"]["elements"])
def search_game(self, name, handle_func):
payload = {
"query": search_query,
"variables": {"category": "games/edition/base|bundles/games|editors|software/edition/base", "count": 1,
"country": "DE", "keywords": name, "locale": self.locale, "sortDir": "DESC",
"allowCountries": self.locale.upper(),
"start": 0, "tag": "", "withMapping": False, "withPrice": True}
}
self.manager.post(graphql_url, payload, lambda data: self._handle_search(data, handle_func))
def _handle_search(self, data, handle_func):
handle_func(data)
def browse_games(self, browse_model: BrowseModel, handle_func):
if self.browse_active:
self.next_browse_request = (browse_model, handle_func)
return
self.browse_active = True
payload = {
"variables": browse_model.__dict__,
"query": game_query
}
self.auth_manager.post(graphql_url, payload, lambda data: self._handle_browse_games(data, handle_func))
def _handle_browse_games(self, data, handle_func):
self.browse_active = False
if not self.next_browse_request:
handle_func(data["data"]["Catalog"]["searchStore"]["elements"])
else:
self.browse_games(*self.next_browse_request)
self.next_browse_request = tuple(())
def get_game(self, slug: str, is_bundle: bool, handle_func):
url = f"https://store-content.ak.epicgames.com/api/{self.locale}/content/{'products' if not is_bundle else 'bundles'}/{slug}"
self.manager.get(url, lambda data: self._handle_get_game(data, handle_func))
def _handle_get_game(self, data, handle_func):
handle_func(data)
def add_to_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"variables": {
"offerId": offer_id,
"namespace": namespace,
"country": self.locale.upper(),
"locale": self.locale
},
"query": add_to_wishlist_query
}
self.auth_manager.post(graphql_url, payload, lambda data: self._handle_add_to_wishlist(data, handle_func))
def _handle_add_to_wishlist(self, data, handle_func):
try:
data = data["data"]["Wishlist"]["addToWishlist"]
if data["success"]:
handle_func(True)
else:
handle_func(False)
except Exception as e:
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()
def remove_from_wishlist(self, namespace, offer_id, handle_func: callable):
payload = {
"variables": {
"offerId": offer_id,
"namespace": namespace,
"operation": "REMOVE"
},
"query": remove_from_wishlist_query
}
self.auth_manager.post(graphql_url, payload, lambda data: self._handle_remove_from_wishlist(data, handle_func))
def _handle_remove_from_wishlist(self, data, handle_func):
try:
data = data["data"]["Wishlist"]["removeFromWishlist"]
if data["success"]:
handle_func(True)
else:
handle_func(False)
except Exception as e:
logger.error(str(e))
handle_func(False)
self.update_wishlist.emit()

View file

@ -0,0 +1,137 @@
import datetime
import random
from dataclasses import dataclass
from rare.utils.utils import get_lang
class _ImageUrlModel:
def __init__(self, front_tall: str = "", offer_image_tall: str = "",
thumbnail: str = "", front_wide: str = ""):
self.front_tall = front_tall
self.offer_image_tall = offer_image_tall
self.thumbnail = thumbnail
self.front_wide = front_wide
@classmethod
def from_json(cls, json_data: list):
tmp = cls()
for item in json_data:
if item["type"] == "Thumbnail":
tmp.thumbnail = item["url"]
elif item["type"] == "DieselStoreFrontTall":
tmp.front_tall = item["url"]
elif item["type"] == "DieselStoreFrontWide":
tmp.front_wide = item["url"]
elif item["type"] == "OfferImageTall":
tmp.offer_image_tall = item["url"]
return tmp
class ShopGame:
# TODO: Copyrights etc
def __init__(self, title: str = "", image_urls: _ImageUrlModel = None, social_links: dict = None,
langs: list = None, reqs: dict = None, publisher: str = "", developer: str = "",
original_price: str = "", discount_price: str = "", tags: list = None, namespace: str = "",
offer_id: str = ""):
self.title = title
self.image_urls = image_urls
self.links = []
if social_links:
for item in social_links:
if item.startswith("link"):
self.links.append(tuple((item.replace("link", ""), social_links[item])))
else:
self.links = []
self.languages = langs
self.reqs = reqs
self.publisher = publisher
self.developer = developer
self.price = original_price
self.discount_price = discount_price
self.tags = tags
self.namespace = namespace
self.offer_id = offer_id
@classmethod
def from_json(cls, api_data: dict, search_data: dict):
if isinstance(api_data, list):
for product in api_data:
if product["_title"] == "home":
api_data = product
break
if "pages" in api_data.keys():
api_data = api_data["pages"][0]
tmp = cls()
tmp.title = search_data.get("title", "Fail")
tmp.image_urls = _ImageUrlModel.from_json(search_data["keyImages"])
links = api_data["data"]["socialLinks"]
tmp.links = []
for item in links:
if item.startswith("link"):
tmp.links.append(tuple((item.replace("link", ""), links[item])))
tmp.available_voice_langs = api_data["data"]["requirements"].get("languages", "Failed")
tmp.reqs = {}
for i, system in enumerate(api_data["data"]["requirements"].get("systems", [])):
try:
tmp.reqs[system["systemType"]] = {}
except KeyError:
continue
for req in system["details"]:
try:
tmp.reqs[system["systemType"]][req["title"]] = (req["minimum"], req["recommended"])
except KeyError:
pass
tmp.publisher = api_data["data"]["meta"].get("publisher", "")
tmp.developer = api_data["data"]["meta"].get("developer", "")
if not tmp.developer:
for i in search_data["customAttributes"]:
if i["key"] == "developerName":
tmp.developer = i["value"]
tmp.price = search_data['price']['totalPrice']['fmtPrice']['originalPrice']
tmp.discount_price = search_data['price']['totalPrice']['fmtPrice']['discountPrice']
tmp.tags = [i.replace("_", " ").capitalize() for i in api_data["data"]["meta"].get("tags", [])]
tmp.namespace = search_data["namespace"]
tmp.offer_id = search_data["id"]
return tmp
@dataclass
class BrowseModel:
category: str = "games/edition/base|bundles/games|editors|software/edition/base"
count: int = 30
locale: str = get_lang()
keywords: str = ""
sortDir: str = "DESC"
start: int = 0
tag: str = ""
withMapping: bool = True
withPrice: bool = True
date: str = f"[,{datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%dT%X')}.{str(random.randint(0, 999)).zfill(3)}Z]"
price: str = ""
@property
def __dict__(self):
payload = {"category": self.category,
"count": self.count,
"country": self.locale.upper(),
"keywords": self.keywords,
"locale": self.locale,
"sortDir": self.sortDir,
"allowCountries": self.locale.upper(),
"start": self.start,
"tag": self.tag,
"withMapping": self.withMapping,
"withPrice": self.withPrice,
"releaseDate": self.date,
"effectiveDate": self.date,
}
if self.price == "free":
payload["freeGame"] = True
elif self.price.startswith("<price>"):
payload["priceRange"] = self.price.replace("<price>", "")
elif self.price == "sale":
payload["onSale"] = True
return payload

View file

@ -0,0 +1,169 @@
import datetime
import logging
from PyQt5.QtCore import Qt, pyqtSignal, QStringListModel
from PyQt5.QtNetwork import QNetworkAccessManager
from PyQt5.QtWidgets import QWidget, QCompleter, QGroupBox, QHBoxLayout, QScrollArea
from custom_legendary.core import LegendaryCore
from rare.components.tabs.shop import ShopApiCore
from rare.components.tabs.shop.constants import search_query
from rare.components.tabs.shop.game_widgets import GameWidget, GameWidgetDiscount
from rare.ui.components.tabs.store.store import Ui_ShopWidget
from rare.utils.extra_widgets import WaitingSpinner, FlowLayout, ButtonLineEdit
from rare.utils.utils import get_lang
logger = logging.getLogger("Shop")
# noinspection PyAttributeOutsideInit,PyBroadException
class ShopWidget(QScrollArea, Ui_ShopWidget):
show_info = pyqtSignal(str)
show_game = pyqtSignal(dict)
free_game_widgets = []
active_search_request = False
next_search = ""
wishlist: list = []
def __init__(self, path, core: LegendaryCore, shop_api: ShopApiCore):
super(ShopWidget, self).__init__()
self.setWidgetResizable(True)
self.setupUi(self)
self.path = path
self.core = core
self.shop_api = shop_api
self.manager = QNetworkAccessManager()
self.free_games_widget = QWidget()
self.free_games_widget.setLayout(FlowLayout())
self.free_games_now = QGroupBox(self.tr("Free Games"))
self.free_games_now.setLayout(QHBoxLayout())
self.free_games_widget.layout().addWidget(self.free_games_now)
self.coming_free_games = QGroupBox(self.tr("Free Games next week"))
self.coming_free_games.setLayout(QHBoxLayout())
self.free_games_widget.layout().addWidget(self.coming_free_games)
self.free_games_stack.addWidget(WaitingSpinner())
self.free_games_stack.addWidget(self.free_games_widget)
self.completer = QCompleter()
self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.data = []
self.search_bar = ButtonLineEdit("fa.search", placeholder_text=self.tr("Search Games"))
self.scrollAreaWidgetContents.layout().insertWidget(0, self.search_bar)
# self.search_bar.textChanged.connect(self.search_games)
self.search_bar.setCompleter(self.completer)
self.search_bar.returnPressed.connect(self.show_search_results)
self.search_bar.buttonClicked.connect(self.show_search_results)
# self.search_bar.textChanged.connect(self.load_completer)
self.wishlist_gb.setLayout(FlowLayout())
self.wishlist_gb.setVisible(False)
self.locale = get_lang()
def load_completer(self, text):
if text != "":
payload = {
"query": search_query,
"variables": {"category": "games/edition/base|bundles/games|editors|software/edition/base",
"count": 20,
"country": self.locale.upper(), "keywords": text, "locale": self.locale,
"sortDir": "DESC",
"allowCountries": self.locale.upper(),
"start": 0, "tag": "", "withMapping": False, "withPrice": True}
}
self.search_request_manager.post("https://www.epicgames.com/graphql", payload)
def load(self):
# load free games
self.shop_api.get_free_games(self.add_free_games)
# load wishlist
self.shop_api.get_wishlist(self.add_wishlist_items)
def update_wishlist(self):
self.shop_api.get_wishlist(self.add_wishlist_items)
def add_wishlist_items(self, wishlist):
QWidget().setLayout(self.wishlist_gb.layout())
self.wishlist_gb.setLayout(FlowLayout())
discounts = 0
for game in wishlist:
if game["offer"]["price"]["totalPrice"]["discount"] > 0:
w = GameWidgetDiscount(self.path, game["offer"])
w.show_info.connect(self.show_game.emit)
self.wishlist_gb.layout().addWidget(w)
discounts += 1
self.wishlist_gb.setVisible(discounts > 0)
def add_free_games(self, free_games):
date = datetime.datetime.now()
free_games_now = []
coming_free_games = []
for game in free_games:
if game["title"] == "Mystery Game":
coming_free_games.append(game)
continue
try:
# parse datetime
try:
end_date = datetime.datetime.strptime(
game["promotions"]["upcomingPromotionalOffers"][0]["promotionalOffers"][0]["endDate"],
'%Y-%m-%dT%H:%M:%S.%fZ')
except Exception:
try:
end_date = datetime.datetime.strptime(
game["promotions"]["promotionalOffers"][0]["promotionalOffers"][0]["endDate"],
'%Y-%m-%dT%H:%M:%S.%fZ')
except Exception:
continue
try:
start_date = datetime.datetime.strptime(
game["promotions"]["upcomingPromotionalOffers"][0]["promotionalOffers"][0]["startDate"],
'%Y-%m-%dT%H:%M:%S.%fZ')
except Exception:
try:
start_date = datetime.datetime.strptime(
game["promotions"]["promotionalOffers"][0]["promotionalOffers"][0]["startDate"],
'%Y-%m-%dT%H:%M:%S.%fZ')
except Exception as e:
print(e)
continue
except TypeError:
print("type error")
continue
if start_date < date < end_date:
free_games_now.append(game)
elif start_date > date:
coming_free_games.append(game)
for free_game in free_games_now:
w = GameWidget(self.path, free_game)
w.show_info.connect(self.show_game.emit)
self.free_games_now.layout().addWidget(w)
self.free_game_widgets.append(w)
self.free_games_now.layout().addStretch(1)
for free_game in coming_free_games:
w = GameWidget(self.path, free_game)
if free_game["title"] != "Mystery Game":
w.show_info.connect(self.show_game.emit)
self.coming_free_games.layout().addWidget(w)
self.free_game_widgets.append(w)
self.coming_free_games.layout().addStretch(1)
# self.coming_free_games.setFixedWidth(int(40 + len(coming_free_games) * 300))
self.free_games_stack.setCurrentIndex(1)
def set_completer(self, search_data):
search_data = search_data["data"]["Catalog"]["searchStore"]["elements"]
titles = [i.get("title") for i in search_data]
model = QStringListModel()
model.setStringList(titles)
self.completer.setModel(model)
def show_search_results(self):
self.show_info.emit(self.search_bar.text())

View file

@ -15,6 +15,7 @@ class Ui_RareSettings(object):
def setupUi(self, RareSettings):
RareSettings.setObjectName("RareSettings")
RareSettings.resize(694, 532)
self.rare_layout = QtWidgets.QGridLayout(RareSettings)
self.rare_layout.setObjectName("rare_layout")
self.settings_group = QtWidgets.QGroupBox(RareSettings)
@ -24,6 +25,13 @@ class Ui_RareSettings(object):
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
self.confirm_start.setObjectName("confirm_start")
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
self.notification = QtWidgets.QCheckBox(self.settings_group)
self.notification.setObjectName("notification")
self.behavior_layout.addWidget(self.notification, 4, 0, 1, 1)
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
self.auto_update.setObjectName("auto_update")
self.behavior_layout.addWidget(self.auto_update, 1, 0, 1, 1)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.behavior_layout.addItem(spacerItem, 2, 1, 2, 1)
self.auto_update = QtWidgets.QCheckBox(self.settings_group)
@ -44,6 +52,15 @@ class Ui_RareSettings(object):
self.log_games = QtWidgets.QCheckBox(self.settings_group)
self.log_games.setObjectName("log_games")
self.behavior_layout.addWidget(self.log_games, 6, 0, 1, 1)
self.confirm_start = QtWidgets.QCheckBox(self.settings_group)
self.confirm_start.setObjectName("confirm_start")
self.behavior_layout.addWidget(self.confirm_start, 2, 0, 1, 1)
self.sys_tray = QtWidgets.QCheckBox(self.settings_group)
self.sys_tray.setObjectName("sys_tray")
self.behavior_layout.addWidget(self.sys_tray, 0, 0, 1, 1)
self.image_cache = QtWidgets.QCheckBox(self.settings_group)
self.image_cache.setObjectName("image_cache")
self.behavior_layout.addWidget(self.image_cache, 6, 0, 1, 1)
self.rare_layout.addWidget(self.settings_group, 2, 0, 1, 1, QtCore.Qt.AlignTop)
self.interface_group = QtWidgets.QGroupBox(RareSettings)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
@ -121,6 +138,13 @@ class Ui_RareSettings(object):
self.img_dir_layout = QtWidgets.QVBoxLayout(self.img_dir_group)
self.img_dir_layout.setObjectName("img_dir_layout")
self.rare_layout.addWidget(self.img_dir_group, 0, 0, 1, 1)
self.label = QtWidgets.QLabel(self.img_dir_group)
self.label.setWordWrap(True)
self.label.setObjectName("label")
self.img_dir_layout.addWidget(self.label)
self.rare_layout.addWidget(self.img_dir_group, 0, 0, 1, 1, QtCore.Qt.AlignTop)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.rare_layout.addItem(spacerItem2, 3, 0, 1, 2)
self.retranslateUi(RareSettings)
QtCore.QMetaObject.connectSlotsByName(RareSettings)
@ -136,6 +160,16 @@ class Ui_RareSettings(object):
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
self.log_games.setText(_translate("RareSettings", "Show console for game debug"))
self.notification.setText(_translate("RareSettings", "Show notification on download completion"))
self.auto_update.setText(_translate("RareSettings", "Update games on application startup"))
self.save_size.setText(_translate("RareSettings", "Restore window size on application startup"))
self.auto_sync_cloud.setText(_translate("RareSettings", "Automatically sync with cloud"))
self.confirm_start.setText(_translate("RareSettings", "Confirm game launch"))
self.sys_tray.setText(_translate("RareSettings", "Exit to System tray"))
self.image_cache.setText(_translate("RareSettings", "Cache images in store"))
self.log_dir_group.setTitle(_translate("RareSettings", "Logs"))
self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory"))
self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
self.interface_group.setTitle(_translate("RareSettings", "Interface"))
self.style_label.setText(_translate("RareSettings", "Style Sheet"))
self.color_select.setItemText(0, _translate("RareSettings", "None"))
@ -150,6 +184,8 @@ class Ui_RareSettings(object):
self.log_dir_open_button.setText(_translate("RareSettings", "Open Log directory"))
self.log_dir_clean_button.setText(_translate("RareSettings", "Clean Log directory"))
self.img_dir_group.setTitle(_translate("RareSettings", "Image Cache Directory"))
self.label.setText(_translate("RareSettings",
"To change image directory, edit XDG_DATA_HOME variable. To change cache directory edit XDG_CACHE_HOME variable"))
if __name__ == "__main__":

View file

@ -1,257 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RareSettings</class>
<widget class="QWidget" name="RareSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>694</width>
<height>532</height>
</rect>
</property>
<property name="windowTitle">
<string>RareSettings</string>
</property>
<layout class="QGridLayout" name="rare_layout">
<item row="2" column="0" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="settings_group">
<property name="title">
<string>Behavior</string>
</property>
<layout class="QGridLayout" name="behavior_layout">
<item row="2" column="0">
<widget class="QCheckBox" name="confirm_start">
<property name="text">
<string>Confirm game launch</string>
</property>
</widget>
</item>
<item row="2" column="1" rowspan="2">
<spacer name="settings_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="auto_update">
<property name="text">
<string>Update games on application startup</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="save_size">
<property name="text">
<string>Restore window size on application startup</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="auto_sync_cloud">
<property name="text">
<string>Automatically sync with cloud</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="notification">
<property name="text">
<string>Show notification on download completion</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="sys_tray">
<property name="text">
<string>Exit to System tray</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="log_games">
<property name="text">
<string>Show console for game debug</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="interface_group">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Interface</string>
</property>
<layout class="QGridLayout" name="interface_layout">
<item row="2" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="style_label">
<property name="text">
<string>Style Sheet</string>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="interface_hspacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="lang_select"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="color_select">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="style_select">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="lang_label">
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item row="1" column="0" alignment="Qt::AlignRight">
<widget class="QLabel" name="color_label">
<property name="text">
<string>Color Scheme</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="interface_info">
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="text">
<string>Restart Rare to apply.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1" alignment="Qt::AlignTop">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Shortcuts</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="desktop_link">
<property name="text">
<string>Create Desktop link</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startmenu_link">
<property name="text">
<string>Create start menu link</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="rpc_layout"/>
</item>
<item row="3" column="0" colspan="2">
<spacer name="rare_vspacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="log_dir_group">
<property name="title">
<string>Logs</string>
</property>
<layout class="QVBoxLayout" name="log_dir_layout">
<item>
<widget class="QPushButton" name="log_dir_open_button">
<property name="text">
<string>Open Log directory</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="log_dir_clean_button">
<property name="text">
<string>Clean Log directory</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="log_dir_size_label">
<property name="text">
<string notr="true"/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="img_dir_group">
<property name="title">
<string>Image Cache Directory</string>
</property>
<layout class="QVBoxLayout" name="img_dir_layout"/>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'browse_games.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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.
from PyQt5 import QtCore, QtWidgets
class Ui_browse_games(object):
def setupUi(self, browse_games):
browse_games.setObjectName("browse_games")
browse_games.resize(706, 541)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(browse_games)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.stack = QtWidgets.QStackedWidget(browse_games)
self.stack.setObjectName("stack")
self.games_page = QtWidgets.QWidget()
self.games_page.setObjectName("games_page")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.games_page)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.games = QtWidgets.QScrollArea(self.games_page)
self.games.setWidgetResizable(True)
self.games.setObjectName("games")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 462, 503))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.games.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout_5.addWidget(self.games)
self.stack.addWidget(self.games_page)
self.error = QtWidgets.QWidget()
self.error.setObjectName("error")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.error)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.error_label = QtWidgets.QLabel(self.error)
self.error_label.setObjectName("error_label")
self.verticalLayout_6.addWidget(self.error_label)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_6.addItem(spacerItem)
self.stack.addWidget(self.error)
self.horizontalLayout_2.addWidget(self.stack)
self.filter_scroll = QtWidgets.QScrollArea(browse_games)
self.filter_scroll.setMaximumSize(QtCore.QSize(200, 16777215))
self.filter_scroll.setWidgetResizable(True)
self.filter_scroll.setObjectName("filter_scroll")
self.scroll_widget = QtWidgets.QWidget()
self.scroll_widget.setGeometry(QtCore.QRect(0, 0, 198, 521))
self.scroll_widget.setObjectName("scroll_widget")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scroll_widget)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.price_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.price_gb.setObjectName("price_gb")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.price_gb)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.clear_price = QtWidgets.QRadioButton(self.price_gb)
self.clear_price.setChecked(True)
self.clear_price.setObjectName("clear_price")
self.verticalLayout_2.addWidget(self.clear_price)
self.free_button = QtWidgets.QRadioButton(self.price_gb)
self.free_button.setObjectName("free_button")
self.verticalLayout_2.addWidget(self.free_button)
self.under10 = QtWidgets.QRadioButton(self.price_gb)
self.under10.setObjectName("under10")
self.verticalLayout_2.addWidget(self.under10)
self.under20 = QtWidgets.QRadioButton(self.price_gb)
self.under20.setObjectName("under20")
self.verticalLayout_2.addWidget(self.under20)
self.under30 = QtWidgets.QRadioButton(self.price_gb)
self.under30.setObjectName("under30")
self.verticalLayout_2.addWidget(self.under30)
self.above = QtWidgets.QRadioButton(self.price_gb)
self.above.setObjectName("above")
self.verticalLayout_2.addWidget(self.above)
self.on_discount = QtWidgets.QRadioButton(self.price_gb)
self.on_discount.setObjectName("on_discount")
self.verticalLayout_2.addWidget(self.on_discount)
self.verticalLayout_3.addWidget(self.price_gb)
self.genre_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.genre_gb.setObjectName("genre_gb")
self.verticalLayout = QtWidgets.QVBoxLayout(self.genre_gb)
self.verticalLayout.setObjectName("verticalLayout")
self.verticalLayout_3.addWidget(self.genre_gb)
self.type_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.type_gb.setObjectName("type_gb")
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.type_gb)
self.verticalLayout_8.setObjectName("verticalLayout_8")
self.verticalLayout_3.addWidget(self.type_gb)
self.platform_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.platform_gb.setObjectName("platform_gb")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.platform_gb)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.verticalLayout_3.addWidget(self.platform_gb)
self.others_gb = QtWidgets.QGroupBox(self.scroll_widget)
self.others_gb.setObjectName("others_gb")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.others_gb)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.verticalLayout_3.addWidget(self.others_gb)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_3.addItem(spacerItem1)
self.filter_scroll.setWidget(self.scroll_widget)
self.horizontalLayout_2.addWidget(self.filter_scroll)
self.retranslateUi(browse_games)
self.stack.setCurrentIndex(0)
QtCore.QMetaObject.connectSlotsByName(browse_games)
def retranslateUi(self, browse_games):
_translate = QtCore.QCoreApplication.translate
browse_games.setWindowTitle(_translate("browse_games", "Form"))
self.error_label.setText(_translate("browse_games", "An error occured"))
self.price_gb.setTitle(_translate("browse_games", "Price"))
self.clear_price.setText(_translate("browse_games", "Clear price filter"))
self.free_button.setText(_translate("browse_games", "Free"))
self.under10.setText(_translate("browse_games", "Under 10"))
self.under20.setText(_translate("browse_games", "Under 20"))
self.under30.setText(_translate("browse_games", "Under 30"))
self.above.setText(_translate("browse_games", "14.99 and above"))
self.on_discount.setText(_translate("browse_games", "Discount"))
self.genre_gb.setTitle(_translate("browse_games", "Genre"))
self.type_gb.setTitle(_translate("browse_games", "Type"))
self.platform_gb.setTitle(_translate("browse_games", "Platform"))
self.others_gb.setTitle(_translate("browse_games", "Other Tags"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
browse_games = QtWidgets.QWidget()
ui = Ui_browse_games()
ui.setupUi(browse_games)
browse_games.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>browse_games</class>
<widget class="QWidget" name="browse_games">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<height>541</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QStackedWidget" name="stack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="games_page">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QScrollArea" name="games">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<height>503</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="error">
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="error_label">
<property name="text">
<string>An error occured</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QScrollArea" name="filter_scroll">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scroll_widget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>198</width>
<height>521</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="price_gb">
<property name="title">
<string>Price</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QRadioButton" name="clear_price">
<property name="text">
<string>Clear price filter</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="free_button">
<property name="text">
<string>Free</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under10">
<property name="text">
<string>Under 10</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under20">
<property name="text">
<string>Under 20</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="under30">
<property name="text">
<string>Under 30</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="above">
<property name="text">
<string>14.99 and above</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="on_discount">
<property name="text">
<string>Discount</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="genre_gb">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="type_gb">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_gb">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="others_gb">
<property name="title">
<string>Other Tags</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'shop_game_info.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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.
from PyQt5 import QtCore, QtWidgets
class Ui_shop_info(object):
def setupUi(self, shop_info):
shop_info.setObjectName("shop_info")
shop_info.resize(702, 468)
self.verticalLayout = QtWidgets.QVBoxLayout(shop_info)
self.verticalLayout.setObjectName("verticalLayout")
self.back_button = QtWidgets.QPushButton(shop_info)
self.back_button.setObjectName("back_button")
self.verticalLayout.addWidget(self.back_button)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.image_stack = QtWidgets.QStackedWidget(shop_info)
self.image_stack.setObjectName("image_stack")
self.horizontalLayout.addWidget(self.image_stack)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.title = QtWidgets.QLabel(shop_info)
self.title.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.title.setObjectName("title")
self.verticalLayout_2.addWidget(self.title)
self.dev = QtWidgets.QLabel(shop_info)
self.dev.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.dev.setObjectName("dev")
self.verticalLayout_2.addWidget(self.dev)
self.owned_label = QtWidgets.QLabel(shop_info)
self.owned_label.setObjectName("owned_label")
self.verticalLayout_2.addWidget(self.owned_label)
self.price = QtWidgets.QLabel(shop_info)
self.price.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse)
self.price.setObjectName("price")
self.verticalLayout_2.addWidget(self.price)
self.discount_price = QtWidgets.QLabel(shop_info)
self.discount_price.setObjectName("discount_price")
self.verticalLayout_2.addWidget(self.discount_price)
self.tags = QtWidgets.QLabel(shop_info)
self.tags.setObjectName("tags")
self.verticalLayout_2.addWidget(self.tags)
self.open_store_button = QtWidgets.QPushButton(shop_info)
self.open_store_button.setObjectName("open_store_button")
self.verticalLayout_2.addWidget(self.open_store_button)
self.wishlist_button = QtWidgets.QPushButton(shop_info)
self.wishlist_button.setObjectName("wishlist_button")
self.verticalLayout_2.addWidget(self.wishlist_button)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.horizontalLayout.addLayout(self.verticalLayout_2)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.req_group_box = QtWidgets.QGroupBox(shop_info)
self.req_group_box.setObjectName("req_group_box")
self.gridLayout_2 = QtWidgets.QGridLayout(self.req_group_box)
self.gridLayout_2.setObjectName("gridLayout_2")
self.verticalLayout.addWidget(self.req_group_box)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem2)
self.retranslateUi(shop_info)
self.image_stack.setCurrentIndex(-1)
QtCore.QMetaObject.connectSlotsByName(shop_info)
def retranslateUi(self, shop_info):
_translate = QtCore.QCoreApplication.translate
shop_info.setWindowTitle(_translate("shop_info", "Form"))
self.back_button.setText(_translate("shop_info", "Back"))
self.title.setText(_translate("shop_info", "Error"))
self.dev.setText(_translate("shop_info", "TextLabel"))
self.owned_label.setText(_translate("shop_info", "You already own this game"))
self.price.setText(_translate("shop_info", "TextLabel"))
self.discount_price.setText(_translate("shop_info", "TextLabel"))
self.tags.setText(_translate("shop_info", "TextLabel"))
self.open_store_button.setText(_translate("shop_info", "Buy Game in Epic Games Store"))
self.wishlist_button.setText(_translate("shop_info", "Add to wishlist"))
self.req_group_box.setTitle(_translate("shop_info", "Requirements"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
shop_info = QtWidgets.QWidget()
ui = Ui_shop_info()
ui.setupUi(shop_info)
shop_info.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>shop_info</class>
<widget class="QWidget" name="shop_info">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>702</width>
<height>468</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="back_button">
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QStackedWidget" name="image_stack">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="title">
<property name="text">
<string>Error</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dev">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="owned_label">
<property name="text">
<string>You already own this game</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="price">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="discount_price">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tags">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="open_store_button">
<property name="text">
<string>Buy Game in Epic Games Store</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="wishlist_button">
<property name="text">
<string>Add to wishlist</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="req_group_box">
<property name="title">
<string>Requirements</string>
</property>
<layout class="QGridLayout" name="gridLayout_2"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'store.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# 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.
from PyQt5 import QtCore, QtWidgets
class Ui_ShopWidget(object):
def setupUi(self, ShopWidget):
ShopWidget.setObjectName("ShopWidget")
ShopWidget.resize(697, 362)
self.verticalLayout = QtWidgets.QVBoxLayout(ShopWidget)
self.verticalLayout.setObjectName("verticalLayout")
self.scrollArea = QtWidgets.QScrollArea(ShopWidget)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 677, 342))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.free_game_group_box = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
self.free_game_group_box.setObjectName("free_game_group_box")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.free_game_group_box)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.free_games_stack = QtWidgets.QStackedWidget(self.free_game_group_box)
self.free_games_stack.setObjectName("free_games_stack")
self.verticalLayout_2.addWidget(self.free_games_stack)
self.verticalLayout_3.addWidget(self.free_game_group_box)
self.wishlist_gb = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
self.wishlist_gb.setObjectName("wishlist_gb")
self.verticalLayout_3.addWidget(self.wishlist_gb)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_3.addItem(spacerItem)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout.addWidget(self.scrollArea)
self.retranslateUi(ShopWidget)
self.free_games_stack.setCurrentIndex(-1)
QtCore.QMetaObject.connectSlotsByName(ShopWidget)
def retranslateUi(self, ShopWidget):
_translate = QtCore.QCoreApplication.translate
ShopWidget.setWindowTitle(_translate("ShopWidget", "Form"))
self.free_game_group_box.setTitle(_translate("ShopWidget", "Free Games"))
self.wishlist_gb.setTitle(_translate("ShopWidget", "Discounts from your wishlist"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
ShopWidget = QtWidgets.QWidget()
ui = Ui_ShopWidget()
ui.setupUi(ShopWidget)
ShopWidget.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShopWidget</class>
<widget class="QWidget" name="ShopWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>697</width>
<height>362</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>677</width>
<height>342</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="free_game_group_box">
<property name="title">
<string>Free Games</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QStackedWidget" name="free_games_stack">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="wishlist_gb">
<property name="title">
<string>Discounts from your wishlist</string>
</property>
</widget>
</item>
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -1,13 +1,20 @@
import io
import os
from logging import getLogger
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal
from PyQt5.QtGui import QMovie
from PIL import Image
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal, QSettings
from PyQt5.QtGui import QMovie, QPixmap
from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QPushButton, \
QStyleOptionTab, QStylePainter, QTabBar
QStyleOptionTab, QStylePainter, QTabBar, QLineEdit, QToolButton
from qtawesome import icon
from rare import resources_path
from rare.ui.utils.pathedit import Ui_PathEdit
from rare.utils.qt_requests import QtRequestManager
logger = getLogger("ExtraWidgets")
class FlowLayout(QLayout):
@ -114,13 +121,6 @@ class FlowLayout(QLayout):
return parent.spacing()
class ClickableLabel(QLabel):
clicked = pyqtSignal()
def __init__(self):
super(ClickableLabel, self).__init__()
class PathEdit(QWidget, Ui_PathEdit):
def __init__(self,
text: str = "",
@ -257,3 +257,78 @@ class SelectViewWidget(QWidget):
self.list_view.setIcon(icon("fa5s.list", color="orange"))
self.icon_view = True
self.toggled.emit()
class ImageLabel(QLabel):
def __init__(self):
super(ImageLabel, self).__init__()
self.path = cache_dir
self.manager = QtRequestManager("bytes")
def update_image(self, url, name, size: tuple = (240, 320)):
self.setFixedSize(*size)
self.img_size = size
self.name = name
for c in r'<>?":|\/* ':
self.name = self.name.replace(c, "")
if self.img_size[0] > self.img_size[1]:
name_extension = "wide"
else:
name_extension = "tall"
self.name = f"{self.name}_{name_extension}.png"
if not os.path.exists(os.path.join(self.path, self.name)):
self.manager.get(url, self.image_ready)
# self.request.finished.connect(self.image_ready)
else:
self.show_image()
def image_ready(self, data):
try:
self.setPixmap(QPixmap())
except RuntimeError:
return
image: Image.Image = Image.open(io.BytesIO(data))
image = image.resize((self.img_size[0], self.img_size[1]))
if QSettings().value("cache_images", True, bool):
image.save(os.path.join(self.path, self.name), format="png")
byte_array = io.BytesIO()
image.save(byte_array, format="PNG")
# pixmap = QPixmap.fromImage(ImageQt(image))
pixmap = QPixmap()
pixmap.loadFromData(byte_array.getvalue())
# pixmap = QPixmap.fromImage(ImageQt.ImageQt(image))
self.setPixmap(pixmap)
def show_image(self):
self.image = QPixmap(os.path.join(self.path, self.name)).scaled(*self.img_size,
transformMode=Qt.SmoothTransformation)
self.setPixmap(self.image)
class ButtonLineEdit(QLineEdit):
buttonClicked = pyqtSignal()
def __init__(self, icon_name, placeholder_text: str, parent=None):
super(ButtonLineEdit, self).__init__(parent)
self.button = QToolButton(self)
self.button.setIcon(icon(icon_name, color="white"))
self.button.setStyleSheet('border: 0px; padding: 0px;')
self.button.setCursor(Qt.ArrowCursor)
self.button.clicked.connect(self.buttonClicked.emit)
self.setPlaceholderText(placeholder_text)
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
buttonSize = self.button.sizeHint()
self.setStyleSheet('QLineEdit {padding-right: %dpx; }' % (buttonSize.width() + frameWidth + 1))
self.setMinimumSize(max(self.minimumSizeHint().width(), buttonSize.width() + frameWidth * 2 + 2),
max(self.minimumSizeHint().height(), buttonSize.height() + frameWidth * 2 + 2))
def resizeEvent(self, event):
buttonSize = self.button.sizeHint()
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
self.button.move(self.rect().right() - frameWidth - buttonSize.width(),
(self.rect().bottom() - buttonSize.height() + 1) / 2)
super(ButtonLineEdit, self).resizeEvent(event)

92
rare/utils/qt_requests.py Normal file
View file

@ -0,0 +1,92 @@
import json
from dataclasses import dataclass
from logging import getLogger
from PyQt5.QtCore import QObject, pyqtSignal, QUrl, QJsonParseError, QJsonDocument
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
logger = getLogger("QtRequests")
class QtRequestManager(QObject):
data_ready = pyqtSignal(object)
request = None
request_active = None
def __init__(self, type: str = "json", authorization_token: str = None):
super(QtRequestManager, self).__init__()
self.manager = QNetworkAccessManager()
self.type = type
self.authorization_token = authorization_token
self.request_queue = []
def post(self, url: str, payload: dict, handle_func):
if not self.request_active:
request = QNetworkRequest(QUrl(url))
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
self.request_active = RequestQueueItem(handle_func=handle_func)
payload = json.dumps(payload).encode("utf-8")
request.setHeader(QNetworkRequest.UserAgentHeader,
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36")
if self.authorization_token is not None:
request.setRawHeader(b"Authorization", self.authorization_token.encode())
self.request = self.manager.post(request, payload)
self.request.finished.connect(self.prepare_data)
else:
self.request_queue.append(
RequestQueueItem(method="post", url=url, payload=payload, handle_func=handle_func))
def get(self, url: str, handle_func: callable):
if not self.request_active:
request = QNetworkRequest(QUrl(url))
request.setHeader(QNetworkRequest.UserAgentHeader,
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36")
self.request_active = RequestQueueItem(handle_func=handle_func)
self.request = self.manager.get(request)
self.request.finished.connect(self.prepare_data)
else:
self.request_queue.append(RequestQueueItem(method="get", url=url, handle_func=handle_func))
def prepare_data(self):
# self.request_active = False
data = {} if self.type == "json" else b""
if self.request:
try:
if self.request.error() == QNetworkReply.NoError:
if self.type == "json":
error = QJsonParseError()
json_data = QJsonDocument.fromJson(self.request.readAll().data(), error)
if QJsonParseError.NoError == error.error:
data = json.loads(json_data.toJson().data().decode())
else:
logger.error(error.errorString())
else:
data = self.request.readAll().data()
except RuntimeError as e:
logger.error(str(e))
self.request_active.handle_func(data)
self.request.deleteLater()
self.request_active = None
if self.request_queue:
if self.request_queue[0].method == "post":
self.post(self.request_queue[0].url, self.request_queue[0].payload, self.request_queue[0].handle_func)
else:
self.get(self.request_queue[0].url, self.request_queue[0].handle_func)
self.request_queue.pop(0)
@dataclass
class RequestQueueItem:
method: str = None
url: str = None
handle_func: callable = None
payload: dict = None

View file

@ -9,6 +9,8 @@ from PyQt5.QtCore import QThread, pyqtSignal
from custom_legendary.core import LegendaryCore
from rare import cache_dir, data_dir
from rare import data_dir, cache_dir
replace_chars = ",;.:-_ "
file = os.path.join(cache_dir, "game_list.json")
@ -75,6 +77,7 @@ def get_grade(steam_code):
def load_json() -> dict:
if not os.path.exists(file):
response = requests.get(url)
steam_ids = json.loads(response.text)["applist"]["apps"]
ids = {}
@ -106,6 +109,7 @@ def get_steam_id(title: str):
ids = json.loads(open(file, "r").read())
if title in ids.keys():
steam_name = [title]
else:
steam_name = difflib.get_close_matches(title, ids.keys(), n=1)
if steam_name:

View file

@ -24,6 +24,7 @@ logger = getLogger("Utils")
s = QSettings("Rare", "Rare")
def download_images(signal: pyqtSignal, core: LegendaryCore):
if not os.path.isdir(image_dir):
os.makedirs(image_dir)
@ -280,12 +281,12 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -
igame = core.get_installed_game(app_name)
if os.path.exists(
os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.expanduser('~/.cache/rare/images'), str),
os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join(data_dir, 'images'), str),
igame.app_name, 'Thumbnail.png')):
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.expanduser('~/.cache/rare/images'), str),
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join(data_dir, 'images'), str),
igame.app_name, 'Thumbnail')
else:
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.expanduser('~/.cache/rare/images'), str),
icon = os.path.join(QSettings('Rare', 'Rare').value('img_dir', os.path.join('images'), str),
igame.app_name, 'DieselGameBoxTall')
# Linux
if platform.system() == "Linux":
@ -329,7 +330,6 @@ def create_desktop_link(app_name, core: LegendaryCore, type_of_link="desktop") -
target = os.path.abspath(sys.argv[0])
# Name of link file
linkName = igame.title
for c in r'<>?":|\/*':
linkName.replace(c, "")
@ -376,3 +376,4 @@ def get_uninstalled_pixmap(app_name: str) -> QPixmap:
else:
pixmap = QPixmap()
return pixmap