Skip to content

Commit

Permalink
Merge pull request #573 from orende/feature/search-by-category
Browse files Browse the repository at this point in the history
Feature: Filter games by category
  • Loading branch information
sharkwouter authored Jul 4, 2023
2 parents 93d6395 + 6a9682a commit d145c37
Show file tree
Hide file tree
Showing 23 changed files with 420 additions and 62 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Added additional tooltips to buttons, labels, menu items and radio buttons (thanks to orende)
- Hide CDPR Goodie Pack Content
- Add notifications on successful download and installation of games (thanks to orende)
- Add category filtering dialog for game library (thanks to orende)

**1.2.2**
- Fix progress bar not showing up for downloads
Expand Down
23 changes: 22 additions & 1 deletion data/ui/application.ui
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,27 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="category_filter_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes" context="category_filter" comments="Tooltip for category filter button">Category filters</property>
<property name="valign">center</property>
<signal name="clicked" handler="on_menu_category_filter_clicked" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">view-conceal-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkSearchEntry" id="header_search">
<property name="visible">True</property>
Expand All @@ -153,7 +174,7 @@
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
</object>
Expand Down
86 changes: 86 additions & 0 deletions data/ui/categoryfilters.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<template class="CategoryFilters" parent="GtkDialog">
<property name="can_focus">False</property>
<property name="default_width">400</property>
<property name="type_hint">dialog</property>
<child type="titlebar">
<placeholder/>
</child>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can-focus">False</property>
<property name="spacing">18</property>
<child>
<object class="GtkGrid" id="genre_filtering_grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<property name="row-homogeneous">True</property>
<property name="column-homogeneous">True</property>

<!-- This space should be populated with filterswitch.ui components -->
<placeholder/>

</object>
</child>
<child>
<object class="GtkBox" id="category_filters_button_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">5</property>
<property name="halign">end</property>
<property name="valign">start</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<child>
<object class="GtkButton" id="reset_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="label" translatable="yes">Reset</property>
<signal name="clicked" handler="on_button_category_filters_reset_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="pack-type">start</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="label" translatable="yes">Cancel</property>
<signal name="clicked" handler="on_button_category_filters_cancel_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="pack-type">start</property>
</packing>
</child>
<child>
<object class="GtkButton" id="apply_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="label" translatable="yes">Apply</property>
<signal name="clicked" handler="on_button_category_filters_apply_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="pack-type">start</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>
32 changes: 32 additions & 0 deletions data/ui/filterswitch.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.24"/>
<template class="FilterSwitch" parent="GtkBox">
<property name="name">filterswitch</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">10</property>
<child>
<object class="GtkCheckButton" id="switch_category_filter">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="halign">end</property>
<property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkLabel" id="label_category_filter">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="no">Label text here</property>
<property name="justify">fill</property>
</object>
</child>
</template>
</interface>
52 changes: 26 additions & 26 deletions minigalaxy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from minigalaxy.game import Game
from minigalaxy.constants import IGNORE_GAME_IDS
from minigalaxy.config import Config
from minigalaxy.logger import logger


class NoDownloadLinkFound(BaseException):
Expand Down Expand Up @@ -102,9 +103,9 @@ def get_library(self):
else:
continue
if not product["url"]:
print("{} ({}) has no store page url".format(product["title"], product['id']))
logger.warn("{} ({}) has no store page url".format(product["title"], product['id']))
game = Game(name=product["title"], url=product["url"], game_id=product["id"],
image_url=product["image"], platform=platform)
image_url=product["image"], platform=platform, category=product["category"])
games.append(game)
if current_page == total_pages:
all_pages_processed = True
Expand Down Expand Up @@ -187,14 +188,14 @@ def get_download_file_info(self, url):
file_info.md5 = xml_data["md5"]
if "total_size" in xml_data.keys() and len(xml_data["total_size"]) > 0:
file_info.size = int(xml_data["total_size"])
except requests.exceptions.RequestException as e:
print("Couldn't retrieve file info. Encountered HTTP exception: {}".format(e))
except requests.exceptions.RequestException:
logger.error("Couldn't retrieve file info. Encountered HTTP exception: {}", exc_info=1)

if not file_info.md5:
print("Couldn't find md5 in xml checksum data")
logger.warn("Couldn't find md5 in xml checksum data")

if not file_info.size:
print("Couldn't find file size in xml checksum data")
logger.warn("Couldn't find file size in xml checksum data")

return file_info

Expand All @@ -207,11 +208,10 @@ def __get_xml_checksum(self, url):
if response_object and response_object.attrib:
result = response_object.attrib
else:
print("Couldn't read xml data. Response with code {} received with the following content: {}".format(
response.status_code, response.text
))
except requests.exceptions.RequestException as e:
print("Couldn't read xml data. Received RequestException : {}".format(e))
logger.error("Couldn't read xml data. Response with code %s received with the following content: %s",
response.status_code, response.text, exc_info=1)
except requests.exceptions.RequestException:
logger.error("Couldn't read xml data. Received RequestException", exc_info=1)
finally:
return result

Expand Down Expand Up @@ -259,7 +259,7 @@ def can_connect(self) -> bool:
def __request(self, url: str = None, params: dict = None) -> dict:
# Refresh the token if needed
if self.active_token_expiration_time < time.time():
print("Refreshing token")
logger.debug("Refreshing token")
refresh_token = self.config.refresh_token
self.config.refresh_token = self.__refresh_token(refresh_token)

Expand All @@ -271,27 +271,22 @@ def __request(self, url: str = None, params: dict = None) -> dict:
try:
response = self.session.get(url, headers=headers, params=params)
if self.debug:
print("Request: {}".format(url))
print("Return code: {}".format(response.status_code))
print("Response body: {}".format(response.text))
print("")
logger.debug("Request %s, return code %s, response body %s", url, response.status_code, response.text)
if response.status_code < 300:
result = response.json()
except requests.exceptions.RequestException as e:
print("Encountered exception while making HTTP request.")
print("Request: {}".format(url))
print("Exception: {}".format(e))
print("")
except requests.exceptions.RequestException:
logger.error("Encountered exception while making HTTP request. Request: %s", url, exc_info=1)
return result

def __request_gamesdb(self, game: Game):
request_url = "https://gamesdb.gog.com/platforms/gog/external_releases/{}".format(game.id)
try:
response = self.session.get(request_url)
respones_dict = response.json()
response_dict = response.json()
except (requests.exceptions.ConnectionError, ValueError):
respones_dict = {}
return respones_dict
logger.error("Error retrieving game info for gamesdb", exc_info=1)
response_dict = {}
return response_dict

def get_gamesdb_info(self, game: Game) -> dict:
gamesdb_dict = {"cover": "", "vertical_cover": "", "background": ""}
Expand All @@ -306,8 +301,13 @@ def get_gamesdb_info(self, game: Game) -> dict:
gamesdb_dict["summary"][summary_key] = response_json["game"]["summary"][summary_key]
gamesdb_dict["genre"] = {}
if len(response_json["game"]["genres"]) > 0:
for genre_key in response_json["game"]["genres"][0]["name"]:
gamesdb_dict["genre"][genre_key] = response_json["game"]["genres"][0]["name"][genre_key]
for genre in response_json["game"]["genres"]:
for genre_key, genre_value in genre["name"].items():
if genre_key in gamesdb_dict["genre"] and len(gamesdb_dict["genre"][genre_key]) > 0:
gamesdb_dict["genre"][genre_key] += ', '
else:
gamesdb_dict["genre"][genre_key] = ''
gamesdb_dict["genre"][genre_key] += genre["name"][genre_key]
else:
gamesdb_dict["summary"] = {}
gamesdb_dict["genre"] = {}
Expand Down
3 changes: 2 additions & 1 deletion minigalaxy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
from typing import List

from minigalaxy.logger import logger
from minigalaxy.paths import CONFIG_FILE_PATH, DEFAULT_INSTALL_DIR


Expand All @@ -21,7 +22,7 @@ def __load(self) -> None:
try:
self.__config = json.loads(file.read())
except json.decoder.JSONDecodeError:
print("Reading config.json failed, creating new config file.")
logger.warn("Reading config.json failed, creating new config file.")
os.remove(self.__config_file)

def __write(self) -> None:
Expand Down
3 changes: 2 additions & 1 deletion minigalaxy/css.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from minigalaxy.logger import logger
from minigalaxy.ui.gtk import Gtk, Gdk
from minigalaxy.paths import CSS_PATH

Expand All @@ -9,5 +10,5 @@ def load_css():
with open(CSS_PATH) as style:
CSS_PROVIDER.load_from_data(style.read().encode('utf-8'))
except Exception:
print("The CSS in {} could not be loaded".format(CSS_PATH))
logger.error("The CSS in %s could not be loaded", CSS_PATH, exc_info=1)
Gtk.StyleContext().add_provider_for_screen(Gdk.Screen.get_default(), CSS_PROVIDER, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
4 changes: 2 additions & 2 deletions minigalaxy/download_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def __download_file(self, download, download_queue):
result = self.__download_operation(download, start_point, download_mode)
break
except RequestException as e:
print(e)
self.logger.error("Error downloading file {}, received error {}".format(download.url, e))
download_attempt += 1
# Successful downloads
if result:
Expand Down Expand Up @@ -364,7 +364,7 @@ def __download_operation(self, download, start_point, download_mode):
try:
file_size = int(download_request.headers.get('content-length'))
except (ValueError, TypeError):
print(f"Couldn't get file size for {download.save_location}. No progress will be shown.")
self.logger.error(f"Couldn't get file size for {download.save_location}. No progress will be shown.")
result = True
if file_size is None or downloaded_size < file_size:
with open(download.save_location, download_mode) as save_file:
Expand Down
3 changes: 2 additions & 1 deletion minigalaxy/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class Game:
def __init__(self, name: str, url: str = "", md5sum=None, game_id: int = 0, install_dir: str = "",
image_url="", platform="linux", dlcs=None):
image_url="", platform="linux", dlcs=None, category=""):
self.name = name
self.url = url
self.md5sum = {} if md5sum is None else md5sum
Expand All @@ -16,6 +16,7 @@ def __init__(self, name: str, url: str = "", md5sum=None, game_id: int = 0, inst
self.image_url = image_url
self.platform = platform
self.dlcs = [] if dlcs is None else dlcs
self.category = category
self.status_file_path = self.get_status_file_path()

def get_stripped_name(self):
Expand Down
12 changes: 6 additions & 6 deletions minigalaxy/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import subprocess
import hashlib
import textwrap
import traceback

from minigalaxy.game import Game
from minigalaxy.logger import logger
from minigalaxy.translation import _
from minigalaxy.launcher import get_execute_command
from minigalaxy.paths import CACHE_DIR, THUMBNAIL_DIR, APPLICATIONS_DIR
Expand Down Expand Up @@ -52,7 +52,7 @@ def install_game( # noqa: C901
):
error_message = ""
tmp_dir = ""
print("Installing {}".format(game.name))
logger.info("Installing {}".format(game.name))
try:
_use_innoextract = use_innoextract and bool(shutil.which('innoextract')) # single decision point
if not error_message:
Expand All @@ -70,12 +70,12 @@ def install_game( # noqa: C901
if not error_message and create_desktop_file:
error_message = create_applications_file(game)
except Exception:
print(traceback.format_exc())
logger.error("Error installing game %s", game.name, exc_info=1)
error_message = _("Unhandled error.")
_removal_error = remove_installer(game, installer, install_dir, keep_installers)
error_message = error_message or _removal_error or postinstaller(game)
if error_message:
print(error_message)
logger.error(error_message)
return error_message


Expand All @@ -92,12 +92,12 @@ def verify_installer_integrity(game, installer):
calculated_checksum = hash_md5.hexdigest()
if installer_file_name in game.md5sum:
if game.md5sum[installer_file_name] == calculated_checksum:
print("{} integrity is preserved. MD5 is: {}".format(installer_file_name, calculated_checksum))
logger.info("%s integrity is preserved. MD5 is: %s", installer_file_name, calculated_checksum)
else:
error_message = _("{} was corrupted. Please download it again.").format(installer_file_name)
break
else:
print("Warning. No info about correct {} MD5 checksum".format(installer_file_name))
logger.warn("Warning. No info about correct %s MD5 checksum", installer_file_name)
return error_message


Expand Down
Loading

0 comments on commit d145c37

Please sign in to comment.