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
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import signal
|
|
||||||
import locale
|
|
||||||
import gettext
|
import gettext
|
||||||
|
import locale
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
|
||||||
VERSION = "@VERSION@"
|
VERSION = "@VERSION@"
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
@@ -32,29 +32,27 @@ if os.name == "nt":
|
|||||||
os.environ["LANGUAGE"] = locale.windows_locale[
|
os.environ["LANGUAGE"] = locale.windows_locale[
|
||||||
windll.kernel32.GetUserDefaultUILanguage()
|
windll.kernel32.GetUserDefaultUILanguage()
|
||||||
]
|
]
|
||||||
pkgdatadir = os.path.join(os.path.dirname(__file__), "..", "share", "cartridges")
|
PKGDATADIR = os.path.join(os.path.dirname(__file__), "..", "share", "cartridges")
|
||||||
localedir = os.path.join(os.path.dirname(__file__), "..", "share", "locale")
|
LOCALEDIR = os.path.join(os.path.dirname(__file__), "..", "share", "locale")
|
||||||
else:
|
else:
|
||||||
pkgdatadir = "@pkgdatadir@"
|
PKGDATADIR = "@pkgdatadir@"
|
||||||
localedir = "@localedir@"
|
LOCALEDIR = "@localedir@"
|
||||||
|
|
||||||
sys.path.insert(1, pkgdatadir)
|
sys.path.insert(1, PKGDATADIR)
|
||||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
|
||||||
if os.name != "nt":
|
if os.name != "nt":
|
||||||
locale.bindtextdomain("cartridges", localedir)
|
locale.bindtextdomain("cartridges", LOCALEDIR)
|
||||||
locale.textdomain("cartridges")
|
locale.textdomain("cartridges")
|
||||||
|
|
||||||
gettext.install("cartridges", localedir)
|
gettext.install("cartridges", LOCALEDIR)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import gi
|
|
||||||
|
|
||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
|
|
||||||
resource = Gio.Resource.load(os.path.join(pkgdatadir, "cartridges.gresource"))
|
resource = Gio.Resource.load(os.path.join(PKGDATADIR, "cartridges.gresource"))
|
||||||
resource._register()
|
resource._register() # pylint: disable=protected-access
|
||||||
|
|
||||||
from src import main
|
from cartridges import main
|
||||||
|
|
||||||
sys.exit(main.main(VERSION))
|
sys.exit(main.main(VERSION))
|
||||||
@@ -26,14 +26,14 @@ from typing import Any, Optional
|
|||||||
from gi.repository import Adw, Gio, GLib, Gtk
|
from gi.repository import Adw, Gio, GLib, Gtk
|
||||||
from PIL import Image, UnidentifiedImageError
|
from PIL import Image, UnidentifiedImageError
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.errors.friendly_error import FriendlyError
|
from cartridges.errors.friendly_error import FriendlyError
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.game_cover import GameCover
|
from cartridges.game_cover import GameCover
|
||||||
from src.store.managers.cover_manager import CoverManager
|
from cartridges.store.managers.cover_manager import CoverManager
|
||||||
from src.store.managers.sgdb_manager import SgdbManager
|
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||||
from src.utils.create_dialog import create_dialog
|
from cartridges.utils.create_dialog import create_dialog
|
||||||
from src.utils.save_cover import convert_cover, save_cover
|
from cartridges.utils.save_cover import convert_cover, save_cover
|
||||||
|
|
||||||
|
|
||||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/details-window.ui")
|
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/details-window.ui")
|
||||||
@@ -17,18 +17,16 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from gi.repository import Adw, GObject, Gtk
|
from gi.repository import Adw, GObject, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game_cover import GameCover
|
from cartridges.game_cover import GameCover
|
||||||
|
from cartridges.utils.run_executable import run_executable
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
@@ -118,21 +116,7 @@ class Game(Gtk.Box):
|
|||||||
self.save()
|
self.save()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
args = (
|
run_executable(self.executable)
|
||||||
"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
|
|
||||||
)
|
|
||||||
|
|
||||||
if shared.schema.get_boolean("exit-after-launch"):
|
if shared.schema.get_boolean("exit-after-launch"):
|
||||||
self.app.quit()
|
self.app.quit()
|
||||||
@@ -23,7 +23,7 @@ from typing import Optional
|
|||||||
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk
|
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk
|
||||||
from PIL import Image, ImageFilter, ImageStat
|
from PIL import Image, ImageFilter, ImageStat
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
|
|
||||||
|
|
||||||
class GameCover:
|
class GameCover:
|
||||||
@@ -23,10 +23,10 @@ from typing import NamedTuple
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import SourceIterable, URLExecutableSource
|
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||||
|
|
||||||
|
|
||||||
class BottlesSourceIterable(SourceIterable):
|
class BottlesSourceIterable(SourceIterable):
|
||||||
@@ -25,9 +25,9 @@ from typing import NamedTuple
|
|||||||
|
|
||||||
from gi.repository import GLib, Gtk
|
from gi.repository import GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.source import Source, SourceIterable
|
from cartridges.importer.source import Source, SourceIterable
|
||||||
|
|
||||||
|
|
||||||
class DesktopSourceIterable(SourceIterable):
|
class DesktopSourceIterable(SourceIterable):
|
||||||
@@ -22,10 +22,10 @@ from typing import NamedTuple
|
|||||||
|
|
||||||
from gi.repository import GLib, Gtk
|
from gi.repository import GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import ExecutableFormatSource, SourceIterable
|
from cartridges.importer.source import ExecutableFormatSource, SourceIterable
|
||||||
|
|
||||||
|
|
||||||
class FlatpakSourceIterable(SourceIterable):
|
class FlatpakSourceIterable(SourceIterable):
|
||||||
@@ -27,10 +27,10 @@ from json import JSONDecodeError
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, NamedTuple, Optional, TypedDict
|
from typing import Iterable, NamedTuple, Optional, TypedDict
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import (
|
from cartridges.importer.source import (
|
||||||
SourceIterable,
|
SourceIterable,
|
||||||
SourceIterationResult,
|
SourceIterationResult,
|
||||||
URLExecutableSource,
|
URLExecutableSource,
|
||||||
@@ -24,14 +24,14 @@ from typing import Any, Optional
|
|||||||
|
|
||||||
from gi.repository import Adw, Gio, GLib, Gtk
|
from gi.repository import Adw, Gio, GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.errors.error_producer import ErrorProducer
|
from cartridges.errors.error_producer import ErrorProducer
|
||||||
from src.errors.friendly_error import FriendlyError
|
from cartridges.errors.friendly_error import FriendlyError
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import UnresolvableLocationError
|
from cartridges.importer.location import UnresolvableLocationError
|
||||||
from src.importer.source import Source
|
from cartridges.importer.source import Source
|
||||||
from src.store.managers.async_manager import AsyncManager
|
from cartridges.store.managers.async_manager import AsyncManager
|
||||||
from src.store.pipeline import Pipeline
|
from cartridges.store.pipeline import Pipeline
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
@@ -22,11 +22,11 @@ from shutil import rmtree
|
|||||||
from sqlite3 import connect
|
from sqlite3 import connect
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import SourceIterable, URLExecutableSource
|
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||||
from src.utils.sqlite import copy_db
|
from cartridges.utils.sqlite import copy_db
|
||||||
|
|
||||||
|
|
||||||
class ItchSourceIterable(SourceIterable):
|
class ItchSourceIterable(SourceIterable):
|
||||||
@@ -22,10 +22,10 @@ import logging
|
|||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import (
|
from cartridges.importer.source import (
|
||||||
ExecutableFormatSource,
|
ExecutableFormatSource,
|
||||||
SourceIterable,
|
SourceIterable,
|
||||||
SourceIterationResult,
|
SourceIterationResult,
|
||||||
@@ -3,7 +3,7 @@ from os import PathLike
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, Mapping, NamedTuple, Optional
|
from typing import Iterable, Mapping, NamedTuple, Optional
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
|
|
||||||
PathSegment = str | PathLike | Path
|
PathSegment = str | PathLike | Path
|
||||||
PathSegments = Iterable[PathSegment]
|
PathSegments = Iterable[PathSegment]
|
||||||
@@ -21,11 +21,11 @@ from shutil import rmtree
|
|||||||
from sqlite3 import connect
|
from sqlite3 import connect
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import SourceIterable, URLExecutableSource
|
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||||
from src.utils.sqlite import copy_db
|
from cartridges.utils.sqlite import copy_db
|
||||||
|
|
||||||
|
|
||||||
class LutrisSourceIterable(SourceIterable):
|
class LutrisSourceIterable(SourceIterable):
|
||||||
@@ -26,12 +26,16 @@ from pathlib import Path
|
|||||||
from shlex import quote as shell_quote
|
from shlex import quote as shell_quote
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.errors.friendly_error import FriendlyError
|
from cartridges.errors.friendly_error import FriendlyError
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath, UnresolvableLocationError
|
from cartridges.importer.location import (
|
||||||
from src.importer.source import Source, SourceIterable
|
Location,
|
||||||
from src.importer.steam_source import SteamSource
|
LocationSubPath,
|
||||||
|
UnresolvableLocationError,
|
||||||
|
)
|
||||||
|
from cartridges.importer.source import Source, SourceIterable
|
||||||
|
from cartridges.importer.steam_source import SteamSource
|
||||||
|
|
||||||
|
|
||||||
class RetroarchSourceIterable(SourceIterable):
|
class RetroarchSourceIterable(SourceIterable):
|
||||||
@@ -22,8 +22,8 @@ from abc import abstractmethod
|
|||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from typing import Any, Collection, Generator, Optional
|
from typing import Any, Collection, Generator, Optional
|
||||||
|
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location
|
from cartridges.importer.location import Location
|
||||||
|
|
||||||
# Type of the data returned by iterating on a Source
|
# Type of the data returned by iterating on a Source
|
||||||
SourceIterationResult = Optional[Game | tuple[Game, tuple[Any]]]
|
SourceIterationResult = Optional[Game | tuple[Game, tuple[Any]]]
|
||||||
@@ -23,11 +23,11 @@ import re
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, NamedTuple
|
from typing import Iterable, NamedTuple
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.location import Location, LocationSubPath
|
from cartridges.importer.location import Location, LocationSubPath
|
||||||
from src.importer.source import SourceIterable, URLExecutableSource
|
from cartridges.importer.source import SourceIterable, URLExecutableSource
|
||||||
from src.utils.steam import SteamFileHelper, SteamInvalidManifestError
|
from cartridges.utils.steam import SteamFileHelper, SteamInvalidManifestError
|
||||||
|
|
||||||
|
|
||||||
class SteamSourceIterable(SourceIterable):
|
class SteamSourceIterable(SourceIterable):
|
||||||
@@ -25,7 +25,7 @@ from os import PathLike
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
|
|
||||||
|
|
||||||
class SessionFileHandler(StreamHandler):
|
class SessionFileHandler(StreamHandler):
|
||||||
@@ -24,7 +24,7 @@ import platform
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
|
|
||||||
|
|
||||||
def setup_logging() -> None:
|
def setup_logging() -> None:
|
||||||
@@ -47,12 +47,12 @@ def setup_logging() -> None:
|
|||||||
},
|
},
|
||||||
"console_formatter": {
|
"console_formatter": {
|
||||||
"format": "%(name)s %(levelname)s - %(message)s",
|
"format": "%(name)s %(levelname)s - %(message)s",
|
||||||
"class": "src.logging.color_log_formatter.ColorLogFormatter",
|
"class": "cartridges.logging.color_log_formatter.ColorLogFormatter",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
"file_handler": {
|
"file_handler": {
|
||||||
"class": "src.logging.session_file_handler.SessionFileHandler",
|
"class": "cartridges.logging.session_file_handler.SessionFileHandler",
|
||||||
"formatter": "file_formatter",
|
"formatter": "file_formatter",
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
"filename": log_filename,
|
"filename": log_filename,
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
import json
|
import json
|
||||||
import lzma
|
import lzma
|
||||||
import os
|
import os
|
||||||
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
@@ -31,39 +32,42 @@ gi.require_version("Adw", "1")
|
|||||||
# pylint: disable=wrong-import-position
|
# pylint: disable=wrong-import-position
|
||||||
from gi.repository import Adw, Gio, GLib, Gtk
|
from gi.repository import Adw, Gio, GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.details_window import DetailsWindow
|
from cartridges.details_window import DetailsWindow
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.bottles_source import BottlesSource
|
from cartridges.importer.bottles_source import BottlesSource
|
||||||
from src.importer.desktop_source import DesktopSource
|
from cartridges.importer.desktop_source import DesktopSource
|
||||||
from src.importer.flatpak_source import FlatpakSource
|
from cartridges.importer.flatpak_source import FlatpakSource
|
||||||
from src.importer.heroic_source import HeroicSource
|
from cartridges.importer.heroic_source import HeroicSource
|
||||||
from src.importer.importer import Importer
|
from cartridges.importer.importer import Importer
|
||||||
from src.importer.itch_source import ItchSource
|
from cartridges.importer.itch_source import ItchSource
|
||||||
from src.importer.legendary_source import LegendarySource
|
from cartridges.importer.legendary_source import LegendarySource
|
||||||
from src.importer.lutris_source import LutrisSource
|
from cartridges.importer.lutris_source import LutrisSource
|
||||||
from src.importer.retroarch_source import RetroarchSource
|
from cartridges.importer.retroarch_source import RetroarchSource
|
||||||
from src.importer.steam_source import SteamSource
|
from cartridges.importer.steam_source import SteamSource
|
||||||
from src.logging.setup import log_system_info, setup_logging
|
from cartridges.logging.setup import log_system_info, setup_logging
|
||||||
from src.preferences import PreferencesWindow
|
from cartridges.preferences import PreferencesWindow
|
||||||
from src.store.managers.cover_manager import CoverManager
|
from cartridges.store.managers.cover_manager import CoverManager
|
||||||
from src.store.managers.display_manager import DisplayManager
|
from cartridges.store.managers.display_manager import DisplayManager
|
||||||
from src.store.managers.file_manager import FileManager
|
from cartridges.store.managers.file_manager import FileManager
|
||||||
from src.store.managers.sgdb_manager import SgdbManager
|
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||||
from src.store.store import Store
|
from cartridges.store.store import Store
|
||||||
from src.utils.migrate_files_v1_to_v2 import migrate_files_v1_to_v2
|
from cartridges.utils.migrate_files_v1_to_v2 import migrate_files_v1_to_v2
|
||||||
from src.window import CartridgesWindow
|
from cartridges.utils.run_executable import run_executable
|
||||||
|
from cartridges.window import CartridgesWindow
|
||||||
|
|
||||||
|
|
||||||
class CartridgesApplication(Adw.Application):
|
class CartridgesApplication(Adw.Application):
|
||||||
state = shared.AppState.DEFAULT
|
state = shared.AppState.DEFAULT
|
||||||
win: CartridgesWindow
|
win: CartridgesWindow
|
||||||
|
init_search_term: Optional[str] = None
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
shared.store = Store()
|
shared.store = Store()
|
||||||
super().__init__(
|
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
|
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")
|
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()
|
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:
|
def load_games_from_disk(self) -> None:
|
||||||
if shared.games_dir.is_dir():
|
if shared.games_dir.is_dir():
|
||||||
for game_file in shared.games_dir.iterdir():
|
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(
|
configure_file(
|
||||||
input: 'cartridges.in',
|
input: 'cartridges.in',
|
||||||
@@ -25,21 +25,21 @@ from typing import Any, Callable, Optional
|
|||||||
|
|
||||||
from gi.repository import Adw, Gio, GLib, Gtk
|
from gi.repository import Adw, Gio, GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.errors.friendly_error import FriendlyError
|
from cartridges.errors.friendly_error import FriendlyError
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.importer.bottles_source import BottlesSource
|
from cartridges.importer.bottles_source import BottlesSource
|
||||||
from src.importer.flatpak_source import FlatpakSource
|
from cartridges.importer.flatpak_source import FlatpakSource
|
||||||
from src.importer.heroic_source import HeroicSource
|
from cartridges.importer.heroic_source import HeroicSource
|
||||||
from src.importer.itch_source import ItchSource
|
from cartridges.importer.itch_source import ItchSource
|
||||||
from src.importer.legendary_source import LegendarySource
|
from cartridges.importer.legendary_source import LegendarySource
|
||||||
from src.importer.location import UnresolvableLocationError
|
from cartridges.importer.location import UnresolvableLocationError
|
||||||
from src.importer.lutris_source import LutrisSource
|
from cartridges.importer.lutris_source import LutrisSource
|
||||||
from src.importer.retroarch_source import RetroarchSource
|
from cartridges.importer.retroarch_source import RetroarchSource
|
||||||
from src.importer.source import Source
|
from cartridges.importer.source import Source
|
||||||
from src.importer.steam_source import SteamSource
|
from cartridges.importer.steam_source import SteamSource
|
||||||
from src.store.managers.sgdb_manager import SgdbManager
|
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||||
from src.utils.create_dialog import create_dialog
|
from cartridges.utils.create_dialog import create_dialog
|
||||||
|
|
||||||
|
|
||||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/preferences.ui")
|
@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"
|
covers_dir = data_dir / "cartridges" / "covers"
|
||||||
|
|
||||||
appdata_dir = Path(os.getenv("appdata") or "C:\\Users\\Default\\AppData\\Roaming")
|
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)")
|
programfiles32_dir = Path(os.getenv("programfiles(x86)") or "C:\\Program Files (x86)")
|
||||||
|
|
||||||
scale_factor = max(
|
try:
|
||||||
monitor.get_scale_factor() for monitor in Gdk.Display.get_default().get_monitors()
|
scale_factor = max(
|
||||||
)
|
monitor.get_scale_factor()
|
||||||
image_size = (200 * scale_factor, 300 * 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
|
# pylint: disable=invalid-name
|
||||||
win = None
|
win = None
|
||||||
@@ -21,8 +21,8 @@ from typing import Any, Callable
|
|||||||
|
|
||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
|
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.manager import Manager
|
from cartridges.store.managers.manager import Manager
|
||||||
|
|
||||||
|
|
||||||
class AsyncManager(Manager):
|
class AsyncManager(Manager):
|
||||||
@@ -25,11 +25,11 @@ import requests
|
|||||||
from gi.repository import GdkPixbuf, Gio
|
from gi.repository import GdkPixbuf, Gio
|
||||||
from requests.exceptions import HTTPError, SSLError
|
from requests.exceptions import HTTPError, SSLError
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.manager import Manager
|
from cartridges.store.managers.manager import Manager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||||
from src.utils.save_cover import convert_cover, save_cover
|
from cartridges.utils.save_cover import convert_cover, save_cover
|
||||||
|
|
||||||
|
|
||||||
class ImageSize(NamedTuple):
|
class ImageSize(NamedTuple):
|
||||||
@@ -17,12 +17,12 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.game_cover import GameCover
|
from cartridges.game_cover import GameCover
|
||||||
from src.store.managers.manager import Manager
|
from cartridges.store.managers.manager import Manager
|
||||||
from src.store.managers.sgdb_manager import SgdbManager
|
from cartridges.store.managers.sgdb_manager import SgdbManager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||||
|
|
||||||
|
|
||||||
class DisplayManager(Manager):
|
class DisplayManager(Manager):
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.async_manager import AsyncManager
|
from cartridges.store.managers.async_manager import AsyncManager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||||
|
|
||||||
|
|
||||||
class FileManager(AsyncManager):
|
class FileManager(AsyncManager):
|
||||||
@@ -22,9 +22,9 @@ from abc import abstractmethod
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
from typing import Any, Callable, Container
|
from typing import Any, Callable, Container
|
||||||
|
|
||||||
from src.errors.error_producer import ErrorProducer
|
from cartridges.errors.error_producer import ErrorProducer
|
||||||
from src.errors.friendly_error import FriendlyError
|
from cartridges.errors.friendly_error import FriendlyError
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
|
|
||||||
|
|
||||||
class Manager(ErrorProducer):
|
class Manager(ErrorProducer):
|
||||||
@@ -21,12 +21,12 @@ from json import JSONDecodeError
|
|||||||
|
|
||||||
from requests.exceptions import HTTPError, SSLError
|
from requests.exceptions import HTTPError, SSLError
|
||||||
|
|
||||||
from src.errors.friendly_error import FriendlyError
|
from cartridges.errors.friendly_error import FriendlyError
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.async_manager import AsyncManager
|
from cartridges.store.managers.async_manager import AsyncManager
|
||||||
from src.store.managers.cover_manager import CoverManager
|
from cartridges.store.managers.cover_manager import CoverManager
|
||||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
from cartridges.store.managers.steam_api_manager import SteamAPIManager
|
||||||
from src.utils.steamgriddb import SgdbAuthError, SgdbHelper
|
from cartridges.utils.steamgriddb import SgdbAuthError, SgdbHelper
|
||||||
|
|
||||||
|
|
||||||
class SgdbManager(AsyncManager):
|
class SgdbManager(AsyncManager):
|
||||||
@@ -20,9 +20,9 @@
|
|||||||
from requests.exceptions import HTTPError, SSLError
|
from requests.exceptions import HTTPError, SSLError
|
||||||
from urllib3.exceptions import ConnectionError as Urllib3ConnectionError
|
from urllib3.exceptions import ConnectionError as Urllib3ConnectionError
|
||||||
|
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.async_manager import AsyncManager
|
from cartridges.store.managers.async_manager import AsyncManager
|
||||||
from src.utils.steam import (
|
from cartridges.utils.steam import (
|
||||||
SteamAPIHelper,
|
SteamAPIHelper,
|
||||||
SteamGameNotFoundError,
|
SteamGameNotFoundError,
|
||||||
SteamNotAGameError,
|
SteamNotAGameError,
|
||||||
@@ -22,8 +22,8 @@ from typing import Iterable
|
|||||||
|
|
||||||
from gi.repository import GObject
|
from gi.repository import GObject
|
||||||
|
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.manager import Manager
|
from cartridges.store.managers.manager import Manager
|
||||||
|
|
||||||
|
|
||||||
class Pipeline(GObject.Object):
|
class Pipeline(GObject.Object):
|
||||||
@@ -20,10 +20,10 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Generator, MutableMapping, Optional
|
from typing import Any, Generator, MutableMapping, Optional
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.store.managers.manager import Manager
|
from cartridges.store.managers.manager import Manager
|
||||||
from src.store.pipeline import Pipeline
|
from cartridges.store.pipeline import Pipeline
|
||||||
|
|
||||||
|
|
||||||
class Store:
|
class Store:
|
||||||
@@ -21,7 +21,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
|
|
||||||
old_data_dir = shared.home / ".local" / "share"
|
old_data_dir = shared.home / ".local" / "share"
|
||||||
old_cartridges_data_dir = old_data_dir / "cartridges"
|
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 gi.repository import Gdk, GdkPixbuf, Gio, GLib
|
||||||
from PIL import Image, ImageSequence, UnidentifiedImageError
|
from PIL import Image, ImageSequence, UnidentifiedImageError
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
|
|
||||||
|
|
||||||
def convert_cover(
|
def convert_cover(
|
||||||
@@ -27,8 +27,8 @@ from typing import TypedDict
|
|||||||
import requests
|
import requests
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.utils.rate_limiter import RateLimiter
|
from cartridges.utils.rate_limiter import RateLimiter
|
||||||
|
|
||||||
|
|
||||||
class SteamError(Exception):
|
class SteamError(Exception):
|
||||||
@@ -26,9 +26,9 @@ import requests
|
|||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.utils.save_cover import convert_cover, save_cover
|
from cartridges.utils.save_cover import convert_cover, save_cover
|
||||||
|
|
||||||
|
|
||||||
class SgdbError(Exception):
|
class SgdbError(Exception):
|
||||||
@@ -21,10 +21,10 @@ from typing import Any, Optional
|
|||||||
|
|
||||||
from gi.repository import Adw, Gio, GLib, Gtk
|
from gi.repository import Adw, Gio, GLib, Gtk
|
||||||
|
|
||||||
from src import shared
|
from cartridges import shared
|
||||||
from src.game import Game
|
from cartridges.game import Game
|
||||||
from src.game_cover import GameCover
|
from cartridges.game_cover import GameCover
|
||||||
from src.utils.relative_date import relative_date
|
from cartridges.utils.relative_date import relative_date
|
||||||
|
|
||||||
|
|
||||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/window.ui")
|
@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')
|
gnome = import('gnome')
|
||||||
python = import('python')
|
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())
|
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')
|
profile = get_option('profile')
|
||||||
if profile == 'development'
|
if profile == 'development'
|
||||||
@@ -20,21 +25,24 @@ elif profile == 'release'
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
conf.set('PYTHON', python.find_installation('python3').full_path())
|
conf.set('PYTHON', py_installation.full_path())
|
||||||
conf.set('PYTHON_VERSION', python.find_installation('python3').language_version())
|
conf.set('PYTHON_VERSION', py_installation.language_version())
|
||||||
conf.set('APP_ID', app_id)
|
conf.set('APP_ID', app_id)
|
||||||
conf.set('PREFIX', prefix)
|
conf.set('PREFIX', prefix)
|
||||||
conf.set('VERSION', meson.project_version())
|
conf.set('VERSION', meson.project_version())
|
||||||
conf.set('PROFILE', profile)
|
conf.set('PROFILE', profile)
|
||||||
conf.set('localedir', join_paths(get_option('prefix'), get_option('localedir')))
|
conf.set('localedir', join_paths(get_option('prefix'), get_option('localedir')))
|
||||||
conf.set('pkgdatadir', pkgdatadir)
|
conf.set('pkgdatadir', pkgdatadir)
|
||||||
|
conf.set('libexecdir', libexecdir)
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
subdir('src')
|
subdir('cartridges')
|
||||||
subdir('po')
|
subdir('po')
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
subdir('windows')
|
subdir('windows')
|
||||||
|
else
|
||||||
|
subdir('search-provider')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
gnome.post_install(
|
gnome.post_install(
|
||||||
|
|||||||
41
po/POTFILES
41
po/POTFILES
@@ -8,25 +8,26 @@ data/gtk/help-overlay.blp
|
|||||||
data/gtk/preferences.blp
|
data/gtk/preferences.blp
|
||||||
data/gtk/window.blp
|
data/gtk/window.blp
|
||||||
|
|
||||||
src/main.py
|
cartridges/main.py
|
||||||
src/window.py
|
cartridges/window.py
|
||||||
src/details_window.py
|
cartridges/details_window.py
|
||||||
src/game.py
|
cartridges/game.py
|
||||||
src/preferences.py
|
cartridges/preferences.py
|
||||||
|
|
||||||
src/utils/create_dialog.py
|
cartridges/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
|
|
||||||
|
|
||||||
src/importer/sources/bottles_source.py
|
cartridges/importer/importer.py
|
||||||
src/importer/sources/desktop_source.py
|
cartridges/importer/source.py
|
||||||
src/importer/sources/flatpak_source.py
|
cartridges/importer/location.py
|
||||||
src/importer/sources/heroic_source.py
|
cartridges/importer/location.py
|
||||||
src/importer/sources/itch_source.py
|
cartridges/importer/bottles_source.py
|
||||||
src/importer/sources/legendary_source.py
|
cartridges/importer/desktop_source.py
|
||||||
src/importer/sources/lutris_source.py
|
cartridges/importer/flatpak_source.py
|
||||||
src/importer/sources/retroarch_source.py
|
cartridges/importer/heroic_source.py
|
||||||
src/importer/sources/steam_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 ""
|
msgstr ""
|
||||||
"Project-Id-Version: Cartridges\n"
|
"Project-Id-Version: Cartridges\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -20,7 +20,7 @@ msgstr ""
|
|||||||
#: data/hu.kramo.Cartridges.desktop.in:3
|
#: data/hu.kramo.Cartridges.desktop.in:3
|
||||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:6
|
#: data/hu.kramo.Cartridges.metainfo.xml.in:6
|
||||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:30 data/gtk/window.blp:47
|
#: 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"
|
msgid "Cartridges"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:34 data/gtk/window.blp:288
|
#: 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"
|
msgid "Game Details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -56,8 +56,8 @@ msgid "Edit Game Details"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/hu.kramo.Cartridges.metainfo.xml.in:42 data/gtk/help-overlay.blp:19
|
#: data/hu.kramo.Cartridges.metainfo.xml.in:42 data/gtk/help-overlay.blp:19
|
||||||
#: data/gtk/window.blp:515 src/details_window.py:271
|
#: data/gtk/window.blp:515 cartridges/details_window.py:271
|
||||||
#: src/importer/importer.py:319 src/importer/importer.py:370
|
#: cartridges/importer/importer.py:319 cartridges/importer/importer.py:370
|
||||||
msgid "Preferences"
|
msgid "Preferences"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ msgstr ""
|
|||||||
msgid "Delete Cover"
|
msgid "Delete Cover"
|
||||||
msgstr ""
|
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"
|
msgid "Title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -93,20 +93,20 @@ msgstr ""
|
|||||||
msgid "More Info"
|
msgid "More Info"
|
||||||
msgstr ""
|
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"
|
msgid "Edit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/gtk/game.blp:107 src/window.py:350
|
#: data/gtk/game.blp:108 cartridges/window.py:350
|
||||||
msgid "Hide"
|
msgid "Hide"
|
||||||
msgstr ""
|
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
|
#: data/gtk/window.blp:435
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/gtk/game.blp:126 src/window.py:352
|
#: data/gtk/game.blp:127 cartridges/window.py:352
|
||||||
msgid "Unhide"
|
msgid "Unhide"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -123,8 +123,8 @@ msgstr ""
|
|||||||
msgid "Keyboard Shortcuts"
|
msgid "Keyboard Shortcuts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/gtk/help-overlay.blp:29 src/game.py:105 src/preferences.py:125
|
#: data/gtk/help-overlay.blp:29 cartridges/game.py:103
|
||||||
#: src/importer/importer.py:394
|
#: cartridges/preferences.py:125 cartridges/importer/importer.py:394
|
||||||
msgid "Undo"
|
msgid "Undo"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ msgstr ""
|
|||||||
msgid "Swaps the behavior of the cover image and the play button"
|
msgid "Swaps the behavior of the cover image and the play button"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/gtk/preferences.blp:25 src/details_window.py:85
|
#: data/gtk/preferences.blp:25 cartridges/details_window.py:85
|
||||||
msgid "Images"
|
msgid "Images"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ msgstr ""
|
|||||||
msgid "Sources"
|
msgid "Sources"
|
||||||
msgstr ""
|
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"
|
msgid "Steam"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ msgstr ""
|
|||||||
msgid "Install Location"
|
msgid "Install Location"
|
||||||
msgstr ""
|
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"
|
msgid "Lutris"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ msgstr ""
|
|||||||
msgid "Import Flatpak Games"
|
msgid "Import Flatpak Games"
|
||||||
msgstr ""
|
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"
|
msgid "Heroic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -253,23 +253,23 @@ msgstr ""
|
|||||||
msgid "Import Sideloaded Games"
|
msgid "Import Sideloaded Games"
|
||||||
msgstr ""
|
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"
|
msgid "Bottles"
|
||||||
msgstr ""
|
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"
|
msgid "itch"
|
||||||
msgstr ""
|
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"
|
msgid "Legendary"
|
||||||
msgstr ""
|
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"
|
msgid "RetroArch"
|
||||||
msgstr ""
|
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"
|
msgid "Flatpak"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ msgstr ""
|
|||||||
msgid "Import Game Launchers"
|
msgid "Import Game Launchers"
|
||||||
msgstr ""
|
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"
|
msgid "Desktop Entries"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -345,11 +345,11 @@ msgstr ""
|
|||||||
msgid "Games you hide will appear here."
|
msgid "Games you hide will appear here."
|
||||||
msgstr ""
|
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"
|
msgid "All Games"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/gtk/window.blp:126 src/main.py:168
|
#: data/gtk/window.blp:126 cartridges/main.py:209
|
||||||
msgid "Added"
|
msgid "Added"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -401,62 +401,67 @@ msgstr ""
|
|||||||
msgid "About Cartridges"
|
msgid "About Cartridges"
|
||||||
msgstr ""
|
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
|
#. 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"
|
msgid "translator_credits"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the date when the game was added
|
#. The variable is the date when the game was added
|
||||||
#: src/window.py:373
|
#: cartridges/window.py:373
|
||||||
msgid "Added: {}"
|
msgid "Added: {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/window.py:376
|
#: cartridges/window.py:376
|
||||||
msgid "Never"
|
msgid "Never"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the date when the game was last played
|
#. The variable is the date when the game was last played
|
||||||
#: src/window.py:380
|
#: cartridges/window.py:380
|
||||||
msgid "Last played: {}"
|
msgid "Last played: {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/details_window.py:76
|
#: cartridges/details_window.py:76
|
||||||
msgid "Apply"
|
msgid "Apply"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/details_window.py:82
|
#: cartridges/details_window.py:82
|
||||||
msgid "Add New Game"
|
msgid "Add New Game"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/details_window.py:83
|
#: cartridges/details_window.py:83
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/details_window.py:93
|
#: cartridges/details_window.py:93
|
||||||
msgid "Executables"
|
msgid "Executables"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Translate this string as you would translate "file"
|
#. Translate this string as you would translate "file"
|
||||||
#: src/details_window.py:108
|
#: cartridges/details_window.py:108
|
||||||
msgid "file.txt"
|
msgid "file.txt"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. As in software
|
#. As in software
|
||||||
#: src/details_window.py:110
|
#: cartridges/details_window.py:110
|
||||||
msgid "program"
|
msgid "program"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Translate this string as you would translate "path to {}"
|
#. 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\\{}"
|
msgid "C:\\path\\to\\{}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. Translate this string as you would translate "path to {}"
|
#. 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/{}"
|
msgid "/path/to/{}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/details_window.py:128
|
#: cartridges/details_window.py:128
|
||||||
msgid ""
|
msgid ""
|
||||||
"To launch the executable \"{}\", use the command:\n"
|
"To launch the executable \"{}\", use the command:\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -469,141 +474,136 @@ msgid ""
|
|||||||
"If the path contains spaces, make sure to wrap it in double quotes!"
|
"If the path contains spaces, make sure to wrap it in double quotes!"
|
||||||
msgstr ""
|
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"
|
msgid "Couldn't Add Game"
|
||||||
msgstr ""
|
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."
|
msgid "Game title cannot be empty."
|
||||||
msgstr ""
|
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."
|
msgid "Executable cannot be empty."
|
||||||
msgstr ""
|
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"
|
msgid "Couldn't Apply Preferences"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the title of the game
|
#. The variable is the title of the game
|
||||||
#: src/game.py:141
|
#: cartridges/game.py:139
|
||||||
msgid "{} launched"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. The variable is the title of the game
|
|
||||||
#: src/game.py:155
|
|
||||||
msgid "{} hidden"
|
msgid "{} hidden"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/game.py:155
|
#: cartridges/game.py:139
|
||||||
msgid "{} unhidden"
|
msgid "{} unhidden"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the title of the game
|
#. The variable is the title of the game
|
||||||
#. The variable is the number of games removed
|
#. 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"
|
msgid "{} removed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:124
|
#: cartridges/preferences.py:124
|
||||||
msgid "All games removed"
|
msgid "All games removed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:172
|
#: cartridges/preferences.py:172
|
||||||
msgid ""
|
msgid ""
|
||||||
"An API key is required to use SteamGridDB. You can generate one {}here{}."
|
"An API key is required to use SteamGridDB. You can generate one {}here{}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:184
|
#: cartridges/preferences.py:184
|
||||||
msgid "Downloading covers…"
|
msgid "Downloading covers…"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:203
|
#: cartridges/preferences.py:203
|
||||||
msgid "Covers updated"
|
msgid "Covers updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:335
|
#: cartridges/preferences.py:335
|
||||||
msgid "Installation Not Found"
|
msgid "Installation Not Found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:336
|
#: cartridges/preferences.py:336
|
||||||
msgid "Select a valid directory."
|
msgid "Select a valid directory."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:372 src/importer/importer.py:317
|
#: cartridges/preferences.py:372 cartridges/importer/importer.py:317
|
||||||
msgid "Warning"
|
msgid "Warning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:406
|
#: cartridges/preferences.py:406
|
||||||
msgid "Invalid Directory"
|
msgid "Invalid Directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/preferences.py:412
|
#: cartridges/preferences.py:412
|
||||||
msgid "Set Location"
|
msgid "Set Location"
|
||||||
msgstr ""
|
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"
|
msgid "Dismiss"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/importer/importer.py:145
|
#: cartridges/importer/importer.py:145
|
||||||
msgid "Importing Games…"
|
msgid "Importing Games…"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/importer/importer.py:338
|
#: cartridges/importer/importer.py:338
|
||||||
msgid "The following errors occured during import:"
|
msgid "The following errors occured during import:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/importer/importer.py:367
|
#: cartridges/importer/importer.py:367
|
||||||
msgid "No new games found"
|
msgid "No new games found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/importer/importer.py:379
|
#: cartridges/importer/importer.py:379
|
||||||
msgid "1 game imported"
|
msgid "1 game imported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the number of games
|
#. The variable is the number of games
|
||||||
#: src/importer/importer.py:383
|
#: cartridges/importer/importer.py:383
|
||||||
msgid "{} games imported"
|
msgid "{} games imported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. A single game removed
|
#. A single game removed
|
||||||
#: src/importer/importer.py:387
|
#: cartridges/importer/importer.py:387
|
||||||
msgid "1 removed"
|
msgid "1 removed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the name of the source
|
#. The variable is the name of the source
|
||||||
#: src/importer/sources/location.py:33
|
#: cartridges/importer/location.py:33
|
||||||
msgid "Select the {} cache directory."
|
msgid "Select the {} cache directory."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the name of the source
|
#. The variable is the name of the source
|
||||||
#: src/importer/sources/location.py:35
|
#: cartridges/importer/location.py:35
|
||||||
msgid "Select the {} configuration directory."
|
msgid "Select the {} configuration directory."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is the name of the source
|
#. The variable is the name of the source
|
||||||
#: src/importer/sources/location.py:37
|
#: cartridges/importer/location.py:37
|
||||||
msgid "Select the {} data directory."
|
msgid "Select the {} data directory."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/store/managers/sgdb_manager.py:46
|
#: cartridges/importer/retroarch_source.py:129
|
||||||
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
|
|
||||||
msgid "No RetroArch Core Selected"
|
msgid "No RetroArch Core Selected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. The variable is a newline separated list of playlists
|
#. 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:"
|
msgid "The following playlists have no default core:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/importer/sources/retroarch_source.py:133
|
#: cartridges/importer/retroarch_source.py:133
|
||||||
msgid "Games with no core selected were not imported"
|
msgid "Games with no core selected were not imported"
|
||||||
msgstr ""
|
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