diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index ba998fdde..266c878a1 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -62,7 +62,7 @@ jobs:
uv run pytest -vv --maxfail=10 --junitxml=pytest-report.xml --cov --cov-report xml:coverage.xml --cov-config=.coveragerc .
- name: Publish test results
- uses: EnricoMi/publish-unit-test-result-action/linux@sha-3a74b29
+ uses: EnricoMi/publish-unit-test-result-action/linux@v2.20.0
if: (!cancelled())
with:
files: |
diff --git a/backend/endpoints/responses/rom.py b/backend/endpoints/responses/rom.py
index 0898550c6..e0b6eb48c 100644
--- a/backend/endpoints/responses/rom.py
+++ b/backend/endpoints/responses/rom.py
@@ -242,6 +242,7 @@ class RomSchema(BaseModel):
path_manual: str | None
url_manual: str | None
+ is_identifying: bool = False
is_unidentified: bool
is_identified: bool
diff --git a/backend/endpoints/sockets/scan.py b/backend/endpoints/sockets/scan.py
index 4b12c7027..da39b3866 100644
--- a/backend/endpoints/sockets/scan.py
+++ b/backend/endpoints/sockets/scan.py
@@ -234,6 +234,7 @@ async def _identify_rom(
fs_rom=fs_rom,
metadata_sources=metadata_sources,
newly_added=newly_added,
+ socket_manager=socket_manager,
)
scan_stats.scanned_roms += 1
@@ -242,6 +243,14 @@ async def _identify_rom(
_added_rom = db_rom_handler.add_rom(scanned_rom)
+ if _added_rom.is_identified:
+ await socket_manager.emit(
+ "scan:scanning_rom",
+ SimpleRomSchema.from_orm_with_factory(_added_rom).model_dump(
+ exclude={"created_at", "updated_at", "rom_user"}
+ ),
+ )
+
# Delete the existing rom files in the DB
db_rom_handler.purge_rom_files(_added_rom.id)
@@ -316,14 +325,9 @@ async def _identify_rom(
await socket_manager.emit(
"scan:scanning_rom",
- {
- "platform_name": platform.name,
- "platform_slug": platform.slug,
- "platform_fs_slug": platform.fs_slug,
- **SimpleRomSchema.from_orm_with_factory(_added_rom).model_dump(
- exclude={"created_at", "updated_at", "rom_user"}
- ),
- },
+ SimpleRomSchema.from_orm_with_factory(_added_rom).model_dump(
+ exclude={"created_at", "updated_at", "rom_user"}
+ ),
)
await socket_manager.emit("", None)
@@ -460,25 +464,25 @@ async def scan_platforms(
if not roms_ids:
roms_ids = []
- sm = _get_socket_manager()
+ socket_manager = _get_socket_manager()
if not metadata_sources:
log.error("No metadata sources provided")
- await sm.emit("scan:done_ko", "No metadata sources provided")
+ await socket_manager.emit("scan:done_ko", "No metadata sources provided")
return None
try:
fs_platforms: list[str] = await fs_platform_handler.get_platforms()
except FolderStructureNotMatchException as e:
log.error(e)
- await sm.emit("scan:done_ko", e.message)
+ await socket_manager.emit("scan:done_ko", e.message)
return None
scan_stats = ScanStats()
async def stop_scan():
log.info(f"{emoji.EMOJI_STOP_SIGN} Scan stopped manually")
- await sm.emit("scan:done", scan_stats.__dict__)
+ await socket_manager.emit("scan:done", scan_stats.__dict__)
redis_client.delete(STOP_SCAN_FLAG)
try:
@@ -506,7 +510,7 @@ async def scan_platforms(
fs_platforms=fs_platforms,
roms_ids=roms_ids,
metadata_sources=metadata_sources,
- socket_manager=sm,
+ socket_manager=socket_manager,
)
missed_platforms = db_platform_handler.mark_missing_platforms(fs_platforms)
@@ -516,13 +520,13 @@ async def scan_platforms(
log.warning(f" - {p.slug} ({p.fs_slug})")
log.info(f"{emoji.EMOJI_CHECK_MARK} Scan completed")
- await sm.emit("scan:done", scan_stats.__dict__)
+ await socket_manager.emit("scan:done", scan_stats.__dict__)
except ScanStoppedException:
await stop_scan()
except Exception as e:
log.error(f"Error in scan_platform: {e}")
# Catch all exceptions and emit error to the client
- await sm.emit("scan:done_ko", str(e))
+ await socket_manager.emit("scan:done_ko", str(e))
# Re-raise the exception to be caught by the error handler
raise e
diff --git a/backend/handler/scan_handler.py b/backend/handler/scan_handler.py
index c871809d8..20aec585f 100644
--- a/backend/handler/scan_handler.py
+++ b/backend/handler/scan_handler.py
@@ -2,8 +2,11 @@ import asyncio
import enum
from typing import Any
+import socketio # type: ignore
+
from config.config_manager import config_manager as cm
-from handler.database import db_platform_handler
+from endpoints.responses.rom import SimpleRomSchema
+from handler.database import db_platform_handler, db_rom_handler
from handler.filesystem import fs_asset_handler, fs_firmware_handler
from handler.filesystem.roms_handler import FSRom
from handler.metadata import (
@@ -281,6 +284,7 @@ async def scan_rom(
fs_rom: FSRom,
metadata_sources: list[str],
newly_added: bool,
+ socket_manager: socketio.AsyncRedisManager | None = None,
) -> Rom:
if not metadata_sources:
log.error("No metadata sources provided")
@@ -384,6 +388,19 @@ async def scan_rom(
return HasheousRom(hasheous_id=None, igdb_id=None, tgdb_id=None, ra_id=None)
+ _added_rom = db_rom_handler.add_rom(Rom(**rom_attrs))
+ _added_rom.is_identifying = True
+
+ if socket_manager:
+ await socket_manager.emit(
+ "scan:scanning_rom",
+ {
+ **SimpleRomSchema.from_orm_with_factory(_added_rom).model_dump(
+ exclude={"created_at", "updated_at", "rom_user"}
+ ),
+ },
+ )
+
# Run hash fetches concurrently
(
playmatch_hash_match,
diff --git a/backend/models/rom.py b/backend/models/rom.py
index 7f1954ac1..009332c30 100644
--- a/backend/models/rom.py
+++ b/backend/models/rom.py
@@ -247,6 +247,10 @@ class Rom(BaseModel):
back_populates="roms",
)
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
+ super().__init__(*args, **kwargs)
+ self._is_identifying = False
+
@property
def platform_slug(self) -> str:
return self.platform.slug
@@ -369,6 +373,15 @@ class Rom(BaseModel):
)
return self.ra_metadata
+ # Used only during scan process
+ @property
+ def is_identifying(self) -> bool:
+ return self._is_identifying or False
+
+ @is_identifying.setter
+ def is_identifying(self, value: bool) -> None:
+ self._is_identifying = value
+
def __repr__(self) -> str:
return self.fs_name
diff --git a/backend/tests/handler/test_fastapi.py b/backend/tests/handler/test_fastapi.py
index e6e91594a..3926329de 100644
--- a/backend/tests/handler/test_fastapi.py
+++ b/backend/tests/handler/test_fastapi.py
@@ -1,5 +1,6 @@
import pytest
+from handler.database import db_platform_handler
from handler.scan_handler import MetadataSource, ScanType, scan_platform, scan_rom
from models.platform import Platform
from models.rom import Rom, RomFile
@@ -28,9 +29,15 @@ async def test_scan_platform():
@pytest.mark.vcr
async def test_scan_rom():
- platform = Platform(fs_slug="n64", igdb_id=4)
+ platform = Platform(id=1, slug="n64", fs_slug="n64", name="Nintendo 64", igdb_id=4)
+ platform = db_platform_handler.add_platform(platform)
+
rom = Rom(
fs_name="Paper Mario (USA).z64",
+ fs_name_no_tags="Paper Mario",
+ fs_name_no_ext="Paper Mario",
+ fs_extension="z64",
+ fs_path="n64/Paper Mario (USA)",
name="Paper Mario",
igdb_id=3340,
fs_size_bytes=1024,
diff --git a/frontend/src/__generated__/models/DetailedRomSchema.ts b/frontend/src/__generated__/models/DetailedRomSchema.ts
index 302b792ce..1365734dc 100644
--- a/frontend/src/__generated__/models/DetailedRomSchema.ts
+++ b/frontend/src/__generated__/models/DetailedRomSchema.ts
@@ -62,6 +62,7 @@ export type DetailedRomSchema = {
has_manual: boolean;
path_manual: (string | null);
url_manual: (string | null);
+ is_identifying?: boolean;
is_unidentified: boolean;
is_identified: boolean;
revision: (string | null);
diff --git a/frontend/src/__generated__/models/SimpleRomSchema.ts b/frontend/src/__generated__/models/SimpleRomSchema.ts
index 2da97adcb..d41e89c58 100644
--- a/frontend/src/__generated__/models/SimpleRomSchema.ts
+++ b/frontend/src/__generated__/models/SimpleRomSchema.ts
@@ -56,6 +56,7 @@ export type SimpleRomSchema = {
has_manual: boolean;
path_manual: (string | null);
url_manual: (string | null);
+ is_identifying?: boolean;
is_unidentified: boolean;
is_identified: boolean;
revision: (string | null);
diff --git a/frontend/src/components/common/Navigation/ScanBtn.vue b/frontend/src/components/common/Navigation/ScanBtn.vue
index 47de62173..209f6b664 100644
--- a/frontend/src/components/common/Navigation/ScanBtn.vue
+++ b/frontend/src/components/common/Navigation/ScanBtn.vue
@@ -57,9 +57,18 @@ socket.on(
socket.on("scan:scanning_rom", (rom: SimpleRom) => {
scanningStore.set(true);
+
+ // Remove the ROM from the recent list and add it back to the top
+ romsStore.removeFromRecent(rom);
romsStore.addToRecent(rom);
+
if (romsStore.currentPlatform?.id === rom.platform_id) {
- romsStore.add([rom]);
+ const existingRom = romsStore.allRoms.find((r) => r.id === rom.id);
+ if (existingRom) {
+ romsStore.update(rom);
+ } else {
+ romsStore.add([rom]);
+ }
}
let scannedPlatform = scanningPlatforms.value.find(
@@ -78,7 +87,15 @@ socket.on("scan:scanning_rom", (rom: SimpleRom) => {
scannedPlatform = scanningPlatforms.value[0];
}
- scannedPlatform?.roms.push(rom);
+ // Check if ROM already exists in the store
+ const existingRom = scannedPlatform?.roms.find((r) => r.id === rom.id);
+ if (existingRom) {
+ scannedPlatform.roms = scannedPlatform.roms.map((r) =>
+ r.id === rom.id ? rom : r,
+ );
+ } else {
+ scannedPlatform?.roms.push(rom);
+ }
});
socket.on("scan:done", () => {
diff --git a/frontend/src/views/Scan.vue b/frontend/src/views/Scan.vue
index 26bc96d4a..66ff63e48 100644
--- a/frontend/src/views/Scan.vue
+++ b/frontend/src/views/Scan.vue
@@ -365,105 +365,113 @@ async function stopScan() {
with-filename
>
-
- Not identified
- mdi-close
-
-
-
- mdi-check-decagram-outline
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ mdi-search-web
+ Identifying…
+
+
+
+
+ mdi-close
+ Not identified
+
+
+
+ mdi-check-decagram-outline
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+