From 1501f452205b91c65c30ca8267d25c8f4b742d1e Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Sun, 5 Apr 2026 23:15:42 -0400 Subject: [PATCH] more changes from review --- backend/config/config_manager.py | 46 +++++++++++++++++-- backend/endpoints/responses/config.py | 6 +-- backend/utils/gamelist_exporter.py | 44 ++++++++++-------- examples/config.example.yml | 2 +- frontend/src/__generated__/index.ts | 1 + .../__generated__/models/ConfigResponse.ts | 3 ++ .../__generated__/models/MetadataMediaType.ts | 5 ++ .../src/__generated__/models/RomSSMetadata.ts | 1 + frontend/src/stores/config.ts | 2 + 9 files changed, 83 insertions(+), 27 deletions(-) create mode 100644 frontend/src/__generated__/models/MetadataMediaType.ts diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index b6e5aab38..9a0d91846 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -297,9 +297,6 @@ class ConfigManager: FIRMWARE_FOLDER_NAME=pydash.get( self._raw_config, "filesystem.firmware_folder", "bios" ), - GAMELIST_AUTO_EXPORT_ON_SCAN=pydash.get( - self._raw_config, "scan.gamelist.export", False - ), SKIP_HASH_CALCULATION=pydash.get( self._raw_config, "filesystem.skip_hash_calculation", False ), @@ -372,15 +369,18 @@ class ConfigManager: "manual", ], ), + GAMELIST_AUTO_EXPORT_ON_SCAN=pydash.get( + self._raw_config, "scan.gamelist.export", False + ), GAMELIST_MEDIA_THUMBNAIL=pydash.get( self._raw_config, "scan.gamelist.media.thumbnail", - "cover", + MetadataMediaType.BOX2D, ), GAMELIST_MEDIA_IMAGE=pydash.get( self._raw_config, "scan.gamelist.media.image", - "screenshot", + MetadataMediaType.SCREENSHOT, ), ) @@ -598,6 +598,42 @@ class ConfigManager: ) sys.exit(3) + valid_thumbnail_options = { + MetadataMediaType.BOX2D, + MetadataMediaType.BOX3D, + MetadataMediaType.MIXIMAGE, + MetadataMediaType.PHYSICAL, + } + if not isinstance(self.config.GAMELIST_MEDIA_THUMBNAIL, str): + log.critical( + "Invalid config.yml: scan.gamelist.media.thumbnail must be a string" + ) + sys.exit(3) + if self.config.GAMELIST_MEDIA_THUMBNAIL not in valid_thumbnail_options: + log.critical( + f"Invalid config.yml: scan.gamelist.media.thumbnail must be one of {valid_thumbnail_options}" + ) + sys.exit(3) + + valid_image_options = { + MetadataMediaType.TITLE_SCREEN, + MetadataMediaType.MIXIMAGE, + MetadataMediaType.BOX2D, + MetadataMediaType.SCREENSHOT, + } + + if not isinstance(self.config.GAMELIST_MEDIA_IMAGE, str): + log.critical( + "Invalid config.yml: scan.gamelist.media.image must be a string" + ) + sys.exit(3) + + if self.config.GAMELIST_MEDIA_IMAGE not in valid_image_options: + log.critical( + f"Invalid config.yml: scan.gamelist.media.image must be one of {valid_image_options}" + ) + sys.exit(3) + def get_config(self) -> Config: try: with open(self.config_file, "r") as config_file: diff --git a/backend/endpoints/responses/config.py b/backend/endpoints/responses/config.py index 6007be53a..764c909ca 100644 --- a/backend/endpoints/responses/config.py +++ b/backend/endpoints/responses/config.py @@ -1,6 +1,6 @@ from typing import TypedDict -from config.config_manager import EjsControls, NetplayICEServer +from config.config_manager import EjsControls, MetadataMediaType, NetplayICEServer class ConfigResponse(TypedDict): @@ -31,5 +31,5 @@ class ConfigResponse(TypedDict): SCAN_REGION_PRIORITY: list[str] SCAN_LANGUAGE_PRIORITY: list[str] SCAN_MEDIA: list[str] - GAMELIST_MEDIA_THUMBNAIL: str - GAMELIST_MEDIA_IMAGE: str + GAMELIST_MEDIA_THUMBNAIL: MetadataMediaType + GAMELIST_MEDIA_IMAGE: MetadataMediaType diff --git a/backend/utils/gamelist_exporter.py b/backend/utils/gamelist_exporter.py index 3fdb2fb7b..244af0e6d 100644 --- a/backend/utils/gamelist_exporter.py +++ b/backend/utils/gamelist_exporter.py @@ -68,8 +68,8 @@ class GamelistExporter: 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 rom.gamelist_metadata and rom.gamelist_metadata.get("box3d_path"): + thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['box3d_path']}" case "miximage": if rom.ss_metadata and rom.ss_metadata.get("miximage_path"): thumbnail_path = ( @@ -89,7 +89,7 @@ class GamelistExporter: ): 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) + # "cover" and "box2d" both map to path_cover_l if thumbnail_path is None and rom.path_cover_l: thumbnail_path = f"{FRONTEND_RESOURCES_PATH}/{rom.path_cover_l}" if thumbnail_path: @@ -180,27 +180,27 @@ class GamelistExporter: if rom.ss_metadata: if rom.ss_metadata.get("box3d_path"): SubElement(game, "box3d").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["box3d_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['box3d_path']}" ) if rom.ss_metadata.get("box2d_back_path"): SubElement(game, "boxback").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["box2d_back_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['box2d_back_path']}" ) if rom.ss_metadata.get("fanart_path"): SubElement(game, "fanart").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["fanart_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['fanart_path']}" ) if rom.ss_metadata.get("logo_path"): SubElement(game, "marquee").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["logo_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['logo_path']}" ) if rom.ss_metadata.get("miximage_path"): SubElement(game, "miximage").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["miximage_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['miximage_path']}" ) if rom.ss_metadata.get("physical_path"): SubElement(game, "physicalmedia").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["physical_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['physical_path']}" ) if rom.ss_metadata.get("title_screen_path"): SubElement(game, "title_screen").text = ( @@ -208,18 +208,26 @@ class GamelistExporter: ) if rom.ss_metadata.get("bezel_path"): SubElement(game, "bezel").text = ( - f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata["bezel_path"]}" + f"{FRONTEND_RESOURCES_PATH}/{rom.ss_metadata['bezel_path']}" ) if rom.gamelist_metadata: - if rom.gamelist_metadata.get("box3d"): - SubElement(game, "box3d").text = rom.gamelist_metadata["box3d"] - if rom.gamelist_metadata.get("box2d_back"): - SubElement(game, "boxback").text = rom.gamelist_metadata["box2d_back"] - if rom.gamelist_metadata.get("fanart"): - SubElement(game, "fanart").text = rom.gamelist_metadata["fanart"] - if rom.gamelist_metadata.get("marquee"): - SubElement(game, "marquee").text = rom.gamelist_metadata["marquee"] + if rom.gamelist_metadata.get("box3d_path"): + SubElement(game, "box3d").text = ( + f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['box3d_path']}" + ) + if rom.gamelist_metadata.get("box2d_back_path"): + SubElement(game, "boxback").text = ( + f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['box2d_back_path']}" + ) + if rom.gamelist_metadata.get("fanart_path"): + SubElement(game, "fanart").text = ( + f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['fanart_path']}" + ) + if rom.gamelist_metadata.get("marquee_path"): + SubElement(game, "marquee").text = ( + f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['marquee_path']}" + ) if rom.gamelist_metadata.get("miximage_path"): SubElement(game, "miximage").text = ( f"{FRONTEND_RESOURCES_PATH}/{rom.gamelist_metadata['miximage_path']}" diff --git a/examples/config.example.yml b/examples/config.example.yml index 103b8ea49..337c01e94 100644 --- a/examples/config.example.yml +++ b/examples/config.example.yml @@ -138,7 +138,7 @@ # export: false # Whether to export gamelist.xml for ES-DE/Batocera/RetroBAT # media: # # Select a media type from the list above (eg. box2d, screenshot, etc.) -# thumbnail: cover # Use as the tag in the exported gamelist.xml +# thumbnail: box2d # Use as the tag in the exported gamelist.xml # image: screenshot # Use as the tag in the exported gamelist.xml # EmulatorJS per-core options diff --git a/frontend/src/__generated__/index.ts b/frontend/src/__generated__/index.ts index a5f92c37f..eba622715 100644 --- a/frontend/src/__generated__/index.ts +++ b/frontend/src/__generated__/index.ts @@ -72,6 +72,7 @@ export type { JobStatus } from './models/JobStatus'; export type { LaunchboxImage } from './models/LaunchboxImage'; export type { ManualMetadata } from './models/ManualMetadata'; export type { MetadataCoverageItem } from './models/MetadataCoverageItem'; +export type { MetadataMediaType } from './models/MetadataMediaType'; export type { MetadataSourcesDict } from './models/MetadataSourcesDict'; export type { MissingRomsCleanupStats } from './models/MissingRomsCleanupStats'; export type { MobyMetadataPlatform } from './models/MobyMetadataPlatform'; diff --git a/frontend/src/__generated__/models/ConfigResponse.ts b/frontend/src/__generated__/models/ConfigResponse.ts index b226710eb..7014b65db 100644 --- a/frontend/src/__generated__/models/ConfigResponse.ts +++ b/frontend/src/__generated__/models/ConfigResponse.ts @@ -3,6 +3,7 @@ /* tslint:disable */ /* eslint-disable */ import type { EjsControls } from './EjsControls'; +import type { MetadataMediaType } from './MetadataMediaType'; import type { NetplayICEServer } from './NetplayICEServer'; export type ConfigResponse = { CONFIG_FILE_MOUNTED: boolean; @@ -32,5 +33,7 @@ export type ConfigResponse = { SCAN_REGION_PRIORITY: Array; SCAN_LANGUAGE_PRIORITY: Array; SCAN_MEDIA: Array; + GAMELIST_MEDIA_THUMBNAIL: MetadataMediaType; + GAMELIST_MEDIA_IMAGE: MetadataMediaType; }; diff --git a/frontend/src/__generated__/models/MetadataMediaType.ts b/frontend/src/__generated__/models/MetadataMediaType.ts new file mode 100644 index 000000000..cff57ce8f --- /dev/null +++ b/frontend/src/__generated__/models/MetadataMediaType.ts @@ -0,0 +1,5 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type MetadataMediaType = 'bezel' | 'box2d' | 'box2d_back' | 'box3d' | 'miximage' | 'physical' | 'screenshot' | 'title_screen' | 'marquee' | 'logo' | 'fanart' | 'video' | 'video_normalized' | 'manual'; diff --git a/frontend/src/__generated__/models/RomSSMetadata.ts b/frontend/src/__generated__/models/RomSSMetadata.ts index c4595ffc3..42ad19a50 100644 --- a/frontend/src/__generated__/models/RomSSMetadata.ts +++ b/frontend/src/__generated__/models/RomSSMetadata.ts @@ -29,6 +29,7 @@ export type RomSSMetadata = { physical_path?: (string | null); marquee_path?: (string | null); logo_path?: (string | null); + title_screen_path?: (string | null); video_path?: (string | null); video_normalized_path?: (string | null); ss_score?: (string | null); diff --git a/frontend/src/stores/config.ts b/frontend/src/stores/config.ts index edea6ab62..11848c4db 100644 --- a/frontend/src/stores/config.ts +++ b/frontend/src/stores/config.ts @@ -39,6 +39,8 @@ const defaultConfig = { SCAN_REGION_PRIORITY: [], SCAN_LANGUAGE_PRIORITY: [], SCAN_MEDIA: [], + GAMELIST_MEDIA_THUMBNAIL: "box2d", + GAMELIST_MEDIA_IMAGE: "screenshot", } as ConfigResponse; export default defineStore("config", {