From c4dd92249129893228e9208b621663d4fe070fea Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 12:54:58 +0000 Subject: [PATCH] perf(roms): make filter_roms file-loading opt-in for the gallery query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit filter_roms feeds both the gallery/list endpoint (SimpleRomSchema, no files) and the feed endpoints (which iterate rom.files / is_top_level). The cleanup commit's unconditional selectinload(Rom.files) + joinedload made the gallery/list and filter-value paths pay for files they never serialize. Gate the files load behind a new `include_files` flag (default False), mirroring the existing `include_file_stats` opt-in, and plumb it through get_roms_scalar. The 9 feed endpoints that actually read rom.files opt in; the gallery/list, filter-values, identifiers, smart-collection, and the three feeds that don't touch files (webrcade, fpkgi, kekatsu) skip the load entirely — keeping the gallery query at zero file cost. https://claude.ai/code/session_01PSXKmejPRzdxLFMN6P2QQ4 --- backend/endpoints/feeds.py | 34 +++++++++++++++++------- backend/handler/database/roms_handler.py | 17 +++++++++--- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/backend/endpoints/feeds.py b/backend/endpoints/feeds.py index 9ed31bad2..e1abc8fbb 100644 --- a/backend/endpoints/feeds.py +++ b/backend/endpoints/feeds.py @@ -213,7 +213,7 @@ async def tinfoil_index_feed( return titledb - roms = db_rom_handler.get_roms_scalar(platform_ids=[switch.id]) + roms = db_rom_handler.get_roms_scalar(platform_ids=[switch.id], include_files=True) return TinfoilFeedSchema( files=[ @@ -329,7 +329,9 @@ def pkgi_ps3_feed( status_code=400, detail=f"Invalid content type: {content_type}" ) from e - roms = db_rom_handler.get_roms_scalar(platform_ids=[ps3_platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[ps3_platform.id], include_files=True + ) txt_lines = [] for rom in roms: @@ -405,7 +407,9 @@ def pkgi_psvita_feed( status_code=400, detail=f"Invalid content type: {content_type}" ) from e - roms = db_rom_handler.get_roms_scalar(platform_ids=[psvita_platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[psvita_platform.id], include_files=True + ) txt_lines = [] for rom in roms: @@ -480,7 +484,9 @@ def pkgi_psp_feed( status_code=400, detail=f"Invalid content type: {content_type}" ) from e - roms = db_rom_handler.get_roms_scalar(platform_ids=[psp_platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[psp_platform.id], include_files=True + ) txt_lines = [] for rom in roms: @@ -663,7 +669,9 @@ def pkgj_psp_games_feed(request: Request) -> Response: status_code=404, detail="PlayStation Portable platform not found" ) - roms = db_rom_handler.get_roms_scalar(platform_ids=[platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[platform.id], include_files=True + ) txt_lines = [] txt_lines.append( "Title ID\tRegion\tType\tName\tPKG direct link\tContent ID\tLast Modification Date\tRAP\tDownload .RAP file\tFile Size\tSHA256" @@ -727,7 +735,9 @@ def pkgj_psp_dlcs_feed(request: Request) -> Response: status_code=404, detail="PlayStation Portable platform not found" ) - roms = db_rom_handler.get_roms_scalar(platform_ids=[platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[platform.id], include_files=True + ) txt_lines = [] txt_lines.append( "Title ID\tRegion\tName\tPKG direct link\tContent ID\tLast Modification Date\tRAP\tDownload .RAP file\tFile Size\tSHA256" @@ -789,7 +799,9 @@ def pkgj_psv_games_feed(request: Request) -> Response: status_code=404, detail="PlayStation Vita platform not found" ) - roms = db_rom_handler.get_roms_scalar(platform_ids=[platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[platform.id], include_files=True + ) txt_lines = [] txt_lines.append( "Title ID\tRegion\tName\tPKG direct link\tzRIF\tContent ID\tLast Modification Date\tOriginal Name\tFile Size\tSHA256\tRequired FW\tApp Version" @@ -854,7 +866,9 @@ def pkgj_psv_dlcs_feed(request: Request) -> Response: status_code=404, detail="PlayStation Vita platform not found" ) - roms = db_rom_handler.get_roms_scalar(platform_ids=[platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[platform.id], include_files=True + ) txt_lines = [] txt_lines.append( "Title ID\tRegion\tName\tPKG direct link\tzRIF\tContent ID\tLast Modification Date\tFile Size\tSHA256" @@ -911,7 +925,9 @@ def pkgj_psx_games_feed(request: Request) -> Response: if not platform: raise HTTPException(status_code=404, detail="PlayStation platform not found") - roms = db_rom_handler.get_roms_scalar(platform_ids=[platform.id]) + roms = db_rom_handler.get_roms_scalar( + platform_ids=[platform.id], include_files=True + ) txt_lines = [] txt_lines.append( "Title ID\tRegion\tName\tPKG direct link\tContent ID\tLast Modification Date\tOriginal Name\tFile Size\tSHA256" diff --git a/backend/handler/database/roms_handler.py b/backend/handler/database/roms_handler.py index b9b516a2a..de50d2361 100644 --- a/backend/handler/database/roms_handler.py +++ b/backend/handler/database/roms_handler.py @@ -586,6 +586,7 @@ class DBRomsHandler(DBBaseHandler): user_id: int | None = None, updated_after: datetime | None = None, include_file_stats: bool = False, + include_files: bool = False, session: Session = None, # type: ignore ) -> Query[Rom]: from handler.scan_handler import MetadataSource @@ -597,10 +598,6 @@ class DBRomsHandler(DBBaseHandler): selectinload(Rom.rom_users).options(noload(RomUser.rom)), # Sort table by metadata (first_release_date) selectinload(Rom.metadatum).options(noload(RomMetadata.rom)), - # Multi-file downloads, 3DS QR codes, and metadata matching - selectinload(Rom.files).options( - joinedload(RomFile.rom).load_only(Rom.fs_path, Rom.fs_name) - ), # Show sibling rom badges on cards selectinload(Rom.sibling_roms).options( noload(Rom.platform), noload(Rom.metadatum) @@ -609,6 +606,17 @@ class DBRomsHandler(DBBaseHandler): selectinload(Rom.notes), ) + # Only load files (and the RomFile.rom backref needed by `is_top_level` / + # `file_name_for_download`) when the caller iterates them — e.g. the + # feed endpoints. The gallery/list and filter-value paths serialize + # SimpleRomSchema without files, so they skip this entirely. + if include_files: + query = query.options( + selectinload(Rom.files).options( + joinedload(RomFile.rom).load_only(Rom.fs_path, Rom.fs_name) + ) + ) + # Correlated subqueries and only undefer when the caller serializes the # gallery-card flags. Feeds and filter-value lookups don't need them. if include_file_stats: @@ -917,6 +925,7 @@ class DBRomsHandler(DBBaseHandler): player_counts_logic=kwargs.get("player_counts_logic", "any"), user_id=kwargs.get("user_id", None), group_by_meta_id=kwargs.get("group_by_meta_id", False), + include_files=kwargs.get("include_files", False), ) return session.scalars(roms).all()