From 3e072873e1a03d5503d6e4e6df1f6914ee4758d4 Mon Sep 17 00:00:00 2001 From: Vargash Date: Tue, 31 Mar 2026 11:00:49 +0200 Subject: [PATCH] feat: new export.gamelist.media.thumbnail configuration --- backend/config/config_manager.py | 6 +++++ .../tests/config/fixtures/config/config.yml | 5 ++++ backend/tests/config/test_config_loader.py | 2 ++ backend/utils/gamelist_exporter.py | 27 ++++++++++++++++--- examples/config.batocera-retrobat.yml | 8 ++++++ examples/config.example.yml | 13 ++++++++- 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index 2755c357b..b04e5d59c 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -126,6 +126,7 @@ class Config: SCAN_REGION_PRIORITY: list[str] SCAN_LANGUAGE_PRIORITY: list[str] SCAN_MEDIA: list[str] + GAMELIST_THUMBNAIL_MEDIA: str def __init__(self, **entries): self.__dict__.update(entries) @@ -341,6 +342,11 @@ class ConfigManager: "manual", ], ), + GAMELIST_THUMBNAIL_MEDIA=pydash.get( + self._raw_config, + "export.gamelist.media.thumbnail", + "cover", + ), ) def _get_ejs_controls(self) -> dict[str, EjsControls]: diff --git a/backend/tests/config/fixtures/config/config.yml b/backend/tests/config/fixtures/config/config.yml index 8975f4c94..78a6fa97c 100644 --- a/backend/tests/config/fixtures/config/config.yml +++ b/backend/tests/config/fixtures/config/config.yml @@ -75,3 +75,8 @@ emulatorjs: 0: value: x value2: BUTTON_2 + +export: + gamelist: + media: + thumbnail: "box3d" diff --git a/backend/tests/config/test_config_loader.py b/backend/tests/config/test_config_loader.py index c1792858a..e5faf6cec 100644 --- a/backend/tests/config/test_config_loader.py +++ b/backend/tests/config/test_config_loader.py @@ -54,6 +54,7 @@ def test_config_loader(): assert loader.config.SCAN_ARTWORK_PRIORITY == ["igdb", "ss"] assert loader.config.SCAN_REGION_PRIORITY == ["jp", "eu", "wor"] assert loader.config.SCAN_LANGUAGE_PRIORITY == ["jp", "es"] + assert loader.config.GAMELIST_THUMBNAIL_MEDIA == "box3d" def test_empty_config_loader(): @@ -86,3 +87,4 @@ def test_empty_config_loader(): assert loader.config.EJS_NETPLAY_ICE_SERVERS == [] assert loader.config.EJS_SETTINGS == {} assert loader.config.EJS_CONTROLS == {} + assert loader.config.GAMELIST_THUMBNAIL_MEDIA == "cover" diff --git a/backend/utils/gamelist_exporter.py b/backend/utils/gamelist_exporter.py index 6e93f9a9e..5c0a2d261 100644 --- a/backend/utils/gamelist_exporter.py +++ b/backend/utils/gamelist_exporter.py @@ -9,6 +9,7 @@ from xml.etree.ElementTree import ( # trunk-ignore(bandit/B405) from fastapi import Request from config import FRONTEND_RESOURCES_PATH, YOUTUBE_BASE_URL +from config.config_manager import config_manager as cm from handler.database import db_platform_handler, db_rom_handler from handler.filesystem import fs_platform_handler from logger.logger import log @@ -51,10 +52,28 @@ class GamelistExporter: SubElement(game, "desc").text = rom.summary # Media files - if rom.path_cover_l: - SubElement(game, "thumbnail").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.path_cover_l}" - ) + thumbnail_path: str | None = None + thumbnail_option = cm.config.GAMELIST_THUMBNAIL_MEDIA + if thumbnail_option == "box3d": + if rom.ss_metadata and rom.ss_metadata.get("box3d_path"): + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['box3d_path']}" + elif rom.gamelist_metadata and rom.gamelist_metadata.get("box3d"): + thumbnail_path = rom.gamelist_metadata["box3d"] + elif thumbnail_option == "miximage": + if rom.ss_metadata and rom.ss_metadata.get("miximage_path"): + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['miximage_path']}" + elif rom.gamelist_metadata and rom.gamelist_metadata.get("miximage_path"): + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['miximage_path']}" + elif thumbnail_option == "physical": + if rom.ss_metadata and rom.ss_metadata.get("physical_path"): + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['physical_path']}" + elif rom.gamelist_metadata and rom.gamelist_metadata.get("physical_path"): + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['physical_path']}" + # "cover" and "box2d" both map to path_cover_l (box2d IS the front cover) + if thumbnail_path is None and rom.path_cover_l: + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.path_cover_l}" + if thumbnail_path: + SubElement(game, "thumbnail").text = thumbnail_path if path_video := rom.path_video: SubElement(game, "video").text = f"{FRONTEND_RESOURCES_PATH}/{path_video}" diff --git a/examples/config.batocera-retrobat.yml b/examples/config.batocera-retrobat.yml index c843adbcb..c72520cb3 100644 --- a/examples/config.batocera-retrobat.yml +++ b/examples/config.batocera-retrobat.yml @@ -183,3 +183,11 @@ system: zx81: zx81 zxspectrum: zxs versions: {} + +export: + gamelist: + media: + # Use the 3D box art as the tag, preferred by Batocera/RetroBAT + # Falls back to front cover if 3D box art has not been scraped + # Other options: cover (default), box2d, miximage, physical + thumbnail: box3d diff --git a/examples/config.example.yml b/examples/config.example.yml index c18ebfa3b..525fe2d0f 100644 --- a/examples/config.example.yml +++ b/examples/config.example.yml @@ -135,6 +135,17 @@ # # Other media assets (might be used in the future) # - marquee # Custom marquee +# export: +# gamelist: +# media: +# # Media asset to use as the tag in the exported gamelist.xml +# # cover - front cover art / box2d (default) +# # box2d - same as cover +# # box3d - 3D box art (falls back to cover if not available) +# # miximage - mixed image (falls back to cover if not available) +# # physical - disc/cartridge image (falls back to cover if not available) +# thumbnail: cover + # EmulatorJS per-core options # emulatorjs: # debug: true # Available options will be logged to the browser console @@ -163,4 +174,4 @@ # value2: BUTTON_2 # Mapping for connected controller # 1: # Player 2 # 2: # Player 3 -# 3: # Player 4 +# 3: # Player 4 \ No newline at end of file