mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 00:27:41 +01:00
[ROMM-1333] Use metadata tag in filename to match game
This commit is contained in:
@@ -178,3 +178,14 @@ class SteamGridDBService:
|
||||
url = self.url.joinpath("search/autocomplete", term)
|
||||
response = await self._request(str(url))
|
||||
return cast(list[SGDBGame], response.get("data", []))
|
||||
|
||||
async def get_game_by_id(self, game_id: int) -> SGDBGame | None:
|
||||
"""Get game details by ID.
|
||||
|
||||
Reference: https://www.steamgriddb.com/api/v2#tag/GAMES/operation/getGameById
|
||||
"""
|
||||
url = self.url.joinpath("games", str(game_id))
|
||||
response = await self._request(str(url))
|
||||
if not response or "data" not in response:
|
||||
return None
|
||||
return cast(SGDBGame, response["data"])
|
||||
|
||||
@@ -33,6 +33,9 @@ PSP_IGDB_ID: Final = 38
|
||||
SWITCH_IGDB_ID: Final = 130
|
||||
ARCADE_IGDB_IDS: Final = [52, 79, 80]
|
||||
|
||||
# Regex to detect IGDB ID tags in filenames like (igdb-12345)
|
||||
IGDB_TAG_REGEX = re.compile(r"\(igdb-(\d+)\)", re.IGNORECASE)
|
||||
|
||||
|
||||
class IGDBPlatform(TypedDict):
|
||||
slug: str
|
||||
@@ -215,6 +218,14 @@ class IGDBHandler(MetadataHandler):
|
||||
def is_enabled(cls) -> bool:
|
||||
return bool(IGDB_CLIENT_ID and IGDB_CLIENT_SECRET)
|
||||
|
||||
@staticmethod
|
||||
def extract_igdb_id_from_filename(fs_name: str) -> int | None:
|
||||
"""Extract IGDB ID from filename tag like (igdb-12345)."""
|
||||
match = IGDB_TAG_REGEX.search(fs_name)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return None
|
||||
|
||||
async def _search_rom(
|
||||
self, search_term: str, platform_igdb_id: int, with_game_type: bool = False
|
||||
) -> Game | None:
|
||||
@@ -345,6 +356,21 @@ class IGDBHandler(MetadataHandler):
|
||||
if not platform_igdb_id:
|
||||
return IGDBRom(igdb_id=None)
|
||||
|
||||
# Check for IGDB ID tag in filename first
|
||||
igdb_id_from_tag = self.extract_igdb_id_from_filename(fs_name)
|
||||
if igdb_id_from_tag:
|
||||
log.debug(f"Found IGDB ID tag in filename: {igdb_id_from_tag}")
|
||||
rom_by_id = await self.get_rom_by_id(igdb_id_from_tag)
|
||||
if rom_by_id["igdb_id"]:
|
||||
log.debug(
|
||||
f"Successfully matched ROM by IGDB ID tag: {fs_name} -> {igdb_id_from_tag}"
|
||||
)
|
||||
return rom_by_id
|
||||
else:
|
||||
log.warning(
|
||||
f"IGDB ID {igdb_id_from_tag} from filename tag not found in IGDB"
|
||||
)
|
||||
|
||||
search_term = fs_rom_handler.get_file_name_with_no_tags(fs_name)
|
||||
fallback_rom = IGDBRom(igdb_id=None)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Final, NotRequired, TypedDict
|
||||
|
||||
@@ -21,6 +22,9 @@ LAUNCHBOX_METADATA_IMAGE_KEY: Final[str] = "romm:launchbox_metadata_image"
|
||||
LAUNCHBOX_MAME_KEY: Final[str] = "romm:launchbox_mame"
|
||||
LAUNCHBOX_FILES_KEY: Final[str] = "romm:launchbox_files"
|
||||
|
||||
# Regex to detect LaunchBox ID tags in filenames like (launchbox-12345)
|
||||
LAUNCHBOX_TAG_REGEX = re.compile(r"\(launchbox-(\d+)\)", re.IGNORECASE)
|
||||
|
||||
|
||||
class LaunchboxPlatform(TypedDict):
|
||||
slug: str
|
||||
@@ -123,6 +127,14 @@ class LaunchboxHandler(MetadataHandler):
|
||||
def is_enabled(cls) -> bool:
|
||||
return LAUNCHBOX_API_ENABLED
|
||||
|
||||
@staticmethod
|
||||
def extract_launchbox_id_from_filename(fs_name: str) -> int | None:
|
||||
"""Extract LaunchBox ID from filename tag like (launchbox-12345)."""
|
||||
match = LAUNCHBOX_TAG_REGEX.search(fs_name)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return None
|
||||
|
||||
async def _get_rom_from_metadata(
|
||||
self, file_name: str, platform_slug: str
|
||||
) -> dict | None:
|
||||
@@ -231,6 +243,21 @@ class LaunchboxHandler(MetadataHandler):
|
||||
if not self.is_enabled():
|
||||
return fallback_rom
|
||||
|
||||
# Check for LaunchBox ID tag in filename first
|
||||
launchbox_id_from_tag = self.extract_launchbox_id_from_filename(fs_name)
|
||||
if launchbox_id_from_tag:
|
||||
log.debug(f"Found LaunchBox ID tag in filename: {launchbox_id_from_tag}")
|
||||
rom_by_id = await self.get_rom_by_id(launchbox_id_from_tag)
|
||||
if rom_by_id["launchbox_id"]:
|
||||
log.debug(
|
||||
f"Successfully matched ROM by LaunchBox ID tag: {fs_name} -> {launchbox_id_from_tag}"
|
||||
)
|
||||
return rom_by_id
|
||||
else:
|
||||
log.warning(
|
||||
f"LaunchBox ID {launchbox_id_from_tag} from filename tag not found in LaunchBox"
|
||||
)
|
||||
|
||||
# We replace " - " with ": " to match Launchbox's naming convention
|
||||
search_term = fs_rom_handler.get_file_name_with_no_tags(fs_name).replace(
|
||||
" - ", ": "
|
||||
@@ -277,6 +304,8 @@ class LaunchboxHandler(MetadataHandler):
|
||||
if not metadata_database_index_entry:
|
||||
return LaunchboxRom(launchbox_id=None)
|
||||
|
||||
# Parse the JSON string from cache
|
||||
metadata_database_index_entry = json.loads(metadata_database_index_entry)
|
||||
game_images = await self._get_game_images(
|
||||
metadata_database_index_entry["DatabaseID"]
|
||||
)
|
||||
|
||||
@@ -26,6 +26,9 @@ PSP_MOBY_ID: Final = 46
|
||||
SWITCH_MOBY_ID: Final = 203
|
||||
ARCADE_MOBY_IDS: Final = [143, 36]
|
||||
|
||||
# Regex to detect MobyGames ID tags in filenames like (moby-12345)
|
||||
MOBYGAMES_TAG_REGEX = re.compile(r"\(moby-(\d+)\)", re.IGNORECASE)
|
||||
|
||||
|
||||
class MobyGamesPlatform(TypedDict):
|
||||
slug: str
|
||||
@@ -79,6 +82,14 @@ class MobyGamesHandler(MetadataHandler):
|
||||
def is_enabled(cls) -> bool:
|
||||
return bool(MOBYGAMES_API_KEY)
|
||||
|
||||
@staticmethod
|
||||
def extract_mobygames_id_from_filename(fs_name: str) -> int | None:
|
||||
"""Extract MobyGames ID from filename tag like (mobygames-12345)."""
|
||||
match = MOBYGAMES_TAG_REGEX.search(fs_name)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return None
|
||||
|
||||
async def _search_rom(
|
||||
self, search_term: str, platform_moby_id: int, split_game_name: bool = False
|
||||
) -> MobyGame | None:
|
||||
@@ -136,6 +147,21 @@ class MobyGamesHandler(MetadataHandler):
|
||||
if not platform_moby_id:
|
||||
return MobyGamesRom(moby_id=None)
|
||||
|
||||
# Check for MobyGames ID tag in filename first
|
||||
mobygames_id_from_tag = self.extract_mobygames_id_from_filename(fs_name)
|
||||
if mobygames_id_from_tag:
|
||||
log.debug(f"Found MobyGames ID tag in filename: {mobygames_id_from_tag}")
|
||||
rom_by_id = await self.get_rom_by_id(mobygames_id_from_tag)
|
||||
if rom_by_id["moby_id"]:
|
||||
log.debug(
|
||||
f"Successfully matched ROM by MobyGames ID tag: {fs_name} -> {mobygames_id_from_tag}"
|
||||
)
|
||||
return rom_by_id
|
||||
else:
|
||||
log.warning(
|
||||
f"MobyGames ID {mobygames_id_from_tag} from filename tag not found in MobyGames"
|
||||
)
|
||||
|
||||
search_term = fs_rom_handler.get_file_name_with_no_tags(fs_name)
|
||||
fallback_rom = MobyGamesRom(moby_id=None)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import NotRequired, TypedDict
|
||||
@@ -16,11 +17,15 @@ from config import (
|
||||
RETROACHIEVEMENTS_API_KEY,
|
||||
)
|
||||
from handler.filesystem import fs_resource_handler
|
||||
from logger.logger import log
|
||||
from models.rom import Rom
|
||||
|
||||
from .base_hander import BaseRom, MetadataHandler
|
||||
from .base_hander import UniversalPlatformSlug as UPS
|
||||
|
||||
# Regex to detect RetroAchievements ID tags in filenames like (ra-12345)
|
||||
RA_TAG_REGEX = re.compile(r"\(ra-(\d+)\)", re.IGNORECASE)
|
||||
|
||||
|
||||
class RAGamesPlatform(TypedDict):
|
||||
slug: str
|
||||
@@ -127,6 +132,14 @@ class RAHandler(MetadataHandler):
|
||||
def is_enabled(cls) -> bool:
|
||||
return bool(RETROACHIEVEMENTS_API_KEY)
|
||||
|
||||
@staticmethod
|
||||
def extract_ra_id_from_filename(fs_name: str) -> int | None:
|
||||
"""Extract RetroAchievements ID from filename tag like (ra-12345)."""
|
||||
match = RA_TAG_REGEX.search(fs_name)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return None
|
||||
|
||||
def _get_hashes_file_path(self, platform_id: int) -> str:
|
||||
platform_resources_path = fs_resource_handler.get_platform_resources_path(
|
||||
platform_id
|
||||
@@ -201,7 +214,25 @@ class RAHandler(MetadataHandler):
|
||||
)
|
||||
|
||||
async def get_rom(self, rom: Rom, ra_hash: str) -> RAGameRom:
|
||||
if not rom.platform.ra_id or not ra_hash:
|
||||
if not rom.platform.ra_id:
|
||||
return RAGameRom(ra_id=None)
|
||||
|
||||
# Check for RetroAchievements ID tag in filename first
|
||||
ra_id_from_tag = self.extract_ra_id_from_filename(rom.fs_name)
|
||||
if ra_id_from_tag:
|
||||
log.debug(f"Found RetroAchievements ID tag in filename: {ra_id_from_tag}")
|
||||
rom_by_id = await self.get_rom_by_id(rom=rom, ra_id=ra_id_from_tag)
|
||||
if rom_by_id["ra_id"]:
|
||||
log.debug(
|
||||
f"Successfully matched ROM by RetroAchievements ID tag: {rom.fs_name} -> {ra_id_from_tag}"
|
||||
)
|
||||
return rom_by_id
|
||||
else:
|
||||
log.warning(
|
||||
f"RetroAchievements ID {ra_id_from_tag} from filename tag not found in RetroAchievements"
|
||||
)
|
||||
|
||||
if not ra_hash:
|
||||
return RAGameRom(ra_id=None)
|
||||
|
||||
ra_game_list_item = await self._search_rom(rom, ra_hash)
|
||||
|
||||
@@ -34,6 +34,38 @@ class SGDBBaseHandler(MetadataHandler):
|
||||
def is_enabled(cls) -> bool:
|
||||
return bool(STEAMGRIDDB_API_KEY)
|
||||
|
||||
async def get_rom_by_id(self, sgdb_id: int) -> SGDBRom:
|
||||
"""Get ROM details by SteamGridDB ID."""
|
||||
if not self.is_enabled():
|
||||
return SGDBRom(sgdb_id=None)
|
||||
|
||||
try:
|
||||
game = await self.sgdb_service.get_game_by_id(sgdb_id)
|
||||
if not game:
|
||||
return SGDBRom(sgdb_id=None)
|
||||
|
||||
# Get covers for the game
|
||||
game_details = await self._get_game_covers(
|
||||
game_id=game["id"],
|
||||
game_name=game["name"],
|
||||
types=(SGDBType.STATIC,),
|
||||
is_nsfw=False,
|
||||
is_humor=False,
|
||||
is_epilepsy=False,
|
||||
)
|
||||
|
||||
first_resource = next(
|
||||
(res for res in game_details["resources"] if res["url"]), None
|
||||
)
|
||||
|
||||
result = SGDBRom(sgdb_id=game["id"])
|
||||
if first_resource:
|
||||
result["url_cover"] = first_resource["url"]
|
||||
return result
|
||||
except Exception as e:
|
||||
log.warning(f"Failed to fetch ROM by SteamGridDB ID {sgdb_id}: {e}")
|
||||
return SGDBRom(sgdb_id=None)
|
||||
|
||||
async def get_details(self, search_term: str) -> list[SGDBResult]:
|
||||
if not self.is_enabled():
|
||||
return []
|
||||
|
||||
@@ -102,6 +102,9 @@ ARCADE_SS_IDS: Final = [
|
||||
269,
|
||||
]
|
||||
|
||||
# Regex to detect ScreenScraper ID tags in filenames like (ss-12345)
|
||||
SS_TAG_REGEX = re.compile(r"\(ssfr-(\d+)\)", re.IGNORECASE)
|
||||
|
||||
|
||||
class SSPlatform(TypedDict):
|
||||
slug: str
|
||||
@@ -279,6 +282,14 @@ class SSHandler(MetadataHandler):
|
||||
def is_enabled(cls) -> bool:
|
||||
return bool(SCREENSCRAPER_USER and SCREENSCRAPER_PASSWORD)
|
||||
|
||||
@staticmethod
|
||||
def extract_ss_id_from_filename(fs_name: str) -> int | None:
|
||||
"""Extract ScreenScraper ID from filename tag like (ss-12345)."""
|
||||
match = SS_TAG_REGEX.search(fs_name)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
return None
|
||||
|
||||
async def _search_rom(
|
||||
self, search_term: str, platform_ss_id: int, split_game_name: bool = False
|
||||
) -> SSGame | None:
|
||||
@@ -332,6 +343,21 @@ class SSHandler(MetadataHandler):
|
||||
if not platform_ss_id:
|
||||
return SSRom(ss_id=None)
|
||||
|
||||
# Check for ScreenScraper ID tag in filename first
|
||||
ss_id_from_tag = self.extract_ss_id_from_filename(file_name)
|
||||
if ss_id_from_tag:
|
||||
log.debug(f"Found ScreenScraper ID tag in filename: {ss_id_from_tag}")
|
||||
rom_by_id = await self.get_rom_by_id(ss_id_from_tag)
|
||||
if rom_by_id["ss_id"]:
|
||||
log.debug(
|
||||
f"Successfully matched ROM by ScreenScraper ID tag: {file_name} -> {ss_id_from_tag}"
|
||||
)
|
||||
return rom_by_id
|
||||
else:
|
||||
log.warning(
|
||||
f"ScreenScraper ID {ss_id_from_tag} from filename tag not found in ScreenScraper"
|
||||
)
|
||||
|
||||
search_term = fs_rom_handler.get_file_name_with_no_tags(file_name)
|
||||
fallback_rom = SSRom(ss_id=None)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user