This commit is contained in:
Georges-Antoine Assi
2026-04-20 21:36:59 -04:00
parent 042ddf16e1
commit 98f1d65a7c
4 changed files with 48 additions and 34 deletions

View File

@@ -159,6 +159,10 @@ def _should_scan_rom(
metadata_sources (list[str]): List of metadata sources to be used.
"""
# When roms_ids is provided, the scan is scoped to those roms only
if rom and roms_ids:
return bool(rom.id in roms_ids)
# This logic is tricky so only touch it if you know what you're doing"""
should_scan = bool(
# Any new roms should be scanned
@@ -170,10 +174,8 @@ def _should_scan_rom(
or (
rom
and (
# Selected ROMs are always scanned
(rom.id in roms_ids)
# Update scan should scan ROMs identified by the selected metadata sources
or (
(
scan_type == ScanType.UPDATE
and rom.is_identified
and any(getattr(rom, f"{source}_id") for source in metadata_sources)

View File

@@ -98,17 +98,23 @@ class TestShouldScanRom:
# Test COMPLETE scan type
def test_complete_scan_always_scans(self, rom: Rom):
"""COMPLETE should always scan regardless of rom state"""
"""COMPLETE should scan everything when unscoped, but respect roms_ids when scoped"""
assert _should_scan_rom(ScanType.COMPLETE, None, [], ["igdb"]) is True
assert _should_scan_rom(ScanType.COMPLETE, rom, [], ["igdb"]) is True
assert _should_scan_rom(ScanType.COMPLETE, rom, [2, 3], ["igdb"]) is True
# Scoped scan: rom not in list → skip even for COMPLETE
assert (
_should_scan_rom(ScanType.COMPLETE, rom, [rom.id + 99], ["igdb"]) is False
)
assert _should_scan_rom(ScanType.COMPLETE, rom, [rom.id], ["igdb"]) is True
# Test HASHES scan type
def test_hashes_scan_always_scans(self, rom: Rom):
"""HASHES should always scan regardless of rom state"""
"""HASHES should scan everything when unscoped, but respect roms_ids when scoped"""
assert _should_scan_rom(ScanType.HASHES, None, [], ["igdb"]) is True
assert _should_scan_rom(ScanType.HASHES, rom, [], ["igdb"]) is True
assert _should_scan_rom(ScanType.HASHES, rom, [2, 3], ["igdb"]) is True
# Scoped scan: rom not in list → skip even for HASHES
assert _should_scan_rom(ScanType.HASHES, rom, [rom.id + 99], ["igdb"]) is False
assert _should_scan_rom(ScanType.HASHES, rom, [rom.id], ["igdb"]) is True
# Test UNMATCHED scan type
def test_unmatched_scan_with_no_rom(self):
@@ -170,17 +176,24 @@ class TestShouldScanRom:
assert result is True
def test_no_scan_when_rom_id_not_in_list(self, rom: Rom):
"""Should follow normal rules when rom.id is not in roms_ids list"""
"""When roms_ids is non-empty, scan is scoped — roms outside the list are skipped for every scan type"""
rom.id = 4
rom.igdb_id = None
rom.moby_id = None
rom.ss_id = None
rom.ra_id = None
rom.launchbox_id = None
roms_ids = [1, 2, 3]
# These should not scan because rom exists and id not in list
assert (
_should_scan_rom(ScanType.NEW_PLATFORMS, rom, roms_ids, ["igdb"]) is False
)
assert _should_scan_rom(ScanType.QUICK, rom, roms_ids, ["igdb"]) is False
assert _should_scan_rom(ScanType.UPDATE, rom, roms_ids, ["igdb"]) is False
assert _should_scan_rom(ScanType.UNMATCHED, rom, roms_ids, ["igdb"]) is True
for scan_type in [
ScanType.NEW_PLATFORMS,
ScanType.QUICK,
ScanType.UPDATE,
ScanType.UNMATCHED,
ScanType.COMPLETE,
ScanType.HASHES,
]:
assert _should_scan_rom(scan_type, rom, roms_ids, ["igdb"]) is False
# Edge cases
def test_empty_roms_ids_list(self, rom: Rom):

View File

@@ -51,7 +51,6 @@ async function resetLastPlayed() {
return;
});
}
</script>
<template>
@@ -65,7 +64,7 @@ async function resetLastPlayed() {
<v-list-item-title class="d-flex">
<v-icon icon="mdi-search-web" class="mr-2" />{{
t("rom.manual-match")
}}
}}
</v-list-item-title>
<v-list-item-subtitle>
{{
@@ -80,14 +79,17 @@ async function resetLastPlayed() {
@click="emitter?.emit('showEditRomDialog', rom)"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-pencil-box" class="mr-2" />{{ t("common.edit") }}
<v-icon icon="mdi-pencil-box" class="mr-2" />{{ t("common.edit") }}
</v-list-item-title>
</v-list-item>
<v-list-item class="py-4 pr-5" @click="emitter?.emit('showRefreshMetadataDialog', rom)">
<v-list-item
class="py-4 pr-5"
@click="emitter?.emit('showRefreshMetadataDialog', rom)"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-magnify-scan" class="mr-2" />{{
t("rom.refresh-metadata")
}}
}}
</v-list-item-title>
</v-list-item>
<v-divider />
@@ -130,7 +132,7 @@ async function resetLastPlayed() {
<v-list-item-title class="d-flex">
<v-icon icon="mdi-bookmark-plus" class="mr-2" />{{
t("rom.add-to-collection")
}}
}}
</v-list-item-title>
</v-list-item>
<v-list-item
@@ -141,7 +143,7 @@ async function resetLastPlayed() {
<v-list-item-title class="d-flex">
<v-icon icon="mdi-bookmark-remove-outline" class="mr-2" />{{
t("rom.remove-from-collection")
}}
}}
</v-list-item-title>
</v-list-item>
<template v-if="auth.scopes.includes('roms.write')">
@@ -151,7 +153,7 @@ async function resetLastPlayed() {
@click="emitter?.emit('showDeleteRomDialog', [rom])"
>
<v-list-item-title class="d-flex">
<v-icon icon="mdi-delete" class="mr-2" />{{ t("rom.delete") }}
<v-icon icon="mdi-delete" class="mr-2" />{{ t("rom.delete") }}
</v-list-item-title>
</v-list-item>
</template>

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
import { useLocalStorage } from "@vueuse/core";
import type { Emitter } from "mitt";
import { storeToRefs } from "pinia";
import { computed, inject, onBeforeUnmount, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { storeToRefs } from "pinia";
import RDialog from "@/components/common/RDialog.vue";
import socket from "@/services/socket";
import storeConfig from "@/stores/config";
@@ -78,11 +78,6 @@ watch(metadataOptions, (newOptions) => {
});
const scanOptions = computed(() => [
{
title: t("scan.quick-scan"),
subtitle: t("scan.quick-scan-desc"),
value: "quick",
},
{
title: t("scan.unmatched-games"),
subtitle: t("scan.unmatched-games-desc"),
@@ -93,13 +88,18 @@ const scanOptions = computed(() => [
subtitle: t("scan.update-metadata-desc"),
value: "update",
},
{
title: t("scan.hashes"),
subtitle: t("scan.hashes-desc"),
value: "hashes",
},
{
title: t("scan.complete-rescan"),
subtitle: t("scan.complete-rescan-desc"),
value: "complete",
},
]);
const scanType = ref("quick");
const scanType = ref("unmatched");
const handleShowRefreshMetadataDialog = (romToRefresh: SimpleRom) => {
rom.value = romToRefresh;
@@ -193,10 +193,7 @@ function closeDialog() {
</v-avatar>
</template>
<template
v-if="item.raw.value === 'launchbox'"
#append
>
<template v-if="item.raw.value === 'launchbox'" #append>
<div class="d-flex align-center">
<span
class="text-caption text-primary text-medium-emphasis mr-4"