Add launchbox to manual match window

This commit is contained in:
Georges-Antoine Assi
2025-09-18 16:34:40 -04:00
parent 3d5c129e42
commit fe2a7e7bff
9 changed files with 153 additions and 18 deletions

View File

@@ -18,30 +18,32 @@ runtimes:
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
lint:
disabled:
- svgo
- oxipng
- pyright
enabled:
- dotenv-linter@3.3.0
- hadolint@2.13.1
- markdownlint@0.45.0
- eslint@9.34.0
- eslint@9.35.0
- actionlint@1.7.7
- bandit@1.8.6
- black@25.1.0
- checkov@3.2.469
- checkov@3.2.471
- git-diff-check
- isort@6.0.1
- mypy@1.17.1
- mypy@1.18.1
- osv-scanner@2.2.2
- prettier@3.6.2:
packages:
- "@trivago/prettier-plugin-sort-imports@5.2.2"
- "@vue/compiler-sfc@3.5.21"
- ruff@0.12.11
- ruff@0.13.0
- shellcheck@0.11.0
- shfmt@3.6.0
- taplo@0.10.0
- trivy@0.66.0
- trufflehog@3.90.5
- trufflehog@3.90.6
- yamllint@1.37.1
ignore:
- linters: [ALL]

View File

@@ -10,6 +10,7 @@ class SearchRomSchema(BaseModel):
ss_id: int | None = None
sgdb_id: int | None = None
flashpoint_id: str | None = None
launchbox_id: int | None = None
hltb_id: int | None = None
platform_id: int
name: str
@@ -20,6 +21,7 @@ class SearchRomSchema(BaseModel):
ss_url_cover: str = ""
sgdb_url_cover: str = ""
flashpoint_url_cover: str = ""
launchbox_url_cover: str = ""
hltb_url_cover: str = ""
is_unidentified: bool
is_identified: bool

View File

@@ -10,12 +10,14 @@ from handler.database import db_rom_handler
from handler.metadata import (
meta_flashpoint_handler,
meta_igdb_handler,
meta_launchbox_handler,
meta_moby_handler,
meta_sgdb_handler,
meta_ss_handler,
)
from handler.metadata.flashpoint_handler import FlashpointRom
from handler.metadata.igdb_handler import IGDBRom
from handler.metadata.launchbox_handler import LaunchboxRom
from handler.metadata.moby_handler import MobyGamesRom
from handler.metadata.sgdb_handler import SGDBRom
from handler.metadata.ss_handler import SSRom
@@ -87,6 +89,7 @@ async def search_rom(
moby_matched_roms: list[MobyGamesRom] = []
ss_matched_roms: list[SSRom] = []
flashpoint_matched_roms: list[FlashpointRom] = []
launchbox_matched_roms: list[LaunchboxRom] = []
if search_by.lower() == "id":
try:
@@ -111,6 +114,7 @@ async def search_rom(
moby_matched_roms,
ss_matched_roms,
flashpoint_matched_roms,
launchbox_matched_roms,
) = await asyncio.gather(
meta_igdb_handler.get_matched_roms_by_name(
search_term, get_main_platform_igdb_id(rom.platform)
@@ -119,7 +123,12 @@ async def search_rom(
search_term, rom.platform.moby_id
),
meta_ss_handler.get_matched_roms_by_name(search_term, rom.platform.ss_id),
meta_flashpoint_handler.get_matched_roms_by_name(rom.fs_name),
meta_flashpoint_handler.get_matched_roms_by_name(
rom.fs_name, rom.platform.slug
),
meta_launchbox_handler.get_matched_roms_by_name(
search_term, rom.platform.slug
),
)
merged_dict: dict[str, dict] = {}
@@ -184,6 +193,21 @@ async def search_rom(
**merged_dict.get(flashpoint_name, {}),
}
for launchbox_rom in launchbox_matched_roms:
if launchbox_rom["launchbox_id"]:
launchbox_name = meta_launchbox_handler.normalize_search_term(
launchbox_rom.get("name", ""),
remove_articles=False,
)
merged_dict[launchbox_name] = {
**launchbox_rom,
"is_identified": True,
"is_unidentified": False,
"platform_id": rom.platform_id,
"launchbox_url_cover": launchbox_rom.pop("url_cover", ""),
**merged_dict.get(launchbox_name, {}),
}
async def get_sgdb_rom(name: str) -> tuple[str, SGDBRom]:
return name, await meta_sgdb_handler.get_details_by_names([name])

View File

@@ -226,7 +226,7 @@ class FlashpointHandler(MetadataHandler):
"""
from handler.filesystem import fs_rom_handler
if not FLASHPOINT_API_ENABLED:
if not self.is_enabled():
return FlashpointRom(flashpoint_id=None)
if platform_slug not in FLASHPOINT_PLATFORM_LIST:
@@ -276,13 +276,18 @@ class FlashpointHandler(MetadataHandler):
log.debug(f"No good match found for '{search_term}' on Flashpoint")
return FlashpointRom(flashpoint_id=None)
async def get_matched_roms_by_name(self, fs_name: str) -> list[FlashpointRom]:
async def get_matched_roms_by_name(
self, fs_name: str, platform_slug: str
) -> list[FlashpointRom]:
"""
Get ROM information by name from Flashpoint.
"""
from handler.filesystem import fs_rom_handler
if not FLASHPOINT_API_ENABLED:
if not self.is_enabled():
return []
if platform_slug not in FLASHPOINT_PLATFORM_LIST:
return []
search_term = fs_rom_handler.get_file_name_with_no_tags(fs_name)

View File

@@ -328,6 +328,15 @@ class LaunchboxHandler(MetadataHandler):
return await self.get_rom_by_id(database_id)
async def get_matched_roms_by_name(
self, search_term: str, platform_slug: str
) -> list[LaunchboxRom]:
if not self.is_enabled():
return []
rom = await self.get_rom(search_term, platform_slug)
return [rom] if rom else []
class SlugToLaunchboxId(TypedDict):
id: int

View File

@@ -9,6 +9,7 @@ export type SearchRomSchema = {
ss_id?: (number | null);
sgdb_id?: (number | null);
flashpoint_id?: (string | null);
launchbox_id?: (number | null);
hltb_id?: (number | null);
platform_id: number;
name: string;
@@ -19,6 +20,7 @@ export type SearchRomSchema = {
ss_url_cover?: string;
sgdb_url_cover?: string;
flashpoint_url_cover?: string;
launchbox_url_cover?: string;
hltb_url_cover?: string;
is_unidentified: boolean;
is_identified: boolean;

View File

@@ -152,7 +152,10 @@ const largeCover = computed(() => {
return (
props.rom.igdb_url_cover ||
props.rom.moby_url_cover ||
props.rom.ss_url_cover
props.rom.ss_url_cover ||
props.rom.launchbox_url_cover ||
props.rom.flashpoint_url_cover ||
props.rom.hltb_url_cover
);
const pathCoverLarge = isWebpEnabled.value
? props.rom.path_cover_large?.replace(EXTENSION_REGEX, ".webp")
@@ -260,7 +263,10 @@ onBeforeUnmount(() => {
!rom.igdb_url_cover &&
!rom.moby_url_cover &&
!rom.ss_url_cover &&
!rom.sgdb_url_cover)
!rom.sgdb_url_cover &&
!rom.launchbox_url_cover &&
!rom.flashpoint_url_cover &&
!rom.hltb_url_cover)
"
class="translucent text-white"
:class="

View File

@@ -14,7 +14,13 @@ defineProps<{ rom: SearchRomSchema }>();
open-delay="500"
>
<template #activator="{ props }">
<v-avatar v-if="rom.igdb_id" v-bind="props" size="30" rounded="1">
<v-avatar
v-if="rom.igdb_id"
class="mr-1 mb-1"
v-bind="props"
size="28"
rounded="1"
>
<v-img src="/assets/scrappers/igdb.png" />
</v-avatar>
</template>
@@ -30,8 +36,8 @@ defineProps<{ rom: SearchRomSchema }>();
<v-avatar
v-if="rom.moby_id"
v-bind="props"
class="ml-1"
size="30"
class="mr-1 mb-1"
size="28"
rounded="1"
>
<v-img src="/assets/scrappers/moby.png" />
@@ -49,14 +55,33 @@ defineProps<{ rom: SearchRomSchema }>();
<v-avatar
v-if="rom.ss_id"
v-bind="props"
class="ml-1"
size="30"
class="mr-1 mb-1"
size="28"
rounded="1"
>
<v-img src="/assets/scrappers/ss.png" />
</v-avatar>
</template>
</v-tooltip>
<v-tooltip
location="top"
class="tooltip"
transition="fade-transition"
text="Launchbox matched"
open-delay="500"
>
<template #activator="{ props }">
<v-avatar
v-bind="props"
v-if="rom.launchbox_id"
class="mr-1 mb-1"
size="28"
rounded="1"
>
<v-img src="/assets/scrappers/launchbox.png" />
</v-avatar>
</template>
</v-tooltip>
<v-tooltip
location="top"
class="tooltip"
@@ -65,7 +90,13 @@ defineProps<{ rom: SearchRomSchema }>();
open-delay="500"
>
<template #activator="{ props }">
<v-avatar v-bind="props" v-if="rom.flashpoint_id" size="30" rounded="1">
<v-avatar
v-bind="props"
v-if="rom.flashpoint_id"
class="mr-1 mb-1"
size="28"
rounded="1"
>
<v-img src="/assets/scrappers/flashpoint.png" />
</v-avatar>
</template>
@@ -78,7 +109,13 @@ defineProps<{ rom: SearchRomSchema }>();
open-delay="500"
>
<template #activator="{ props }">
<v-avatar v-bind="props" v-if="rom.hltb_id" size="30" rounded="1">
<v-avatar
v-bind="props"
v-if="rom.hltb_id"
class="mr-1 mb-1"
size="28"
rounded="1"
>
<v-img src="/assets/scrappers/hltb.png" />
</v-avatar>
</template>

View File

@@ -24,6 +24,7 @@ type MatchedSource = {
| "Mobygames"
| "Screenscraper"
| "Flashpoint"
| "Launchbox"
| "HowLongToBeat"
| "SteamGridDB";
logo_path: string;
@@ -54,6 +55,7 @@ const isIGDBFiltered = ref(true);
const isMobyFiltered = ref(true);
const isSSFiltered = ref(true);
const isFlashpointFiltered = ref(true);
const isLaunchboxFiltered = ref(true);
const isHLTBFiltered = ref(true);
const computedAspectRatio = computed(() => {
const ratio =
@@ -94,6 +96,11 @@ function toggleSourceFilter(source: MatchedSource["name"]) {
heartbeat.value.METADATA_SOURCES.FLASHPOINT_API_ENABLED
) {
isFlashpointFiltered.value = !isFlashpointFiltered.value;
} else if (
source == "Launchbox" &&
heartbeat.value.METADATA_SOURCES.LAUNCHBOX_API_ENABLED
) {
isLaunchboxFiltered.value = !isLaunchboxFiltered.value;
} else if (
source == "HowLongToBeat" &&
heartbeat.value.METADATA_SOURCES.HLTB_API_ENABLED
@@ -106,6 +113,7 @@ function toggleSourceFilter(source: MatchedSource["name"]) {
(rom.moby_id && isMobyFiltered.value) ||
(rom.ss_id && isSSFiltered.value) ||
(rom.flashpoint_id && isFlashpointFiltered.value) ||
(rom.launchbox_id && isLaunchboxFiltered.value) ||
(rom.hltb_id && isHLTBFiltered.value)
) {
return true;
@@ -138,6 +146,7 @@ async function searchRom() {
(rom.moby_id && isMobyFiltered.value) ||
(rom.ss_id && isSSFiltered.value) ||
(rom.flashpoint_id && isFlashpointFiltered.value) ||
(rom.launchbox_id && isLaunchboxFiltered.value) ||
(rom.hltb_id && isHLTBFiltered.value)
) {
return true;
@@ -203,6 +212,13 @@ function showSources(matchedRom: SearchRomSchema) {
logo_path: "/assets/scrappers/flashpoint.png",
});
}
if (matchedRom.launchbox_url_cover) {
sources.value.push({
url_cover: matchedRom.launchbox_url_cover,
name: "Launchbox",
logo_path: "/assets/scrappers/launchbox.png",
});
}
if (matchedRom.hltb_url_cover) {
sources.value.push({
url_cover: matchedRom.hltb_url_cover,
@@ -260,6 +276,7 @@ async function updateRom(
ss_id: selectedRom.ss_id || null,
moby_id: selectedRom.moby_id || null,
flashpoint_id: selectedRom.flashpoint_id || null,
launchbox_id: selectedRom.launchbox_id || null,
hltb_id: selectedRom.hltb_id || null,
name: selectedRom.name || null,
slug: selectedRom.slug || null,
@@ -270,6 +287,7 @@ async function updateRom(
selectedRom.ss_url_cover ||
selectedRom.moby_url_cover ||
selectedRom.flashpoint_url_cover ||
selectedRom.launchbox_url_cover ||
selectedRom.hltb_url_cover ||
null,
};
@@ -423,6 +441,36 @@ onBeforeUnmount(() => {
</v-avatar>
</template>
</v-tooltip>
<v-tooltip
location="top"
class="tooltip"
transition="fade-transition"
:text="
heartbeat.value.METADATA_SOURCES.LAUNCHBOX_API_ENABLED
? 'Filter Launchbox matches'
: 'Launchbox source is not enabled'
"
open-delay="500"
>
<template #activator="{ props }">
<v-avatar
@click="toggleSourceFilter('Launchbox')"
v-bind="props"
class="ml-3 cursor-pointer opacity-40"
:class="{
'opacity-100':
isLaunchboxFiltered &&
heartbeat.value.METADATA_SOURCES.LAUNCHBOX_API_ENABLED,
'cursor-not-allowed':
!heartbeat.value.METADATA_SOURCES.LAUNCHBOX_API_ENABLED,
}"
size="30"
rounded="1"
>
<v-img src="/assets/scrappers/launchbox.png" />
</v-avatar>
</template>
</v-tooltip>
<v-tooltip
location="top"
class="tooltip"