Merge pull request #2729 from rommapp/feat/redirect-related-games

feat: Redirect related games to RomM entry if exists
This commit is contained in:
Zurdi
2025-11-26 17:25:38 +01:00
committed by GitHub
5 changed files with 158 additions and 5 deletions

View File

@@ -2,7 +2,7 @@
<!-- trunk-ignore-all(markdownlint/MD033) -->
**Description**
<sup>Explain the changes or enhancements you are proposing with this pull request.</sup>
Explain the changes or enhancements you are proposing with this pull request.
**Checklist**
<sup>Please check all that apply.</sup>
@@ -12,4 +12,4 @@
- [ ] I've assigned reviewers for this PR
- [ ] I've added unit tests that cover the changes
#### Screenshots
#### Screenshots (if applicable)

View File

@@ -448,6 +448,78 @@ async def download_roms(
)
@protected_route(
router.get,
"/by-metadata-provider",
[] if DISABLE_DOWNLOAD_ENDPOINT_AUTH else [Scope.ROMS_READ],
responses={status.HTTP_404_NOT_FOUND: {}},
)
def get_rom_by_metadata(
request: Request,
igdb: Annotated[int | None, Query(description="IGDB ID to search by")] = None,
moby: Annotated[int | None, Query(description="MobyGames ID to search by")] = None,
ss: Annotated[
int | None, Query(description="ScreenScraper ID to search by")
] = None,
ra: Annotated[
int | None, Query(description="RetroAchievements ID to search by")
] = None,
launchbox: Annotated[
int | None, Query(description="LaunchBox ID to search by")
] = None,
hasheous: Annotated[
int | None, Query(description="Hasheous ID to search by")
] = None,
tgdb: Annotated[int | None, Query(description="TGDB ID to search by")] = None,
flashpoint: Annotated[
str | None, Query(description="Flashpoint ID to search by")
] = None,
hltb: Annotated[int | None, Query(description="HLTB ID to search by")] = None,
) -> DetailedRomSchema:
"""Retrieve a rom by metadata ID."""
rom = db_rom_handler.get_rom_by_metadata_id(
igdb=igdb,
moby=moby,
ss=ss,
ra=ra,
launchbox=launchbox,
hasheous=hasheous,
tgdb=tgdb,
flashpoint=flashpoint,
hltb=hltb,
)
if not rom:
provided_ids = {
"igdb_id": igdb,
"moby_id": moby,
"ss_id": ss,
"ra_id": ra,
"launchbox_id": launchbox,
"hasheous_id": hasheous,
"tgdb_id": tgdb,
"flashpoint_id": flashpoint,
"hltb_id": hltb,
}
metadata_info = [
f"{key}={value}" for key, value in provided_ids.items() if value is not None
]
if not metadata_info:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="At least one metadata ID must be provided",
)
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"ROM not found with metadata: {', '.join(metadata_info)}",
)
return DetailedRomSchema.from_orm_with_request(rom, request)
@protected_route(
router.get,
"/{id}",
@@ -938,7 +1010,7 @@ async def update_rom(
path_manual = await fs_resource_handler.get_manual(
rom=rom,
overwrite=True,
url_manual=url_manual,
url_manual=str(url_manual) if url_manual else None,
)
cleaned_data.update(
{

View File

@@ -977,3 +977,44 @@ class DBRomsHandler(DBBaseHandler):
)
)
return result.rowcount > 0
@begin_session
@with_details
def get_rom_by_metadata_id(
self,
igdb: int | None = None,
moby: int | None = None,
ss: int | None = None,
ra: int | None = None,
launchbox: int | None = None,
hasheous: int | None = None,
tgdb: int | None = None,
flashpoint: str | None = None,
hltb: int | None = None,
*,
query: Query = None,
session: Session = None,
) -> Rom | None:
"""Get a ROM by any metadata ID."""
filters = []
param_map = [
(igdb, Rom.igdb_id),
(moby, Rom.moby_id),
(ss, Rom.ss_id),
(ra, Rom.ra_id),
(launchbox, Rom.launchbox_id),
(hasheous, Rom.hasheous_id),
(tgdb, Rom.tgdb_id),
(flashpoint, Rom.flashpoint_id),
(hltb, Rom.hltb_id),
]
for value, column in param_map:
if value is not None:
filters.append(column == value)
if not filters:
return None
# Use OR to find ROM matching any of the provided metadata IDs
return session.scalar(query.filter(or_(*filters)).limit(1))

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed } from "vue";
import { computed, onMounted, ref } from "vue";
import type { IGDBRelatedGame } from "@/__generated__";
import romApi from "@/services/api/rom";
import storeGalleryView from "@/stores/galleryView";
import { getMissingCoverImage } from "@/utils/covers";
@@ -9,15 +10,40 @@ const props = defineProps<{
}>();
const galleryViewStore = storeGalleryView();
const romId = ref<number | null>(null);
const missingCoverImage = computed(() => getMissingCoverImage(props.game.name));
const computedAspectRatio = computed(() =>
galleryViewStore.getAspectRatio({ boxartStyle: "cover_path" }),
);
const gameLink = computed(() => {
if (romId.value !== null) {
return `/rom/${romId.value}`;
}
return `https://www.igdb.com/games/${props.game.slug}`;
});
const linkTarget = computed(() => {
return romId.value !== null ? "_self" : "_blank";
});
onMounted(async () => {
await romApi
.getRomByMetadataProvider({ provider: "igdb", id: props.game.id })
.then((response) => {
console.log("Fetched ROM by metadata provider:", response.data);
romId.value = response.data.id;
})
.catch((error) => {
console.error("Error fetching ROM by metadata provider:", error);
// Keep romId.value as null to fall back to IGDB link
});
});
</script>
<template>
<a :href="`https://www.igdb.com/games/${game.slug}`" target="_blank">
<a :href="gameLink" :target="linkTarget">
<v-card>
<v-tooltip
activator="parent"

View File

@@ -182,6 +182,19 @@ async function getRom({
return api.get(`/roms/${romId}`);
}
async function getRomByMetadataProvider({
provider,
id,
}: {
provider: string;
id: number;
}): Promise<{ data: DetailedRom }> {
const params = { [provider]: id };
return api.get(`/roms/by-metadata-provider/`, {
params,
});
}
async function searchRom({
romId,
searchTerm,
@@ -483,6 +496,7 @@ export default {
getRecentRoms,
getRecentPlayedRoms,
getRom,
getRomByMetadataProvider,
downloadRom,
bulkDownloadRoms,
searchRom,