@@ -132,6 +132,15 @@ template $PreferencesWindow : Adw.PreferencesWindow {
|
||||
valign: center;
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ActionRow {
|
||||
title: _("Import Flatpak Games");
|
||||
activatable-widget: lutris_import_flatpak_switch;
|
||||
|
||||
Switch lutris_import_flatpak_switch {
|
||||
valign: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ExpanderRow heroic_expander_row {
|
||||
@@ -216,6 +225,29 @@ template $PreferencesWindow : Adw.PreferencesWindow {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ExpanderRow flatpak_expander_row {
|
||||
title: _("Flatpak");
|
||||
show-enable-switch: true;
|
||||
|
||||
Adw.ActionRow flatpak_data_action_row {
|
||||
title: _("Install Location");
|
||||
|
||||
Button flatpak_config_file_chooser_button {
|
||||
icon-name: "folder-symbolic";
|
||||
valign: center;
|
||||
}
|
||||
}
|
||||
|
||||
Adw.ActionRow flatpak_import_launchers_row {
|
||||
title: _("Import Game Launchers");
|
||||
activatable-widget: flatpak_import_launchers_switch;
|
||||
|
||||
Switch flatpak_import_launchers_switch {
|
||||
valign: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
<key name="lutris-import-steam" type="b">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key name="lutris-import-flatpak" type="b">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key name="heroic" type="b">
|
||||
<default>true</default>
|
||||
</key>
|
||||
@@ -61,6 +64,15 @@
|
||||
<key name="legendary-location" type="s">
|
||||
<default>"~/.config/legendary/"</default>
|
||||
</key>
|
||||
<key name="flatpak" type="b">
|
||||
<default>true</default>
|
||||
</key>
|
||||
<key name="flatpak-location" type="s">
|
||||
<default>"/var/lib/flatpak/exports"</default>
|
||||
</key>
|
||||
<key name="flatpak-import-launchers" type="b">
|
||||
<default>false</default>
|
||||
</key>
|
||||
<key name="sgdb-key" type="s">
|
||||
<default>""</default>
|
||||
</key>
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"--filesystem=~/.var/app/net.lutris.Lutris/:ro",
|
||||
"--filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic/:ro",
|
||||
"--filesystem=~/.var/app/com.usebottles.bottles/data/bottles/:ro",
|
||||
"--filesystem=~/.var/app/io.itch.itch/config/itch/:ro"
|
||||
"--filesystem=~/.var/app/io.itch.itch/config/itch/:ro",
|
||||
"--filesystem=/var/lib/flatpak:ro"
|
||||
],
|
||||
"cleanup" : [
|
||||
"/include",
|
||||
@@ -96,6 +97,20 @@
|
||||
"sha256": "bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "python3-pyxdg",
|
||||
"buildsystem": "simple",
|
||||
"build-commands": [
|
||||
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"pyxdg\" --no-build-isolation"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl",
|
||||
"sha256": "bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -41,13 +41,13 @@ class BottlesSourceIterator(SourceIterator):
|
||||
|
||||
data = self.source.data_location["library.yml"].read_text("utf-8")
|
||||
library: dict = yaml.safe_load(data)
|
||||
added_time = int(time())
|
||||
|
||||
for entry in library.values():
|
||||
# Build game
|
||||
values = {
|
||||
"version": shared.SPEC_VERSION,
|
||||
"source": self.source.id,
|
||||
"added": int(time()),
|
||||
"added": added_time,
|
||||
"name": entry["name"],
|
||||
"game_id": self.source.game_id_format.format(game_id=entry["id"]),
|
||||
"executable": self.source.executable_format.format(
|
||||
|
||||
134
src/importer/sources/flatpak_source.py
Normal file
134
src/importer/sources/flatpak_source.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# flatpak_source.py
|
||||
#
|
||||
# Copyright 2022-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
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
import subprocess
|
||||
from xdg import IconTheme
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.source import Source, SourceIterationResult, SourceIterator
|
||||
|
||||
|
||||
class FlatpakSourceIterator(SourceIterator):
|
||||
source: "FlatpakSource"
|
||||
|
||||
def generator_builder(self) -> SourceIterationResult:
|
||||
"""Generator method producing games"""
|
||||
|
||||
added_time = int(time())
|
||||
|
||||
IconTheme.icondirs.append(self.source.data_location["icons"])
|
||||
|
||||
try:
|
||||
process = subprocess.run(
|
||||
("flatpak-spawn", "--host", "flatpak", "list", "--columns=application"),
|
||||
capture_output=True,
|
||||
encoding="utf-8",
|
||||
check=True,
|
||||
)
|
||||
flatpak_ids = process.stdout.split("\n")
|
||||
|
||||
to_remove = (
|
||||
{"hu.kramo.Cartridges"}
|
||||
if shared.schema.get_boolean("flatpak-import-launchers")
|
||||
else {
|
||||
"hu.kramo.Cartridges",
|
||||
"com.valvesoftware.Steam",
|
||||
"net.lutris.Lutris",
|
||||
"com.heroicgameslauncher.hgl",
|
||||
"com.usebottles.Bottles",
|
||||
"io.itch.itch",
|
||||
}
|
||||
)
|
||||
|
||||
for item in to_remove:
|
||||
if item in flatpak_ids:
|
||||
flatpak_ids.remove(item)
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
return
|
||||
|
||||
for entry in (self.source.data_location["applications"]).iterdir():
|
||||
flatpak_id = entry.stem
|
||||
|
||||
if flatpak_id not in flatpak_ids:
|
||||
continue
|
||||
|
||||
with entry.open("r", encoding="utf-8") as open_file:
|
||||
string = open_file.read()
|
||||
|
||||
desktop_values = {"Name": None, "Icon": None, "Categories": None}
|
||||
for key in desktop_values:
|
||||
if regex := re.findall(f"{key}=(.*)\n", string):
|
||||
desktop_values[key] = regex[0]
|
||||
|
||||
if not desktop_values["Name"]:
|
||||
continue
|
||||
|
||||
if not desktop_values["Categories"]:
|
||||
continue
|
||||
|
||||
if not "Game" in desktop_values["Categories"].split(";"):
|
||||
continue
|
||||
|
||||
values = {
|
||||
"source": self.source.id,
|
||||
"added": added_time,
|
||||
"name": desktop_values["Name"],
|
||||
"game_id": self.source.game_id_format.format(game_id=flatpak_id),
|
||||
"executable": self.source.executable_format.format(
|
||||
flatpak_id=flatpak_id
|
||||
),
|
||||
}
|
||||
game = Game(values, allow_side_effects=False)
|
||||
|
||||
additional_data = {}
|
||||
if icon_name := desktop_values["Icon"]:
|
||||
if icon_path := IconTheme.getIconPath(icon_name, 512):
|
||||
additional_data = {"local_icon_path": Path(icon_path)}
|
||||
else:
|
||||
pass
|
||||
|
||||
# Produce game
|
||||
yield (game, additional_data)
|
||||
|
||||
|
||||
class FlatpakSource(Source):
|
||||
"""Generic Flatpak source"""
|
||||
|
||||
name = "Flatpak"
|
||||
iterator_class = FlatpakSourceIterator
|
||||
executable_format = "flatpak run {flatpak_id}"
|
||||
available_on = set(("linux",))
|
||||
|
||||
data_location = Location(
|
||||
schema_key="flatpak-location",
|
||||
candidates=(
|
||||
"/var/lib/flatpak/exports/",
|
||||
shared.data_dir / "flatpak" / "exports",
|
||||
),
|
||||
paths={
|
||||
"applications": (True, "share/applications"),
|
||||
"icons": (True, "share/icons"),
|
||||
},
|
||||
)
|
||||
@@ -68,7 +68,7 @@ class HeroicSourceIterator(SourceIterator):
|
||||
}
|
||||
|
||||
def game_from_library_entry(
|
||||
self, entry: HeroicLibraryEntry
|
||||
self, entry: HeroicLibraryEntry, added_time: int
|
||||
) -> SourceIterationResult:
|
||||
"""Helper method used to build a Game from a Heroic library entry"""
|
||||
|
||||
@@ -81,9 +81,8 @@ class HeroicSourceIterator(SourceIterator):
|
||||
runner = entry["runner"]
|
||||
service = self.sub_sources[runner]["service"]
|
||||
values = {
|
||||
"version": shared.SPEC_VERSION,
|
||||
"source": self.source.id,
|
||||
"added": int(time()),
|
||||
"added": added_time,
|
||||
"name": entry["title"],
|
||||
"developer": entry["developer"],
|
||||
"game_id": self.source.game_id_format.format(
|
||||
@@ -119,9 +118,12 @@ class HeroicSourceIterator(SourceIterator):
|
||||
# Invalid library.json file, skip it
|
||||
logging.warning("Couldn't open Heroic file: %s", str(file))
|
||||
continue
|
||||
|
||||
added_time = int(time())
|
||||
|
||||
for entry in library:
|
||||
try:
|
||||
result = self.game_from_library_entry(entry)
|
||||
result = self.game_from_library_entry(entry, added_time)
|
||||
except KeyError:
|
||||
# Skip invalid games
|
||||
logging.warning("Invalid Heroic game skipped in %s", str(file))
|
||||
@@ -130,7 +132,7 @@ class HeroicSourceIterator(SourceIterator):
|
||||
|
||||
|
||||
class HeroicSource(URLExecutableSource):
|
||||
"""Generic heroic games launcher source"""
|
||||
"""Generic Heroic Games Launcher source"""
|
||||
|
||||
name = "Heroic"
|
||||
iterator_class = HeroicSourceIterator
|
||||
@@ -141,9 +143,8 @@ class HeroicSource(URLExecutableSource):
|
||||
schema_key="heroic-location",
|
||||
candidates=(
|
||||
"~/.var/app/com.heroicgameslauncher.hgl/config/heroic/",
|
||||
shared.config_dir / "heroic/",
|
||||
"~/.config/heroic/",
|
||||
shared.appdata_dir / "heroic/",
|
||||
shared.config_dir / "heroic",
|
||||
shared.appdata_dir / "heroic",
|
||||
),
|
||||
paths={
|
||||
"config.json": (False, "config.json"),
|
||||
|
||||
@@ -59,11 +59,12 @@ class ItchSourceIterator(SourceIterator):
|
||||
connection = connect(db_path)
|
||||
cursor = connection.execute(db_request)
|
||||
|
||||
added_time = int(time())
|
||||
|
||||
# Create games from the db results
|
||||
for row in cursor:
|
||||
values = {
|
||||
"version": shared.SPEC_VERSION,
|
||||
"added": int(time()),
|
||||
"added": added_time,
|
||||
"source": self.source.id,
|
||||
"name": row[1],
|
||||
"game_id": self.source.game_id_format.format(game_id=row[0]),
|
||||
@@ -87,9 +88,8 @@ class ItchSource(URLExecutableSource):
|
||||
schema_key="itch-location",
|
||||
candidates=(
|
||||
"~/.var/app/io.itch.itch/config/itch/",
|
||||
shared.config_dir / "itch/",
|
||||
"~/.config/itch/",
|
||||
shared.appdata_dir / "itch/",
|
||||
shared.config_dir / "itch",
|
||||
shared.appdata_dir / "itch",
|
||||
),
|
||||
paths={"butler.db": (False, "db/butler.db")},
|
||||
)
|
||||
|
||||
@@ -32,7 +32,9 @@ from src.importer.sources.source import Source, SourceIterationResult, SourceIte
|
||||
class LegendarySourceIterator(SourceIterator):
|
||||
source: "LegendarySource"
|
||||
|
||||
def game_from_library_entry(self, entry: dict) -> SourceIterationResult:
|
||||
def game_from_library_entry(
|
||||
self, entry: dict, added_time: int
|
||||
) -> SourceIterationResult:
|
||||
# Skip non-games
|
||||
if entry["is_dlc"]:
|
||||
return None
|
||||
@@ -40,8 +42,7 @@ class LegendarySourceIterator(SourceIterator):
|
||||
# Build game
|
||||
app_name = entry["app_name"]
|
||||
values = {
|
||||
"version": shared.SPEC_VERSION,
|
||||
"added": int(time()),
|
||||
"added": added_time,
|
||||
"source": self.source.id,
|
||||
"name": entry["title"],
|
||||
"game_id": self.source.game_id_format.format(game_id=app_name),
|
||||
@@ -72,10 +73,13 @@ class LegendarySourceIterator(SourceIterator):
|
||||
except (JSONDecodeError, OSError):
|
||||
logging.warning("Couldn't open Legendary file: %s", str(file))
|
||||
return
|
||||
|
||||
added_time = int(time())
|
||||
|
||||
# Generate games from library
|
||||
for entry in library.values():
|
||||
try:
|
||||
result = self.game_from_library_entry(entry)
|
||||
result = self.game_from_library_entry(entry, added_time)
|
||||
except KeyError as error:
|
||||
# Skip invalid games
|
||||
logging.warning(
|
||||
@@ -93,10 +97,7 @@ class LegendarySource(Source):
|
||||
iterator_class = LegendarySourceIterator
|
||||
data_location: Location = Location(
|
||||
schema_key="legendary-location",
|
||||
candidates=(
|
||||
shared.config_dir / "legendary/",
|
||||
"~/.config/legendary",
|
||||
),
|
||||
candidates=(shared.config_dir / "legendary",),
|
||||
paths={
|
||||
"installed.json": (False, "installed.json"),
|
||||
"metadata": (True, "metadata"),
|
||||
|
||||
@@ -48,19 +48,24 @@ class LutrisSourceIterator(SourceIterator):
|
||||
AND configPath IS NOT NULL
|
||||
AND installed
|
||||
AND (runner IS NOT "steam" OR :import_steam)
|
||||
AND (runner IS NOT "flatpak" OR :import_flatpak)
|
||||
;
|
||||
"""
|
||||
params = {"import_steam": shared.schema.get_boolean("lutris-import-steam")}
|
||||
params = {
|
||||
"import_steam": shared.schema.get_boolean("lutris-import-steam"),
|
||||
"import_flatpak": shared.schema.get_boolean("lutris-import-flatpak"),
|
||||
}
|
||||
db_path = copy_db(self.source.data_location["pga.db"])
|
||||
connection = connect(db_path)
|
||||
cursor = connection.execute(request, params)
|
||||
|
||||
added_time = int(time())
|
||||
|
||||
# Create games from the DB results
|
||||
for row in cursor:
|
||||
# Create game
|
||||
values = {
|
||||
"version": shared.SPEC_VERSION,
|
||||
"added": int(time()),
|
||||
"added": added_time,
|
||||
"hidden": row[4],
|
||||
"name": row[1],
|
||||
"source": f"{self.source.id}_{row[3]}",
|
||||
@@ -83,7 +88,7 @@ class LutrisSourceIterator(SourceIterator):
|
||||
|
||||
|
||||
class LutrisSource(URLExecutableSource):
|
||||
"""Generic lutris source"""
|
||||
"""Generic Lutris source"""
|
||||
|
||||
name = "Lutris"
|
||||
iterator_class = LutrisSourceIterator
|
||||
@@ -96,8 +101,7 @@ class LutrisSource(URLExecutableSource):
|
||||
schema_key="lutris-location",
|
||||
candidates=(
|
||||
"~/.var/app/net.lutris.Lutris/data/lutris/",
|
||||
shared.data_dir / "lutris/",
|
||||
"~/.local/share/lutris/",
|
||||
shared.data_dir / "lutris",
|
||||
),
|
||||
paths={
|
||||
"pga.db": (False, "pga.db"),
|
||||
@@ -108,8 +112,7 @@ class LutrisSource(URLExecutableSource):
|
||||
schema_key="lutris-cache-location",
|
||||
candidates=(
|
||||
"~/.var/app/net.lutris.Lutris/cache/lutris/",
|
||||
shared.cache_dir / "lutris/",
|
||||
"~/.cache/lutris",
|
||||
shared.cache_dir / "lutris",
|
||||
),
|
||||
paths={
|
||||
"coverart": (True, "coverart"),
|
||||
|
||||
@@ -87,7 +87,7 @@ class Source(Iterable):
|
||||
@property
|
||||
def game_id_format(self) -> str:
|
||||
"""The string format used to construct game IDs"""
|
||||
return self.name.lower() + "_{game_id}"
|
||||
return self.id + "_{game_id}"
|
||||
|
||||
@property
|
||||
def is_available(self):
|
||||
|
||||
@@ -66,6 +66,9 @@ class SteamSourceIterator(SourceIterator):
|
||||
"""Generator method producing games"""
|
||||
appid_cache = set()
|
||||
manifests = self.get_manifests()
|
||||
|
||||
added_time = int(time())
|
||||
|
||||
for manifest in manifests:
|
||||
# Get metadata from manifest
|
||||
steam = SteamFileHelper()
|
||||
@@ -87,8 +90,7 @@ class SteamSourceIterator(SourceIterator):
|
||||
|
||||
# Build game from local data
|
||||
values = {
|
||||
"version": shared.SPEC_VERSION,
|
||||
"added": int(time()),
|
||||
"added": added_time,
|
||||
"name": local_data["name"],
|
||||
"source": self.source.id,
|
||||
"game_id": self.source.game_id_format.format(game_id=appid),
|
||||
@@ -117,8 +119,8 @@ class SteamSource(URLExecutableSource):
|
||||
schema_key="steam-location",
|
||||
candidates=(
|
||||
"~/.var/app/com.valvesoftware.Steam/data/Steam/",
|
||||
shared.data_dir / "Steam/",
|
||||
"~/.steam/",
|
||||
shared.data_dir / "Steam",
|
||||
"~/.steam",
|
||||
shared.programfiles32_dir / "Steam",
|
||||
),
|
||||
paths={
|
||||
|
||||
@@ -34,6 +34,7 @@ from src.details_window import DetailsWindow
|
||||
from src.game import Game
|
||||
from src.importer.importer import Importer
|
||||
from src.importer.sources.bottles_source import BottlesSource
|
||||
from src.importer.sources.flatpak_source import FlatpakSource
|
||||
from src.importer.sources.heroic_source import HeroicSource
|
||||
from src.importer.sources.itch_source import ItchSource
|
||||
from src.importer.sources.legendary_source import LegendarySource
|
||||
@@ -222,6 +223,9 @@ class CartridgesApplication(Adw.Application):
|
||||
if shared.schema.get_boolean("bottles"):
|
||||
importer.add_source(BottlesSource())
|
||||
|
||||
if shared.schema.get_boolean("flatpak"):
|
||||
importer.add_source(FlatpakSource())
|
||||
|
||||
if shared.schema.get_boolean("itch"):
|
||||
importer.add_source(ItchSource())
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ from gi.repository import Adw, Gio, GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.importer.sources.bottles_source import BottlesSource
|
||||
from src.importer.sources.flatpak_source import FlatpakSource
|
||||
from src.importer.sources.heroic_source import HeroicSource
|
||||
from src.importer.sources.itch_source import ItchSource
|
||||
from src.importer.sources.legendary_source import LegendarySource
|
||||
@@ -59,6 +60,7 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
lutris_cache_action_row = Gtk.Template.Child()
|
||||
lutris_cache_file_chooser_button = Gtk.Template.Child()
|
||||
lutris_import_steam_switch = Gtk.Template.Child()
|
||||
lutris_import_flatpak_switch = Gtk.Template.Child()
|
||||
|
||||
heroic_expander_row = Gtk.Template.Child()
|
||||
heroic_config_action_row = Gtk.Template.Child()
|
||||
@@ -79,6 +81,11 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
legendary_config_action_row = Gtk.Template.Child()
|
||||
legendary_config_file_chooser_button = Gtk.Template.Child()
|
||||
|
||||
flatpak_expander_row = Gtk.Template.Child()
|
||||
flatpak_data_action_row = Gtk.Template.Child()
|
||||
flatpak_config_file_chooser_button = Gtk.Template.Child()
|
||||
flatpak_import_launchers_switch = Gtk.Template.Child()
|
||||
|
||||
sgdb_key_group = Gtk.Template.Child()
|
||||
sgdb_key_entry_row = Gtk.Template.Child()
|
||||
sgdb_switch = Gtk.Template.Child()
|
||||
@@ -124,6 +131,7 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
# Sources settings
|
||||
for source_class in (
|
||||
BottlesSource,
|
||||
FlatpakSource,
|
||||
HeroicSource,
|
||||
ItchSource,
|
||||
LegendarySource,
|
||||
@@ -168,9 +176,11 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
"cover-launches-game",
|
||||
"high-quality-images",
|
||||
"lutris-import-steam",
|
||||
"lutris-import-flatpak",
|
||||
"heroic-import-epic",
|
||||
"heroic-import-gog",
|
||||
"heroic-import-sideload",
|
||||
"flatpak-import-launchers",
|
||||
"sgdb",
|
||||
"sgdb-prefer",
|
||||
"sgdb-animated",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# local_cover_manager.py
|
||||
#
|
||||
# Copyright 2023 Geoffrey Coulaud
|
||||
# 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
|
||||
@@ -17,12 +18,13 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from pathlib import Path
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
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 save_cover, resize_cover
|
||||
from src.utils.save_cover import resize_cover, save_cover
|
||||
|
||||
|
||||
class LocalCoverManager(Manager):
|
||||
@@ -31,12 +33,39 @@ class LocalCoverManager(Manager):
|
||||
run_after = (SteamAPIManager,)
|
||||
|
||||
def manager_logic(self, game: Game, additional_data: dict) -> None:
|
||||
# Ensure that the cover path is in the additional data
|
||||
try:
|
||||
image_path: Path = additional_data["local_image_path"]
|
||||
except KeyError:
|
||||
return
|
||||
if not image_path.is_file():
|
||||
return
|
||||
# Save the image
|
||||
save_cover(game.game_id, resize_cover(image_path))
|
||||
if image_path := additional_data.get("local_image_path"):
|
||||
if not image_path.is_file():
|
||||
return
|
||||
save_cover(game.game_id, resize_cover(image_path))
|
||||
elif icon_path := additional_data.get("local_icon_path"):
|
||||
cover_width, cover_height = shared.image_size
|
||||
|
||||
dest_width = cover_width * 0.7
|
||||
dest_height = cover_width * 0.7
|
||||
|
||||
dest_x = cover_width * 0.15
|
||||
dest_y = (cover_height - dest_height) / 2
|
||||
|
||||
image = GdkPixbuf.Pixbuf.new_from_file(str(icon_path)).scale_simple(
|
||||
dest_width, dest_height, GdkPixbuf.InterpType.BILINEAR
|
||||
)
|
||||
|
||||
cover = image.scale_simple(
|
||||
1, 2, GdkPixbuf.InterpType.BILINEAR
|
||||
).scale_simple(cover_width, cover_height, GdkPixbuf.InterpType.BILINEAR)
|
||||
|
||||
image.composite(
|
||||
cover,
|
||||
dest_x,
|
||||
dest_y,
|
||||
dest_width,
|
||||
dest_height,
|
||||
dest_x,
|
||||
dest_y,
|
||||
1,
|
||||
1,
|
||||
GdkPixbuf.InterpType.BILINEAR,
|
||||
255,
|
||||
)
|
||||
|
||||
save_cover(game.game_id, resize_cover(pixbuf=cover))
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
|
||||
from gi.repository import Gio
|
||||
from gi.repository import Gdk, Gio, GLib
|
||||
from PIL import Image, ImageSequence, UnidentifiedImageError
|
||||
|
||||
from src import shared
|
||||
@@ -63,7 +63,13 @@ def resize_cover(cover_path=None, pixbuf=None):
|
||||
else "webp",
|
||||
)
|
||||
except UnidentifiedImageError:
|
||||
return None
|
||||
try:
|
||||
Gdk.Texture.new_from_filename(str(cover_path)).save_to_tiff(
|
||||
tmp_path := Gio.File.new_tmp("XXXXXX.tiff")[0].get_path()
|
||||
)
|
||||
return resize_cover(tmp_path)
|
||||
except GLib.GError:
|
||||
return None
|
||||
|
||||
return tmp_path
|
||||
|
||||
|
||||
Reference in New Issue
Block a user