Files
romm/backend/handler/scan_handler.py
Georges-Antoine Assi e349ce6104 changes from bot review
2025-09-21 20:51:30 -04:00

802 lines
28 KiB
Python

import asyncio
import enum
from typing import Any
from config.config_manager import config_manager as cm
from handler.database import db_platform_handler
from handler.filesystem import fs_asset_handler, fs_firmware_handler
from handler.filesystem.roms_handler import FSRom
from handler.metadata import (
meta_flashpoint_handler,
meta_hasheous_handler,
meta_hltb_handler,
meta_igdb_handler,
meta_launchbox_handler,
meta_moby_handler,
meta_playmatch_handler,
meta_ra_handler,
meta_sgdb_handler,
meta_ss_handler,
meta_tgdb_handler,
)
from handler.metadata.flashpoint_handler import FLASHPOINT_PLATFORM_LIST, FlashpointRom
from handler.metadata.hasheous_handler import HASHEOUS_PLATFORM_LIST, HasheousRom
from handler.metadata.hltb_handler import HLTB_PLATFORM_LIST, HLTBRom
from handler.metadata.igdb_handler import IGDB_PLATFORM_LIST, IGDBRom
from handler.metadata.launchbox_handler import LAUNCHBOX_PLATFORM_LIST, LaunchboxRom
from handler.metadata.moby_handler import MOBYGAMES_PLATFORM_LIST, MobyGamesRom
from handler.metadata.playmatch_handler import PlaymatchRomMatch
from handler.metadata.ra_handler import RA_PLATFORM_LIST, RAGameRom
from handler.metadata.sgdb_handler import SGDBRom
from handler.metadata.ss_handler import SCREENSAVER_PLATFORM_LIST, SSRom
from logger.formatter import BLUE, LIGHTYELLOW
from logger.formatter import highlight as hl
from logger.logger import log
from models.assets import Save, Screenshot, State
from models.firmware import Firmware
from models.platform import Platform
from models.rom import Rom
from models.user import User
from utils import emoji
LOGGER_MODULE_NAME = {"module_name": "scan"}
@enum.unique
class ScanType(enum.StrEnum):
NEW_PLATFORMS = "new_platforms"
QUICK = "quick"
UNIDENTIFIED = "unidentified"
PARTIAL = "partial"
COMPLETE = "complete"
HASHES = "hashes"
@enum.unique
class MetadataSource(enum.StrEnum):
IGDB = "igdb" # IGDB
MOBY = "moby" # MobyGames
SS = "ss" # Screenscraper
RA = "ra" # RetroAchievements
LB = "lb" # Launchbox
HASHEOUS = "hasheous" # Hasheous
TGDB = "tgdb" # TheGamesDB
SGDB = "sgdb" # SteamGridDB
FLASHPOINT = "flashpoint" # Flashpoint Project
HLTB = "hltb" # HowLongToBeat
def get_main_platform_igdb_id(platform: Platform):
cnfg = cm.get_config()
if platform.fs_slug in cnfg.PLATFORMS_VERSIONS.keys():
main_platform_slug = cnfg.PLATFORMS_VERSIONS[platform.fs_slug]
main_platform = db_platform_handler.get_platform_by_fs_slug(main_platform_slug)
if main_platform:
main_platform_igdb_id = main_platform.igdb_id
else:
main_platform = meta_igdb_handler.get_platform(main_platform_slug)
main_platform_igdb_id = main_platform["igdb_id"]
if not main_platform_igdb_id:
main_platform_igdb_id = platform.igdb_id
else:
main_platform_igdb_id = platform.igdb_id
return main_platform_igdb_id
def get_priority_ordered_metadata_sources(
metadata_sources: list[MetadataSource], priority_type: str = "metadata"
) -> list[MetadataSource]:
"""Get metadata sources ordered by priority from config
Args:
metadata_sources: List of available metadata sources
priority_type: Type of priority to use ("metadata" or "artwork")
Returns:
List of metadata sources ordered by priority
"""
cnfg = cm.get_config()
if priority_type == "metadata":
priority_order = cnfg.SCAN_METADATA_PRIORITY
else:
priority_order = cnfg.SCAN_ARTWORK_PRIORITY
# Filter priority order to only include sources that are available
ordered_sources = [
MetadataSource(source)
for source in priority_order
if source in metadata_sources
]
# Add any remaining sources that weren't in the priority list
remaining_sources = [
MetadataSource(source)
for source in metadata_sources
if source not in ordered_sources
]
return ordered_sources + remaining_sources
async def scan_platform(
fs_slug: str,
fs_platforms: list[str],
) -> Platform:
"""Get platform details
Args:
fs_slug: short name of the platform
Returns
Platform object
"""
platform_attrs: dict[str, Any] = {}
platform_attrs["fs_slug"] = fs_slug
cnfg = cm.get_config()
swapped_platform_bindings = {v: k for k, v in cnfg.PLATFORMS_BINDING.items()}
swapped_platform_versions = {v: k for k, v in cnfg.PLATFORMS_VERSIONS.items()}
# Sometimes users change the name of the folder, so we try to match it with the config
if fs_slug not in fs_platforms:
log.warning(
f"{hl(fs_slug)} not found in file system, trying to match via config",
extra=LOGGER_MODULE_NAME,
)
if fs_slug in swapped_platform_bindings.keys():
platform = db_platform_handler.get_platform_by_fs_slug(fs_slug)
if platform:
platform_attrs["fs_slug"] = swapped_platform_bindings[platform.slug]
elif fs_slug in swapped_platform_versions.keys():
platform = db_platform_handler.get_platform_by_fs_slug(fs_slug)
if platform:
platform_attrs["fs_slug"] = swapped_platform_versions[platform.slug]
try:
if fs_slug in cnfg.PLATFORMS_BINDING.keys():
platform_attrs["slug"] = cnfg.PLATFORMS_BINDING[fs_slug]
elif fs_slug in cnfg.PLATFORMS_VERSIONS.keys():
platform_attrs["slug"] = cnfg.PLATFORMS_VERSIONS[fs_slug]
else:
platform_attrs["slug"] = fs_slug
except (KeyError, TypeError, AttributeError):
platform_attrs["slug"] = fs_slug
igdb_platform = meta_igdb_handler.get_platform(platform_attrs["slug"])
moby_platform = meta_moby_handler.get_platform(platform_attrs["slug"])
ss_platform = meta_ss_handler.get_platform(platform_attrs["slug"])
ra_platform = meta_ra_handler.get_platform(platform_attrs["slug"])
launchbox_platform = meta_launchbox_handler.get_platform(platform_attrs["slug"])
hasheous_platform = meta_hasheous_handler.get_platform(platform_attrs["slug"])
tgdb_platform = meta_tgdb_handler.get_platform(platform_attrs["slug"])
flashpoint_platform = meta_flashpoint_handler.get_platform(platform_attrs["slug"])
hltb_platform = meta_hltb_handler.get_platform(platform_attrs["slug"])
platform_attrs["name"] = platform_attrs["slug"].replace("-", " ").title()
platform_attrs.update(
{
**hasheous_platform,
**tgdb_platform,
**launchbox_platform,
**ra_platform,
**moby_platform,
**ss_platform,
**igdb_platform,
"igdb_id": igdb_platform.get("igdb_id")
or hasheous_platform.get("igdb_id")
or None,
"ra_id": ra_platform.get("ra_id") or hasheous_platform.get("ra_id") or None,
"tgdb_id": moby_platform.get("tgdb_id")
or hasheous_platform.get("tgdb_id")
or None,
"name": igdb_platform.get("name")
or ss_platform.get("name")
or moby_platform.get("name")
or ra_platform.get("name")
or launchbox_platform.get("name")
or hasheous_platform.get("name")
or tgdb_platform.get("name")
or flashpoint_platform.get("name")
or hltb_platform.get("name")
or platform_attrs["slug"].replace("-", " ").title(),
"url_logo": igdb_platform.get("url_logo")
or tgdb_platform.get("url_logo")
or "",
}
)
if (
platform_attrs["igdb_id"]
or platform_attrs["moby_id"]
or platform_attrs["ss_id"]
or platform_attrs["ra_id"]
or platform_attrs["launchbox_id"]
or hasheous_platform["hasheous_id"]
or tgdb_platform["tgdb_id"]
or flashpoint_platform["slug"]
or hltb_platform["slug"]
):
log.info(
f"Folder {hl(platform_attrs['slug'])}[{hl(fs_slug, color=LIGHTYELLOW)}] identified as {hl(platform_attrs['name'], color=BLUE)} {emoji.EMOJI_VIDEO_GAME}",
extra={"module_name": "scan"},
)
else:
log.warning(
f"Platform {hl(platform_attrs['slug'])} not identified {emoji.EMOJI_CROSS_MARK}",
extra=LOGGER_MODULE_NAME,
)
platform_attrs["missing_from_fs"] = False
return Platform(**platform_attrs)
async def scan_firmware(
platform: Platform,
file_name: str,
firmware: Firmware | None = None,
) -> Firmware:
firmware_path = fs_firmware_handler.get_firmware_fs_structure(platform.fs_slug)
# Set default properties
firmware_attrs = {
"id": firmware.id if firmware else None,
"platform_id": platform.id,
}
file_path = f"{firmware_path}/{file_name}"
file_size = await fs_firmware_handler.get_file_size(file_path)
firmware_attrs.update(
{
"file_path": firmware_path,
"file_name": file_name,
"file_name_no_tags": fs_firmware_handler.get_file_name_with_no_tags(
file_name
),
"file_name_no_ext": fs_firmware_handler.get_file_name_with_no_extension(
file_name
),
"file_extension": fs_firmware_handler.parse_file_extension(file_name),
"file_size_bytes": file_size,
}
)
file_hashes = await fs_firmware_handler.calculate_file_hashes(
firmware_path=firmware_path,
file_name=file_name,
)
firmware_attrs.update(**file_hashes)
return Firmware(**firmware_attrs)
async def scan_rom(
scan_type: ScanType,
platform: Platform,
rom: Rom,
fs_rom: FSRom,
metadata_sources: list[str],
newly_added: bool,
) -> Rom:
if not metadata_sources:
log.error("No metadata sources provided")
raise ValueError("No metadata sources provided")
filesize = sum([file.file_size_bytes for file in fs_rom["files"]])
rom_attrs = {
"platform_id": platform.id,
"name": fs_rom["fs_name"],
"fs_name": fs_rom["fs_name"],
"multi": fs_rom["multi"],
"crc_hash": fs_rom["crc_hash"],
"md5_hash": fs_rom["md5_hash"],
"sha1_hash": fs_rom["sha1_hash"],
"ra_hash": fs_rom["ra_hash"],
"fs_size_bytes": filesize,
"url_cover": "",
"url_manual": "",
"url_screenshots": [],
}
if rom:
rom_attrs.update(
{
"id": rom.id,
"fs_path": rom.fs_path,
"fs_name_no_tags": rom.fs_name_no_tags,
"fs_name_no_ext": rom.fs_name_no_ext,
"fs_extension": rom.fs_extension,
"regions": rom.regions,
"revision": rom.revision,
"languages": rom.languages,
"tags": rom.tags,
}
)
# Update properties from existing rom if not a complete rescan
if not newly_added and scan_type != ScanType.COMPLETE:
rom_attrs.update(
{
"igdb_id": rom.igdb_id,
"moby_id": rom.moby_id,
"ss_id": rom.ss_id,
"sgdb_id": rom.sgdb_id,
"ra_id": rom.ra_id,
"launchbox_id": rom.launchbox_id,
"hasheous_id": rom.hasheous_id,
"tgdb_id": rom.tgdb_id,
"name": rom.name,
"slug": rom.slug,
"summary": rom.summary,
"igdb_metadata": rom.igdb_metadata,
"moby_metadata": rom.moby_metadata,
"ss_metadata": rom.ss_metadata,
"ra_metadata": rom.ra_metadata,
"launchbox_metadata": rom.launchbox_metadata,
"hasheous_metadata": rom.hasheous_metadata,
"path_cover_s": rom.path_cover_s,
"path_cover_l": rom.path_cover_l,
"path_screenshots": rom.path_screenshots,
"path_manual": rom.path_manual,
"url_cover": rom.url_cover,
"url_screenshots": rom.url_screenshots,
"url_manual": rom.url_manual,
}
)
async def fetch_playmatch_hash_match() -> PlaymatchRomMatch:
if (
MetadataSource.IGDB 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 rom.is_unidentified)
)
):
return await meta_playmatch_handler.lookup_rom(fs_rom["files"])
return PlaymatchRomMatch(igdb_id=None)
async def fetch_hasheous_hash_match() -> HasheousRom:
if (
MetadataSource.HASHEOUS in metadata_sources
and platform.hasheous_id
and (
newly_added
or scan_type == ScanType.COMPLETE
or (
scan_type == ScanType.PARTIAL
and not rom.hasheous_id
and rom.slug in HASHEOUS_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
return await meta_hasheous_handler.lookup_rom(
platform.slug, fs_rom["files"]
)
return HasheousRom(hasheous_id=None, igdb_id=None, tgdb_id=None, ra_id=None)
# Run hash fetches concurrently
(
playmatch_hash_match,
hasheous_hash_match,
) = await asyncio.gather(
fetch_playmatch_hash_match(),
fetch_hasheous_hash_match(),
)
async def fetch_igdb_rom(
playmatch_rom: PlaymatchRomMatch, hasheous_rom: HasheousRom
) -> IGDBRom:
if (
MetadataSource.IGDB 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
and rom.slug in IGDB_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
# Use Hasheous match to get the IGDB ID
h_igdb_id = hasheous_rom.get("igdb_id")
if h_igdb_id:
log.debug(
f"{hl(rom_attrs['fs_name'])} identified by Hasheous as "
f"{hl(str(h_igdb_id), color=BLUE)} {emoji.EMOJI_ALIEN_MONSTER}",
extra=LOGGER_MODULE_NAME,
)
return await meta_igdb_handler.get_rom_by_id(h_igdb_id)
# Use Playmatch matches to get the IGDB ID
if playmatch_rom["igdb_id"] is not None:
log.debug(
f"{hl(rom_attrs['fs_name'])} identified by Playmatch as "
f"{hl(str(playmatch_rom["igdb_id"]), color=BLUE)} {emoji.EMOJI_ALIEN_MONSTER}",
extra=LOGGER_MODULE_NAME,
)
return await meta_igdb_handler.get_rom_by_id(playmatch_rom["igdb_id"])
# If no matches found, use the file name to get the IGDB ID
main_platform_igdb_id = 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
)
return IGDBRom(igdb_id=None)
async def fetch_flashpoint_rom() -> FlashpointRom:
if (
MetadataSource.FLASHPOINT in metadata_sources
and platform.slug in FLASHPOINT_PLATFORM_LIST
and (
newly_added
or scan_type == ScanType.COMPLETE
or (
scan_type == ScanType.PARTIAL
and not rom.flashpoint_id
and platform.slug in FLASHPOINT_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
return await meta_flashpoint_handler.get_rom(
rom_attrs["fs_name"], platform.slug
)
return FlashpointRom(flashpoint_id=None)
async def fetch_hltb_rom() -> HLTBRom:
if (
MetadataSource.HLTB in metadata_sources
and platform.slug in HLTB_PLATFORM_LIST
and (
newly_added
or scan_type == ScanType.COMPLETE
or (scan_type == ScanType.PARTIAL and not rom.hltb_id)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
return await meta_hltb_handler.get_rom(rom_attrs["fs_name"], platform.slug)
return HLTBRom(hltb_id=None)
async def fetch_moby_rom() -> MobyGamesRom:
if (
MetadataSource.MOBY in metadata_sources
and platform.moby_id
and (
newly_added
or scan_type == ScanType.COMPLETE
or (
scan_type == ScanType.PARTIAL
and not rom.moby_id
and rom.slug in MOBYGAMES_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
return await meta_moby_handler.get_rom(
rom_attrs["fs_name"], platform_moby_id=platform.moby_id
)
return MobyGamesRom(moby_id=None)
async def fetch_ss_rom() -> SSRom:
if (
MetadataSource.SS in metadata_sources
and platform.ss_id
and (
newly_added
or scan_type == ScanType.COMPLETE
or (
scan_type == ScanType.PARTIAL
and not rom.ss_id
and rom.slug in SCREENSAVER_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
return await meta_ss_handler.get_rom(
rom_attrs["fs_name"], platform_ss_id=platform.ss_id
)
return SSRom(ss_id=None)
async def fetch_launchbox_rom(platform_slug: str) -> LaunchboxRom:
if MetadataSource.LB in metadata_sources and (
newly_added
or scan_type == ScanType.COMPLETE
or (
scan_type == ScanType.PARTIAL
and not rom.launchbox_id
and rom.slug in LAUNCHBOX_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
):
return await meta_launchbox_handler.get_rom(
rom_attrs["fs_name"], platform_slug
)
return LaunchboxRom(launchbox_id=None)
async def fetch_ra_rom(hasheous_rom: HasheousRom) -> RAGameRom:
if (
MetadataSource.RA in metadata_sources
and platform.ra_id
and (
newly_added
or scan_type == ScanType.COMPLETE
or scan_type == ScanType.HASHES
or (
scan_type == ScanType.PARTIAL
and not rom.ra_id
and rom.slug in RA_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
# Use Hasheous match to get the IGDB ID
h_ra_id = hasheous_rom.get("ra_id")
if h_ra_id:
log.debug(
f"{hl(rom_attrs['fs_name'])} identified by Hasheous as "
f"{hl(str(h_ra_id), color=BLUE)} {emoji.EMOJI_ALIEN_MONSTER}",
extra=LOGGER_MODULE_NAME,
)
return await meta_ra_handler.get_rom_by_id(rom=rom, ra_id=h_ra_id)
return await meta_ra_handler.get_rom(rom=rom, ra_hash=rom_attrs["ra_hash"])
return RAGameRom(ra_id=None)
async def fetch_hasheous_rom(hasheous_rom: HasheousRom) -> HasheousRom:
if (
MetadataSource.HASHEOUS in metadata_sources
and platform.hasheous_id
and (
newly_added
or scan_type == ScanType.COMPLETE
or (
scan_type == ScanType.PARTIAL
and not rom.hasheous_id
and rom.slug in HASHEOUS_PLATFORM_LIST
)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
)
):
(
igdb_game,
ra_game,
) = await asyncio.gather(
meta_hasheous_handler.get_igdb_game(hasheous_rom),
meta_hasheous_handler.get_ra_game(hasheous_rom),
)
return HasheousRom(
{
**hasheous_rom,
**ra_game,
**igdb_game,
}
)
return HasheousRom(hasheous_id=None, igdb_id=None, tgdb_id=None, ra_id=None)
# Run metadata fetches concurrently
(
igdb_handler_rom,
moby_handler_rom,
ss_handler_rom,
ra_handler_rom,
launchbox_handler_rom,
hasheous_handler_rom,
flashpoint_handler_rom,
hltb_handler_rom,
) = await asyncio.gather(
fetch_igdb_rom(playmatch_hash_match, hasheous_hash_match),
fetch_moby_rom(),
fetch_ss_rom(),
fetch_ra_rom(hasheous_hash_match),
fetch_launchbox_rom(platform.slug),
fetch_hasheous_rom(hasheous_hash_match),
fetch_flashpoint_rom(),
fetch_hltb_rom(),
)
metadata_handlers = {
MetadataSource.IGDB: igdb_handler_rom,
MetadataSource.MOBY: moby_handler_rom,
MetadataSource.SS: ss_handler_rom,
MetadataSource.RA: ra_handler_rom,
MetadataSource.LB: launchbox_handler_rom,
MetadataSource.HASHEOUS: hasheous_handler_rom,
MetadataSource.FLASHPOINT: flashpoint_handler_rom,
MetadataSource.HLTB: hltb_handler_rom,
}
# Get metadata sources and apply in priority order
available_sources = [
name for name, handler in metadata_handlers.items() if handler.get(f"{name}_id")
]
priority_ordered = get_priority_ordered_metadata_sources(
available_sources, "metadata"
)
# Reverse priority order to apply highest priority last
for source_name in reversed(priority_ordered):
rom_attrs.update({**metadata_handlers[source_name]})
# Get artwork sources and apply in reverse priority order (highest priority last)
priority_ordered_artwork = get_priority_ordered_metadata_sources(
available_sources, "artwork"
)
# Reverse priority order to apply highest priority last
for source_name in reversed(priority_ordered_artwork):
handler_data = metadata_handlers[source_name]
if handler_data.get("url_cover"):
rom_attrs["url_cover"] = handler_data.get("url_cover")
if handler_data.get("url_screenshots"):
rom_attrs["url_screenshots"] = handler_data.get("url_screenshots")
if handler_data.get("url_manual"):
rom_attrs["url_manual"] = handler_data.get("url_manual")
# Stop IDs from getting overridden by empty values
rom_attrs.update(
{
"igdb_id": igdb_handler_rom.get("igdb_id")
or hasheous_handler_rom.get("igdb_id")
or rom_attrs.get("igdb_id"),
"ss_id": ss_handler_rom.get("ss_id") or rom_attrs.get("ss_id"),
"moby_id": moby_handler_rom.get("moby_id") or rom_attrs.get("moby_id"),
"ra_id": ra_handler_rom.get("ra_id")
or hasheous_handler_rom.get("ra_id")
or rom_attrs.get("ra_id"),
"launchbox_id": launchbox_handler_rom.get("launchbox_id")
or rom_attrs.get("launchbox_id"),
"hasheous_id": hasheous_handler_rom.get("hasheous_id")
or rom_attrs.get("hasheous_id"),
"tgdb_id": hasheous_handler_rom.get("tgdb_id") or rom_attrs.get("tgdb_id"),
"flashpoint_id": flashpoint_handler_rom.get("flashpoint_id")
or rom_attrs.get("flashpoint_id"),
"hltb_id": hltb_handler_rom.get("hltb_id") or rom_attrs.get("hltb_id"),
}
)
# Don't overwrite existing fields on partial scans
if not newly_added and scan_type == ScanType.PARTIAL:
rom_attrs.update(
{
"name": rom.name or rom_attrs.get("name") or None,
"summary": rom.summary or rom_attrs.get("summary") or None,
"url_cover": rom.url_cover or rom_attrs.get("url_cover") or None,
"url_manual": rom.url_manual or rom_attrs.get("url_manual") or None,
"url_screenshots": rom.url_screenshots
or rom_attrs.get("url_screenshots")
or [],
}
)
# If not found in any metadata source, we return the rom with the default values
if (
not igdb_handler_rom.get("igdb_id")
and not moby_handler_rom.get("moby_id")
and not ss_handler_rom.get("ss_id")
and not ra_handler_rom.get("ra_id")
and not launchbox_handler_rom.get("launchbox_id")
and not hasheous_handler_rom.get("hasheous_id")
and not flashpoint_handler_rom.get("flashpoint_id")
and not hltb_handler_rom.get("hltb_id")
):
log.warning(
f"{hl(rom_attrs['fs_name'])} not identified {emoji.EMOJI_CROSS_MARK}",
extra=LOGGER_MODULE_NAME,
)
return Rom(**rom_attrs)
async def fetch_sgdb_details() -> SGDBRom:
"""Fetch SteamGridDB details for the ROM."""
if (
MetadataSource.SGDB in metadata_sources
and newly_added
or scan_type == ScanType.COMPLETE
or (scan_type == ScanType.PARTIAL and not rom.sgdb_id)
or (scan_type == ScanType.UNIDENTIFIED and rom.is_unidentified)
):
game_names = [
igdb_handler_rom.get("name", None),
hasheous_handler_rom.get("name", None),
ss_handler_rom.get("name", None),
moby_handler_rom.get("name", None),
launchbox_handler_rom.get("name", None),
rom_attrs["fs_name_no_tags"],
]
game_names = [name for name in game_names if name]
return await meta_sgdb_handler.get_details_by_names(game_names)
return SGDBRom(sgdb_id=None)
sgdb_hander_rom = await fetch_sgdb_details()
if sgdb_hander_rom.get("sgdb_id"):
rom_attrs.update({**sgdb_hander_rom})
log.info(
f"{hl(rom_attrs['fs_name'])} identified as {hl(rom_attrs['name'], color=BLUE)} {emoji.EMOJI_ALIEN_MONSTER}",
extra=LOGGER_MODULE_NAME,
)
if rom.multi:
for file in fs_rom["files"]:
log.info(
f"\t ยท {hl(file.file_name, color=LIGHTYELLOW)}",
extra=LOGGER_MODULE_NAME,
)
rom_attrs["missing_from_fs"] = False
return Rom(**rom_attrs)
async def _scan_asset(file_name: str, asset_path: str):
file_path = f"{asset_path}/{file_name}"
file_size = await fs_asset_handler.get_file_size(file_path)
return {
"file_path": asset_path,
"file_name": file_name,
"file_name_no_tags": fs_asset_handler.get_file_name_with_no_tags(file_name),
"file_name_no_ext": fs_asset_handler.get_file_name_with_no_extension(file_name),
"file_extension": fs_asset_handler.parse_file_extension(file_name),
"file_size_bytes": file_size,
}
async def scan_save(
file_name: str,
user: User,
platform_fs_slug: str,
rom_id: int,
emulator: str | None = None,
) -> Save:
saves_path = fs_asset_handler.build_saves_file_path(
user=user, platform_fs_slug=platform_fs_slug, rom_id=rom_id, emulator=emulator
)
scanned_asset = await _scan_asset(file_name, saves_path)
return Save(**scanned_asset)
async def scan_state(
file_name: str,
user: User,
platform_fs_slug: str,
rom_id: int,
emulator: str | None = None,
) -> State:
states_path = fs_asset_handler.build_states_file_path(
user=user, platform_fs_slug=platform_fs_slug, rom_id=rom_id, emulator=emulator
)
scanned_asset = await _scan_asset(file_name, states_path)
return State(**scanned_asset)
async def scan_screenshot(
file_name: str,
user: User,
platform_fs_slug: str,
rom_id: int,
) -> Screenshot:
screenshots_path = fs_asset_handler.build_screenshots_file_path(
user=user, platform_fs_slug=platform_fs_slug, rom_id=rom_id
)
scanned_asset = await _scan_asset(file_name, screenshots_path)
return Screenshot(**scanned_asset)