diff --git a/cartridges/details_dialog.py b/cartridges/details_dialog.py index 8d85586a2..f2bf5ccd9 100644 --- a/cartridges/details_dialog.py +++ b/cartridges/details_dialog.py @@ -68,7 +68,8 @@ def __init__(self, game: Optional[Game] = None, **kwargs: Any): # Make it so only one dialog can be open at a time self.__class__.is_open = True - self.connect("closed", lambda *_: self.set_is_open(False)) + self.tmp_cover_path = None + self.connect("closed", self.on_closed) self.game: Optional[Game] = game self.game_cover: GameCover = GameCover({self.cover}) @@ -160,11 +161,20 @@ def set_exec_info_a11y_label(*_args: Any) -> None: self.set_focus(self.name) def delete_pixbuf(self, *_args: Any) -> None: + if self.tmp_cover_path: + self.tmp_cover_path.unlink(missing_ok=True) + self.game_cover.new_cover() self.cover_button_delete_revealer.set_reveal_child(False) self.cover_changed = True + def on_closed(self, *args): + if self.tmp_cover_path: + self.tmp_cover_path.unlink(missing_ok=True) + + self.set_is_open(False) + def apply_preferences(self, *_args: Any) -> None: final_name = self.name.get_text() final_developer = self.developer.get_text() @@ -240,6 +250,7 @@ def apply_preferences(self, *_args: Any) -> None: save_cover( self.game.game_id, self.game_cover.path, + self.game_cover.pixbuf, ) shared.store.add_game(self.game, {}, run_pipeline=False) @@ -295,26 +306,29 @@ def set_cover(self, _source: Any, result: Gio.Task, *_args: Any) -> None: return def thread_func() -> None: - new_path = None + is_animated = False try: with Image.open(path) as image: if getattr(image, "is_animated", False): - new_path = convert_cover(path) - except UnidentifiedImageError: + is_animated = True + except (UnidentifiedImageError, OSError, ValueError): pass - if not new_path: - new_path = convert_cover( + if is_animated: + if self.tmp_cover_path: + self.tmp_cover_path.unlink(missing_ok=True) + self.tmp_cover_path = convert_cover(path) + self.game_cover.new_cover(self.tmp_cover_path) + else: + self.game_cover.new_cover( pixbuf=shared.store.managers[CoverManager].composite_cover( Path(path) ) ) - if new_path: - self.game_cover.new_cover(new_path) - self.cover_button_delete_revealer.set_reveal_child(True) - self.cover_changed = True + self.cover_button_delete_revealer.set_reveal_child(True) + self.cover_changed = True self.toggle_loading() diff --git a/cartridges/game_cover.py b/cartridges/game_cover.py index 46dd6740c..c6f3759c4 100644 --- a/cartridges/game_cover.py +++ b/cartridges/game_cover.py @@ -45,12 +45,22 @@ def __init__(self, pictures: set[Gtk.Picture], path: Optional[Path] = None) -> N self.pictures = pictures self.new_cover(path) - def new_cover(self, path: Optional[Path] = None) -> None: + def new_cover( + self, + path: Optional[Path] = None, + pixbuf: Optional[GdkPixbuf.Pixbuf] = None + ) -> None: self.animation = None self.texture = None self.blurred = None self.luminance = None self.path = path + self.pixbuf = pixbuf + + if pixbuf: + self.texture = Gdk.Texture.new_for_pixbuf(pixbuf) + self.set_texture(self.texture) + return if path: if path.suffix == ".gif": diff --git a/cartridges/store/managers/cover_manager.py b/cartridges/store/managers/cover_manager.py index 34a358040..5cf2f259d 100644 --- a/cartridges/store/managers/cover_manager.py +++ b/cartridges/store/managers/cover_manager.py @@ -19,10 +19,10 @@ # SPDX-License-Identifier: GPL-3.0-or-later from pathlib import Path -from typing import NamedTuple +from typing import NamedTuple, Optional import requests -from gi.repository import GdkPixbuf, Gio +from gi.repository import GdkPixbuf, Gio, GLib from requests.exceptions import HTTPError, SSLError from cartridges import shared @@ -128,9 +128,21 @@ def composite_cover( """ # Load source image - source = GdkPixbuf.Pixbuf.new_from_file( - str(convert_cover(image_path, resize=False)) - ) + try: + source = GdkPixbuf.Pixbuf.new_from_file( + str(image_path) + ) + except GLib.Error: + tmp_cover_path = convert_cover(image_path, resize=False) + + if tmp_cover_path: + source = GdkPixbuf.Pixbuf.new_from_file( + str(tmp_cover_path) + ) + tmp_cover_path.unlink(missing_ok=True) + else: + return None + source_size = ImageSize(source.get_width(), source.get_height()) cover_size = ImageSize._make(shared.image_size) @@ -192,7 +204,8 @@ def main(self, game: Game, additional_data: dict) -> None: save_cover( game.game_id, - convert_cover( - pixbuf=self.composite_cover(image_path, **composite_kwargs) - ), + pixbuf=self.composite_cover(image_path, **composite_kwargs), ) + + if key == "online_cover_url": + image_path.unlink(missing_ok=True) diff --git a/cartridges/utils/save_cover.py b/cartridges/utils/save_cover.py index d5ad44ff6..29d2d849e 100644 --- a/cartridges/utils/save_cover.py +++ b/cartridges/utils/save_cover.py @@ -30,24 +30,11 @@ def convert_cover( cover_path: Optional[Path] = None, - pixbuf: Optional[GdkPixbuf.Pixbuf] = None, resize: bool = True, ) -> Optional[Path]: if not cover_path and not pixbuf: return None - pixbuf_extensions = set() - for pixbuf_format in GdkPixbuf.Pixbuf.get_formats(): - for pixbuf_extension in pixbuf_format.get_extensions(): - pixbuf_extensions.add(pixbuf_extension) - - if not resize and cover_path and cover_path.suffix.lower()[1:] in pixbuf_extensions: - return cover_path - - if pixbuf: - cover_path = Path(Gio.File.new_tmp("XXXXXX.tiff")[0].get_path()) - pixbuf.savev(str(cover_path), "tiff", ["compression"], ["1"]) - try: with Image.open(cover_path) as image: if getattr(image, "is_animated", False): @@ -76,7 +63,7 @@ def convert_cover( if shared.schema.get_boolean("high-quality-images") else shared.TIFF_COMPRESSION, ) - except UnidentifiedImageError: + except (UnidentifiedImageError, OSError, ValueError): try: Gdk.Texture.new_from_filename(str(cover_path)).save_to_tiff( tmp_path := Gio.File.new_tmp("XXXXXX.tiff")[0].get_path() @@ -88,7 +75,11 @@ def convert_cover( return tmp_path -def save_cover(game_id: str, cover_path: Path) -> None: +def save_cover( + game_id: str, + cover_path: Optional[Path] = None, + pixbuf: Optional[GdkPixbuf.Pixbuf] = None, +) -> None: shared.covers_dir.mkdir(parents=True, exist_ok=True) animated_path = shared.covers_dir / f"{game_id}.gif" @@ -98,7 +89,15 @@ def save_cover(game_id: str, cover_path: Path) -> None: animated_path.unlink(missing_ok=True) static_path.unlink(missing_ok=True) - if not cover_path: + if not cover_path and not pixbuf: + return + + if pixbuf: + pixbuf.savev(str(static_path), "tiff", ["compression"], ["1"]) + + if game_id in shared.win.game_covers: + shared.win.game_covers[game_id].new_cover(static_path) + return copyfile( diff --git a/cartridges/utils/steamgriddb.py b/cartridges/utils/steamgriddb.py index d4d8affca..e1cd6f907 100644 --- a/cartridges/utils/steamgriddb.py +++ b/cartridges/utils/steamgriddb.py @@ -134,7 +134,11 @@ def conditionaly_update_cover(self, game: Game) -> None: tmp_file = Gio.File.new_tmp()[0] tmp_file_path = tmp_file.get_path() Path(tmp_file_path).write_bytes(response.content) - save_cover(game.game_id, convert_cover(tmp_file_path)) + tmp_cover_path = convert_cover(tmp_file_path) + if tmp_cover_path: + save_cover(game.game_id, tmp_cover_path) + tmp_cover_path.unlink(missing_ok=True) + tmp_file_path.unlink(missing_ok=True) except SgdbAuthError as error: # Let caller handle auth errors raise error