Commit Graph

1513 Commits

Author SHA1 Message Date
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 15b485b118 fix(collections): address reviewer feedback on types and concurrency
- Use CollectionSchema instead of ReturnType<typeof collectionsStore.getCollection>
  in AddRoms.vue and RemoveRoms.vue (simpler, per gantoine review)
- Wrap bulk INSERT in a savepoint so a concurrent duplicate-key violation
  is caught via IntegrityError and ignored rather than aborting the transaction
- Only bump Collection.updated_at in remove_roms_from_collection when rows
  were actually deleted (rowcount > 0), matching add_roms_to_collection behavior

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 06:46:12 -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 105ed84e43 feat: show TheGamesDB link on platform drawer when tgdb_id is set
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 19:04:51 -04:00
Georges-Antoine Assi e8a6e9f01d final fixes 2026-04-12 18:43:24 -04:00
Georges-Antoine Assi d45afb5dde more fixes 2026-04-12 18:32:15 -04:00
Georges-Antoine Assi f2df03361e fix: cast spread-dict back to RAUserGameProgression to preserve typing
Pylance infers dict[str, Unknown] from the {**old, "highest_award_kind": ...}
spread, which then fails to assign to list[RAUserGameProgression]. Wrap in
typing.cast so the TypedDict type survives the reassignment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 18:08:18 -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 3ce0873931 fix 2026-04-12 16:09:36 -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 85f9444b57 fix: restore get_roms_by_fs_name after stripped @with_details
Commit 3991e1b6e removed `@with_details` from `get_roms_by_fs_name` but
left the body using the `query` parameter that decorator was supposed
to inject, so every scan hit `'NoneType' object has no attribute
'filter'` and crashed the platform identification task.

Make the function self-contained: build `select(Rom)` directly and
eager-load only `Rom.platform`, the one relationship the scan loop
actually needs (via `rom.platform_slug` / `rom.platform.fs_slug`).
Keeps the prior commit's intent of avoiding the heavy `with_details`
eager-load on every batch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 09:10:27 -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
Georges-Antoine Assi 522df9d31a feat: add libretro thumbnails as an artwork source
Adds the libretro thumbnail repository as a first-class artwork source so
region-correct box art (PAL/Europe, Japan, etc.) can be matched directly
to ROM filenames, addressing rommapp/romm#3239.

Implementation follows the SGDB handler pattern (artwork-only, no game
metadata): MetadataSource enum entry, scan-time fetch wired into the
SCAN_ARTWORK_PRIORITY loop, /search/roms integration, MatchRom dialog
chip + cover selection, and a heartbeat flag.

Matching is exact case-insensitive against the directory listing first
(so a ROM named "(Europe)" lands on the (Europe) artwork), with a
JaroWinkler fuzzy fallback at 0.8 that strips parenthetical tags from
both sides. Listings are cached in Redis with a 24h TTL.

`libretro_id` is persisted on the Rom model as the SHA1 hex of the
matched libretro filename — stable across scans, distinct per region,
indexed for lookup. Migration 0077 adds the column.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:57:20 -04:00
Georges-Antoine Assi 3991e1b6ed fix: scan stalls on platforms with 10k+ already-scanned ROMs
The scan was spending excessive time on large platforms even when all ROMs
were already scanned. Root causes: per-ROM UPDATE queries for skipped ROMs
(10k individual writes), missing composite index on (platform_id, fs_name)
causing full table scans, NOT IN clauses with 10k+ values in
mark_missing_roms(), and redundant filesystem reads.

Changes:
- Add bulk_mark_present() for batch-updating skipped ROMs in one query
- Move skip detection from _identify_rom to the batch loop so skipped ROMs
  never enter the async scan pipeline, and report progress for them
- Add composite index idx_roms_platform_id_fs_name via migration 0077
- Rewrite mark_missing_roms() with flip-based approach: mark all missing,
  then un-mark present ones in chunks of 1000
- Cache filesystem reads in scan_platforms() to avoid double directory
  traversal (precounting + scanning)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 19:20:40 -04:00
Georges-Antoine Assi 686c609b3a Merge pull request #3155 from tmgast/feature/play-session-ingest
Add play session ingest for game time tracking
2026-04-11 11:23:02 -04:00
Georges-Antoine Assi 6db9d45928 actually fix 2026-04-07 22:53:44 -04:00
Georges-Antoine Assi 5529fdad7b Merge branch 'master' into romm-3232 2026-04-07 22:39:22 -04:00
Georges-Antoine Assi f227a3145d changes from bot review 2026-04-07 22:32:40 -04:00
Georges-Antoine Assi 6c88e098ba [ROMM-3232] Fix content_hash not updated 2026-04-07 21:48:51 -04:00
Alex Vanderveen 6aca8fdfcf Parse Content-Type essence before validating resource downloads
Problem
_check_content_type used the full Content-Type header string (lowercased) and matched it with startswith(...) against allowed prefixes.

That is mostly fine when the server sends a bare type like application/pdf. It breaks down when vendors send parameters on the same header (e.g. name="…", charset=…). In theory application/force-download; name="…" should still start with application/force-download, but in practice you can get:

Leading whitespace or a UTF‑8 BOM before the type token, so the string no longer starts with your prefix even though the MIME type is correct.
Confusing logs: logging only the lowercased full header is fine, but the decision should be based on the standardized MIME essence (type + subtype, no parameters), which is what other stacks use for “what is this?”
So the fix is to parse the header the usual way and only then apply your allowlist.

What changed
_content_type_essence(header_value)

Takes everything before the first ; (the essence).
Strips whitespace, lowercases, strips a leading BOM (\ufeff) so odd clients/proxies don’t break the check.
_check_content_type

Reads the raw content-type header once.
Runs startswith on the essence, not on the full header with parameters.
Rejects if the essence is empty (missing or useless header).
Logging uses the raw header string (or (missing header)), so operators still see exactly what the server sent.
Call sites and allowed prefixes (image/, application/pdf, etc.) are unchanged; only how the string is normalized before comparison changes.

Security / SSRF
This does not replace URL / SSRF controls; it only makes post-fetch type checking consistent with how Content-Type is defined (essence vs parameters). You are not widening the allowlist—same prefixes, stricter handling of “empty” and clearer matching on the actual type token.

Risk / regression
Low: same allowed prefixes, strictly more tolerant of benign formatting (whitespace, BOM, parameters). The only stricter case is empty essence after strip (e.g. malformed header), which correctly fails the check.

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

I have reviewed the proposal and these edits will handle cases where the string we match against for the content_type is cleaned up more before comparing against the allow list of content_types.

I have tested this, and confirm that I do not get any errors loading PDFs for game manuals using this.  Please consider this, as this should be compatible with the existing content type allowlist, and easily work with any new types added to it.
2026-04-07 20:25:55 -04:00
Georges-Antoine Assi b36a8b0cdf manual types should support octet-stream and force-download response 2026-04-07 14:22:43 -04:00
Georges-Antoine Assi 4e5ee69343 more simplify 2026-04-06 22:29:55 -04:00
Georges-Antoine Assi b2e1e094f7 some simplification 2026-04-06 21:53:49 -04:00
Georges-Antoine Assi ae03b0b322 ingest playtime with sync sessions 2026-04-06 21:32:13 -04:00
Georges-Antoine Assi e5cb29ae80 Merge branch 'master' into feature/play-session-ingest 2026-04-06 12:42:15 -04:00
Georges-Antoine Assi 2dc1678931 changes from bot review 2026-04-06 11:22:44 -04:00
Georges-Antoine Assi f2619ac0d1 Merge branch 'master' into pegasus-metadata-export 2026-04-06 11:06:08 -04:00
Georges-Antoine Assi 21eee327b0 Merge branch 'master' into save-sync 2026-04-06 09:09:53 -04:00
Georges-Antoine Assi 26387c3bc1 fix check 2026-04-06 09:09:46 -04:00
Georges-Antoine Assi da3cd453e2 thing 2026-04-05 22:41:34 -04:00
Georges-Antoine Assi a61ff81e22 Merge branch 'master' into gamelist-customize 2026-04-05 22:11:02 -04:00
Georges-Antoine Assi 9b5feefb4f fix migrations 2026-04-05 22:06:04 -04:00
Georges-Antoine Assi f2e8e337b2 Merge branch 'master' into save-sync 2026-04-05 21:47:53 -04:00
Georges-Antoine Assi 8664f6203f bad removal 2026-04-05 19:19:06 -04:00
Georges-Antoine Assi e0214f100d more bot cleanup 2026-04-05 19:15:33 -04:00
Georges-Antoine Assi 7c41fb5bac revert fs_name sibling roms 2026-04-05 17:57:48 -04:00
Georges-Antoine Assi 6abf60b0fe last fixes 2026-04-03 11:49:32 -04:00
Georges-Antoine Assi bae0d0c698 more changes from rview 2026-04-03 11:29:55 -04:00
Georges-Antoine Assi ceddf2f9e9 changes from bot review 2026-04-03 11:24:02 -04:00
Georges-Antoine Assi ec8583016b mega ton of fixes for 4.8 2026-04-03 10:54:31 -04:00
Vargash f4c965be33 feat: add new export.gamelist.media.image configuration
also fix broken title_screen/miximage/physical key name usage
2026-03-31 11:16:40 +02:00
Eric Daras 8540514b46 add outbound HTTP proxy support for backend requests 2026-03-27 07:02:14 +01:00
Georges-Antoine Assi a3ebe16a39 run fmt 2026-03-24 15:32:48 -04:00