mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 14:56:01 +00:00
moer fixes
This commit is contained in:
@@ -173,18 +173,22 @@ class FSHandler:
|
||||
|
||||
@asynccontextmanager
|
||||
async def _atomic_write(self, target_path: Path):
|
||||
"""Context manager for atomic file writing using a system temp file."""
|
||||
fd, temp_path_str = tempfile.mkstemp()
|
||||
"""Context manager for atomic file writing.
|
||||
|
||||
Creates the temp file in the same directory as the target so that
|
||||
os.rename is always an atomic same-filesystem operation.
|
||||
"""
|
||||
fd, temp_path_str = tempfile.mkstemp(
|
||||
dir=str(target_path.parent), prefix=".romm_tmp_"
|
||||
)
|
||||
temp_path = Path(temp_path_str)
|
||||
os.close(fd)
|
||||
|
||||
try:
|
||||
yield temp_path
|
||||
# Move to final location
|
||||
shutil.move(str(temp_path), str(target_path))
|
||||
os.replace(str(temp_path), str(target_path))
|
||||
|
||||
except Exception:
|
||||
# Clean up temporary file on error
|
||||
async_temp = AnyioPath(temp_path)
|
||||
if await async_temp.exists():
|
||||
await async_temp.unlink()
|
||||
|
||||
@@ -143,9 +143,7 @@ def test_export_gamelist_xml_release_date(platform_with_roms):
|
||||
|
||||
release_date = game.find("releasedate")
|
||||
assert release_date is not None
|
||||
assert release_date.text is not None
|
||||
# Should be in YYYYMMDDTHHMMSS format
|
||||
assert release_date.text.startswith("19920623")
|
||||
assert release_date.text == "19920623T120000"
|
||||
|
||||
|
||||
def test_export_gamelist_xml_minimal_rom(platform_with_minimal_rom):
|
||||
|
||||
@@ -151,7 +151,7 @@ class TestExportMetadata:
|
||||
assert game["tag"] == "Retro"
|
||||
assert game["description"] == "A classic platformer game."
|
||||
assert game["rating"] == "92%"
|
||||
assert isinstance(game["release"], str) and game["release"].startswith("1992-")
|
||||
assert game["release"] == "1992-06-23"
|
||||
assert game["x-region"] == "USA"
|
||||
assert game["x-language"] == "en"
|
||||
assert "x-romm-id" in game
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
from xml.etree.ElementTree import ( # trunk-ignore(bandit/B405)
|
||||
Element,
|
||||
SubElement,
|
||||
@@ -23,7 +23,9 @@ class GamelistExporter:
|
||||
|
||||
def _format_release_date(self, timestamp: int) -> str:
|
||||
"""Format release date to YYYYMMDDTHHMMSS format"""
|
||||
return datetime.fromtimestamp(timestamp / 1000).strftime("%Y%m%dT%H%M%S")
|
||||
return datetime.fromtimestamp(timestamp / 1000, tz=UTC).strftime(
|
||||
"%Y%m%dT%H%M%S"
|
||||
)
|
||||
|
||||
def _create_game_element(self, rom: Rom, request: Request | None) -> Element:
|
||||
"""Create a <game> element for a ROM"""
|
||||
@@ -180,7 +182,7 @@ class GamelistExporter:
|
||||
root = Element("gameList")
|
||||
|
||||
for rom in roms:
|
||||
if rom and not rom.missing_from_fs and rom.fs_name != "gamelist.xml":
|
||||
if rom and not rom.missing_from_fs:
|
||||
game_element = self._create_game_element(rom, request=request)
|
||||
root.append(game_element)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import Request
|
||||
@@ -33,7 +33,7 @@ class PegasusExporter:
|
||||
|
||||
def _format_release_date(self, timestamp: int) -> str:
|
||||
"""Format release date to YYYY-MM-DD format"""
|
||||
return datetime.fromtimestamp(timestamp / 1000).strftime("%Y-%m-%d")
|
||||
return datetime.fromtimestamp(timestamp / 1000, tz=UTC).strftime("%Y-%m-%d")
|
||||
|
||||
def _format_rating(self, average_rating: float) -> str:
|
||||
"""Format rating as percentage (0-100%). Input is on 0-10 scale."""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import enum
|
||||
import fnmatch
|
||||
import json
|
||||
import os
|
||||
from collections.abc import Sequence
|
||||
@@ -117,14 +118,32 @@ def process_changes(changes: Sequence[Change]) -> None:
|
||||
if not ENABLE_RESCAN_ON_FILESYSTEM_CHANGE:
|
||||
return
|
||||
|
||||
# Filter for valid events, ignoring excluded files/dirs from config
|
||||
# Filter for valid events, applying the same exclusion rules as the scanner:
|
||||
# exact-match and fnmatch patterns for files, plus excluded directory names
|
||||
# checked against every path component so events inside excluded dirs are ignored.
|
||||
config = cm.get_config()
|
||||
excluded_names = set(config.EXCLUDED_SINGLE_FILES + config.EXCLUDED_MULTI_FILES)
|
||||
excluded_patterns = (
|
||||
config.EXCLUDED_SINGLE_FILES
|
||||
+ config.EXCLUDED_MULTI_FILES
|
||||
+ config.EXCLUDED_MULTI_PARTS_FILES
|
||||
)
|
||||
|
||||
def _is_excluded(path: str) -> bool:
|
||||
parts = path.strip("/").split("/")
|
||||
for part in parts:
|
||||
if part.startswith(".romm_tmp_"):
|
||||
return True
|
||||
if any(
|
||||
part == pat or fnmatch.fnmatch(part, pat) for pat in excluded_patterns
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
changes = [
|
||||
change
|
||||
for change in changes
|
||||
if change[0] in VALID_EVENTS
|
||||
and os.path.basename(os.fsdecode(change[1])) not in excluded_names
|
||||
and not _is_excluded(os.fsdecode(change[1]).split(LIBRARY_BASE_PATH)[-1])
|
||||
]
|
||||
if not changes:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user