Commit Graph

287 Commits

Author SHA1 Message Date
Georges-Antoine Assi
8210bab806 Tolerate forward-compat values and malformed YAML in config loader
Sample configs shipped with newer releases can reference media types or
options unknown to older versions; previously the loader called
sys.exit(3) and refused to boot. Now drop unknown scan.media entries,
fall back to defaults for unknown gamelist media thumbnail/image values,
and recover from YAML parse errors with a clear critical log instead of
a traceback. Type-mismatch validation still hard-fails since those are
real misconfigurations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:26:43 -04:00
Georges-Antoine Assi
8999b66574 Preserve local LaunchBox data on UPDATE scans and video extensions
UPDATE scans with a known launchbox_id were calling get_rom_by_id(),
which only returns remote data and bypassed get_rom()'s local-first
merge — so local-only fields like Notes were getting clobbered and
local media matching fidelity dropped. get_rom_by_id() now optionally
takes fs_name/platform_slug and merges the local entry when its
DatabaseID matches the requested id.

Also fixes populate_rom_specific_paths writing every video as
video.mp4 regardless of source extension; since store_media_file is a
byte copy, .mkv/.webm contents would be served as .mp4 and break MIME
sniffing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:32:34 -04:00
Georges-Antoine Assi
d8ef6f0c05 Merge branch 'master' into local-lb-fix 2026-05-09 13:20:31 -04:00
Georges-Antoine Assi
d009662499 Merge pull request #3342 from rommapp/copilot/add-copy-default-config-yml
Auto-create missing `config.yml` on startup in mounted config volume
2026-05-09 11:00:22 -04:00
Georges-Antoine Assi
783d9a257e test(backend): cover artwork upload validation for roms and collections
Adds rejection + acceptance tests for update_rom, add_collection, and
update_collection artwork uploads, mirroring the existing avatar tests:
non-image content returns 400, and a real PNG uploaded under a misleading
filename like payload.html is stored with the trusted .png extension.

Also fixes two `return HTTPException(...)` → `raise` in raw.py so the 404
path actually surfaces instead of silently returning the exception object.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 09:37:44 -04:00
Georges-Antoine Assi
53f14f5710 fix(backend): validate uploaded images with libmagic before storing
Avatar, ROM artwork, and collection artwork uploads now sniff the file
header with libmagic and reject anything that isn't PNG/JPEG/WebP/GIF,
saving the file with an extension derived from the detected MIME rather
than the user-supplied filename. Pairs with the raw asset endpoint,
which decides inline vs attachment from the on-disk extension.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 09:18:02 -04:00
copilot-swe-agent[bot]
9ef217339f test(config): cover auto-created config directory
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/0c94e1c8-0f79-4f5f-a72b-e45832906d00

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-08 13:54:19 +00:00
copilot-swe-agent[bot]
fdd9047b37 feat(config): auto-create missing config.yml on startup
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/0c94e1c8-0f79-4f5f-a72b-e45832906d00

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-08 13:52:53 +00:00
Georges-Antoine Assi
5e3a2707b0 cleanup 2026-05-03 19:39:19 -04:00
copilot-swe-agent[bot]
da005cf81a Optimize fnmatch check and use consistent n64 filename in test
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/8cbbc2ca-a3e3-4c61-9e47-f8544d59231a

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-03 23:36:23 +00:00
copilot-swe-agent[bot]
78c0b5b894 Fix broken test_rename_fs_rom_same_name function definition after new test insertion
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/8cbbc2ca-a3e3-4c61-9e47-f8544d59231a

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-03 23:35:19 +00:00
copilot-swe-agent[bot]
9593c30292 Address PR review: normalize exclusion sets, avoid duplicates, add multi-dot test for get_rom_files
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/8cbbc2ca-a3e3-4c61-9e47-f8544d59231a

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-03 23:34:30 +00:00
copilot-swe-agent[bot]
101629628e Simplify extension exclusion to use ends-with check instead of sub-extension iteration
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/a81b2023-a243-4721-bc5e-c6fa1a473a79

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-03 22:46:21 +00:00
copilot-swe-agent[bot]
55cd0cfc4f Support compound suffix exclusions like "hash.txt" for multi-dot filenames
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/d1c69638-bfa0-480e-8050-d565b234ea44

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-03 01:29:04 +00:00
copilot-swe-agent[bot]
21de7e21f8 Fix file exclusion for multi-dot filenames (e.g. game.nds.hash.txt)
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/2f711770-100b-4e9e-a66e-ab1a74f025f8

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-02 16:50:30 +00:00
Georges-Antoine Assi
c4431e0ae4 cleanup 2026-04-30 15:22:22 -04:00
Georges-Antoine Assi
962a9bfa7e one more 2026-04-30 14:39:52 -04:00
Georges-Antoine Assi
fc8d69dc0c update tests 2026-04-30 14:18:49 -04:00
Georges-Antoine Assi
0e7359132b test: update fs handler tests for STRUCTURE_PATH_A/B refactor
Switch the get_platform_fs_structure, get_firmware_fs_structure,
get_platforms_directory and get_roms_fs_structure tests from mocking
os.path.exists to mocking glob.glob, matching the new STRUCTURE_PATH_B
detection logic. Rename the existing high_priority/normal_structure
variants to structure_a/structure_b for clarity, and fix
test_platform_specific_behavior so its monkeypatch wraps the calls
instead of being applied after them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:00:53 -04:00
copilot-swe-agent[bot]
ee1faf363c fix: update test mocks to use job.id attribute instead of get_id()
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/f053983a-ae39-44b3-a59d-083d65f2055d

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-04-29 19:57:57 +00:00
Georges-Antoine Assi
aee17e51d5 fix 2026-04-29 14:43:37 -04:00
copilot-swe-agent[bot]
80b5fdf21f fix: add periodic janitor to clean up orphaned upload tmp directories
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/2e55dd7f-d99d-48a2-ae15-0b8c4817cc6e

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-04-29 16:42:12 +00:00
Georges-Antoine Assi
c2d6b08a6b Merge pull request #3293 from rommapp/copilot/fix-update-tags-metadata
feat: open refresh metadata dialog with scan type and metadata source selection
2026-04-20 21:57:29 -04:00
Georges-Antoine Assi
ac45d1a32f changes from bot review 2026-04-20 21:51:30 -04:00
Georges-Antoine Assi
98f1d65a7c fixes 2026-04-20 21:36:59 -04:00
zurdi
d7a896b5da fix: re-parse tags from filename when renaming a rom
Renaming a rom via PUT /roms/{id} only updated fs_name and its
derivatives, leaving regions/languages/tags/revision/version stale
against the new filename. Re-parse them whenever fs_name changes so
edits like "patapon (Fr En)" -> "Patapon (Fr, En)" are reflected in
the database.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:23:33 +00:00
Georges-Antoine Assi
fad2e5c77d Use correct LaunchBox MameFile schema
The sample_metadata.zip test fixture used invented tag names
(<Description>, FileName ending in .zip). Real LaunchBox Mame.xml
(see backend/tasks/fixtures/launchbox/mame.xml) uses <Name> as the
full title and keys MameFile entries by the stem (e.g. wrlok_l3),
no extension. Read Name instead of Description, and fall back to
the stem when the raw fs_name lookup misses.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:31:16 -04:00
Georges-Antoine Assi
f33844be40 Find local LaunchBox media for remote-only rom matches
remote_media_req built a MediaRequest with platform_name=None, which
made _build_local_media_context bail and never search on-disk
Images/Manuals/Videos. Any rom that matched only via Metadata.xml (no
local XML entry) ended up with images: [] even though LaunchBox art
was sitting on disk. Thread platform_name and fs_name into
remote_media_req, falling back to the remote entry's Platform field
when no slug is available (e.g. lookups by database id).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 12:32:54 -04:00
Georges-Antoine Assi
8fc426b35c Import local LaunchBox videos
LaunchBox stores videos under /romm/launchbox/Videos/<Platform>/<Game>.<ext>
but the handler only extracted YouTube IDs; LAUNCHBOX_VIDEOS_DIR was
defined and unused. Add _get_video() to surface local videos as
launchbox-file:// URLs, thread a populate_rom_specific_paths() through
the scan pipeline to set video_path once the rom is known, and mirror
the SS/gamelist branch in the scan socket so store_media_file actually
copies the video into the rom's resource directory. Rom.path_video now
also reads from launchbox_metadata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 12:13:42 -04:00
Georges-Antoine Assi
a83f3bdca7 Wire up LaunchBox MAME arcade lookups
The scheduled task populated LAUNCHBOX_MAME_KEY from Mame.xml but no
handler code ever read it, so arcade ROMs like pacman.zip never matched
against Metadata.xml (which keys on full titles like "Pac-Man"). Add
RemoteSource.get_mame_entry() and an ARCADE branch in get_rom() that
resolves the MAME filename to its Description before name lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 12:00:26 -04:00
Georges-Antoine Assi
b18edb1528 fix trunk issues 2026-04-16 11:15:58 -04:00
copilot-swe-agent[bot]
bf246f842f fix: add cus region to default ScreenScraper fallback regions
Games where only custom (cus) region artwork exists were not fetched
unless users explicitly added 'cus' to their region priority config.
Adding 'cus' to the default fallback list ensures custom artwork is
always considered as a fallback when no other regional artwork exists.

Agent-Logs-Url: https://github.com/rommapp/romm/sessions/75c3e455-f7dd-4bd6-bec7-0460987e40a0

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-04-16 14:28:51 +00:00
Spinnich
14342b5726 fix(tests): use client.request(\"DELETE\") to send JSON body in tests
Starlette's TestClient (httpx-based) does not expose body kwargs on the
delete() convenience method; client.request(\"DELETE\", ..., json=...) is
the correct approach. Also switch datetime.utcnow() to
datetime.now(timezone.utc) to silence Python 3.13 deprecation warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:02:26 -04:00
Spinnich
9aab88ad8b fix(tests): fix remaining DELETE body and timestamp comparison issues
- Switch content= to data= for DELETE requests (Starlette TestClient is
  requests-based and does not accept content= keyword argument)
- Fix test_bumps_updated_at to record time before the API call and use >=
  comparison, avoiding false failures when MariaDB truncates DATETIME to
  whole seconds and creation/update land in the same second

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:53:57 -04:00
Spinnich
fae6d869f1 fix(tests): correct test failures in collection endpoint tests
- Replace client.delete(json=...) with content=json.dumps()+Content-Type header
  (Starlette TestClient does not forward json= on DELETE requests)
- Adjust duplicate-name test to expect HTTP 500 matching CollectionAlreadyExistsException
- Add description="" to collections created without it to satisfy Pydantic schema
- Strip tzinfo before comparing updated_at to avoid offset-naive/aware TypeError

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 20:43:12 -04:00
Spinnich
1c9f010918 Fix isort import ordering in test_collection.py 2026-04-14 20:33:45 -04:00
Spinnich
4cc0c868b7 Fix linting and formatting issues from trunk CI 2026-04-14 20:27:45 -04:00
Spinnich
2ecefa3d3f Fix race condition in collection and favorite rom membership updates
Replace full rom_ids list replacement with atomic POST/DELETE endpoints
that add or remove individual ROMs from a collection. This prevents
concurrent rapid clicks from overwriting each other (last-write-wins).

Also fix missing session.flush() in add_rom_user() and add collection
endpoint tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:08:53 -04:00
Georges-Antoine Assi
154165fadf final fixes 2026-04-12 19:26:34 -04:00
Georges-Antoine Assi
d45afb5dde more fixes 2026-04-12 18:32:15 -04:00
Georges-Antoine Assi
628d8d8bae refactor: pass RAGamesPlatform dict into calculate_hash, normalize extension
Callers now pass the full platform dict and rom.fs_extension; the service
normalizes the extension (optional leading dot, case-insensitive) before
checking the compressed-archive skip set, so ROMs stored with bare
extensions like "zip" correctly hit the skip path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 18:05:44 -04:00
Georges-Antoine Assi
8f1b8f41d7 perf: skip RAHasher subprocess for archived disc-platform ROMs
RAHasher was being spawned for every hashable ROM regardless of file
type. When the source file is a zip/7z/tar and the RA platform needs
an on-disk disc image (PSX, PS2, PSP, Saturn, Dreamcast, Sega CD,
3DO, PC-FX, Neo Geo CD, TurboGrafx CD, Atari Jaguar CD, Wii), the
subprocess fails with "Unsupported console for buffer hash: {id}"
after paying full process-spawn overhead per ROM — a serious slowdown
when indexing large zipped collections (e.g. myrient PS2/PSP sets).

calculate_hash now short-circuits those combinations with a debug log
and no subprocess. Raw disc images (.iso, .chd, .cue/.bin) and
archives on cartridge platforms still go through RAHasher as before.

Also centralize COMPRESSED_FILE_EXTENSIONS in utils/filesystem.py so
roms_handler (is_compressed_file / hashing), rahasher (skip logic),
and feeds (PKGi passthrough) share one source of truth. The shared
set adds .rar, which is_compressed_file now recognizes too.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 17:18:14 -04:00
Georges-Antoine Assi
1b5943bd93 feat: fetch libretro Named_Snaps/Titles/Logos gated by SCAN_MEDIA
get_rom now also fetches Named_Snaps, Named_Titles, and Named_Logos
when the matching MetadataMediaType (SCREENSHOT, TITLE_SCREEN, LOGO)
is in SCAN_MEDIA. Box art is still fetched unconditionally — it drives
url_cover and libretro_id. Matching extras are appended to
url_screenshots so the scan_handler artwork priority loop picks them
up without further changes. All enabled listings are fetched
concurrently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 16:01:37 -04:00
Georges-Antoine Assi
62e540d60e changes from bot review 2026-04-12 15:43:14 -04:00
Georges-Antoine Assi
f94c1491e5 fix: respect SCAN_ARTWORK_PRIORITY for SGDB and align libretro search with SGDB pattern
SGDB's url_cover used to clobber whatever the artwork priority loop
picked, which meant user-set priorities (e.g. libretro > sgdb) and
manually uploaded covers were silently overridden. SGDB now only
replaces the current url_cover when it outranks every other source
that produced one under SCAN_ARTWORK_PRIORITY, and never over a
manual cover preserved on UPDATE/UNMATCHED scans. Default artwork
priority gains sgdb at the top so existing "SGDB wins" behavior is
preserved for default configs.

On the /search/roms endpoint, libretro is now an enrichment source
alongside SGDB instead of a primary match source: it decorates
entries resolved by IGDB/Moby/SS/Flashpoint/Launchbox with
libretro_id and libretro_url_cover, mirroring how SGDB works.
get_matched_roms_by_name is removed from the libretro handler since
nothing else calls it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:52:44 -04:00
Georges-Antoine Assi
4928041593 manual cleanu 2026-04-12 11:04:12 -04:00
Georges-Antoine Assi
6df31e2cc0 Merge branch 'master' into libretro-handler 2026-04-12 10:14:26 -04:00
Georges-Antoine Assi
f9f3dfd927 changes from bot review 2026-04-12 09:50:54 -04:00
Georges-Antoine Assi
abc69c790f fix scanning 2026-04-12 09:35:34 -04:00
Georges-Antoine Assi
de9efb3da8 refactor: simplify mark_missing_roms with a single flips dict
Collapse the two parallel id lists and their mirrored chunked-update
loops into a `flips: dict[bool, list[int]]` keyed by desired state, and
drop unused rom assignments in the related tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:13:33 -04:00