diff --git a/backend/handler/metadata/pm_handler.py b/backend/handler/metadata/pm_handler.py index 830ea90bc..8971f8b0a 100644 --- a/backend/handler/metadata/pm_handler.py +++ b/backend/handler/metadata/pm_handler.py @@ -5,6 +5,7 @@ import httpx import yarl from fastapi import HTTPException, status from logger.logger import log +from models.rom import RomFile from utils.context import ctx_httpx_client from backend.config import PLAYMATCH_API_ENABLED @@ -54,9 +55,7 @@ class PlaymatchHandler: def __init__(self): self.base_url = "https://playmatch.retrorealm.dev/api" - async def lookup_rom( - self, file_name: str, file_size: int, md5: str | None, sha1: str | None - ) -> list[PlaymatchRomMatch]: + async def lookup_rom(self, rom_file: RomFile) -> list[PlaymatchRomMatch]: """ Identify a ROM file using Playmatch API. @@ -76,10 +75,10 @@ class PlaymatchHandler: response = await self._request( url, { - "fileName": file_name, - "fileSize": file_size, - "md5": md5, - "sha1": sha1, + "fileName": rom_file.file_name, + "fileSize": rom_file.file_size_bytes, + "md5": rom_file.md5_hash, + "sha1": rom_file.sha1_hash, }, ) except httpx.HTTPStatusError as e: diff --git a/backend/handler/scan_handler.py b/backend/handler/scan_handler.py index b211ab701..08fb874a3 100644 --- a/backend/handler/scan_handler.py +++ b/backend/handler/scan_handler.py @@ -43,11 +43,12 @@ class ScanType(Enum): class MetadataSource: - IGDB = "igdb" - MOBY = "moby" - SS = "ss" - RA = "ra" - LB = "lb" + IGDB = "igdb" # IGDB + MOBY = "moby" # MobyGames + SS = "ss" # Screenscraper + RA = "ra" # RetroAchivements + LB = "lb" # Launchbox + PM = "pm" # Playmatch async def _get_main_platform_igdb_id(platform: Platform): @@ -88,6 +89,8 @@ async def scan_platform( MetadataSource.MOBY, MetadataSource.SS, MetadataSource.RA, + MetadataSource.LB, + MetadataSource.PM, ] platform_attrs: dict[str, Any] = {} @@ -301,7 +304,7 @@ async def scan_rom( } ) - async def fetch_igdb_rom(): + async def fetch_igdb_rom() -> IGDBRom: if ( MetadataSource.IGDB in metadata_sources and platform.igdb_id @@ -312,26 +315,6 @@ async def scan_rom( or (scan_type == ScanType.UNIDENTIFIED and not rom.igdb_id) ) ): - rom_file = fs_rom["files"][0] - pm_matches = await meta_pm_handler.lookup_rom( - rom_file.file_name, - rom_file.file_size_bytes, - rom_file.md5_hash, - rom_file.sha1_hash, - ) - - for pm_match in pm_matches: - if pm_match["provider"] == PlaymatchProvider.IGDB: - pm_igdbid = pm_match["provider_game_id"] - if pm_igdbid is not None: - log.debug( - emoji.emojize( - f"{hl(rom_attrs['fs_name'])} identified by Playmatch as {hl(str(pm_igdbid), color=BLUE)} :alien_monster:" - ), - extra=LOGGER_MODULE_NAME, - ) - return await meta_igdb_handler.get_rom_by_id(pm_igdbid) - main_platform_igdb_id = await _get_main_platform_igdb_id(platform) return await meta_igdb_handler.get_rom( rom_attrs["fs_name"], main_platform_igdb_id or platform.igdb_id @@ -339,7 +322,7 @@ async def scan_rom( return IGDBRom(igdb_id=None) - async def fetch_moby_rom(): + async def fetch_moby_rom() -> MobyGamesRom: if ( MetadataSource.MOBY in metadata_sources and platform.moby_id @@ -356,7 +339,7 @@ async def scan_rom( return MobyGamesRom(moby_id=None) - async def fetch_ss_rom(): + async def fetch_ss_rom() -> SSRom: if ( MetadataSource.SS in metadata_sources and platform.ss_id @@ -373,7 +356,7 @@ async def scan_rom( return SSRom(ss_id=None) - async def fetch_launchbox_rom(platform_slug: str): + async def fetch_launchbox_rom(platform_slug: str) -> LaunchboxRom: if MetadataSource.LB in metadata_sources and ( newly_added or scan_type == ScanType.COMPLETE @@ -386,7 +369,7 @@ async def scan_rom( return LaunchboxRom(launchbox_id=None) - async def fetch_ra_rom(): + async def fetch_ra_rom() -> RAGameRom: if ( MetadataSource.RA in metadata_sources and platform.ra_id @@ -402,6 +385,43 @@ async def scan_rom( return RAGameRom(ra_id=None) + # Playmatch currectly only supports IGDB IDs + async def fetch_playmatch_rom() -> IGDBRom: + if ( + MetadataSource.PM in metadata_sources + and platform.igdb_id + and ( + newly_added + or scan_type == ScanType.COMPLETE + or (scan_type == ScanType.PARTIAL and not rom.igdb_id) + or (scan_type == ScanType.UNIDENTIFIED and not rom.igdb_id) + ) + ): + rom_file = fs_rom["files"][0] + pm_matches = await meta_pm_handler.lookup_rom(rom_file) + + for pm_match in pm_matches: + if pm_match["provider"] != PlaymatchProvider.IGDB: + continue + + pm_igdbid = pm_match.get("provider_game_id") + if pm_igdbid is None: + continue + + # Log the successful identification + log.debug( + emoji.emojize( + f"{hl(rom_attrs['fs_name'])} identified by Playmatch as " + f"{hl(str(pm_igdbid), color=BLUE)} :alien_monster:" + ), + extra=LOGGER_MODULE_NAME, + ) + + # Return the ROM data from IGDB + return await meta_igdb_handler.get_rom_by_id(pm_igdbid) + + return IGDBRom(igdb_id=None) + # Run both metadata fetches concurrently ( igdb_handler_rom, @@ -409,18 +429,21 @@ async def scan_rom( ss_handler_rom, ra_handler_rom, launchbox_handler_rom, + playmatch_handler_rom, ) = await asyncio.gather( fetch_igdb_rom(), fetch_moby_rom(), fetch_ss_rom(), fetch_ra_rom(), fetch_launchbox_rom(platform.slug), + fetch_playmatch_rom(), ) if newly_added: # Reversed to prioritize IGDB rom_attrs.update( { + **playmatch_handler_rom, **ra_handler_rom, **launchbox_handler_rom, **moby_handler_rom, @@ -430,6 +453,8 @@ async def scan_rom( ) else: # Only update fields if match is found + if playmatch_handler_rom.get("igdb_id"): + rom_attrs.update({**playmatch_handler_rom}) if ra_handler_rom.get("ra_id"): rom_attrs.update({**ra_handler_rom}) if moby_handler_rom.get("moby_id"):