mirror of
https://github.com/lutris/lutris
synced 2024-09-18 15:22:27 +00:00
Add lutris.net search
This commit is contained in:
parent
4f0f86c951
commit
fda53e66ef
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue