Implement search provider (#201)
* Begin work on search provider * Initial search provider work, organize meson * Initial work on icons * Implement LaunchSearch * Don't hold arbitrary reference to service I don't know why Lollypop does this * Send notification, pad images * Update translations * Fix init_search_term typing
This commit is contained in:
@@ -19,11 +19,11 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import locale
|
||||
import gettext
|
||||
import locale
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
VERSION = "@VERSION@"
|
||||
if os.name == "nt":
|
||||
@@ -32,29 +32,27 @@ if os.name == "nt":
|
||||
os.environ["LANGUAGE"] = locale.windows_locale[
|
||||
windll.kernel32.GetUserDefaultUILanguage()
|
||||
]
|
||||
pkgdatadir = os.path.join(os.path.dirname(__file__), "..", "share", "cartridges")
|
||||
localedir = os.path.join(os.path.dirname(__file__), "..", "share", "locale")
|
||||
PKGDATADIR = os.path.join(os.path.dirname(__file__), "..", "share", "cartridges")
|
||||
LOCALEDIR = os.path.join(os.path.dirname(__file__), "..", "share", "locale")
|
||||
else:
|
||||
pkgdatadir = "@pkgdatadir@"
|
||||
localedir = "@localedir@"
|
||||
PKGDATADIR = "@pkgdatadir@"
|
||||
LOCALEDIR = "@localedir@"
|
||||
|
||||
sys.path.insert(1, pkgdatadir)
|
||||
sys.path.insert(1, PKGDATADIR)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
if os.name != "nt":
|
||||
locale.bindtextdomain("cartridges", localedir)
|
||||
locale.bindtextdomain("cartridges", LOCALEDIR)
|
||||
locale.textdomain("cartridges")
|
||||
|
||||
gettext.install("cartridges", localedir)
|
||||
gettext.install("cartridges", LOCALEDIR)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import gi
|
||||
|
||||
from gi.repository import Gio
|
||||
|
||||
resource = Gio.Resource.load(os.path.join(pkgdatadir, "cartridges.gresource"))
|
||||
resource._register()
|
||||
resource = Gio.Resource.load(os.path.join(PKGDATADIR, "cartridges.gresource"))
|
||||
resource._register() # pylint: disable=protected-access
|
||||
|
||||
from src import main
|
||||
from cartridges import main
|
||||
|
||||
sys.exit(main.main(VERSION))
|
||||
@@ -26,14 +26,14 @@ from typing import Any, Optional
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
|
||||
from src import shared
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.game_cover import GameCover
|
||||
from src.store.managers.cover_manager import CoverManager
|
||||
from src.store.managers.sgdb_manager import SgdbManager
|
||||
from src.utils.create_dialog import create_dialog
|
||||
from src.utils.save_cover import convert_cover, save_cover
|
||||
from cartridges import shared
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
from cartridges.game_cover import GameCover
|
||||
from cartridges.store.managers.cover_manager import CoverManager
|
||||
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||
from cartridges.utils.create_dialog import create_dialog
|
||||
from cartridges.utils.save_cover import convert_cover, save_cover
|
||||
|
||||
|
||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/details-window.ui")
|
||||
@@ -17,18 +17,16 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import Any, Optional
|
||||
|
||||
from gi.repository import Adw, GObject, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.game_cover import GameCover
|
||||
from cartridges import shared
|
||||
from cartridges.game_cover import GameCover
|
||||
from cartridges.utils.run_executable import run_executable
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
@@ -118,21 +116,7 @@ class Game(Gtk.Box):
|
||||
self.save()
|
||||
self.update()
|
||||
|
||||
args = (
|
||||
"flatpak-spawn --host /bin/sh -c " + shlex.quote(self.executable) # Flatpak
|
||||
if os.getenv("FLATPAK_ID") == shared.APP_ID
|
||||
else self.executable # Others
|
||||
)
|
||||
|
||||
logging.info("Starting %s: %s", self.name, str(args))
|
||||
# pylint: disable=consider-using-with
|
||||
subprocess.Popen(
|
||||
args,
|
||||
cwd=shared.home,
|
||||
shell=True,
|
||||
start_new_session=True,
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == "nt" else 0, # type: ignore
|
||||
)
|
||||
run_executable(self.executable)
|
||||
|
||||
if shared.schema.get_boolean("exit-after-launch"):
|
||||
self.app.quit()
|
||||
@@ -23,7 +23,7 @@ from typing import Optional
|
||||
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk
|
||||
from PIL import Image, ImageFilter, ImageStat
|
||||
|
||||
from src import shared
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
class GameCover:
|
||||
@@ -23,10 +23,10 @@ from typing import NamedTuple
|
||||
|
||||
import yaml
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import SourceIterable, URLExecutableSource
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||
|
||||
|
||||
class BottlesSourceIterable(SourceIterable):
|
||||
@@ -25,9 +25,9 @@ from typing import NamedTuple
|
||||
|
||||
from gi.repository import GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.source import Source, SourceIterable
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.source import Source, SourceIterable
|
||||
|
||||
|
||||
class DesktopSourceIterable(SourceIterable):
|
||||
@@ -22,10 +22,10 @@ from typing import NamedTuple
|
||||
|
||||
from gi.repository import GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import ExecutableFormatSource, SourceIterable
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import ExecutableFormatSource, SourceIterable
|
||||
|
||||
|
||||
class FlatpakSourceIterable(SourceIterable):
|
||||
@@ -27,10 +27,10 @@ from json import JSONDecodeError
|
||||
from pathlib import Path
|
||||
from typing import Iterable, NamedTuple, Optional, TypedDict
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import (
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import (
|
||||
SourceIterable,
|
||||
SourceIterationResult,
|
||||
URLExecutableSource,
|
||||
@@ -24,14 +24,14 @@ from typing import Any, Optional
|
||||
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.errors.error_producer import ErrorProducer
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.importer.location import UnresolvableLocationError
|
||||
from src.importer.source import Source
|
||||
from src.store.managers.async_manager import AsyncManager
|
||||
from src.store.pipeline import Pipeline
|
||||
from cartridges import shared
|
||||
from cartridges.errors.error_producer import ErrorProducer
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import UnresolvableLocationError
|
||||
from cartridges.importer.source import Source
|
||||
from cartridges.store.managers.async_manager import AsyncManager
|
||||
from cartridges.store.pipeline import Pipeline
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
@@ -22,11 +22,11 @@ from shutil import rmtree
|
||||
from sqlite3 import connect
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import SourceIterable, URLExecutableSource
|
||||
from src.utils.sqlite import copy_db
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||
from cartridges.utils.sqlite import copy_db
|
||||
|
||||
|
||||
class ItchSourceIterable(SourceIterable):
|
||||
@@ -22,10 +22,10 @@ import logging
|
||||
from json import JSONDecodeError
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import (
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import (
|
||||
ExecutableFormatSource,
|
||||
SourceIterable,
|
||||
SourceIterationResult,
|
||||
@@ -3,7 +3,7 @@ from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Mapping, NamedTuple, Optional
|
||||
|
||||
from src import shared
|
||||
from cartridges import shared
|
||||
|
||||
PathSegment = str | PathLike | Path
|
||||
PathSegments = Iterable[PathSegment]
|
||||
@@ -21,11 +21,11 @@ from shutil import rmtree
|
||||
from sqlite3 import connect
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import SourceIterable, URLExecutableSource
|
||||
from src.utils.sqlite import copy_db
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||
from cartridges.utils.sqlite import copy_db
|
||||
|
||||
|
||||
class LutrisSourceIterable(SourceIterable):
|
||||
@@ -26,12 +26,16 @@ from pathlib import Path
|
||||
from shlex import quote as shell_quote
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath, UnresolvableLocationError
|
||||
from src.importer.source import Source, SourceIterable
|
||||
from src.importer.steam_source import SteamSource
|
||||
from cartridges import shared
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import (
|
||||
Location,
|
||||
LocationSubPath,
|
||||
UnresolvableLocationError,
|
||||
)
|
||||
from cartridges.importer.source import Source, SourceIterable
|
||||
from cartridges.importer.steam_source import SteamSource
|
||||
|
||||
|
||||
class RetroarchSourceIterable(SourceIterable):
|
||||
@@ -22,8 +22,8 @@ from abc import abstractmethod
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, Collection, Generator, Optional
|
||||
|
||||
from src.game import Game
|
||||
from src.importer.location import Location
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location
|
||||
|
||||
# Type of the data returned by iterating on a Source
|
||||
SourceIterationResult = Optional[Game | tuple[Game, tuple[Any]]]
|
||||
@@ -23,11 +23,11 @@ import re
|
||||
from pathlib import Path
|
||||
from typing import Iterable, NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.location import Location, LocationSubPath
|
||||
from src.importer.source import SourceIterable, URLExecutableSource
|
||||
from src.utils.steam import SteamFileHelper, SteamInvalidManifestError
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.location import Location, LocationSubPath
|
||||
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||
from cartridges.utils.steam import SteamFileHelper, SteamInvalidManifestError
|
||||
|
||||
|
||||
class SteamSourceIterable(SourceIterable):
|
||||
@@ -25,7 +25,7 @@ from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from src import shared
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
class SessionFileHandler(StreamHandler):
|
||||
@@ -24,7 +24,7 @@ import platform
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from src import shared
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
def setup_logging() -> None:
|
||||
@@ -47,12 +47,12 @@ def setup_logging() -> None:
|
||||
},
|
||||
"console_formatter": {
|
||||
"format": "%(name)s %(levelname)s - %(message)s",
|
||||
"class": "src.logging.color_log_formatter.ColorLogFormatter",
|
||||
"class": "cartridges.logging.color_log_formatter.ColorLogFormatter",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"file_handler": {
|
||||
"class": "src.logging.session_file_handler.SessionFileHandler",
|
||||
"class": "cartridges.logging.session_file_handler.SessionFileHandler",
|
||||
"formatter": "file_formatter",
|
||||
"level": "DEBUG",
|
||||
"filename": log_filename,
|
||||
@@ -20,6 +20,7 @@
|
||||
import json
|
||||
import lzma
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from typing import Any, Optional
|
||||
|
||||
@@ -31,39 +32,42 @@ gi.require_version("Adw", "1")
|
||||
# pylint: disable=wrong-import-position
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.details_window import DetailsWindow
|
||||
from src.game import Game
|
||||
from src.importer.bottles_source import BottlesSource
|
||||
from src.importer.desktop_source import DesktopSource
|
||||
from src.importer.flatpak_source import FlatpakSource
|
||||
from src.importer.heroic_source import HeroicSource
|
||||
from src.importer.importer import Importer
|
||||
from src.importer.itch_source import ItchSource
|
||||
from src.importer.legendary_source import LegendarySource
|
||||
from src.importer.lutris_source import LutrisSource
|
||||
from src.importer.retroarch_source import RetroarchSource
|
||||
from src.importer.steam_source import SteamSource
|
||||
from src.logging.setup import log_system_info, setup_logging
|
||||
from src.preferences import PreferencesWindow
|
||||
from src.store.managers.cover_manager import CoverManager
|
||||
from src.store.managers.display_manager import DisplayManager
|
||||
from src.store.managers.file_manager import FileManager
|
||||
from src.store.managers.sgdb_manager import SgdbManager
|
||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||
from src.store.store import Store
|
||||
from src.utils.migrate_files_v1_to_v2 import migrate_files_v1_to_v2
|
||||
from src.window import CartridgesWindow
|
||||
from cartridges import shared
|
||||
from cartridges.details_window import DetailsWindow
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.bottles_source import BottlesSource
|
||||
from cartridges.importer.desktop_source import DesktopSource
|
||||
from cartridges.importer.flatpak_source import FlatpakSource
|
||||
from cartridges.importer.heroic_source import HeroicSource
|
||||
from cartridges.importer.importer import Importer
|
||||
from cartridges.importer.itch_source import ItchSource
|
||||
from cartridges.importer.legendary_source import LegendarySource
|
||||
from cartridges.importer.lutris_source import LutrisSource
|
||||
from cartridges.importer.retroarch_source import RetroarchSource
|
||||
from cartridges.importer.steam_source import SteamSource
|
||||
from cartridges.logging.setup import log_system_info, setup_logging
|
||||
from cartridges.preferences import PreferencesWindow
|
||||
from cartridges.store.managers.cover_manager import CoverManager
|
||||
from cartridges.store.managers.display_manager import DisplayManager
|
||||
from cartridges.store.managers.file_manager import FileManager
|
||||
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||
from cartridges.store.store import Store
|
||||
from cartridges.utils.migrate_files_v1_to_v2 import migrate_files_v1_to_v2
|
||||
from cartridges.utils.run_executable import run_executable
|
||||
from cartridges.window import CartridgesWindow
|
||||
|
||||
|
||||
class CartridgesApplication(Adw.Application):
|
||||
state = shared.AppState.DEFAULT
|
||||
win: CartridgesWindow
|
||||
init_search_term: Optional[str] = None
|
||||
|
||||
def __init__(self) -> None:
|
||||
shared.store = Store()
|
||||
super().__init__(
|
||||
application_id=shared.APP_ID, flags=Gio.ApplicationFlags.FLAGS_NONE
|
||||
application_id=shared.APP_ID,
|
||||
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
|
||||
)
|
||||
|
||||
def do_activate(self) -> None: # pylint: disable=arguments-differ
|
||||
@@ -147,8 +151,47 @@ class CartridgesApplication(Adw.Application):
|
||||
sort_action, shared.state_schema.get_value("sort-mode")
|
||||
)
|
||||
|
||||
if self.init_search_term: # For command line activation
|
||||
shared.win.search_bar.set_search_mode(True)
|
||||
shared.win.search_entry.set_text(self.init_search_term)
|
||||
shared.win.search_entry.set_position(-1)
|
||||
|
||||
shared.win.present()
|
||||
|
||||
def do_command_line(self, command_line) -> int:
|
||||
for index, arg in enumerate(args := command_line.get_arguments()):
|
||||
if arg == "--search":
|
||||
try:
|
||||
self.init_search_term = args[index + 1]
|
||||
except IndexError:
|
||||
pass
|
||||
break
|
||||
|
||||
if arg == "--launch":
|
||||
try:
|
||||
game_id = args[index + 1]
|
||||
data = json.load((shared.games_dir / (game_id + ".json")).open("r"))
|
||||
executable = (
|
||||
shlex.join(data["executable"])
|
||||
if isinstance(data["executable"], list)
|
||||
else data["executable"]
|
||||
)
|
||||
name = data["name"]
|
||||
|
||||
run_executable(executable)
|
||||
except (IndexError, KeyError, OSError, json.decoder.JSONDecodeError):
|
||||
return 1
|
||||
|
||||
notification = Gio.Notification.new(_("Cartridges"))
|
||||
notification.set_body(_("{} launched").format(name))
|
||||
self.send_notification(
|
||||
"launch",
|
||||
notification,
|
||||
)
|
||||
return 0
|
||||
self.activate()
|
||||
return 0
|
||||
|
||||
def load_games_from_disk(self) -> None:
|
||||
if shared.games_dir.is_dir():
|
||||
for game_file in shared.games_dir.iterdir():
|
||||
@@ -1,4 +1,4 @@
|
||||
moduledir = join_paths(pkgdatadir, 'src')
|
||||
moduledir = join_paths(python_dir, 'cartridges')
|
||||
|
||||
configure_file(
|
||||
input: 'cartridges.in',
|
||||
@@ -25,21 +25,21 @@ from typing import Any, Callable, Optional
|
||||
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.importer.bottles_source import BottlesSource
|
||||
from src.importer.flatpak_source import FlatpakSource
|
||||
from src.importer.heroic_source import HeroicSource
|
||||
from src.importer.itch_source import ItchSource
|
||||
from src.importer.legendary_source import LegendarySource
|
||||
from src.importer.location import UnresolvableLocationError
|
||||
from src.importer.lutris_source import LutrisSource
|
||||
from src.importer.retroarch_source import RetroarchSource
|
||||
from src.importer.source import Source
|
||||
from src.importer.steam_source import SteamSource
|
||||
from src.store.managers.sgdb_manager import SgdbManager
|
||||
from src.utils.create_dialog import create_dialog
|
||||
from cartridges import shared
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
from cartridges.importer.bottles_source import BottlesSource
|
||||
from cartridges.importer.flatpak_source import FlatpakSource
|
||||
from cartridges.importer.heroic_source import HeroicSource
|
||||
from cartridges.importer.itch_source import ItchSource
|
||||
from cartridges.importer.legendary_source import LegendarySource
|
||||
from cartridges.importer.location import UnresolvableLocationError
|
||||
from cartridges.importer.lutris_source import LutrisSource
|
||||
from cartridges.importer.retroarch_source import RetroarchSource
|
||||
from cartridges.importer.source import Source
|
||||
from cartridges.importer.steam_source import SteamSource
|
||||
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||
from cartridges.utils.create_dialog import create_dialog
|
||||
|
||||
|
||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/preferences.ui")
|
||||
@@ -51,13 +51,20 @@ games_dir = data_dir / "cartridges" / "games"
|
||||
covers_dir = data_dir / "cartridges" / "covers"
|
||||
|
||||
appdata_dir = Path(os.getenv("appdata") or "C:\\Users\\Default\\AppData\\Roaming")
|
||||
local_appdata_dir = Path(os.getenv("csidl_local_appdata") or "C:\\Users\\Default\\AppData\\Local")
|
||||
local_appdata_dir = Path(
|
||||
os.getenv("csidl_local_appdata") or "C:\\Users\\Default\\AppData\\Local"
|
||||
)
|
||||
programfiles32_dir = Path(os.getenv("programfiles(x86)") or "C:\\Program Files (x86)")
|
||||
|
||||
scale_factor = max(
|
||||
monitor.get_scale_factor() for monitor in Gdk.Display.get_default().get_monitors()
|
||||
)
|
||||
image_size = (200 * scale_factor, 300 * scale_factor)
|
||||
try:
|
||||
scale_factor = max(
|
||||
monitor.get_scale_factor()
|
||||
for monitor in Gdk.Display.get_default().get_monitors()
|
||||
)
|
||||
except AttributeError: # If shared.py is imported by the search provider
|
||||
pass
|
||||
else:
|
||||
image_size = (200 * scale_factor, 300 * scale_factor)
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
win = None
|
||||
@@ -21,8 +21,8 @@ from typing import Any, Callable
|
||||
|
||||
from gi.repository import Gio
|
||||
|
||||
from src.game import Game
|
||||
from src.store.managers.manager import Manager
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.manager import Manager
|
||||
|
||||
|
||||
class AsyncManager(Manager):
|
||||
@@ -25,11 +25,11 @@ import requests
|
||||
from gi.repository import GdkPixbuf, Gio
|
||||
from requests.exceptions import HTTPError, SSLError
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.store.managers.manager import Manager
|
||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||
from src.utils.save_cover import convert_cover, save_cover
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.manager import Manager
|
||||
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||
from cartridges.utils.save_cover import convert_cover, save_cover
|
||||
|
||||
|
||||
class ImageSize(NamedTuple):
|
||||
@@ -17,12 +17,12 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.game_cover import GameCover
|
||||
from src.store.managers.manager import Manager
|
||||
from src.store.managers.sgdb_manager import SgdbManager
|
||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.game_cover import GameCover
|
||||
from cartridges.store.managers.manager import Manager
|
||||
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||
|
||||
|
||||
class DisplayManager(Manager):
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
import json
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.store.managers.async_manager import AsyncManager
|
||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.async_manager import AsyncManager
|
||||
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||
|
||||
|
||||
class FileManager(AsyncManager):
|
||||
@@ -22,9 +22,9 @@ from abc import abstractmethod
|
||||
from time import sleep
|
||||
from typing import Any, Callable, Container
|
||||
|
||||
from src.errors.error_producer import ErrorProducer
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from cartridges.errors.error_producer import ErrorProducer
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
|
||||
|
||||
class Manager(ErrorProducer):
|
||||
@@ -21,12 +21,12 @@ from json import JSONDecodeError
|
||||
|
||||
from requests.exceptions import HTTPError, SSLError
|
||||
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.store.managers.async_manager import AsyncManager
|
||||
from src.store.managers.cover_manager import CoverManager
|
||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||
from src.utils.steamgriddb import SgdbAuthError, SgdbHelper
|
||||
from cartridges.errors.friendly_error import FriendlyError
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.async_manager import AsyncManager
|
||||
from cartridges.store.managers.cover_manager import CoverManager
|
||||
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||
from cartridges.utils.steamgriddb import SgdbAuthError, SgdbHelper
|
||||
|
||||
|
||||
class SgdbManager(AsyncManager):
|
||||
@@ -20,9 +20,9 @@
|
||||
from requests.exceptions import HTTPError, SSLError
|
||||
from urllib3.exceptions import ConnectionError as Urllib3ConnectionError
|
||||
|
||||
from src.game import Game
|
||||
from src.store.managers.async_manager import AsyncManager
|
||||
from src.utils.steam import (
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.async_manager import AsyncManager
|
||||
from cartridges.utils.steam import (
|
||||
SteamAPIHelper,
|
||||
SteamGameNotFoundError,
|
||||
SteamNotAGameError,
|
||||
@@ -22,8 +22,8 @@ from typing import Iterable
|
||||
|
||||
from gi.repository import GObject
|
||||
|
||||
from src.game import Game
|
||||
from src.store.managers.manager import Manager
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.manager import Manager
|
||||
|
||||
|
||||
class Pipeline(GObject.Object):
|
||||
@@ -20,10 +20,10 @@
|
||||
import logging
|
||||
from typing import Any, Generator, MutableMapping, Optional
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.store.managers.manager import Manager
|
||||
from src.store.pipeline import Pipeline
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.store.managers.manager import Manager
|
||||
from cartridges.store.pipeline import Pipeline
|
||||
|
||||
|
||||
class Store:
|
||||
@@ -21,7 +21,7 @@ import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from src import shared
|
||||
from cartridges import shared
|
||||
|
||||
old_data_dir = shared.home / ".local" / "share"
|
||||
old_cartridges_data_dir = old_data_dir / "cartridges"
|
||||
24
cartridges/utils/run_executable.py
Normal file
24
cartridges/utils/run_executable.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from shlex import quote
|
||||
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
def run_executable(executable) -> None:
|
||||
args = (
|
||||
"flatpak-spawn --host /bin/sh -c " + quote(executable) # Flatpak
|
||||
if os.getenv("FLATPAK_ID") == shared.APP_ID
|
||||
else executable # Others
|
||||
)
|
||||
|
||||
logging.info("Launching `%s`", str(args))
|
||||
# pylint: disable=consider-using-with
|
||||
subprocess.Popen(
|
||||
args,
|
||||
cwd=shared.home,
|
||||
shell=True,
|
||||
start_new_session=True,
|
||||
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == "nt" else 0, # type: ignore
|
||||
)
|
||||
@@ -25,7 +25,7 @@ from typing import Optional
|
||||
from gi.repository import Gdk, GdkPixbuf, Gio, GLib
|
||||
from PIL import Image, ImageSequence, UnidentifiedImageError
|
||||
|
||||
from src import shared
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
def convert_cover(
|
||||
@@ -27,8 +27,8 @@ from typing import TypedDict
|
||||
import requests
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from src import shared
|
||||
from src.utils.rate_limiter import RateLimiter
|
||||
from cartridges import shared
|
||||
from cartridges.utils.rate_limiter import RateLimiter
|
||||
|
||||
|
||||
class SteamError(Exception):
|
||||
@@ -26,9 +26,9 @@ import requests
|
||||
from gi.repository import Gio
|
||||
from requests.exceptions import HTTPError
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.utils.save_cover import convert_cover, save_cover
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.utils.save_cover import convert_cover, save_cover
|
||||
|
||||
|
||||
class SgdbError(Exception):
|
||||
@@ -21,10 +21,10 @@ from typing import Any, Optional
|
||||
|
||||
from gi.repository import Adw, Gio, GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.game_cover import GameCover
|
||||
from src.utils.relative_date import relative_date
|
||||
from cartridges import shared
|
||||
from cartridges.game import Game
|
||||
from cartridges.game_cover import GameCover
|
||||
from cartridges.utils.relative_date import relative_date
|
||||
|
||||
|
||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/window.ui")
|
||||
14
meson.build
14
meson.build
@@ -8,7 +8,12 @@ i18n = import('i18n')
|
||||
gnome = import('gnome')
|
||||
python = import('python')
|
||||
|
||||
py_installation = python.find_installation('python3')
|
||||
python_dir = join_paths(get_option('prefix'), py_installation.get_install_dir())
|
||||
|
||||
python_dir = join_paths(get_option('prefix'), py_installation.get_install_dir())
|
||||
pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), meson.project_name())
|
||||
libexecdir = join_paths(get_option('prefix'), get_option('libexecdir'))
|
||||
|
||||
profile = get_option('profile')
|
||||
if profile == 'development'
|
||||
@@ -20,21 +25,24 @@ elif profile == 'release'
|
||||
endif
|
||||
|
||||
conf = configuration_data()
|
||||
conf.set('PYTHON', python.find_installation('python3').full_path())
|
||||
conf.set('PYTHON_VERSION', python.find_installation('python3').language_version())
|
||||
conf.set('PYTHON', py_installation.full_path())
|
||||
conf.set('PYTHON_VERSION', py_installation.language_version())
|
||||
conf.set('APP_ID', app_id)
|
||||
conf.set('PREFIX', prefix)
|
||||
conf.set('VERSION', meson.project_version())
|
||||
conf.set('PROFILE', profile)
|
||||
conf.set('localedir', join_paths(get_option('prefix'), get_option('localedir')))
|
||||
conf.set('pkgdatadir', pkgdatadir)
|
||||
conf.set('libexecdir', libexecdir)
|
||||
|
||||
subdir('data')
|
||||
subdir('src')
|
||||
subdir('cartridges')
|
||||
subdir('po')
|
||||
|
||||
if host_machine.system() == 'windows'
|
||||
subdir('windows')
|
||||
else
|
||||
subdir('search-provider')
|
||||
endif
|
||||
|
||||
gnome.post_install(
|
||||
|
||||
41
po/POTFILES
41
po/POTFILES
@@ -8,25 +8,26 @@ data/gtk/help-overlay.blp
|
||||
data/gtk/preferences.blp
|
||||
data/gtk/window.blp
|
||||
|
||||
src/main.py
|
||||
src/window.py
|
||||
src/details_window.py
|
||||
src/game.py
|
||||
src/preferences.py
|
||||
cartridges/main.py
|
||||
cartridges/window.py
|
||||
cartridges/details_window.py
|
||||
cartridges/game.py
|
||||
cartridges/preferences.py
|
||||
|
||||
src/utils/create_dialog.py
|
||||
src/importer/importer.py
|
||||
src/importer/sources/source.py
|
||||
src/importer/sources/location.py
|
||||
src/importer/sources/location.py
|
||||
src/store/managers/sgdb_manager.py
|
||||
cartridges/utils/create_dialog.py
|
||||
|
||||
src/importer/sources/bottles_source.py
|
||||
src/importer/sources/desktop_source.py
|
||||
src/importer/sources/flatpak_source.py
|
||||
src/importer/sources/heroic_source.py
|
||||
src/importer/sources/itch_source.py
|
||||
src/importer/sources/legendary_source.py
|
||||
src/importer/sources/lutris_source.py
|
||||
src/importer/sources/retroarch_source.py
|
||||
src/importer/sources/steam_source.py
|
||||
cartridges/importer/importer.py
|
||||
cartridges/importer/source.py
|
||||
cartridges/importer/location.py
|
||||
cartridges/importer/location.py
|
||||
cartridges/importer/bottles_source.py
|
||||
cartridges/importer/desktop_source.py
|
||||
cartridges/importer/flatpak_source.py
|
||||
cartridges/importer/heroic_source.py
|
||||
cartridges/importer/itch_source.py
|
||||
cartridges/importer/legendary_source.py
|
||||
cartridges/importer/lutris_source.py
|
||||
cartridges/importer/retroarch_source.py
|
||||
cartridges/importer/steam_source.py
|
||||
|
||||
cartridges/store/managers/sgdb_manager.py
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Cartridges\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-26 13:51+0200\n"
|
||||
"POT-Creation-Date: 2023-10-10 22:22+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -20,7 +20,7 @@ msgstr ""
|
||||
#: data/hu.kramo.Cartridges.desktop.in:3
|
||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:6
|
||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:30 data/gtk/window.blp:47
|
||||
#: data/gtk/window.blp:80
|
||||
#: data/gtk/window.blp:80 cartridges/main.py:185
|
||||
msgid "Cartridges"
|
||||
msgstr ""
|
||||
|
||||
@@ -47,7 +47,7 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:34 data/gtk/window.blp:288
|
||||
#: src/details_window.py:71
|
||||
#: cartridges/details_window.py:71
|
||||
msgid "Game Details"
|
||||
msgstr ""
|
||||
|
||||
@@ -56,8 +56,8 @@ msgid "Edit Game Details"
|
||||
msgstr ""
|
||||
|
||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:42 data/gtk/help-overlay.blp:19
|
||||
#: data/gtk/window.blp:515 src/details_window.py:271
|
||||
#: src/importer/importer.py:319 src/importer/importer.py:370
|
||||
#: data/gtk/window.blp:515 cartridges/details_window.py:271
|
||||
#: cartridges/importer/importer.py:319 cartridges/importer/importer.py:370
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
|
||||
@@ -73,7 +73,7 @@ msgstr ""
|
||||
msgid "Delete Cover"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/details-window.blp:100 data/gtk/game.blp:80
|
||||
#: data/gtk/details-window.blp:100 data/gtk/game.blp:81
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
@@ -93,20 +93,20 @@ msgstr ""
|
||||
msgid "More Info"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/game.blp:102 data/gtk/game.blp:121 data/gtk/window.blp:415
|
||||
#: data/gtk/game.blp:103 data/gtk/game.blp:122 data/gtk/window.blp:415
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/game.blp:107 src/window.py:350
|
||||
#: data/gtk/game.blp:108 cartridges/window.py:350
|
||||
msgid "Hide"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/game.blp:112 data/gtk/game.blp:131 data/gtk/preferences.blp:40
|
||||
#: data/gtk/game.blp:113 data/gtk/game.blp:132 data/gtk/preferences.blp:40
|
||||
#: data/gtk/window.blp:435
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/game.blp:126 src/window.py:352
|
||||
#: data/gtk/game.blp:127 cartridges/window.py:352
|
||||
msgid "Unhide"
|
||||
msgstr ""
|
||||
|
||||
@@ -123,8 +123,8 @@ msgstr ""
|
||||
msgid "Keyboard Shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/help-overlay.blp:29 src/game.py:105 src/preferences.py:125
|
||||
#: src/importer/importer.py:394
|
||||
#: data/gtk/help-overlay.blp:29 cartridges/game.py:103
|
||||
#: cartridges/preferences.py:125 cartridges/importer/importer.py:394
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
||||
@@ -178,7 +178,7 @@ msgstr ""
|
||||
msgid "Swaps the behavior of the cover image and the play button"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:25 src/details_window.py:85
|
||||
#: data/gtk/preferences.blp:25 cartridges/details_window.py:85
|
||||
msgid "Images"
|
||||
msgstr ""
|
||||
|
||||
@@ -206,7 +206,7 @@ msgstr ""
|
||||
msgid "Sources"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:83 src/importer/sources/steam_source.py:114
|
||||
#: data/gtk/preferences.blp:83 cartridges/importer/steam_source.py:114
|
||||
msgid "Steam"
|
||||
msgstr ""
|
||||
|
||||
@@ -217,7 +217,7 @@ msgstr ""
|
||||
msgid "Install Location"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:100 src/importer/sources/lutris_source.py:92
|
||||
#: data/gtk/preferences.blp:100 cartridges/importer/lutris_source.py:92
|
||||
msgid "Lutris"
|
||||
msgstr ""
|
||||
|
||||
@@ -233,7 +233,7 @@ msgstr ""
|
||||
msgid "Import Flatpak Games"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:137 src/importer/sources/heroic_source.py:355
|
||||
#: data/gtk/preferences.blp:137 cartridges/importer/heroic_source.py:355
|
||||
msgid "Heroic"
|
||||
msgstr ""
|
||||
|
||||
@@ -253,23 +253,23 @@ msgstr ""
|
||||
msgid "Import Sideloaded Games"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:170 src/importer/sources/bottles_source.py:86
|
||||
#: data/gtk/preferences.blp:170 cartridges/importer/bottles_source.py:86
|
||||
msgid "Bottles"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:187 src/importer/sources/itch_source.py:81
|
||||
#: data/gtk/preferences.blp:187 cartridges/importer/itch_source.py:81
|
||||
msgid "itch"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:204 src/importer/sources/legendary_source.py:97
|
||||
#: data/gtk/preferences.blp:204 cartridges/importer/legendary_source.py:97
|
||||
msgid "Legendary"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:221 src/importer/sources/retroarch_source.py:142
|
||||
#: data/gtk/preferences.blp:221 cartridges/importer/retroarch_source.py:142
|
||||
msgid "RetroArch"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:238 src/importer/sources/flatpak_source.py:118
|
||||
#: data/gtk/preferences.blp:238 cartridges/importer/flatpak_source.py:118
|
||||
msgid "Flatpak"
|
||||
msgstr ""
|
||||
|
||||
@@ -277,7 +277,7 @@ msgstr ""
|
||||
msgid "Import Game Launchers"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/preferences.blp:259 src/importer/sources/desktop_source.py:217
|
||||
#: data/gtk/preferences.blp:259 cartridges/importer/desktop_source.py:215
|
||||
msgid "Desktop Entries"
|
||||
msgstr ""
|
||||
|
||||
@@ -345,11 +345,11 @@ msgstr ""
|
||||
msgid "Games you hide will appear here."
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/window.blp:75 data/gtk/window.blp:106 src/main.py:166
|
||||
#: data/gtk/window.blp:75 data/gtk/window.blp:106 cartridges/main.py:207
|
||||
msgid "All Games"
|
||||
msgstr ""
|
||||
|
||||
#: data/gtk/window.blp:126 src/main.py:168
|
||||
#: data/gtk/window.blp:126 cartridges/main.py:209
|
||||
msgid "Added"
|
||||
msgstr ""
|
||||
|
||||
@@ -401,62 +401,67 @@ msgstr ""
|
||||
msgid "About Cartridges"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the title of the game
|
||||
#: cartridges/main.py:186 cartridges/game.py:125
|
||||
msgid "{} launched"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: Replace this with your name for it to show up in the about window
|
||||
#: src/main.py:208
|
||||
#: cartridges/main.py:249
|
||||
msgid "translator_credits"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the date when the game was added
|
||||
#: src/window.py:373
|
||||
#: cartridges/window.py:373
|
||||
msgid "Added: {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/window.py:376
|
||||
#: cartridges/window.py:376
|
||||
msgid "Never"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the date when the game was last played
|
||||
#: src/window.py:380
|
||||
#: cartridges/window.py:380
|
||||
msgid "Last played: {}"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:76
|
||||
#: cartridges/details_window.py:76
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:82
|
||||
#: cartridges/details_window.py:82
|
||||
msgid "Add New Game"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:83
|
||||
#: cartridges/details_window.py:83
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:93
|
||||
#: cartridges/details_window.py:93
|
||||
msgid "Executables"
|
||||
msgstr ""
|
||||
|
||||
#. Translate this string as you would translate "file"
|
||||
#: src/details_window.py:108
|
||||
#: cartridges/details_window.py:108
|
||||
msgid "file.txt"
|
||||
msgstr ""
|
||||
|
||||
#. As in software
|
||||
#: src/details_window.py:110
|
||||
#: cartridges/details_window.py:110
|
||||
msgid "program"
|
||||
msgstr ""
|
||||
|
||||
#. Translate this string as you would translate "path to {}"
|
||||
#: src/details_window.py:115 src/details_window.py:117
|
||||
#: cartridges/details_window.py:115 cartridges/details_window.py:117
|
||||
msgid "C:\\path\\to\\{}"
|
||||
msgstr ""
|
||||
|
||||
#. Translate this string as you would translate "path to {}"
|
||||
#: src/details_window.py:121 src/details_window.py:123
|
||||
#: cartridges/details_window.py:121 cartridges/details_window.py:123
|
||||
msgid "/path/to/{}"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:128
|
||||
#: cartridges/details_window.py:128
|
||||
msgid ""
|
||||
"To launch the executable \"{}\", use the command:\n"
|
||||
"\n"
|
||||
@@ -469,141 +474,136 @@ msgid ""
|
||||
"If the path contains spaces, make sure to wrap it in double quotes!"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:171 src/details_window.py:177
|
||||
#: cartridges/details_window.py:171 cartridges/details_window.py:177
|
||||
msgid "Couldn't Add Game"
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:171 src/details_window.py:213
|
||||
#: cartridges/details_window.py:171 cartridges/details_window.py:213
|
||||
msgid "Game title cannot be empty."
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:177 src/details_window.py:221
|
||||
#: cartridges/details_window.py:177 cartridges/details_window.py:221
|
||||
msgid "Executable cannot be empty."
|
||||
msgstr ""
|
||||
|
||||
#: src/details_window.py:212 src/details_window.py:220
|
||||
#: cartridges/details_window.py:212 cartridges/details_window.py:220
|
||||
msgid "Couldn't Apply Preferences"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the title of the game
|
||||
#: src/game.py:141
|
||||
msgid "{} launched"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the title of the game
|
||||
#: src/game.py:155
|
||||
#: cartridges/game.py:139
|
||||
msgid "{} hidden"
|
||||
msgstr ""
|
||||
|
||||
#: src/game.py:155
|
||||
#: cartridges/game.py:139
|
||||
msgid "{} unhidden"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the title of the game
|
||||
#. The variable is the number of games removed
|
||||
#: src/game.py:169 src/importer/importer.py:391
|
||||
#: cartridges/game.py:153 cartridges/importer/importer.py:391
|
||||
msgid "{} removed"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:124
|
||||
#: cartridges/preferences.py:124
|
||||
msgid "All games removed"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:172
|
||||
#: cartridges/preferences.py:172
|
||||
msgid ""
|
||||
"An API key is required to use SteamGridDB. You can generate one {}here{}."
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:184
|
||||
#: cartridges/preferences.py:184
|
||||
msgid "Downloading covers…"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:203
|
||||
#: cartridges/preferences.py:203
|
||||
msgid "Covers updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:335
|
||||
#: cartridges/preferences.py:335
|
||||
msgid "Installation Not Found"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:336
|
||||
#: cartridges/preferences.py:336
|
||||
msgid "Select a valid directory."
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:372 src/importer/importer.py:317
|
||||
#: cartridges/preferences.py:372 cartridges/importer/importer.py:317
|
||||
msgid "Warning"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:406
|
||||
#: cartridges/preferences.py:406
|
||||
msgid "Invalid Directory"
|
||||
msgstr ""
|
||||
|
||||
#: src/preferences.py:412
|
||||
#: cartridges/preferences.py:412
|
||||
msgid "Set Location"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/create_dialog.py:33 src/importer/importer.py:318
|
||||
#: cartridges/utils/create_dialog.py:33 cartridges/importer/importer.py:318
|
||||
msgid "Dismiss"
|
||||
msgstr ""
|
||||
|
||||
#: src/importer/importer.py:145
|
||||
#: cartridges/importer/importer.py:145
|
||||
msgid "Importing Games…"
|
||||
msgstr ""
|
||||
|
||||
#: src/importer/importer.py:338
|
||||
#: cartridges/importer/importer.py:338
|
||||
msgid "The following errors occured during import:"
|
||||
msgstr ""
|
||||
|
||||
#: src/importer/importer.py:367
|
||||
#: cartridges/importer/importer.py:367
|
||||
msgid "No new games found"
|
||||
msgstr ""
|
||||
|
||||
#: src/importer/importer.py:379
|
||||
#: cartridges/importer/importer.py:379
|
||||
msgid "1 game imported"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the number of games
|
||||
#: src/importer/importer.py:383
|
||||
#: cartridges/importer/importer.py:383
|
||||
msgid "{} games imported"
|
||||
msgstr ""
|
||||
|
||||
#. A single game removed
|
||||
#: src/importer/importer.py:387
|
||||
#: cartridges/importer/importer.py:387
|
||||
msgid "1 removed"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the name of the source
|
||||
#: src/importer/sources/location.py:33
|
||||
#: cartridges/importer/location.py:33
|
||||
msgid "Select the {} cache directory."
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the name of the source
|
||||
#: src/importer/sources/location.py:35
|
||||
#: cartridges/importer/location.py:35
|
||||
msgid "Select the {} configuration directory."
|
||||
msgstr ""
|
||||
|
||||
#. The variable is the name of the source
|
||||
#: src/importer/sources/location.py:37
|
||||
#: cartridges/importer/location.py:37
|
||||
msgid "Select the {} data directory."
|
||||
msgstr ""
|
||||
|
||||
#: src/store/managers/sgdb_manager.py:46
|
||||
msgid "Couldn't Authenticate SteamGridDB"
|
||||
msgstr ""
|
||||
|
||||
#: src/store/managers/sgdb_manager.py:47
|
||||
msgid "Verify your API key in preferences"
|
||||
msgstr ""
|
||||
|
||||
#: src/importer/sources/retroarch_source.py:129
|
||||
#: cartridges/importer/retroarch_source.py:129
|
||||
msgid "No RetroArch Core Selected"
|
||||
msgstr ""
|
||||
|
||||
#. The variable is a newline separated list of playlists
|
||||
#: src/importer/sources/retroarch_source.py:131
|
||||
#: cartridges/importer/retroarch_source.py:131
|
||||
msgid "The following playlists have no default core:"
|
||||
msgstr ""
|
||||
|
||||
#: src/importer/sources/retroarch_source.py:133
|
||||
#: cartridges/importer/retroarch_source.py:133
|
||||
msgid "Games with no core selected were not imported"
|
||||
msgstr ""
|
||||
|
||||
#: cartridges/store/managers/sgdb_manager.py:46
|
||||
msgid "Couldn't Authenticate SteamGridDB"
|
||||
msgstr ""
|
||||
|
||||
#: cartridges/store/managers/sgdb_manager.py:47
|
||||
msgid "Verify your API key in preferences"
|
||||
msgstr ""
|
||||
|
||||
292
search-provider/cartridges-search-provider.in
Executable file
292
search-provider/cartridges-search-provider.in
Executable file
@@ -0,0 +1,292 @@
|
||||
#!@PYTHON@
|
||||
|
||||
# cartridges-search-provider.in
|
||||
#
|
||||
# Copyright 2023 kramo
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Heavily inspired by:
|
||||
# https://gitlab.gnome.org/World/lollypop/-/blob/master/search-provider/lollypop-sp.in
|
||||
|
||||
import json
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gdk", "4.0")
|
||||
gi.require_version("GdkPixbuf", "2.0")
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
from gi.repository import GdkPixbuf, Gio, GLib
|
||||
|
||||
from cartridges import shared
|
||||
|
||||
|
||||
class Server:
|
||||
def __init__(self, con, path):
|
||||
method_outargs = {}
|
||||
method_inargs = {}
|
||||
for interface in Gio.DBusNodeInfo.new_for_xml(self.__doc__).interfaces:
|
||||
for method in interface.methods:
|
||||
method_outargs[method.name] = (
|
||||
"(" + "".join([arg.signature for arg in method.out_args]) + ")"
|
||||
)
|
||||
method_inargs[method.name] = tuple(
|
||||
arg.signature for arg in method.in_args
|
||||
)
|
||||
|
||||
con.register_object(
|
||||
object_path=path,
|
||||
interface_info=interface,
|
||||
method_call_closure=self.on_method_call,
|
||||
)
|
||||
|
||||
self.method_inargs = method_inargs
|
||||
self.method_outargs = method_outargs
|
||||
|
||||
def on_method_call(
|
||||
self,
|
||||
_connection,
|
||||
_sender,
|
||||
_object_path,
|
||||
_interface_name,
|
||||
method_name,
|
||||
parameters,
|
||||
invocation,
|
||||
):
|
||||
args = list(parameters.unpack())
|
||||
for i, sig in enumerate(self.method_inargs[method_name]):
|
||||
if sig == "h":
|
||||
msg = invocation.get_message()
|
||||
fd_list = msg.get_unix_fd_list()
|
||||
args[i] = fd_list.get(args[i])
|
||||
|
||||
try:
|
||||
result = getattr(self, method_name)(*args)
|
||||
|
||||
# out_args is atleast (signature1).
|
||||
# We therefore always wrap the result as a tuple.
|
||||
# Refer to https://bugzilla.gnome.org/show_bug.cgi?id=765603
|
||||
result = (result,)
|
||||
|
||||
out_args = self.method_outargs[method_name]
|
||||
if out_args != "()":
|
||||
variant = GLib.Variant(out_args, result)
|
||||
invocation.return_value(variant)
|
||||
else:
|
||||
invocation.return_value(None)
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
pass
|
||||
|
||||
|
||||
class SearchCartridgesService(Server, Gio.Application):
|
||||
"""
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
<interface name="org.gnome.Shell.SearchProvider2">
|
||||
|
||||
<method name="GetInitialResultSet">
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="as" name="results" direction="out" />
|
||||
</method>
|
||||
|
||||
<method name="GetSubsearchResultSet">
|
||||
<arg type="as" name="previous_results" direction="in" />
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="as" name="results" direction="out" />
|
||||
</method>
|
||||
|
||||
<method name="GetResultMetas">
|
||||
<arg type="as" name="identifiers" direction="in" />
|
||||
<arg type="aa{sv}" name="metas" direction="out" />
|
||||
</method>
|
||||
|
||||
<method name="ActivateResult">
|
||||
<arg type="s" name="identifier" direction="in" />
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="u" name="timestamp" direction="in" />
|
||||
</method>
|
||||
|
||||
<method name="LaunchSearch">
|
||||
<arg type="as" name="terms" direction="in" />
|
||||
<arg type="u" name="timestamp" direction="in" />
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
||||
"""
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
__SEARCH_BUS = "org.gnome.Shell.SearchProvider2"
|
||||
__PATH_BUS = "@PREFIX@/SearchProvider"
|
||||
|
||||
def __init__(self):
|
||||
Gio.Application.__init__(
|
||||
self,
|
||||
application_id="@APP_ID@.SearchProvider",
|
||||
flags=Gio.ApplicationFlags.IS_SERVICE,
|
||||
inactivity_timeout=10000,
|
||||
)
|
||||
|
||||
self.games = {}
|
||||
self.load_games_from_disk()
|
||||
|
||||
self.__bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||
Gio.bus_own_name_on_connection(
|
||||
self.__bus, self.__SEARCH_BUS, Gio.BusNameOwnerFlags.NONE, None, None
|
||||
)
|
||||
Server.__init__(self, self.__bus, self.__PATH_BUS)
|
||||
|
||||
def load_games_from_disk(self):
|
||||
if not shared.games_dir.is_dir():
|
||||
return
|
||||
|
||||
for game_file in shared.games_dir.iterdir():
|
||||
try:
|
||||
data = json.load(game_file.open())
|
||||
except (OSError, json.decoder.JSONDecodeError):
|
||||
continue
|
||||
|
||||
try:
|
||||
if any({data["hidden"], data["blacklisted"], data["removed"]}):
|
||||
continue
|
||||
|
||||
self.games[data["game_id"]] = (data["name"], data["developer"])
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
def ActivateResult(self, game_id, _array, _utime):
|
||||
argv = ["cartridges", "--launch", game_id]
|
||||
(pid, _stdin, _stdout, _stderr) = GLib.spawn_async(
|
||||
argv,
|
||||
flags=GLib.SpawnFlags.SEARCH_PATH,
|
||||
standard_input=False,
|
||||
standard_output=False,
|
||||
standard_error=False,
|
||||
)
|
||||
GLib.spawn_close_pid(pid)
|
||||
|
||||
def GetInitialResultSet(self, terms):
|
||||
return self.__search(terms)
|
||||
|
||||
def GetResultMetas(self, game_ids):
|
||||
results = []
|
||||
|
||||
try:
|
||||
for game_id in game_ids:
|
||||
empty_pixbuf = GdkPixbuf.Pixbuf.new(
|
||||
GdkPixbuf.Colorspace.RGB, True, 8, 32, 32
|
||||
)
|
||||
pixbuf = None
|
||||
if (path := shared.covers_dir / (game_id + ".tiff")).is_file():
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||
str(path), -1, 32, True
|
||||
)
|
||||
except GLib.GError as e:
|
||||
print(e)
|
||||
continue
|
||||
elif (path := shared.covers_dir / (game_id + ".gif")).is_file():
|
||||
try:
|
||||
pixbuf = GdkPixbuf.PixbufAnimation.new_from_file(
|
||||
str(path)
|
||||
).get_static_image()
|
||||
except GLib.GError as e:
|
||||
print(e)
|
||||
continue
|
||||
d = {
|
||||
"id": GLib.Variant("s", game_id),
|
||||
"name": GLib.Variant("s", self.games[game_id][0]),
|
||||
}
|
||||
if pixbuf:
|
||||
pixbuf.composite(
|
||||
empty_pixbuf,
|
||||
6,
|
||||
0,
|
||||
21,
|
||||
32,
|
||||
6,
|
||||
0,
|
||||
21 / pixbuf.get_width(),
|
||||
32 / pixbuf.get_height(),
|
||||
GdkPixbuf.InterpType.NEAREST,
|
||||
255,
|
||||
)
|
||||
|
||||
d["icon-data"] = GLib.Variant(
|
||||
"(iiibiiay)",
|
||||
[
|
||||
empty_pixbuf.get_width(),
|
||||
empty_pixbuf.get_height(),
|
||||
empty_pixbuf.get_rowstride(),
|
||||
empty_pixbuf.get_has_alpha(),
|
||||
empty_pixbuf.get_bits_per_sample(),
|
||||
empty_pixbuf.get_n_channels(),
|
||||
empty_pixbuf.read_pixel_bytes().get_data(),
|
||||
],
|
||||
)
|
||||
if self.games[game_id][1]:
|
||||
d["description"] = GLib.Variant(
|
||||
"s", GLib.markup_escape_text(self.games[game_id][1])
|
||||
)
|
||||
results.append(d)
|
||||
except Exception as e: # pylint: disable=broad-exception-caught
|
||||
print("SearchCartridgesService::GetResultMetas():", e)
|
||||
return []
|
||||
return results
|
||||
|
||||
def GetSubsearchResultSet(self, _previous_results, new_terms):
|
||||
return self.__search(new_terms)
|
||||
|
||||
def LaunchSearch(self, terms, _utime):
|
||||
search = " ".join(terms)
|
||||
argv = ["cartridges", "--search", search]
|
||||
(pid, _stdin, _stdout, _stderr) = GLib.spawn_async(
|
||||
argv,
|
||||
flags=GLib.SpawnFlags.SEARCH_PATH,
|
||||
standard_input=False,
|
||||
standard_output=False,
|
||||
standard_error=False,
|
||||
)
|
||||
GLib.spawn_close_pid(pid)
|
||||
|
||||
def __search(self, terms):
|
||||
game_ids = []
|
||||
search = " ".join(terms).lower()
|
||||
try:
|
||||
for game_id, data in self.games.items():
|
||||
print(game_id, data)
|
||||
if search in data[0].lower():
|
||||
game_ids.append(game_id)
|
||||
continue
|
||||
if data[1] and search in data[1].lower():
|
||||
game_ids.append(game_id)
|
||||
continue
|
||||
except Exception as e: # pylint: disable=broad-exception-caught
|
||||
print("SearchCartridgesService::__search():", e)
|
||||
return game_ids
|
||||
|
||||
|
||||
def main():
|
||||
service = SearchCartridgesService()
|
||||
service.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
search-provider/hu.kramo.Cartridges.SearchProvider.ini
Normal file
5
search-provider/hu.kramo.Cartridges.SearchProvider.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[Shell Search Provider]
|
||||
DesktopId=@APP_ID@.desktop
|
||||
BusName=@APP_ID@.SearchProvider
|
||||
ObjectPath=@PREFIX@/SearchProvider
|
||||
Version=2
|
||||
@@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=@APP_ID@.SearchProvider
|
||||
Exec=@libexecdir@/cartridges-search-provider
|
||||
25
search-provider/meson.build
Normal file
25
search-provider/meson.build
Normal file
@@ -0,0 +1,25 @@
|
||||
# Heavily inspired by https://gitlab.gnome.org/World/lollypop/-/blob/master/search-provider/meson.build
|
||||
|
||||
service_dir = join_paths(get_option('datadir'), 'dbus-1', 'services')
|
||||
serarch_provider_dir = join_paths(get_option('datadir'), 'gnome-shell', 'search-providers')
|
||||
|
||||
configure_file(
|
||||
input: 'cartridges-search-provider.in',
|
||||
output: 'cartridges-search-provider',
|
||||
configuration: conf,
|
||||
install_dir: libexecdir
|
||||
)
|
||||
|
||||
configure_file(
|
||||
input: 'hu.kramo.Cartridges.SearchProvider.service.in',
|
||||
output: app_id + '.SearchProvider.service',
|
||||
configuration: conf,
|
||||
install_dir: service_dir
|
||||
)
|
||||
|
||||
configure_file(
|
||||
input: 'hu.kramo.Cartridges.SearchProvider.ini',
|
||||
output: app_id + '.SearchProvider.ini',
|
||||
configuration: conf,
|
||||
install_dir: serarch_provider_dir
|
||||
)
|
||||
Reference in New Issue
Block a user