Commit Graph

9252 Commits

Author SHA1 Message Date
Georges-Antoine Assi
f5474ee588 fix(tests): bump pytest-asyncio for pytest 9 compatibility
pytest-asyncio 0.23 is incompatible with pytest 9 (it calls the removed
Package.obj attribute, causing an INTERNALERROR during collection).
Bump to ~= 1.1 and set asyncio_default_fixture_loop_scope to silence the
1.x deprecation warning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:43:53 -04:00
Georges-Antoine Assi
350fc8dd84 tiny fix 2026-04-15 19:20:17 -04:00
Georges-Antoine Assi
7ce1a9faca Merge pull request #3266 from Spinnich/fix/gitattributes-line-endings
fix(git): enforce LF line endings for shell scripts via .gitattributes
2026-04-15 18:49:40 -04:00
Georges-Antoine Assi
341709674a tiny fix 2026-04-15 18:39:17 -04:00
Georges-Antoine Assi
833cf28be9 Merge pull request #3264 from Namaneo/fix-download
Fix download failing when hash has not been computed
2026-04-15 18:37:30 -04:00
Georges-Antoine Assi
e5f2e1fcf0 Merge pull request #3268 from rommapp/dependabot/uv/pytest-9.0.3
chore(deps): bump pytest from 8.4.1 to 9.0.3
2026-04-15 15:58:01 -04:00
dependabot[bot]
64330f397f chore(deps): bump pytest from 8.4.1 to 9.0.3
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.1 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.1...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 19:46:30 +00:00
Georges-Antoine Assi
66cad62b4d Merge pull request #3267 from rommapp/dependabot/npm_and_yarn/frontend/follow-redirects-1.16.0
chore(deps): bump follow-redirects from 1.15.11 to 1.16.0 in /frontend
2026-04-15 15:36:10 -04:00
dependabot[bot]
d501799bba chore(deps): bump follow-redirects from 1.15.11 to 1.16.0 in /frontend
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-15 19:14:20 +00:00
Spinnich
e84ac3c46a fix(git): add eol=lf for .gitattributes itself and renormalize to LF
The .gitattributes file was stored with CRLF endings, which prevents
git from parsing its rules reliably on Linux/macOS. Add a self-referential
eol=lf rule and renormalize the file to LF in the index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 11:53:14 -04:00
Spinnich
077296e978 fix(git): enforce LF line endings for shell scripts via .gitattributes
Windows developers with core.autocrlf=true (or the default) will have
git check out shell scripts with CRLF endings. When those files are
copied into a Linux Docker container they fail to execute:

  exec /entrypoint.sh: no such file or directory

Adding eol=lf attributes for *.sh, entrypoint.sh, and
docker/init_scripts/init ensures the repository always stores and checks
out these files with Unix line endings, regardless of platform or
git config.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:08:02 -04:00
Georges-Antoine Assi
04d21e8c18 Merge pull request #3263 from Spinnich/fix/collection-race-condition
Fix race condition in collection and favorite rom membership updates
2026-04-15 09:01:45 -04:00
Julien Loir
97e5bbe1cc Fix download failing when hash has not been computed 2026-04-15 13:50:48 +02:00
Spinnich
e8ec8a0b18 fix(linting): fix import order in AddRoms.vue and RemoveRoms.vue
Move @/__generated__ import after third-party modules to satisfy
trunk's @trivago/prettier-plugin-sort-imports ordering rules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 06:56:07 -04: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
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
7724aabb95 Merge pull request #3259 from rommapp/copilot/add-indices-to-roms-rom-files
Add missing index on `rom_files(rom_id)` to fix slow game listing on large ROM sets
2026-04-14 09:09:43 -04:00
copilot-swe-agent[bot]
ce5624b470 Add index on rom_files(rom_id) to fix PostgreSQL performance issue
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/7bb628cb-282d-44c3-b912-9c81212611a9

Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-04-14 02:35:19 +00:00
copilot-swe-agent[bot]
99aa32ee44 Initial plan 2026-04-14 02:30:58 +00:00
Georges-Antoine Assi
35df20b97a Merge pull request #3257 from rommapp/dependabot/uv/pillow-12.2.0
chore(deps): bump pillow from 12.1.1 to 12.2.0
2026-04-13 20:39:56 -04:00
dependabot[bot]
4b71e0aab0 chore(deps): bump pillow from 12.1.1 to 12.2.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.1.1...12.2.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.2.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-13 23:39:05 +00:00
Georges-Antoine Assi
3388b3413b Merge pull request #3254 from rommapp/romm-3251
feat: show TheGamesDB link on platform drawer when tgdb_id is set
2026-04-12 19:49:41 -04:00
Georges-Antoine Assi
154165fadf final fixes 2026-04-12 19:26:34 -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
fe4d78ae3f Merge pull request #3253 from rommapp/romm-3250
perf: skip RAHasher subprocess for archived disc-platform ROMs
2026-04-12 18:49:50 -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
dbb455ece7 Merge pull request #3248 from rommapp/copilot/fix-collapsible-menu-overflow
Fix settings drawer logout button overflowing on mobile
2026-04-12 16:29:48 -04:00
Georges-Antoine Assi
85f01f70cc Merge pull request #3252 from rommapp/libretro-handler
feat: add libretro thumbnails as an artwork source
2026-04-12 16:26:11 -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
b3c79ab6ff fix migration order 2026-04-12 10:15:53 -04:00
Georges-Antoine Assi
6df31e2cc0 Merge branch 'master' into libretro-handler 2026-04-12 10:14:26 -04:00
Georges-Antoine Assi
59b5eefad9 Merge pull request #3249 from rommapp/10k-games-scan
fix: scan stalls on platforms with 10k+ already-scanned ROMs
2026-04-12 10:13:21 -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