Skip to content

feat(roms): filter games by matched metadata provider#3615

Open
Spinnich wants to merge 1 commit into
rommapp:masterfrom
Spinnich:feat/filter-by-metadata-provider
Open

feat(roms): filter games by matched metadata provider#3615
Spinnich wants to merge 1 commit into
rommapp:masterfrom
Spinnich:feat/filter-by-metadata-provider

Conversation

@Spinnich

@Spinnich Spinnich commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Description

Resolves #3439. Adds a Metadata provider gallery filter so a library can be
narrowed by which providers each game matched (IGDB, MobyGames, ScreenScraper,
RetroAchievements, LaunchBox, Hasheous, Flashpoint, HowLongToBeat, ES-DE,
Libretro), mirroring the existing multi-value filters (Player count, Age rating)
with the same any / all / none logic toggle.

The filter is exposed everywhere the other multi-value filters already live:
the classic (v1) and v2 gallery filter drawers, bookmarkable URL state, the
request cache key, and smart-collection create/serialize/summary. Provider
values are slugs that line up 1:1 across the backend column map, the shared
frontend options registry, and the MetadataSource enum.

Why static options? Unlike genres/age-ratings, the provider set is a fixed
enum, not derived from library content, so the drawer always offers all 10. This
is intentional and supports the curation use case in the issue (e.g. find games
with no IGDB match by selecting IGDB + none).


Files modified

Backend

  • backend/handler/database/roms_handler.pyMETADATA_PROVIDER_COLUMNS slug→column map and _filter_by_metadata_providers (any=OR, all=AND, none=NOT-OR over each provider's id column); wired through filter_roms and get_roms_scalar. Unknown slugs are ignored.
  • backend/endpoints/roms/__init__.pymetadata_providers + metadata_providers_logic query params on GET /roms, passed to filter_roms, and added to the unscoped-cache gate.
  • backend/handler/database/collections_handler.py — smart collections apply the criterion via get_smart_collection_roms. NOTE: this file was the only CRLF file in the backend; it has been normalized to LF, which is why its diff is large (it's almost entirely line-ending).
  • backend/tests/handler/test_db_handler.py — handler filter coverage (any/all/none + unknown-slug no-op).
  • backend/tests/endpoints/roms/test_rom.py — endpoint coverage (filter + none logic).
  • backend/tests/handler/database/test_collections_handler.py — smart-collection pass-through coverage.

Frontend (shared)

  • frontend/src/utils/index.ts — shared METADATA_PROVIDER_OPTIONS registry (slug + brand-name title) used by both UIs.
  • frontend/src/stores/galleryFilter.tsselectedMetadataProviders / metadataProvidersLogic state, setters, isFiltered() / resetFilters().
  • frontend/src/services/api/rom.tsmetadata_providers request params.
  • frontend/src/services/cache/api.ts — provider selection folded into the request cache key (prevents stale-cache collisions).

Frontend (v1 classic UI)

  • frontend/src/stores/roms.ts — passes the new filter to getRoms.
  • frontend/src/components/Gallery/AppBar/common/FilterDrawer/Base.vue — new drawer row + URL read/write.

Frontend (v2 UI)

  • frontend/src/v2/stores/galleryRoms.ts — passes the new filter to getRoms.
  • frontend/src/v2/components/Gallery/FilterDrawer.vue — new multi-select section (slugs as values, brand names as titles) + logic toggle.
  • frontend/src/v2/components/Gallery/GalleryShell.vue — added to the refetch watcher and active-filter count.
  • frontend/src/v2/composables/useGalleryFilterUrl/index.ts?metadataProviders=…&metadataProvidersLogic=… URL round-trip (v1↔v2 compatible).
  • frontend/src/v2/utils/smartCollectionCriteria.ts — serialization + human-readable summary field.
  • frontend/src/v2/components/Dialogs/CreateSmartCollectionDialog.vue — snapshot includes the new fields.
  • frontend/src/v2/utils/smartCollectionCriteria.test.ts — new unit tests (serialization + summary).

i18n

  • frontend/src/locales/*/platform.json (18 locales) — platform.metadata-provider key (translated; parity check passes).

Testing notes

  • Backend full suite: 1793 passed (run with -p no:randomly for deterministic ordering).
  • Frontend: 443 passed; vue-tsc typecheck clean.
  • trunk fmt && trunk check: no issues. check_i18n_locales.py: all complete.
  • New tests cover any/all/none logic, the unknown-slug no-op, the endpoint, the smart-collection pass-through, and smart-collection serialization/summary.
  • If you see test_filter_by_search_term_* fail in a random-ordered run, it's pre-existing InnoDB MATCH...AGAINST flakiness (passes deterministically and in isolation; unrelated to this change).
  • Not done: manual browser pass of the v2 drawer in both themes / all input modalities.

Reviewer attention

  1. Provider set vs the "Show matched" toggle. This filter's 10 providers align with the on-card chip registry (metadataProviders.ts) — what users actually see next to a ROM — which excludes tgdb/sgdb and includes hltb/gamelist/libretro. The existing _filter_by_matched toggle uses a different 8-provider set (includes tgdb, excludes hltb/gamelist/libretro). Please confirm aligning the filter with the chip registry (rather than the toggle) is the intended mapping.
  2. Static options, not from filter_values — all 10 providers always show even on platforms with zero matches for some.
  3. Smart collections: metadata_providers is now wired into get_smart_collection_roms; player_counts remains a pre-existing gap there and was intentionally left out of scope.
  4. collections_handler.py CRLF→LF: review with whitespace hidden (?w=1) — the logical change is just 2 added lines.
  5. No OpenAPI regen needed: no response schema changed (RomFiltersDict is untouched); request params are built manually in rom.ts.

AI assistance disclosure: this change was implemented with Claude Code (Anthropic), reviewed by the author.

Checklist

  • I've tested the changes locally (full backend + frontend suites, trunk, i18n)
  • I've updated relevant comments
  • I've assigned reviewers for this PR
  • I've added unit tests that cover the changes

Adds a "Metadata provider" gallery filter so a library can be narrowed by
which providers each ROM matched (IGDB, MobyGames, ScreenScraper, etc.),
mirroring the existing multi-value filters (player count, age rating).

Backend:
- New `_filter_by_metadata_providers` keyed off each provider's id column
  on Rom, with any/all/none logic; wired through filter_roms,
  get_roms_scalar, the /roms endpoint, and the unscoped cache gate.
- Smart collections apply the criterion via get_smart_collection_roms.
- Unknown provider slugs are ignored (no-op) so stale bookmarks or
  hand-edited URLs don't error.

Frontend (v1 + v2):
- Shared METADATA_PROVIDER_OPTIONS registry; provider list is a fixed set
  rather than data-derived, so options are static.
- Wired into the galleryFilter store, rom API service, request cache key,
  both filter drawers, v2 URL persistence, the v2 refetch watcher, and
  smart-collection serialization/summary.
- Added the `platform.metadata-provider` key to all locales.

Tests:
- Backend handler, endpoint, and smart-collection coverage (any/all/none
  plus unknown-slug).
- Frontend smartCollectionCriteria serialization/summary coverage.

Also normalized collections_handler.py to LF (it was the only CRLF file in
the backend, which tripped git-diff-check on the edited lines).

AI assistance: implemented with Claude Code (Anthropic).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Spinnich Spinnich requested a review from gantoine June 26, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Filter games by matched metadata providers

1 participant