Merge pull request #2987 from ItzCobaltboy/feat_auto_export_gamelist

feat: Auto Export Game List on scan
This commit is contained in:
Georges-Antoine Assi
2026-02-05 17:32:06 -05:00
committed by GitHub
3 changed files with 42 additions and 3 deletions

View File

@@ -76,6 +76,7 @@ class Config:
EXCLUDED_MULTI_FILES: list[str]
EXCLUDED_MULTI_PARTS_EXT: list[str]
EXCLUDED_MULTI_PARTS_FILES: list[str]
GAMELIST_AUTO_EXPORT_ON_SCAN: bool
PLATFORMS_BINDING: dict[str, str]
PLATFORMS_VERSIONS: dict[str, str]
ROMS_FOLDER_NAME: str
@@ -223,6 +224,9 @@ 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.export_gamelist", False
),
SKIP_HASH_CALCULATION=pydash.get(
self._raw_config, "filesystem.skip_hash_calculation", False
),
@@ -364,6 +368,9 @@ 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.export_gamelist 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")
@@ -570,6 +577,7 @@ class ConfigManager:
"language": self.config.SCAN_LANGUAGE_PRIORITY,
},
"media": self.config.SCAN_MEDIA,
"export_gamelist": self.config.GAMELIST_AUTO_EXPORT_ON_SCAN,
},
}

View File

@@ -50,6 +50,7 @@ from models.rom import Rom, RomFile
from tasks.tasks import update_job_meta
from utils import emoji
from utils.context import initialize_context
from utils.gamelist_exporter import GamelistExporter
STOP_SCAN_FLAG: Final = "scan:stop"
@@ -683,6 +684,32 @@ async def scan_platforms(
log.warning(f" - {p.slug} ({p.fs_slug})")
log.info(f"{emoji.EMOJI_CHECK_MARK} Scan completed")
# Export gamelist.xml if enabled in config
config = cm.get_config()
if config.GAMELIST_AUTO_EXPORT_ON_SCAN:
log.info("Auto-exporting gamelist.xml for all platforms...")
gamelist_exporter = GamelistExporter(local_export=True)
platforms_by_slug = {
p.fs_slug: p for p in db_platform_handler.get_platforms()
}
for platform_slug in platform_list:
platform = platforms_by_slug.get(platform_slug)
if platform:
export_success = await gamelist_exporter.export_platform_to_file(
platform.id,
request=None,
)
if export_success:
log.info(
f"Auto-exported gamelist.xml for platform {platform.name} after scan"
)
else:
log.warning(
f"Failed to auto-export gamelist.xml for platform {platform.name} after scan"
)
log.info("Gamelist.xml auto-export completed.")
await socket_manager.emit("scan:done", scan_stats.to_dict())
except ScanStoppedException:
await stop_scan()

View File

@@ -25,7 +25,7 @@ class GamelistExporter:
"""Format release date to YYYYMMDDTHHMMSS format"""
return datetime.fromtimestamp(timestamp / 1000).strftime("%Y%m%dT%H%M%S")
def _create_game_element(self, rom: Rom, request: Request) -> Element:
def _create_game_element(self, rom: Rom, request: Request | None) -> Element:
"""Create a <game> element for a ROM"""
game = Element("game")
@@ -33,6 +33,10 @@ class GamelistExporter:
if self.local_export:
SubElement(game, "path").text = f"./{rom.fs_name}"
else:
if request is None:
raise ValueError(
"Request object must be provided for non-local exports"
)
SubElement(game, "path").text = str(
request.url_for(
"get_rom_content",
@@ -155,7 +159,7 @@ class GamelistExporter:
return game
def export_platform_to_xml(self, platform_id: int, request: Request) -> str:
def export_platform_to_xml(self, platform_id: int, request: Request | None) -> str:
"""Export a platform's ROMs to gamelist.xml format
Args:
@@ -188,7 +192,7 @@ class GamelistExporter:
async def export_platform_to_file(
self,
platform_id: int,
request: Request,
request: Request | None,
) -> bool:
"""Export platform ROMs to gamelist.xml file in the platform's directory