Skip to content

Commit 4f5f855

Browse files
authored
Merge pull request #3395 from rommapp/copilot/fix-complete-rescan-metadata
Fix Complete Rescan not clearing unselected metadata sources
2 parents c4a1cfb + a245ac7 commit 4f5f855

2 files changed

Lines changed: 160 additions & 16 deletions

File tree

backend/handler/scan_handler.py

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -791,22 +791,83 @@ async def fetch_hasheous_rom(hasheous_rom: HasheousRom) -> HasheousRom:
791791
fetch_libretro_rom(),
792792
)
793793

794-
metadata_handlers = {
795-
MetadataSource.IGDB: igdb_handler_rom,
796-
MetadataSource.MOBY: moby_handler_rom,
797-
MetadataSource.SS: ss_handler_rom,
798-
MetadataSource.RA: ra_handler_rom,
799-
MetadataSource.LAUNCHBOX: launchbox_handler_rom,
800-
MetadataSource.HASHEOUS: hasheous_handler_rom,
801-
MetadataSource.FLASHPOINT: flashpoint_handler_rom,
802-
MetadataSource.HLTB: hltb_handler_rom,
803-
MetadataSource.GAMELIST: gamelist_handler_rom,
804-
MetadataSource.LIBRETRO: libretro_handler_rom,
794+
metadata_handlers: dict[MetadataSource, dict] = {
795+
MetadataSource.IGDB: {
796+
"handler": igdb_handler_rom,
797+
"id_field": "igdb_id",
798+
"metadata_field": "igdb_metadata",
799+
},
800+
MetadataSource.MOBY: {
801+
"handler": moby_handler_rom,
802+
"id_field": "moby_id",
803+
"metadata_field": "moby_metadata",
804+
},
805+
MetadataSource.SS: {
806+
"handler": ss_handler_rom,
807+
"id_field": "ss_id",
808+
"metadata_field": "ss_metadata",
809+
},
810+
MetadataSource.RA: {
811+
"handler": ra_handler_rom,
812+
"id_field": "ra_id",
813+
"metadata_field": "ra_metadata",
814+
},
815+
MetadataSource.LAUNCHBOX: {
816+
"handler": launchbox_handler_rom,
817+
"id_field": "launchbox_id",
818+
"metadata_field": "launchbox_metadata",
819+
},
820+
MetadataSource.HASHEOUS: {
821+
"handler": hasheous_handler_rom,
822+
"id_field": "hasheous_id",
823+
"metadata_field": "hasheous_metadata",
824+
},
825+
MetadataSource.FLASHPOINT: {
826+
"handler": flashpoint_handler_rom,
827+
"id_field": "flashpoint_id",
828+
"metadata_field": "flashpoint_metadata",
829+
},
830+
MetadataSource.HLTB: {
831+
"handler": hltb_handler_rom,
832+
"id_field": "hltb_id",
833+
"metadata_field": "hltb_metadata",
834+
},
835+
MetadataSource.GAMELIST: {
836+
"handler": gamelist_handler_rom,
837+
"id_field": "gamelist_id",
838+
"metadata_field": "gamelist_metadata",
839+
},
840+
MetadataSource.LIBRETRO: {
841+
"handler": libretro_handler_rom,
842+
"id_field": "libretro_id",
843+
"metadata_field": None,
844+
},
845+
MetadataSource.SGDB: {
846+
"handler": {},
847+
"id_field": "sgdb_id",
848+
"metadata_field": None,
849+
},
850+
MetadataSource.TGDB: {
851+
"handler": {},
852+
"id_field": "tgdb_id",
853+
"metadata_field": None,
854+
},
805855
}
806856

857+
# For COMPLETE rescans, explicitly clear metadata IDs and metadata for unselected sources
858+
# This ensures that when a source is no longer selected, its data is removed from the ROM
859+
if not newly_added and scan_type == ScanType.COMPLETE:
860+
for source, fields in metadata_handlers.items():
861+
if source not in metadata_sources:
862+
rom_attrs[fields["id_field"]] = None
863+
if fields["metadata_field"]:
864+
rom_attrs[fields["metadata_field"]] = {}
865+
807866
# Determine which metadata sources are available
808867
available_sources = [
809-
name for name, handler in metadata_handlers.items() if handler.get(f"{name}_id")
868+
name
869+
for name, fields in metadata_handlers.items()
870+
if fields["handler"].get(fields["id_field"])
810871
]
811872

812873
# Apply metadata priority order
@@ -815,7 +876,7 @@ async def fetch_hasheous_rom(hasheous_rom: HasheousRom) -> HasheousRom:
815876
)
816877
# Reverse priority order to apply highest priority last
817878
for source_name in reversed(priority_ordered):
818-
handler_data = metadata_handlers[source_name]
879+
handler_data = metadata_handlers[source_name]["handler"]
819880
# Only update fields that have valid values
820881
for key, field_value in handler_data.items():
821882
if field_value:
@@ -827,7 +888,7 @@ async def fetch_hasheous_rom(hasheous_rom: HasheousRom) -> HasheousRom:
827888
)
828889
# Reverse priority order to apply highest priority last
829890
for source_name in reversed(priority_ordered_artwork):
830-
handler_data = metadata_handlers[source_name]
891+
handler_data = metadata_handlers[source_name]["handler"]
831892
for field in ["url_cover", "url_screenshots", "url_manual"]:
832893
# Only update fields that have valid values
833894
field_value = handler_data.get(field)
@@ -931,7 +992,9 @@ async def fetch_sgdb_details(playmatch_rom: PlaymatchRomMatch) -> SGDBRom:
931992
)
932993
if sgdb_cover and not manual_cover_preserved:
933994
cover_sources = [
934-
name for name, h in metadata_handlers.items() if h.get("url_cover")
995+
name
996+
for name, fields in metadata_handlers.items()
997+
if fields["handler"].get("url_cover")
935998
]
936999
ranked = get_priority_ordered_metadata_sources(
9371000
cover_sources + [MetadataSource.SGDB], "artwork"

backend/tests/handler/test_fastapi.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
from unittest.mock import AsyncMock, patch
2+
13
import pytest
24

3-
from handler.database import db_platform_handler
5+
from handler.database import db_platform_handler, db_rom_handler
6+
from handler.metadata import meta_hasheous_handler, meta_playmatch_handler
7+
from handler.metadata.hasheous_handler import HasheousRom
48
from handler.scan_handler import MetadataSource, ScanType, scan_platform, scan_rom
59
from models.platform import Platform
610
from models.rom import Rom, RomFile
@@ -93,3 +97,80 @@ async def test_scan_rom():
9397
# assert rom.hasheous_id == 4872
9498
# assert rom.fs_size_bytes == 23175094
9599
# assert rom.tags == []
100+
101+
102+
@patch.object(meta_playmatch_handler, "is_enabled", return_value=False)
103+
@patch.object(meta_hasheous_handler, "get_ra_game", new_callable=AsyncMock)
104+
@patch.object(meta_hasheous_handler, "get_igdb_game", new_callable=AsyncMock)
105+
@patch.object(meta_hasheous_handler, "lookup_rom", new_callable=AsyncMock)
106+
async def test_scan_rom_complete_clears_unselected_metadata(
107+
mock_lookup, mock_get_igdb, mock_get_ra, mock_playmatch_enabled
108+
):
109+
"""COMPLETE rescan with newly_added=False must clear id and *_metadata
110+
fields for sources that are no longer in metadata_sources."""
111+
hasheous_result = HasheousRom(
112+
hasheous_id=999,
113+
igdb_id=None,
114+
tgdb_id=None,
115+
ra_id=None,
116+
name="Mock Hasheous Game",
117+
)
118+
mock_lookup.return_value = hasheous_result
119+
mock_get_igdb.return_value = hasheous_result
120+
mock_get_ra.return_value = hasheous_result
121+
122+
platform = Platform(
123+
id=1,
124+
slug="n64",
125+
fs_slug="n64",
126+
name="Nintendo 64",
127+
igdb_id=4,
128+
ra_id=2,
129+
hasheous_id=64,
130+
)
131+
platform = db_platform_handler.add_platform(platform)
132+
133+
rom = Rom(
134+
platform_id=platform.id,
135+
fs_name="Paper Mario (USA).z64",
136+
fs_name_no_tags="Paper Mario",
137+
fs_name_no_ext="Paper Mario",
138+
fs_extension="z64",
139+
fs_path="n64/Paper Mario (USA)",
140+
name="Paper Mario",
141+
igdb_id=3340,
142+
igdb_metadata={"summary": "stale IGDB metadata"},
143+
ra_id=1234,
144+
ra_metadata={"name": "stale RA metadata"},
145+
hasheous_id=4872,
146+
fs_size_bytes=1024,
147+
tags=[],
148+
)
149+
rom = db_rom_handler.add_rom(rom)
150+
151+
async with initialize_context():
152+
result = await scan_rom(
153+
platform=platform,
154+
scan_type=ScanType.COMPLETE,
155+
rom=rom,
156+
fs_rom={
157+
"fs_name": "Paper Mario (USA).z64",
158+
"flat": True,
159+
"nested": False,
160+
"files": [],
161+
"crc_hash": "",
162+
"md5_hash": "",
163+
"sha1_hash": "",
164+
"ra_hash": "",
165+
},
166+
metadata_sources=[MetadataSource.HASHEOUS],
167+
newly_added=False,
168+
)
169+
170+
# IGDB and RA were unselected — their id and metadata must be cleared.
171+
assert result.igdb_id is None
172+
assert result.igdb_metadata == {}
173+
assert result.ra_id is None
174+
assert result.ra_metadata == {}
175+
# Hasheous is still selected and should remain populated.
176+
assert result.hasheous_id == 999

0 commit comments

Comments
 (0)