Add lutris.net search

This commit is contained in:
Mathieu Comandon 2019-01-25 01:52:38 -08:00
parent 4f0f86c951
commit fda53e66ef
5 changed files with 74 additions and 49 deletions

View file

@ -151,7 +151,7 @@ def get_api_games(game_slugs=None, page="1", query_type="games"):
def search_games(query):
query = query.lower().strip()[:32]
url = settings.SITE_URL + "/api/games?search=%s" % query
url = settings.SITE_URL + "/api/games?%s" % urllib.parse.urlencode({"search": query})
response = http.Request(url, headers={"Content-Type": "application/json"})
try:
response.get()
@ -160,9 +160,9 @@ def search_games(query):
return None
response_data = response.json
api_games = response_data.get("results", [])
for game in api_games:
game["id"] = None
game["installed"] = 0
for index, game in enumerate(api_games):
game["id"] = index * -1
game["installed"] = 1
game["runner"] = None
game["platform"] = None
game["lastplayed"] = None

View file

@ -90,17 +90,9 @@ class LutrisWindow(Gtk.ApplicationWindow):
self.load_icon_type_from_settings(view_type)
# Window initialization
store_games = pga.get_games(show_installed_first=self.show_installed_first)
self.game_actions = GameActions(application=application, window=self)
self.game_store = GameStore(
store_games,
self.icon_type,
self.filter_installed,
self.view_sorting,
self.view_sorting_ascending,
self.show_installed_first,
)
self.game_store.connect("sorting-changed", self.on_game_store_sorting_changed)
self.game_store = self.get_store()
self.view = self.get_view(view_type)
GObject.add_emission_hook(Game, "game-updated", self.on_game_updated)
@ -272,6 +264,20 @@ class LutrisWindow(Gtk.ApplicationWindow):
def view_sorting_ascending(self):
return settings.read_setting("view_sorting_ascending") != "false"
def get_store(self, games=None):
"""Return an instance of GameStore"""
games = games or pga.get_games(show_installed_first=self.show_installed_first)
game_store = GameStore(
games,
self.icon_type,
self.filter_installed,
self.view_sorting,
self.view_sorting_ascending,
self.show_installed_first,
)
game_store.connect("sorting-changed", self.on_game_store_sorting_changed)
return game_store
def sync_services(self):
"""Sync local lutris library with current Steam games and desktop games"""
def full_sync(syncer_cls):
@ -566,7 +572,10 @@ class LutrisWindow(Gtk.ApplicationWindow):
self.game_store.modelfilter.refilter()
self.game_store.modelsort.clear_cache()
self.game_store.sort_view(self.view_sorting, self.view_sorting_ascending)
self.no_results_overlay.props.visible = len(self.game_store.modelfilter) == 0
self.no_results_overlay.props.visible = (
not self.game_store
and len(self.game_store.modelfilter) == 0
)
def on_show_installed_first_state_change(self, action, value):
"""Callback to handle installed games first toggle"""
@ -626,10 +635,12 @@ class LutrisWindow(Gtk.ApplicationWindow):
def on_game_searched(self, panel, query):
"""Called when the game-searched event is emitted"""
logger.info("Searching for :%s" % query)
self.game_store.games = api.search_games(query)
self.game_store.store.clear()
self.game_store.load()
self.view.destroy()
self.game_store = self.get_store(api.search_games(query) if query else None)
self.game_store.set_icon_type(self.icon_type)
self.game_store.load(from_search=bool(query))
self.switch_view(self.get_view_type())
self.invalidate_game_filter()
return True
def game_selection_changed(self, _widget):
@ -649,12 +660,11 @@ class LutrisWindow(Gtk.ApplicationWindow):
def on_game_installed(self, view, game_id):
"""Callback to handle newly installed games"""
self.game_store.add_or_update(game_id)
GLib.idle_add(self.view.queue_draw)
self.sidebar_listbox.update()
def update_game(self, slug):
for pga_game in pga.get_games_where(slug=slug):
self.game_store.update_game_by_id(pga_game["id"])
self.game_store.update(pga_game)
@GtkTemplate.Callback
def on_add_game_button_clicked(self, *_args):

View file

@ -24,7 +24,7 @@ from . import (
COL_INSTALLED_AT,
COL_INSTALLED_AT_TEXT,
COL_PLAYTIME,
COL_PLAYTIME_TEXT
COL_PLAYTIME_TEXT,
)
@ -43,7 +43,7 @@ class GameStore(GObject.Object):
"platform": COL_PLATFORM,
"lastplayed": COL_LASTPLAYED,
"installed_at": COL_INSTALLED_AT,
"playtime": COL_PLAYTIME
"playtime": COL_PLAYTIME,
}
def __init__(
@ -56,7 +56,8 @@ class GameStore(GObject.Object):
show_installed_first=False,
):
super(GameStore, self).__init__()
self.games = games
self.games = games or []
self.search_mode = False
self.games_to_refresh = set()
self.icon_type = icon_type
self.filter_installed = filter_installed
@ -91,14 +92,10 @@ class GameStore(GObject.Object):
self.modelsort = Gtk.TreeModelSort.sort_new_with_model(self.modelfilter)
self.modelsort.connect("sort-column-changed", self.on_sort_column_changed)
self.sort_view(sort_key, sort_ascending)
self.medias = {
"banner": {},
"icon": {}
}
self.medias = {"banner": {}, "icon": {}}
self.media_loaded = False
self.connect('media-loaded', self.on_media_loaded)
self.connect('icon-loaded', self.on_icon_loaded)
AsyncCall(self.get_missing_media)
self.connect("media-loaded", self.on_media_loaded)
self.connect("icon-loaded", self.on_icon_loaded)
def __str__(self):
return (
@ -106,7 +103,10 @@ class GameStore(GObject.Object):
"filter_text: {filter_text}>".format(**self.__dict__)
)
def load(self):
def load(self, from_search=False):
if not self.games:
return
self.search_mode = from_search
self.add_games(self.games)
@property
@ -115,6 +115,8 @@ class GameStore(GObject.Object):
def add_games(self, games):
"""Add games to the store"""
self.media_loaded = False
AsyncCall(self.get_missing_media, None, [game["slug"] for game in games])
for game in list(games):
GLib.idle_add(self.add_game, game)
@ -129,9 +131,7 @@ class GameStore(GObject.Object):
unavailable_banners = [
slug for slug in slugs if not self.has_icon(slug, "banner")
]
unavailable_icons = [
slug for slug in slugs if not self.has_icon(slug, "icon")
]
unavailable_icons = [slug for slug in slugs if not self.has_icon(slug, "icon")]
# Remove duplicate slugs
missing_media_slugs = list(set(unavailable_banners) | set(unavailable_icons))
@ -156,7 +156,7 @@ class GameStore(GObject.Object):
"""Filter function for the game model"""
if self.filter_installed:
installed = model.get_value(_iter, COL_INSTALLED)
if not installed:
if not installed and not self.search_mode:
return False
if self.filter_text:
name = model.get_value(_iter, COL_NAME)
@ -198,6 +198,16 @@ class GameStore(GObject.Object):
if model_row[COL_ID] == int(game_id):
return model_row
def get_row_by_slug(self, slug):
"""Return a row by its slug.
Requires slugs to be unique, thus only works for search mode
"""
if not self.search_mode:
raise RuntimeError("get_row_by_slug can only be used with search_mode")
for model_row in self.store:
if model_row[COL_SLUG] == slug:
return model_row
def remove_game(self, game_id):
"""Remove a game from the view."""
game_index = 0
@ -213,11 +223,12 @@ class GameStore(GObject.Object):
self.store.remove(row.iter)
def update_game_by_id(self, game_id):
"""Update game informations."""
game = pga.get_game_by_field(game_id, "id")
return self.update(game)
return self.update(
pga.get_game_by_field(game_id, "id")
)
def update(self, pga_game):
"""Update game informations."""
game = PgaGame(pga_game)
row = self.get_row_by_id(game.id)
if not row:
@ -249,22 +260,28 @@ class GameStore(GObject.Object):
logger.warning("%s has not icon", game_slug)
return
if media_type != self.icon_type:
logger.debug("%s type does not match %s", media_type, self.icon_type)
return
if self.search_mode:
GLib.idle_add(self.update_icon, game_slug)
return
for pga_game in pga.get_games_by_slug(game_slug):
logger.debug("Updating %s", pga_game["id"])
GLib.idle_add(self.update, pga_game)
def update_icon(self, game_slug):
row = self.get_row_by_slug(game_slug)
row[COL_ICON] = get_pixbuf_for_game(game_slug, self.icon_type, True)
def fetch_icon(self, slug):
if not self.media_loaded:
self.games_to_refresh.add(slug)
return
for media_type in ('banner', 'icon'):
for media_type in ("banner", "icon"):
url = self.medias[media_type].get(slug)
if url:
download_media(url, get_icon_path(slug, media_type))
self.emit('icon-loaded', slug, media_type)
self.emit("icon-loaded", slug, media_type)
def on_media_loaded(self, response):
for slug in self.games_to_refresh:
@ -272,10 +289,7 @@ class GameStore(GObject.Object):
self.refresh_icon(slug)
def add_games_by_ids(self, game_ids):
self.media_loaded = False
games = pga.get_games_by_ids(game_ids)
AsyncCall(self.get_missing_media, None, [game["slug"] for game in games])
self.add_games(games)
self.add_games(pga.get_games_by_ids(game_ids))
def add_game_by_id(self, game_id):
"""Add a game into the store."""
@ -300,7 +314,7 @@ class GameStore(GObject.Object):
game.installed_at,
game.installed_at_text,
game.playtime,
game.playtime_text
game.playtime_text,
)
)
if not self.has_icon(game.slug):
@ -317,6 +331,8 @@ class GameStore(GObject.Object):
self.icon_type = icon_type
for row in self.store:
row[COL_ICON] = get_pixbuf_for_game(
row[COL_SLUG], icon_type, is_installed=row[COL_INSTALLED]
row[COL_SLUG],
icon_type,
is_installed=row[COL_INSTALLED],
)
self.emit("icons-changed", icon_type)

View file

@ -177,7 +177,6 @@ class ServiceSyncBox(Gtk.Box):
(len(games), "s were" if len(games) > 1 else " was")
)
window.game_store.add_games_by_ids(games)
GLib.idle_add(window.view.queue_draw)
def on_switch_changed(self, switch, _data):
write_setting("sync_at_startup", switch.get_active(), self.identifier)

View file

@ -681,7 +681,7 @@
<property name="label" translatable="yes">No Games Found</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.4399999999999999"/>
<attribute name="scale" value="1.44"/>
</attributes>
</object>
<packing>