Composite manually added covers - fixes #177
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
import os
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import Any, Optional
|
||||
|
||||
@@ -29,9 +30,10 @@ from src import shared
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.game_cover import GameCover
|
||||
from src.store.managers.cover_manager import CoverManager
|
||||
from src.store.managers.sgdb_manager import SGDBManager
|
||||
from src.utils.create_dialog import create_dialog
|
||||
from src.utils.save_cover import resize_cover, save_cover
|
||||
from src.utils.save_cover import convert_cover, save_cover
|
||||
|
||||
|
||||
@Gtk.Template(resource_path=shared.PREFIX + "/gtk/details-window.ui")
|
||||
@@ -281,15 +283,17 @@ class DetailsWindow(Adw.Window):
|
||||
except GLib.GError:
|
||||
return
|
||||
|
||||
def resize() -> None:
|
||||
if cover := resize_cover(path):
|
||||
self.game_cover.new_cover(cover)
|
||||
self.cover_button_delete_revealer.set_reveal_child(True)
|
||||
self.cover_changed = True
|
||||
def thread_func() -> None:
|
||||
if new_path := convert_cover(
|
||||
pixbuf=shared.store.managers[CoverManager].composite_cover(Path(path))
|
||||
):
|
||||
self.game_cover.new_cover(new_path)
|
||||
self.cover_button_delete_revealer.set_reveal_child(True)
|
||||
self.cover_changed = True
|
||||
self.toggle_loading()
|
||||
|
||||
self.toggle_loading()
|
||||
GLib.Thread.new(None, resize)
|
||||
GLib.Thread.new(None, thread_func)
|
||||
|
||||
def set_executable(self, _source: Any, result: Gio.Task, *_args: Any) -> None:
|
||||
try:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Optional
|
||||
from typing import Optional
|
||||
|
||||
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk
|
||||
from PIL import Image, ImageFilter, ImageStat
|
||||
|
||||
@@ -150,7 +150,6 @@ class DesktopSourceIterable(SourceIterable):
|
||||
try:
|
||||
icon_str = keyfile.get_string("Desktop Entry", "Icon")
|
||||
except GLib.GError:
|
||||
print("AAAAAAAAAAAAAAAAAAAAAAA")
|
||||
yield game
|
||||
continue
|
||||
else:
|
||||
|
||||
@@ -22,14 +22,14 @@ from pathlib import Path
|
||||
from typing import NamedTuple
|
||||
|
||||
import requests
|
||||
from gi.repository import Gio, GdkPixbuf
|
||||
from gi.repository import GdkPixbuf, Gio
|
||||
from requests.exceptions import HTTPError, SSLError
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.store.managers.manager import Manager
|
||||
from src.store.managers.steam_api_manager import SteamAPIManager
|
||||
from src.utils.save_cover import resize_cover, save_cover
|
||||
from src.utils.save_cover import convert_cover, save_cover
|
||||
|
||||
|
||||
class ImageSize(NamedTuple):
|
||||
@@ -110,18 +110,16 @@ class CoverManager(Manager):
|
||||
stretch = 1 - (resized_height / cover_size.height)
|
||||
return stretch <= max_stretch
|
||||
|
||||
def save_composited_cover(
|
||||
def composite_cover(
|
||||
self,
|
||||
game: Game,
|
||||
image_path: Path,
|
||||
scale: float = 1,
|
||||
blur_size: ImageSize = ImageSize(2, 2),
|
||||
) -> None:
|
||||
) -> GdkPixbuf.Pixbuf:
|
||||
"""
|
||||
Save the image composited with a background blur.
|
||||
Return the image composited with a background blur.
|
||||
If the image is stretchable, just stretch it.
|
||||
|
||||
:param game: The game to save the cover for
|
||||
:param path: Path where the source image is located
|
||||
:param scale:
|
||||
Scale of the smalled image side
|
||||
@@ -130,14 +128,15 @@ class CoverManager(Manager):
|
||||
"""
|
||||
|
||||
# Load source image
|
||||
source = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
|
||||
source = GdkPixbuf.Pixbuf.new_from_file(
|
||||
str(convert_cover(image_path, resize=False))
|
||||
)
|
||||
source_size = ImageSize(source.get_width(), source.get_height())
|
||||
cover_size = ImageSize._make(shared.image_size)
|
||||
|
||||
# Stretch if possible
|
||||
if scale == 1 and self.is_stretchable(source_size, cover_size):
|
||||
save_cover(game.game_id, resize_cover(pixbuf=source))
|
||||
return
|
||||
return source
|
||||
|
||||
# Create the blurred cover background
|
||||
# fmt: off
|
||||
@@ -164,7 +163,7 @@ class CoverManager(Manager):
|
||||
GdkPixbuf.InterpType.BILINEAR,
|
||||
255,
|
||||
)
|
||||
save_cover(game.game_id, resize_cover(pixbuf=cover))
|
||||
return cover
|
||||
|
||||
def main(self, game: Game, additional_data: dict) -> None:
|
||||
if game.blacklisted:
|
||||
@@ -185,13 +184,15 @@ class CoverManager(Manager):
|
||||
continue
|
||||
|
||||
# Icon cover
|
||||
if key == "local_icon_path":
|
||||
self.save_composited_cover(
|
||||
game,
|
||||
image_path,
|
||||
scale=0.7,
|
||||
blur_size=ImageSize(1, 2),
|
||||
)
|
||||
return
|
||||
composite_kwargs = {}
|
||||
|
||||
self.save_composited_cover(game, image_path)
|
||||
if key == "local_icon_path":
|
||||
composite_kwargs["scale"] = 0.7
|
||||
composite_kwargs["blur_size"] = ImageSize(1, 2)
|
||||
|
||||
save_cover(
|
||||
game.game_id,
|
||||
convert_cover(
|
||||
pixbuf=self.composite_cover(image_path, **composite_kwargs)
|
||||
),
|
||||
)
|
||||
|
||||
@@ -28,12 +28,22 @@ from PIL import Image, ImageSequence, UnidentifiedImageError
|
||||
from src import shared
|
||||
|
||||
|
||||
def resize_cover(
|
||||
cover_path: Optional[Path] = None, pixbuf: Optional[GdkPixbuf.Pixbuf] = None
|
||||
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"])
|
||||
@@ -42,7 +52,8 @@ def resize_cover(
|
||||
with Image.open(cover_path) as image:
|
||||
if getattr(image, "is_animated", False):
|
||||
frames = tuple(
|
||||
frame.resize((200, 300)) for frame in ImageSequence.Iterator(image)
|
||||
frame.resize((200, 300)) if resize else frame
|
||||
for frame in ImageSequence.Iterator(image)
|
||||
)
|
||||
|
||||
tmp_path = Path(Gio.File.new_tmp("XXXXXX.gif")[0].get_path())
|
||||
@@ -59,7 +70,7 @@ def resize_cover(
|
||||
image = image.convert("RGBA")
|
||||
|
||||
tmp_path = Path(Gio.File.new_tmp("XXXXXX.tiff")[0].get_path())
|
||||
image.resize(shared.image_size).save(
|
||||
(image.resize(shared.image_size) if resize else image).save(
|
||||
tmp_path,
|
||||
compression="tiff_adobe_deflate"
|
||||
if shared.schema.get_boolean("high-quality-images")
|
||||
@@ -70,7 +81,7 @@ def resize_cover(
|
||||
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)
|
||||
return convert_cover(tmp_path)
|
||||
except GLib.GError:
|
||||
return None
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from requests.exceptions import HTTPError
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.utils.save_cover import resize_cover, save_cover
|
||||
from src.utils.save_cover import convert_cover, save_cover
|
||||
|
||||
|
||||
class SGDBError(Exception):
|
||||
@@ -134,7 +134,7 @@ class SGDBHelper:
|
||||
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, resize_cover(tmp_file_path))
|
||||
save_cover(game.game_id, convert_cover(tmp_file_path))
|
||||
except SGDBAuthError as error:
|
||||
# Let caller handle auth errors
|
||||
raise error
|
||||
|
||||
Reference in New Issue
Block a user