diff --git a/backend/config/config_manager.py b/backend/config/config_manager.py index 05d68a7c7..b6491db62 100644 --- a/backend/config/config_manager.py +++ b/backend/config/config_manager.py @@ -57,7 +57,6 @@ DEFAULT_EXCLUDED_DIRS: Final = [ ".fseventsd", ".DocumentRevisions-V100", "System Volume Information", - "assets", ] @@ -456,10 +455,15 @@ class ConfigManager: "Invalid config.yml: exclude.roms.multi_file.parts.names must be a list" ) sys.exit(3) + if not isinstance(self.config.GAMELIST_AUTO_EXPORT_ON_SCAN, bool): log.critical("Invalid config.yml: scan.gamelist.export must be a boolean") sys.exit(3) + if not isinstance(self.config.PEGASUS_AUTO_EXPORT_ON_SCAN, bool): + log.critical("Invalid config.yml: scan.pegasus.export must be a boolean") + sys.exit(3) + if not isinstance(self.config.PLATFORMS_BINDING, dict): log.critical("Invalid config.yml: system.platforms must be a dictionary") sys.exit(3) diff --git a/backend/handler/filesystem/base_handler.py b/backend/handler/filesystem/base_handler.py index c397e0c3e..cea4a20e1 100644 --- a/backend/handler/filesystem/base_handler.py +++ b/backend/handler/filesystem/base_handler.py @@ -175,8 +175,8 @@ class FSHandler: async def _atomic_write(self, target_path: Path): """Context manager for atomic file writing. - Creates the temp file in the same directory as the target so that - os.rename is always an atomic same-filesystem operation. + Creates the temp file in the same directory as the target so the + final os.replace() occurs on the same filesystem. """ fd, temp_path_str = tempfile.mkstemp( dir=str(target_path.parent), prefix=".romm_tmp_" diff --git a/backend/tests/utils/test_gamelist_exporter.py b/backend/tests/utils/test_gamelist_exporter.py index a3ec7068c..09c1fb0ba 100644 --- a/backend/tests/utils/test_gamelist_exporter.py +++ b/backend/tests/utils/test_gamelist_exporter.py @@ -39,7 +39,7 @@ def platform_with_roms(admin_user: User): "genres": ["Platformer", "Adventure"], "companies": ["Nintendo", "Nintendo EAD"], "first_release_date": 709257600, # 1992-06-23 UTC in seconds; view *1000 - "total_rating": 9.2, # view uses this as igdb_rating + "total_rating": 92.0, # view uses this directly as a 0-100 igdb_rating } }, ) diff --git a/backend/tests/utils/test_pegasus_exporter.py b/backend/tests/utils/test_pegasus_exporter.py index 2497f490d..bd75abdb1 100644 --- a/backend/tests/utils/test_pegasus_exporter.py +++ b/backend/tests/utils/test_pegasus_exporter.py @@ -128,7 +128,7 @@ class TestExportMetadata: "genres": ["Platformer", "Adventure"], "companies": ["Nintendo", "Nintendo EAD"], "first_release_date": 709257600, # 1992-06-23 UTC in seconds; view *1000 - "total_rating": 9.2, # view uses this as igdb_rating + "total_rating": 92.0, # view uses this directly as a 0-100 igdb_rating } }, ) diff --git a/backend/utils/pegasus_exporter.py b/backend/utils/pegasus_exporter.py index 562c47eea..52d457e3d 100644 --- a/backend/utils/pegasus_exporter.py +++ b/backend/utils/pegasus_exporter.py @@ -37,7 +37,8 @@ class PegasusExporter: def _format_rating(self, average_rating: float) -> str: """Format rating as percentage (0-100%). Input is on 0-10 scale.""" - return f"{int(average_rating * 10)}%" + clamped_rating = max(0, min(100, average_rating)) + return f"{int(clamped_rating)}%" def _escape_multiline(self, text: str) -> str: """Indent continuation lines for multi-line values in Pegasus format""" @@ -74,14 +75,18 @@ class PegasusExporter: extended: dict[str, list[str]] = { "box_full": [ss.get("box3d_path", ""), gl.get("box3d_path", "")], - "box_back": [ss.get("box2d_back_path", ""), gl.get("box2d_back", "")], + "box_back": [ss.get("box2d_back_path", ""), gl.get("box2d_back_path", "")], "logo": [ss.get("logo_path", "")], "marquee": [gl.get("marquee_path", "")], "cartridge": [ss.get("physical_path", ""), gl.get("physical_path", "")], - "background": [ss.get("fanart_path", ""), gl.get("fanart", "")], - "titlescreen": [ss.get("title_screen", ""), gl.get("title_screen", "")], + "background": [ss.get("fanart_path", ""), gl.get("fanart_path", "")], + "titlescreen": [ + ss.get("title_screen_path", ""), + gl.get("title_screen_path", ""), + ], "bezel": [ss.get("bezel_path", "")], } + for pegasus_key, candidates in extended.items(): if pegasus_key in assets: continue diff --git a/examples/config.example.yml b/examples/config.example.yml index 117a1a1b9..20cdf92a4 100644 --- a/examples/config.example.yml +++ b/examples/config.example.yml @@ -142,7 +142,7 @@ # thumbnail: box2d # Use as the tag in the exported gamelist.xml # image: screenshot # Use as the tag in the exported gamelist.xml # pegasus: -# export: false # Whether to export gamelist.xml for Pegasus +# export: false # Whether to export metadata.pegasus.txt for Pegasus # EmulatorJS per-core options # emulatorjs: