Process EGS media

This commit is contained in:
Mathieu Comandon 2021-07-05 21:19:53 -07:00
parent 44fae7b2dc
commit cadac7d3de
4 changed files with 99 additions and 4 deletions

View file

@ -129,7 +129,6 @@ def get_pixbuf_for_game(image_abspath, size, is_installed=True):
def convert_to_background(background_path, target_size=(320, 1080)):
"""Converts a image to a pane background"""
coverart = Image.open(background_path)
coverart = coverart.convert("RGBA")
@ -159,6 +158,43 @@ def convert_to_background(background_path, target_size=(320, 1080)):
return background
def thumbnail_image(base_image, target_size):
base_width, base_height = base_image.size
base_ratio = base_width / base_height
target_width, target_height = target_size
target_ratio = target_width / target_height
# Resize and crop coverart
if base_ratio >= target_ratio:
width = int(base_width * (target_height / base_height))
height = target_height
else:
width = target_width
height = int(base_height * (target_width / base_width))
x_offset = int((width - target_width) / 2)
y_offset = int((height - target_height) / 2)
base_image = base_image.resize((width, height), resample=Image.BICUBIC)
base_image = base_image.crop((x_offset, y_offset, width - x_offset, height - y_offset))
return base_image
def paste_overlay(base_image, overlay_image, position=0.7):
base_width, base_height = base_image.size
overlay_width, overlay_height = overlay_image.size
offset_x = int((base_width - overlay_width) / 2)
offset_y = int((base_height - overlay_height) / 2)
base_image.paste(
overlay_image, (
offset_x,
offset_y,
overlay_width + offset_x,
overlay_height + offset_y
),
mask=overlay_image
)
return base_image
def image2pixbuf(image):
"""Converts a PIL Image to a GDK Pixbuf"""
image_array = array.array('B', image.tobytes())

View file

@ -55,6 +55,7 @@ class BaseService(GObject.Object):
local = False
drm_free = False # DRM free games can be added to Lutris from an existing install
medias = {}
extra_medias = {}
default_format = "icon"
__gsignals__ = {
@ -84,10 +85,19 @@ class BaseService(GObject.Object):
def load_icons(self):
"""Download all game media from the service"""
for icon_type in self.medias:
service_media = self.medias[icon_type]()
all_medias = self.medias.copy()
all_medias.update(self.extra_medias)
# Download icons
for icon_type in all_medias:
service_media = all_medias[icon_type]()
media_urls = service_media.get_media_urls()
download_icons(media_urls, service_media)
# Process icons
for icon_type in all_medias:
service_media = all_medias[icon_type]()
service_media.render()
if self.id != "lutris":
for service_media_class in (LutrisIcon, LutrisBanner):
service_media = service_media_class()

View file

@ -9,6 +9,7 @@ from lutris import settings
from lutris.config import LutrisConfig, write_game_config
from lutris.database.games import add_game, get_game_by_field
from lutris.database.services import ServiceGameCollection
from lutris.gui.widgets.utils import Image, paste_overlay, thumbnail_image
from lutris.services.base import OnlineService
from lutris.services.service_game import ServiceGame
from lutris.services.service_media import ServiceMedia
@ -17,10 +18,42 @@ from lutris.util.egs.egs_launcher import EGSLauncher
from lutris.util.log import logger
from lutris.util.strings import slugify
EGS_GAME_ART_PATH = os.path.expanduser("~/.cache/lutris/egs/game_box")
EGS_GAME_BOX_PATH = os.path.expanduser("~/.cache/lutris/egs/game_box_tall")
EGS_LOGO_PATH = os.path.expanduser("~/.cache/lutris/egs/game_logo")
EGS_BANNERS_PATH = os.path.expanduser("~/.cache/lutris/egs/banners")
EGS_BOX_ART_PATH = os.path.expanduser("~/.cache/lutris/egs/boxart")
BANNER_SIZE = (316, 178)
BOX_ART_SIZE = (200, 267)
class DieselGameMedia(ServiceMedia):
service = "egs"
file_pattern = "%s.jpg"
min_logo_x = 300
min_logo_y = 150
def _render_filename(self, filename):
game_box_path = os.path.join(self.dest_path, filename)
logo_path = os.path.join(EGS_LOGO_PATH, filename.replace(".jpg", ".png"))
has_logo = os.path.exists(logo_path)
thumb_image = Image.open(game_box_path)
thumb_image = thumb_image.convert("RGBA")
thumb_image = thumbnail_image(thumb_image, self.size)
if has_logo:
logo_image = Image.open(logo_path)
logo_image = logo_image.convert("RGBA")
logo_width, logo_height = logo_image.size
if logo_width > self.min_logo_x:
logo_image = logo_image.resize((self.min_logo_x, int(
logo_height * (self.min_logo_x / logo_width))), resample=Image.BICUBIC)
elif logo_height > self.min_logo_y:
logo_image = logo_image.resize(
(int(logo_width * (self.min_logo_y / logo_height)), self.min_logo_y), resample=Image.BICUBIC)
thumb_image = paste_overlay(thumb_image, logo_image)
thumb_path = os.path.join(self.dest_path, filename)
thumb_image = thumb_image.convert("RGB")
thumb_image.save(thumb_path)
def get_media_url(self, detail):
for image in detail.get("keyImages", []):
@ -31,13 +64,21 @@ class DieselGameMedia(ServiceMedia):
class DieselGameBoxTall(DieselGameMedia):
"""EGS tall game box"""
size = (200, 267)
min_logo_x = 100
min_logo_y = 100
dest_path = os.path.join(settings.CACHE_DIR, "egs/game_box_tall")
api_field = "DieselGameBoxTall"
def render(self):
for filename in os.listdir(self.dest_path):
self._render_filename(filename)
class DieselGameBox(DieselGameMedia):
class DieselGameBox(DieselGameBoxTall):
"""EGS game box"""
size = (316, 178)
min_logo_x = 300
min_logo_y = 150
dest_path = os.path.join(settings.CACHE_DIR, "egs/game_box")
api_field = "DieselGameBox"
@ -45,6 +86,8 @@ class DieselGameBox(DieselGameMedia):
class DieselGameBoxLogo(DieselGameMedia):
"""EGS game box"""
size = (200, 100)
file_pattern = "%s.png"
visible = False
dest_path = os.path.join(settings.CACHE_DIR, "egs/game_logo")
api_field = "DieselGameBoxLogo"
@ -74,6 +117,8 @@ class EpicGamesStoreService(OnlineService):
medias = {
"game_box": DieselGameBox,
"box_tall": DieselGameBoxTall,
}
extra_medias = {
"logo": DieselGameBoxLogo,
}
default_format = "box_tall"

View file

@ -18,6 +18,7 @@ class ServiceMedia:
service = NotImplemented
size = NotImplemented
source = "remote" # set to local if the files don't need to be downloaded
visible = True # This media should be displayed as an option in the UI
small_size = None
dest_path = None
file_pattern = NotImplemented
@ -88,3 +89,6 @@ class ServiceMedia:
logger.error(ex.code)
return None
return cache_path
def render(self):
"""Used if the media requires extra processing"""