1
0
Fork 0
mirror of synced 2024-06-23 08:40:45 +12:00

Better Code to browse games, use PIL images to save smaller

This commit is contained in:
Dummerle 2021-06-17 00:34:23 +02:00
parent c0f4577870
commit e7038be33a
9 changed files with 299 additions and 120 deletions

View file

@ -5,8 +5,9 @@ import random
from PyQt5.QtCore import QUrl, pyqtSignal, QJsonParseError, QJsonDocument
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
from PyQt5.QtWidgets import QWidget
from PyQt5.QtWidgets import QWidget, QCheckBox, QVBoxLayout, QLabel
from rare.components.tabs.shop.constants import game_query, Constants
from rare.components.tabs.shop.game_widgets import GameWidget
from rare.ui.components.tabs.store.browse_games import Ui_browse_games
from rare.utils.extra_widgets import FlowLayout, WaitingSpinner
@ -18,9 +19,10 @@ logger = logging.getLogger("BrowseGames")
class BrowseGames(QWidget, Ui_browse_games):
show_game = pyqtSignal(dict)
price = ""
platform = (False, False)
tags = []
types = []
request_active = False
next_request = ()
next_request = False
def __init__(self, path):
super(BrowseGames, self).__init__()
@ -44,20 +46,41 @@ class BrowseGames(QWidget, Ui_browse_games):
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)
self.win_cb.toggled.connect(
lambda: self.prepare_request(platform=(self.win_cb.isChecked(), self.mac_cb.isChecked())))
self.mac_cb.toggled.connect(
lambda: self.prepare_request(platform=(self.win_cb.isChecked(), self.mac_cb.isChecked())))
constants = Constants()
def prepare_request(self, price: str = None, platform: tuple = None):
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 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 platform is not None:
self.platform = platform
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)
if self.request_active:
self.next_request = (self.price, self.platform)
self.next_request = True
return
locale = get_lang()
@ -78,12 +101,10 @@ class BrowseGames(QWidget, Ui_browse_games):
elif self.price == "sale":
payload["variables"]["onSale"] = True
if self.platform[0]:
payload["variables"]["tag"] = "9547"
if self.platform[1]:
payload["variables"]["tag"] += "|10719"
elif self.platform[1]:
payload["variables"]["tag"] = "10719"
payload["variables"]["tag"] = "|".join(self.tags)
if self.types:
payload["variables"]["category"] = "|".join(self.types)
request = QNetworkRequest(QUrl("https://www.epicgames.com/graphql"))
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
@ -92,7 +113,6 @@ class BrowseGames(QWidget, Ui_browse_games):
self.game_request.finished.connect(self.show_games)
def show_games(self):
if self.game_request:
if self.game_request.error() == QNetworkReply.NoError:
error = QJsonParseError()
@ -107,17 +127,23 @@ class BrowseGames(QWidget, Ui_browse_games):
self.stack.setCurrentIndex(1)
else:
QWidget().setLayout(self.games_widget.layout())
self.games_widget.setLayout(FlowLayout())
if games:
self.games_widget.setLayout(FlowLayout())
for game in games:
w = GameWidget(self.path, game, 275)
self.games_widget.layout().addWidget(w)
w.show_info.connect(self.show_game.emit)
for game in games:
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)
self.request_active = False
if self.next_request:
self.prepare_request(*self.next_request)
self.prepare_request()
self.next_request = ()
return
@ -127,40 +153,24 @@ class BrowseGames(QWidget, Ui_browse_games):
else:
logger.error(self.game_request.errorString())
if self.next_request:
self.prepare_request(*self.next_request)
self.next_request = ()
self.prepare_request()
self.next_request = False
else:
self.stack.setCurrentIndex(1)
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 "
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,74 @@
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 "

View file

@ -1,7 +1,10 @@
import io
import json
import logging
import os
from PIL import Image
from PIL.ImageQt import ImageQt
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSignal, QSettings, Qt, QUrl, QJsonParseError, QJsonDocument
from PyQt5.QtGui import QPixmap
@ -29,6 +32,15 @@ class GameWidget(QWidget):
self.path = path
self.layout = QVBoxLayout()
self.image = ImageLabel()
self.layout.addWidget(self.image)
self.title_label = QLabel(json_info["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"]
@ -59,25 +71,20 @@ class GameWidget(QWidget):
# No image found
logger.error(f"No image found for {self.title}")
self.layout.addWidget(self.image)
self.title_label = QLabel(json_info["title"])
self.title_label.setWordWrap(True)
self.layout.addWidget(self.title_label)
self.setLayout(self.layout)
def image_ready(self, save: bool):
if self.image_request:
if self.image_request.error() == QNetworkReply.NoError:
data = self.image_request.readAll().data()
image: Image.Image = Image.open(io.BytesIO(data))
image = image.resize((self.width, int(self.width * 9 / 16)))
if save:
with open(os.path.join(self.path, f"{self.title}_wide.png"), "wb") as file:
file.write(data)
file.close()
image.save(os.path.join(self.path, self.json_info["title"] + "_wide.png"), format="png")
pixmap = QPixmap()
pixmap.loadFromData(data)
self.image.setPixmap(pixmap.scaled(self.width, int(self.width * 9 / 16),
transformMode=Qt.SmoothTransformation))
pixmap.fromImage(ImageQt(image))
self.image.setPixmap(pixmap)
def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
self.show_info.emit(self.json_info)

View file

@ -96,17 +96,20 @@ class ShopGameInfo(QWidget, Ui_shop_info):
min_label.setFont(bold_font)
rec_label = QLabel(self.tr("Recommend"))
rec_label.setFont(bold_font)
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)
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))

View file

@ -63,7 +63,8 @@ class ShopGame:
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"]["systems"]):
print(api_data["data"]["requirements"])
for i, system in enumerate(api_data["data"]["requirements"].get("systems", [])):
try:
tmp.reqs[system["systemType"]] = {}
except KeyError:

View file

@ -0,0 +1,57 @@
import requests
from PyQt5.QtCore import QThread, pyqtSignal
from custom_legendary.core import LegendaryCore
from rare.components.tabs.shop.browse_games import game_query
from rare.components.tabs.shop.shop_models import ShopGame
from rare.utils.utils import get_lang
class AnalyzeThread(QThread):
get_tags = pyqtSignal(dict)
def __init__(self, core: LegendaryCore):
super(AnalyzeThread, self).__init__()
self.tags = {}
self.core = core
def run(self) -> None:
locale = get_lang()
for igame in self.core.get_installed_list():
data = {
"query": game_query,
"variables": {"category": "games/edition/base|bundles/games|editors|software/edition/base",
"count": 20,
"country": locale.upper(), "keywords": igame.title, "locale": locale, "sortDir": "DESC",
"allowCountries": locale.upper(),
"start": 0, "tag": "", "withMapping": False, "withPrice": True}
}
try:
search_game = \
requests.post("https://www.epicgames.com/graphql", json=data).json()["data"]["Catalog"]["searchStore"][
"elements"][0]
except:
continue
slug = search_game["productSlug"]
is_bundle = False
for i in search_game["categories"]:
if "bundles" in i.get("path", ""):
is_bundle = True
api_game = requests.get(
f"https://store-content.ak.epicgames.com/api/{locale}/content/{'products' if not is_bundle else 'bundles'}/{slug}").json()
if api_game.get("error"):
print(igame.title)
continue
game = ShopGame.from_json(api_game, search_game)
for i in game.tags:
if i not in self.tags.keys():
self.tags[i] = 1
else:
self.tags[i] += 1
print(self.tags)

View file

@ -79,17 +79,26 @@ class Ui_browse_games(object):
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.win_cb = QtWidgets.QCheckBox(self.platform_gb)
self.win_cb.setObjectName("win_cb")
self.verticalLayout_7.addWidget(self.win_cb)
self.mac_cb = QtWidgets.QCheckBox(self.platform_gb)
self.mac_cb.setObjectName("mac_cb")
self.verticalLayout_7.addWidget(self.mac_cb)
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)
@ -111,9 +120,10 @@ class Ui_browse_games(object):
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.win_cb.setText(_translate("browse_games", "Windows"))
self.mac_cb.setText(_translate("browse_games", "Mac"))
self.others_gb.setTitle(_translate("browse_games", "Other Tags"))
if __name__ == "__main__":

View file

@ -148,37 +148,46 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="platform_gb">
<property name="title">
<string>Platform</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QCheckBox" name="win_cb">
<property name="text">
<string>Windows</string>
</property>
</widget>
<widget class="QGroupBox" name="genre_gb">
<property name="title">
<string>Genre</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mac_cb">
<property name="text">
<string>Mac</string>
</property>
</widget>
<widget class="QGroupBox" name="type_gb">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8"/>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<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>

View file

@ -1,6 +1,9 @@
import io
import os
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal, QUrl
from PIL import Image
from PIL.ImageQt import ImageQt
from PyQt5.QtCore import Qt, QRect, QSize, QPoint, pyqtSignal, QUrl, QSettings
from PyQt5.QtGui import QMovie, QPixmap
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtWidgets import QLayout, QStyle, QSizePolicy, QLabel, QFileDialog, QHBoxLayout, QWidget, QPushButton, \
@ -284,10 +287,15 @@ class ImageLabel(QLabel):
if self.request:
if self.request.error() == QNetworkReply.NoError:
with open(os.path.join(self.path, self.name), "wb") as file:
file.write(self.request.readAll().data())
file.close()
self.show_image()
data = self.request.readAll().data()
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")
pixmap = QPixmap()
pixmap.fromImage(ImageQt(image))
self.setPixmap(pixmap)
else:
return