From 62aff0e7cc985b9201141c51e06319a7df306392 Mon Sep 17 00:00:00 2001 From: kramo <93832451+kra-mo@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:24:48 +0200 Subject: [PATCH] Basic animated cover support --- src/game.py | 10 ++++++++++ src/meson.build | 3 ++- src/utils/create_details_window.py | 17 +++++++++++++++-- src/utils/display_animation.py | 25 +++++++++++++++++++++++++ src/utils/save_cover.py | 14 +++++++++++++- src/window.py | 14 ++++++++++++++ 6 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 src/utils/display_animation.py diff --git a/src/game.py b/src/game.py index c75bfff..146aa1e 100644 --- a/src/game.py +++ b/src/game.py @@ -25,6 +25,7 @@ import sys from gi.repository import GdkPixbuf, Gio, Gtk +from .display_animation import display_animation from .save_game import save_game @@ -58,6 +59,12 @@ class game(Gtk.Box): # pylint: disable=invalid-name self.blacklisted = "blacklisted" in data self.pixbuf = self.get_cover() + self.animation_path = ( + self.parent_widget.data_dir + / "cartridges" + / "animated_covers" + / self.game_id + ) self.cover.set_pixbuf(self.pixbuf) self.title.set_label(self.name) @@ -82,6 +89,9 @@ class game(Gtk.Box): # pylint: disable=invalid-name self.menu_button.set_menu_model(self.game_options) self.menu_button.get_popover().connect("notify::visible", self.hide_play) + if self.animation_path.is_file(): + display_animation(self.cover.set_pixbuf, self.animation_path) + def launch(self): # Generate launch arguments, either list (no shell) or a string (for shell). args = ( diff --git a/src/meson.build b/src/meson.build index bd8b536..84e8640 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,7 +33,8 @@ cartridges_sources = [ 'utils/save_game.py', 'utils/save_cover.py', 'utils/create_dialog.py', - 'utils/create_details_window.py' + 'utils/create_details_window.py', + 'utils/display_animation.py' ] install_data(cartridges_sources, install_dir: moduledir) diff --git a/src/utils/create_details_window.py b/src/utils/create_details_window.py index 2f9cae3..a99c0d4 100644 --- a/src/utils/create_details_window.py +++ b/src/utils/create_details_window.py @@ -25,6 +25,7 @@ import time from gi.repository import Adw, GdkPixbuf, Gio, GLib, GObject, Gtk from .create_dialog import create_dialog +from .display_animation import display_animation from .save_cover import save_cover from .save_game import save_game from .steamgriddb import SGDBSave @@ -37,6 +38,7 @@ def create_details_window(parent_widget, game_id=None): games = parent_widget.games pixbuf = None + animation_path = None cover_deleted = False cover_button_edit = Gtk.Button( @@ -84,6 +86,11 @@ def create_details_window(parent_widget, game_id=None): else: window.set_title(_("Edit Game Details")) cover = Gtk.Picture.new_for_pixbuf(parent_widget.games[game_id].pixbuf) + animation_path = ( + parent_widget.data_dir / "cartridges" / "animated_covers" / game_id + ) + if animation_path.is_file(): + display_animation(cover.set_pixbuf, animation_path) developer = Gtk.Entry.new_with_buffer( Gtk.EntryBuffer.new(games[game_id].developer, -1) ) @@ -218,6 +225,8 @@ def create_details_window(parent_widget, game_id=None): def set_cover(_source, result, _unused): nonlocal pixbuf + nonlocal animation_path + try: path = filechooser.open_finish(result).get_path() except GLib.GError: @@ -225,20 +234,23 @@ def create_details_window(parent_widget, game_id=None): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, 200, 300, False) + cover.set_pixbuf(pixbuf) except GLib.GError: + animation_path = path animated_pixbuf = GdkPixbuf.PixbufAnimation.new_from_file(path) pixbuf = animated_pixbuf.get_static_image().scale_simple( 200, 300, GdkPixbuf.InterpType.BILINEAR ) + display_animation(cover.set_pixbuf, animation=animated_pixbuf) cover_button_delete_revealer.set_reveal_child(True) - cover.set_pixbuf(pixbuf) def close_window(_widget, _callback=None): window.close() def apply_preferences(_widget, _callback=None): nonlocal pixbuf + nonlocal animation_path nonlocal cover_deleted nonlocal game_id @@ -323,7 +335,8 @@ def create_details_window(parent_widget, game_id=None): if game_id in parent_widget.pixbufs: parent_widget.pixbufs.pop(game_id) - save_cover(parent_widget, game_id, None, pixbuf) + save_cover(parent_widget, game_id, None, pixbuf, animation_path) + elif not ( parent_widget.data_dir / "cartridges" / "covers" / f"{game_id}.tiff" ).is_file(): diff --git a/src/utils/display_animation.py b/src/utils/display_animation.py new file mode 100644 index 0000000..cdc2f38 --- /dev/null +++ b/src/utils/display_animation.py @@ -0,0 +1,25 @@ +from gi.repository import GdkPixbuf, GLib + + +def display_animation( + function, path=None, animation=None, parent_widget=None, game_id=None +): + if not animation: + animation = GdkPixbuf.PixbufAnimation.new_from_file(str(path)) + + anim_iter = animation.get_iter() + + def update_animation(): + nonlocal anim_iter + + if parent_widget and parent_widget.current_anim != game_id: + return + + anim_iter.advance() + pixbuf = anim_iter.get_pixbuf().scale_simple( + 200, 300, GdkPixbuf.InterpType.BILINEAR + ) + GLib.timeout_add(anim_iter.get_delay_time(), update_animation) + function(pixbuf) + + update_animation() diff --git a/src/utils/save_cover.py b/src/utils/save_cover.py index 2ad5aa8..6d5f854 100644 --- a/src/utils/save_cover.py +++ b/src/utils/save_cover.py @@ -18,10 +18,14 @@ # SPDX-License-Identifier: GPL-3.0-or-later +from shutil import copyfile + from gi.repository import GdkPixbuf, Gio -def save_cover(parent_widget, game_id, cover_path=None, pixbuf=None): +def save_cover( + parent_widget, game_id, cover_path=None, pixbuf=None, animation_path=None +): covers_dir = parent_widget.data_dir / "cartridges" / "covers" covers_dir.mkdir(parents=True, exist_ok=True) @@ -38,3 +42,11 @@ def save_cover(parent_widget, game_id, cover_path=None, pixbuf=None): ["compression"], ["8"] if parent_widget.schema.get_boolean("high-quality-images") else ["7"], ) + + animated_covers_dir = parent_widget.data_dir / "cartridges" / "animated_covers" + (animated_covers_dir / game_id).unlink(missing_ok=True) + + if animation_path: + animated_covers_dir.mkdir(parents=True, exist_ok=True) + with animated_covers_dir / game_id as open_file: + copyfile(animation_path, open_file) diff --git a/src/window.py b/src/window.py index ddcbfb6..9276f26 100644 --- a/src/window.py +++ b/src/window.py @@ -25,6 +25,7 @@ from shutil import rmtree from gi.repository import Adw, GdkPixbuf, Gio, GLib, Gtk +from .display_animation import display_animation from .game import game from .get_games import get_games from .save_game import save_game @@ -99,6 +100,7 @@ class CartridgesWindow(Adw.ApplicationWindow): self.active_game_id = None self.loading = None self.scaled_pixbuf = None + self.current_anim = None self.overview.set_measure_overlay(self.overview_box, True) self.overview.set_clip_overlay(self.overview_box, False) @@ -286,6 +288,18 @@ class CartridgesWindow(Adw.ApplicationWindow): pixbuf = current_game.pixbuf self.overview_cover.set_pixbuf(pixbuf) + new_id = game_id if self.current_anim != game_id else f"{game_id}_new" + + self.current_anim = new_id + + if current_game.animation_path.is_file(): + display_animation( + self.overview_cover.set_pixbuf, + current_game.animation_path, + parent_widget=self, + game_id=new_id, + ) + self.scaled_pixbuf = pixbuf.scale_simple(2, 3, GdkPixbuf.InterpType.BILINEAR) self.overview_blurred_cover.set_pixbuf(self.scaled_pixbuf) self.set_overview_opacity()