From 89afebd9fd7160f7ebfb7a291c9187e0d426525c Mon Sep 17 00:00:00 2001 From: Dummerle Date: Wed, 9 Jun 2021 00:00:00 +0200 Subject: [PATCH] Show search results: Not pretty: no images --- rare/components/tabs/shop/__init__.py | 15 +++- rare/components/tabs/shop/search_results.py | 49 ++++++++++++ rare/components/tabs/shop/shop_info.py | 33 +++++--- rare/components/tabs/shop/shop_widget.py | 84 ++++++++++----------- rare/utils/models.py | 45 ++++++----- 5 files changed, 153 insertions(+), 73 deletions(-) create mode 100644 rare/components/tabs/shop/search_results.py diff --git a/rare/components/tabs/shop/__init__.py b/rare/components/tabs/shop/__init__.py index b7d85f0d..d363e6b6 100644 --- a/rare/components/tabs/shop/__init__.py +++ b/rare/components/tabs/shop/__init__.py @@ -1,5 +1,6 @@ from PyQt5.QtWidgets import QStackedWidget +from rare.components.tabs.shop.search_results import SearchResults from rare.components.tabs.shop.shop_info import ShopGameInfo from rare.components.tabs.shop.shop_widget import ShopWidget @@ -13,17 +14,27 @@ class Shop(QStackedWidget): self.shop = ShopWidget() self.addWidget(self.shop) + self.search_results = SearchResults() + self.addWidget(self.search_results) + self.search_results.show_info.connect(self.show_game_info) + self.info = ShopGameInfo() self.addWidget(self.info) self.info.back_button.clicked.connect(lambda: self.setCurrentIndex(0)) self.shop.show_info.connect(self.show_info) + self.shop.show_game.connect(self.show_game_info) def load(self): if not self.init: self.init = True self.shop.load() - def show_info(self, slug): - self.info.update_game(slug) + def show_game_info(self, data): + self.info.update_game(data) + self.setCurrentIndex(2) + + def show_info(self, data): + self.search_results.show_results(data) self.setCurrentIndex(1) + diff --git a/rare/components/tabs/shop/search_results.py b/rare/components/tabs/shop/search_results.py new file mode 100644 index 00000000..2cedd48d --- /dev/null +++ b/rare/components/tabs/shop/search_results.py @@ -0,0 +1,49 @@ +from PyQt5 import QtGui +from PyQt5.QtCore import pyqtSignal +from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QScrollArea + + +class SearchResults(QScrollArea): + show_info = pyqtSignal(dict) + + def __init__(self): + super(SearchResults, self).__init__() + self.widget = QWidget() + self.layout = QVBoxLayout() + self.widget.setLayout(self.layout) + self.setWidget(self.widget) + + def show_results(self, results: list): + QVBoxLayout().addWidget(self.widget) + self.widget = QWidget() + self.layout = QVBoxLayout() + for i in range(self.layout.count()): + self.layout.removeItem(i) + for res in results: + w = _SearchResultItem(res) + w.show_info.connect(self.show_info.emit) + self.layout.addWidget(w) + self.layout.addStretch(1) + self.widget.setLayout(self.layout) + self.setWidget(self.widget) + + +class _SearchResultItem(QWidget): + res: dict + show_info = pyqtSignal(dict) + + def __init__(self, result: dict): + super(_SearchResultItem, self).__init__() + self.layout = QHBoxLayout() + self.res = result + self.title = QLabel(self.res["title"]) + self.layout.addWidget(self.title) + original_price = result['price']['totalPrice']['fmtPrice']['originalPrice'] + self.price = QLabel(f"{self.tr('Original price: ')}{original_price}") + self.layout.addWidget(self.price) + + self.setLayout(self.layout) + + def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None: + if a0.button() == 1: + self.show_info.emit(self.res) diff --git a/rare/components/tabs/shop/shop_info.py b/rare/components/tabs/shop/shop_info.py index 8bdc92bd..fbe970bc 100644 --- a/rare/components/tabs/shop/shop_info.py +++ b/rare/components/tabs/shop/shop_info.py @@ -1,20 +1,19 @@ import json import logging -import os import webbrowser -import requests -from PyQt5.QtCore import QLocale, QUrl, QJsonDocument, QJsonParseError, Qt +from PyQt5.QtCore import QLocale, QUrl, QJsonDocument, QJsonParseError from PyQt5.QtGui import QPixmap from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply from PyQt5.QtWidgets import QWidget -from rare.utils import models from rare.ui.components.tabs.store.shop_game_info import Ui_shop_info +from rare.utils import models class ShopGameInfo(QWidget, Ui_shop_info): - slug = "" + game: models.ShopGame + data: dict # TODO GANZ VIEL def __init__(self): @@ -23,12 +22,18 @@ class ShopGameInfo(QWidget, Ui_shop_info): self.open_store_button.clicked.connect(self.button_clicked) self.manager = QNetworkAccessManager() - def update_game(self, slug: str): - self.title.setText(self.tr("Loading")) - self.price.setText(self.tr("Loading")) + def update_game(self, data: dict): + slug = data["productSlug"] + if "/home" in slug: + slug = slug.replace("/home", "") + self.slug = slug + self.title.setText(data["title"]) + self.price.setText(data['price']['totalPrice']['fmtPrice']['originalPrice']) self.dev.setText(self.tr("Loading")) self.image.setPixmap(QPixmap()) - self.slug = slug + self.data = data + + # init API request locale = QLocale.system().name().split("_")[0] url = f"https://store-content.ak.epicgames.com/api/{locale}/content/products/{slug}" # game = api_utils.get_product(slug, locale) @@ -37,7 +42,7 @@ class ShopGameInfo(QWidget, Ui_shop_info): # self.request.finished.connect(self.request.deleteLater if self.request else None) def data_received(self): - logging.info(f"Data of game {self.slug} received") + logging.info(f"Data of game {self.data['title']} received") if self.request: if self.request.error() == QNetworkReply.NoError: error = QJsonParseError() @@ -52,19 +57,23 @@ class ShopGameInfo(QWidget, Ui_shop_info): return else: return - self.game = models.ShopGame.from_json(game) + self.game = models.ShopGame.from_json(game, self.data) # print(game) self.title.setText(self.game.title) - + """ if not os.path.exists(path := os.path.expanduser(f"~/.cache/rare/cache/{self.game.title}.png")): url = game["pages"][0]["_images_"][0] open(os.path.expanduser(path), "wb").write(requests.get(url).content) width = 360 self.image.setPixmap(QPixmap(path).scaled(width, int(width * 9 / 16), transformMode=Qt.SmoothTransformation)) + """ try: self.dev.setText(",".join(self.game.developer)) except KeyError: pass + + # self.price.setText(self.game.price) + self.request.deleteLater() def button_clicked(self): diff --git a/rare/components/tabs/shop/shop_widget.py b/rare/components/tabs/shop/shop_widget.py index 06ad233b..3be1a464 100644 --- a/rare/components/tabs/shop/shop_widget.py +++ b/rare/components/tabs/shop/shop_widget.py @@ -18,7 +18,8 @@ from rare.utils.utils import get_lang class ShopWidget(QWidget, Ui_ShopWidget): - show_info = pyqtSignal(str) + show_info = pyqtSignal(list) + show_game = pyqtSignal(dict) free_game_widgets = [] def __init__(self): @@ -32,12 +33,16 @@ class ShopWidget(QWidget, Ui_ShopWidget): self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.search.setCompleter(self.completer) - self.search.returnPressed.connect(self.show_game) + self.search.returnPressed.connect(self.show_search_result) self.data = [] def load(self): - if not os.path.exists(p := os.path.expanduser(f"~/.cache/rare/cache/")): - os.makedirs(p) + if p := os.getenv("XDG_CACHE_HOME"): + self.path = p + else: + self.path = os.path.expanduser("~/.cache/rare/cache/") + if not os.path.exists(self.path): + os.makedirs(self.path) url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions" self.free_game_request = self.manager.get(QNetworkRequest(QUrl(url))) self.free_game_request.readyRead.connect(self.add_free_games) @@ -50,8 +55,8 @@ class ShopWidget(QWidget, Ui_ShopWidget): if self.free_game_request.error() == QNetworkReply.NoError: try: free_games = json.loads(self.free_game_request.readAll().data().decode()) + print(free_games) except JSONDecodeError: - print(self.free_game_request.readAll().data().decode()) return else: return @@ -68,7 +73,8 @@ class ShopWidget(QWidget, Ui_ShopWidget): try: # parse datetime end_date = datetime.datetime.strptime( - game["promotions"].get("promotionalOffers", game["promotions"].get("upcomingPromotionalOffers"))[0]["promotionalOffers"][0]["endDate"], + game["promotions"].get("promotionalOffers", game["promotions"].get("upcomingPromotionalOffers"))[0][ + "promotionalOffers"][0]["endDate"], '%Y-%m-%dT%H:%M:%S.%fZ') start_date = datetime.datetime.strptime( game["promotions"].get("promotionalOffers", game["promotions"].get("upcomingPromotionalOffers"))[0][ @@ -89,21 +95,21 @@ class ShopWidget(QWidget, Ui_ShopWidget): coming_free_games.append(game) for free_game in free_games_now: - w = GameWidget(free_game) - w.show_info.connect(self.show_info) + w = GameWidget(free_game, self.path) + w.show_info.connect(lambda x: self.search_games(x, True)) self.free_game_now.layout().addWidget(w) self.free_game_widgets.append(w) self.free_game_group_box_2.setMinimumHeight(200) for free_game in coming_free_games: - w = GameWidget(free_game) + w = GameWidget(free_game, self.path) if free_game["title"] != "Mystery Game": w.show_info.connect(self.show_info) self.comming_free_game.layout().addWidget(w) self.free_game_widgets.append(w) self.free_games_stack.setCurrentIndex(0) - def search_games(self, text): + def search_games(self, text, show_direct=False): if text == "": self.search_results.setVisible(False) else: @@ -123,11 +129,9 @@ class ShopWidget(QWidget, Ui_ShopWidget): request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") self.search_request = self.manager.post(request, payload) # self.search_request = self.manager.post(QNetworkRequest(QUrl("https://www.epicgames.com/graphql")), payload) - self.search_request.readyRead.connect(self.show_search_results) - self.search_request.finished.connect( - self.search_request.deleteLater if self.search_request else None) + self.search_request.finished.connect(lambda: self.show_search_results(show_direct)) - def show_search_results(self): + def show_search_results(self, show_direct=False): if self.search_request: if self.search_request.error() == QNetworkReply.NoError: error = QJsonParseError() @@ -138,72 +142,68 @@ class ShopWidget(QWidget, Ui_ShopWidget): logging.error(error.errorString()) self.search_results.setVisible(False) return - #response = .decode(encoding="utf-8") - #print(response) - #results = json.loads(response) + # response = .decode(encoding="utf-8") + # print(response) + # results = json.loads(response) else: return else: return self.data = data + if show_direct: + self.show_search_result(True) + return titles = [i.get("title") for i in data] model = QStringListModel() model.setStringList(titles) self.completer.setModel(model) - self.completer.popup() + # self.completer.popup() # self.search_results.setLayout(layout) # self.search_results.setVisible(True) + if self.search_request: + self.search_request.deleteLater() - def show_game(self): - if self.data: - slug = self.data[0].get("productSlug") - self.show_info.emit(slug) - - -class SearchResultItem(QWidget): - def __init__(self, json_info): - super(SearchResultItem, self).__init__() - self.layout = QHBoxLayout() - self.title = QLabel(json_info.get("title", "undefined")) - self.layout.addWidget(self.title) - self.slug = json_info.get("productSlug", "undefined") - - self.setLayout(self.layout) + def show_search_result(self, show_direct=False): + if not show_direct: + if self.data: + self.show_info.emit(self.data) + else: + self.show_game.emit(self.data[0]) class GameWidget(QWidget): show_info = pyqtSignal(str) - def __init__(self, json_info): + def __init__(self, json_info, path: str): super(GameWidget, self).__init__() self.layout = QVBoxLayout() self.image = QLabel() self.slug = json_info["productSlug"] - if not os.path.exists(path := os.path.expanduser(f"~/.cache/rare/cache/{json_info['title']}.png")): + self.title = json_info["title"] + if not os.path.exists(p := os.path.join(path, f"{json_info['title']}.png")): for img in json_info["keyImages"]: if json_info["title"] != "Mystery Game": if img["type"] == "DieselStoreFrontWide": - with open(path, "wb") as img_file: + with open(p, "wb") as img_file: content = requests.get(img["url"]).content img_file.write(content) break else: if img["type"] == "VaultClosed": - with open(path, "wb") as img_file: + with open(p, "wb") as img_file: content = requests.get(img["url"]).content img_file.write(content) break else: print("No image found") width = 300 - self.image.setPixmap(QPixmap(os.path.expanduser(f"~/.cache/rare/cache/{json_info['title']}.png")) + self.image.setPixmap(QPixmap(os.path.join(path, f"{json_info['title']}.png")) .scaled(width, int(width * 9 / 16), transformMode=Qt.SmoothTransformation)) self.layout.addWidget(self.image) - self.title = QLabel(json_info["title"]) - self.layout.addWidget(self.title) - + self.title_label = QLabel(json_info["title"]) + self.layout.addWidget(self.title_label) self.setLayout(self.layout) def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None: - self.show_info.emit(self.slug) + self.show_info.emit(self.title) diff --git a/rare/utils/models.py b/rare/utils/models.py index cd2f6685..416c90d5 100644 --- a/rare/utils/models.py +++ b/rare/utils/models.py @@ -39,7 +39,8 @@ class InstallQueueItemModel: class ShopGame: # TODO: Copyrights etc def __init__(self, title: str = "", image_urls: dict = None, social_links: dict = None, - langs: list = None, reqs: list = None, publisher: str = "", developer: str = ""): + langs: list = None, reqs: list = None, publisher: str = "", developer: str = "", + price: str = ""): self.title = title self.image_urls = image_urls self.links = [] @@ -53,32 +54,42 @@ class ShopGame: self.reqs = reqs # {"Betriebssystem":win7, processor:i9 9900k, ram...}; Note: name from language self.publisher = publisher self.developer = developer + self.price = price @classmethod - def from_json(cls, data: dict): - if isinstance(data, list): - for product in data: + def from_json(cls, api_data: dict, search_data: dict): + print(api_data) + if isinstance(api_data, list): + for product in api_data: if product["_title"] == "home": - data = product + api_data = product + print("home") break + tmp = cls() - tmp.title = data.get("productName", "undefined") - tmp.img_urls = { + tmp.title = api_data.get("productName", "undefined") + """tmp.img_urls = { "DieselImage": data["pages"][0]["data"]["about"]["image"]["src"], "banner": data["pages"][0]["data"]["hero"]["backgroundImageUrl"] - } - links = data["pages"][0]["data"]["socialLinks"] + }""" + links = api_data["pages"][0]["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 = data["pages"][0]["data"]["requirements"]["languages"] + tmp.available_voice_langs = api_data["pages"][0]["data"]["requirements"]["languages"] tmp.reqs = [] - """for system in data["pages"][0]["data"]["requirements"]["systems"]: - tmp.reqs.append({"name": system, "value": []}) - for i in system: - tmp.reqs[system].append(tuple((i[system]["minimum"], i[system]["recommend"]))) - """ - tmp.publisher = data["pages"][0]["data"]["meta"].get("publisher", "undefined") - tmp.developer = data["pages"][0]["data"]["meta"].get("developer", "undefined") + for i, system in enumerate(api_data["pages"][0]["data"]["requirements"]["systems"]): + tmp.reqs.append({"name": system["systemType"], "value": []}) + for req in system["details"]: + tmp.reqs[i]["value"].append(tuple((req["minimum"], req["recommended"]))) + + tmp.publisher = api_data["pages"][0]["data"]["meta"].get("publisher", "undefined") + tmp.developer = api_data["pages"][0]["data"]["meta"].get("developer", "undefined") + tmp.price = { + "normal": search_data["price"]["totalPrice"]["originalPrice"] + } + if price := search_data["price"]["totalPrice"].get("discountPrice"): + tmp.price["discount"] = price + return tmp