Commit Graph

9543 Commits

Author SHA1 Message Date
Georges-Antoine Assi
b9b82c751b more cleanup 2026-05-25 11:07:03 -04:00
Georges-Antoine Assi
9a87348c22 cleanup 2026-05-25 11:02:35 -04:00
Claude
95b1a99f2a perf(roms): avoid hydrating full Rom rows for siblings on list endpoint
The paginated ROM list eager-loaded sibling_roms via selectinload, which
hydrated full Rom ORM instances (including heavy JSON metadata columns)
for every sibling even though only an existence/count check was needed
on the frontend. On large collections this dominated request latency.

Split sibling handling by response shape:
- SimpleRomSchema (list): siblings is now list[int]; populated per page
  by a single SELECT against the sibling_roms view projecting only
  (rom_id, sibling_rom_id) — no Rom row hydration.
- DetailedRomSchema (detail): keeps full SiblingRomSchema objects, with
  load_only on (id, name, fs_name_no_tags, fs_name_no_ext) so sibling
  rows stop dragging in JSON metadata.

Frontend usage already only consumes siblings.length on list views; the
detail-page VersionSwitcher continues to receive the richer schema.
2026-05-24 23:17:34 +00:00
Georges-Antoine Assi
63644d0c6f Merge pull request #3426 from rommapp/claude/loving-darwin-pveIr
Defer optional handler initialization with lazy factories
2026-05-24 16:18:24 -04:00
Georges-Antoine Assi
bb2b99099b fix(sync): isolate SSH handler init errors to per-device failure
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:12:10 -04:00
Georges-Antoine Assi
5342f96863 remove comment 2026-05-24 16:04:10 -04:00
Georges-Antoine Assi
0eec8b0e47 Merge pull request #3424 from rommapp/copilot/fix-csrf-token-issue
Refresh CSRF cookie on OIDC session authentication changes
2026-05-24 15:57:30 -04:00
Georges-Antoine Assi
be476cb7dc Only set CSRF cookie on http.response.start
ASGI spec only allows headers on the http.response.start message;
appending Set-Cookie to body messages is out-of-spec and may break on
some servers. Early-return for non-start messages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:46:50 -04:00
Georges-Antoine Assi
79127696e5 Merge pull request #3423 from rommapp/copilot/fix-boot-loop-opentelemetry
Guard init startup against OpenTelemetry boot-loop conditions
2026-05-24 15:29:51 -04:00
Georges-Antoine Assi
354207d683 Drop redundant out_arr reset in otel_prefix
All callers declare a fresh `local -a wrap=()` before invoking, so the
in-function reset is unnecessary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:17:42 -04:00
Georges-Antoine Assi
a4f1f516bc Simplify OTEL wrapper helper and fix shell quoting
Collapse `otel_prefix` and `otel_prefix_str` into a single nameref-based
helper. Watchfiles call sites embed the array as a shell-quoted prefix
via `${wrap[*]@Q}`, which also fixes a quoting bug where an
`OTEL_SERVICE_NAME_PREFIX` containing a single quote would produce an
invalid command string and break the watcher.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 14:57:52 -04:00
Claude
f434fde772 Refactor OTEL wrapping into shared helpers
Collapse the duplicated OTEL_SDK_DISABLED / opentelemetry-instrument
branches in run_startup, start_bin_gunicorn, start_bin_watcher, and
start_bin_sync_watcher into two small helpers:

- otel_prefix: emits the wrapper as NUL-delimited argv tokens (for
  direct process invocation).
- otel_prefix_str: emits the wrapper as a shell-string prefix (for
  embedding inside `watchfiles --target-type command`).

Each call site becomes a single command instead of a 2- or 3-way
branch with a fully duplicated command body. As a side effect, the
watcher functions now also gain the `command -v opentelemetry-instrument`
fallback that the gunicorn/startup paths added.
2026-05-24 18:21:40 +00:00
Georges-Antoine Assi
98d03a5720 Merge pull request #3422 from rommapp/copilot/fix-folder-tag-import-issue
Support `<folder>` entries in gamelist.xml metadata import
2026-05-24 14:14:14 -04:00
Georges-Antoine Assi
8af556ee46 run fmt 2026-05-24 14:07:45 -04:00
Georges-Antoine Assi
acc1e630b7 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-24 14:05:58 -04:00
Georges-Antoine Assi
4d8c2ef54b Merge branch 'master' into copilot/fix-folder-tag-import-issue 2026-05-24 13:33:36 -04:00
Georges-Antoine Assi
ae09707197 Merge pull request #3421 from rommapp/copilot/fix-gamelist-xml-import
Handle malformed ES-DE `<alternativeEmulator>` tags during gamelist.xml import
2026-05-24 13:31:32 -04:00
Georges-Antoine Assi
e7bde8a190 simplify 2026-05-24 13:13:11 -04:00
Georges-Antoine Assi
5fdb69d53c fixes 2026-05-24 13:09:07 -04:00
Claude
26bdc11e13 refactor(filesystem): lazy-init launchbox + sync handlers, drop tolerate_missing_base
Apply the same lazy-factory pattern to FSLaunchboxHandler and FSSyncHandler
that ssh_sync_handler now uses. With both opt-in features deferred to
first-use, the tolerate_missing_base escape hatch on FSHandler is no longer
needed — every handler now fails loudly on mkdir failure, which is the
right behavior for the always-on core paths (assets, library, resources).

Touched call sites:
  - resources_handler._resolve_local_file_uri (launchbox)
  - sync_watcher.py, endpoints/device.py, tasks/manual/sync_folder_scan.py
    (fs_sync)

Net effect:
  - Default installs never poke /romm/launchbox or /romm/sync at startup.
  - Misconfigured opt-in users get a clear, actionable PermissionError at
    the call site instead of a silent warning followed by mystery failures.
  - tolerate_missing_base, its tests, and one stale log import are gone.
2026-05-24 14:59:03 +00:00
Claude
1890958ff2 refactor(sync): make SSH sync handler lazy-initialized
The module-level SSHSyncHandler() singleton ran filesystem side effects
(mkdir on SYNC_SSH_KEYS_PATH) at import time, which meant even default
installs with push-pull sync disabled would touch /romm/sync — and the
previous tolerate-and-warn fallback could leave users wondering why
sync silently does nothing.

Replace the eager singleton with a functools.cache'd factory. The
handler is now constructed on first use, so:

  - Default-install users (ENABLE_SYNC_PUSH_PULL=false, no manual sync
    triggered) never touch /romm/sync.
  - Users opting in get a clear, actionable RuntimeError pointing at
    the unwritable path and the env var to override, at the call site
    rather than buried in a startup stack trace.

Also document in env.template that enabling either sync mode requires
a writable volume at $ROMM_BASE_PATH/sync.
2026-05-24 14:49:53 +00:00
copilot-swe-agent[bot]
f94206aa53 Refresh CSRF cookie when auth user changes
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-24 14:46:31 +00:00
copilot-swe-agent[bot]
dad72fe946 Initial plan 2026-05-24 14:40:02 +00:00
Claude
358e91d33c fix(sync): tolerate unwritable SSH keys directory at startup
The SSH sync handler is instantiated at module import time, so any
PermissionError on mkdir would crash the entire app rather than just
disabling the push-pull sync feature. This affected users whose /romm
mount didn't include a writable sync subdirectory (common on Unraid
and similar setups that mount specific subpaths).

Mirror the FSHandler pattern: log a warning and continue. Keys are
expected to be pre-mounted per the module docstring, and
_resolve_key_path already handles a missing directory gracefully.

Fixes #3419
2026-05-24 14:36:14 +00:00
copilot-swe-agent[bot]
03d869e859 Fix gamelist parsing with alternativeEmulator fallback
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-24 14:15:24 +00:00
copilot-swe-agent[bot]
84a0dd9ef2 Conditionally disable opentelemetry wrapper during init startup
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-24 14:14:32 +00:00
copilot-swe-agent[bot]
9611ebedb4 Support folder tags in gamelist metadata import
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
2026-05-24 14:11:01 +00:00
copilot-swe-agent[bot]
5a38244140 Initial plan 2026-05-24 14:07:36 +00:00
copilot-swe-agent[bot]
5fbdd04472 Initial plan 2026-05-24 14:06:40 +00:00
copilot-swe-agent[bot]
e81337be53 Initial plan 2026-05-24 14:06:26 +00:00
Georges-Antoine Assi
1240902ba6 Merge pull request #3417 from Spinnich/fix/screenscraper-skip-name-search-after-notgame
fix(screenscraper): skip name search after notgame hash lookup
2026-05-23 21:01:54 -04:00
Georges-Antoine Assi
22666631e9 Merge branch 'master' into fix/screenscraper-skip-name-search-after-notgame 2026-05-23 20:54:19 -04:00
Georges-Antoine Assi
599ccb5f14 refactor(screenscraper): return notgame flag as tuple from lookup_rom
Instead of smuggling an internal control flag through the SSRom dict,
lookup_rom now returns (SSRom, is_not_game: bool). scan_handler unpacks
the tuple and short-circuits the name-search fallback when either an
ss_id matched or the hash lookup flagged the entry as notgame.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:52:43 -04:00
Georges-Antoine Assi
5c980d82cc fix(screenscraper): wire notgame flag through to scan_handler
The previous commit added a notgame skip-name-search check in scan_handler
but used a different key ("notgame") than what lookup_rom returned
("not_game"), so the fallback was never actually skipped. Align both on
SSRom.not_game, pop the internal flag before returning the SSRom to the
rest of the scan pipeline, and rename the helper to _is_not_game for
consistency with the field name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:22:00 -04:00
Georges-Antoine Assi
1e66436ca1 Merge pull request #3414 from Spinnich/fix/screenscraper-add-user-credentials-media-endpoints
fix(screenscraper): re-add user credentials to media file downloads
2026-05-23 19:57:08 -04:00
Georges-Antoine Assi
d9040b29cf fix(screenscraper): re-add user credentials in rom update path + tests
The rom update endpoint downloads SS media via store_media_file without
re-attaching the user credentials that were stripped before storage, so
edit/update flows fall back to the anonymous quota. Wrap the URL with
add_ss_auth_to_url at the call site, matching the existing scan path.

Add unit tests covering the new URL helpers and edge cases:

- test_base_handler: strip/restore sensitive query params, including
  case-insensitive key matching, no-duplicate restoration, special-char
  encoding, blank values, and URL-component preservation.
- test_ss_handler: add_ss_auth_to_url honors empty user/password,
  doesn't duplicate pre-existing creds, handles the storage-shaped
  stripped URL, and round-trips with extract_media_from_ss_game so the
  stored URL never carries user creds while the download URL uses the
  current runtime creds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 19:50:23 -04:00
Georges-Antoine Assi
ca6ecff4a0 cleanup 2026-05-23 19:07:43 -04:00
Georges-Antoine Assi
dce7101b4f Merge pull request #3407 from rommapp/playmatch-metadata-souce
Add playmatch as explicit metadata source
2026-05-23 13:20:56 -04:00
Spinnich
2ae6e1c07c fix(screenscraper): skip name search after notgame hash lookup
When jeuInfos.php returned a notgame entry (BIOS files, ZZZ hacks, etc.),
lookup_rom() had no notgame check, leading to two bugs:
- notgame entries with a real ID were stored as valid SS matches
- notgame entries with a falsy ID fell through to a jeuRecherche.php name
  search that always returned nothing (pointless quota usage)

Adds _is_notgame() and NOTGAME_NAME_PREFIX, returns SSRom(notgame=True)
from lookup_rom() on a notgame hit, and guards the get_rom() fallback in
scan_handler so the name search is skipped entirely. Also adds the missing
notgame filter to _search_rom() so ZZZ entries can't match by name either.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 17:01:41 +00:00
Spinnich
aec1c4ed33 fix(screenscraper): re-add user credentials to media file downloads
SS media URLs are stripped of ssid/sspassword before DB storage (correct),
but downloads were issued against the credential-less stored URLs, causing
them to count against the anonymous IP quota instead of the user's account.

Adds restore_sensitive_query_params() as the principled complement to
strip_sensitive_query_params(), and add_ss_auth_to_url() in ss_handler
which re-attaches credentials at download time without storing them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 15:27:43 +00:00
Georges-Antoine Assi
715ec658bd changes from bot review 2026-05-23 07:37:23 -04:00
Georges-Antoine Assi
d806676d12 add to setup 2026-05-22 22:36:21 -04:00
Georges-Antoine Assi
4724f4538a add to watcher 2026-05-22 22:35:30 -04:00
Georges-Antoine Assi
4a89dadb55 update scna pages 2026-05-22 22:32:33 -04:00
Georges-Antoine Assi
18ecc1f374 use intersection 2026-05-22 21:56:10 -04:00
Georges-Antoine Assi
fa0b7324c9 Merge pull request #3406 from rommapp/wipe-resources-complete-rescan
fix: wipe downloaded resources on COMPLETE rescan
2026-05-22 21:51:23 -04:00
Georges-Antoine Assi
aed926ac9e Add playmatch as explicit metadata source 2026-05-22 21:44:33 -04:00
Georges-Antoine Assi
da5675dca5 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-22 21:14:16 -04:00
Georges-Antoine Assi
c73899a884 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-22 21:13:22 -04:00
Georges-Antoine Assi
a5ed1d95ea fix: wipe downloaded resources on COMPLETE rescan
A COMPLETE rescan never deleted previously downloaded asset files, and
the post-scan download steps skip work when a file already exists
(store_media_file) or when the source URL is unchanged (get_cover). This
meant covers, screenshots, manuals, and SS/gamelist/LaunchBox extended
media were reused even when a fresh fetch returned a different URL —
defeating the region-priority fix in #3396.

- scan socket: for a COMPLETE rescan of an existing ROM, remove the
  cover, manual, screenshots, and all extended media directories before
  re-fetching, so the download steps pull fresh files.
- scan_handler: reset url_cover/url_screenshots/url_manual and the
  matching path_* fields for COMPLETE rescans before the priority loops
  run, so stale DB values are nulled when no selected source supplies
  them (clearing for deselected sources falls out as a subset).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 17:32:02 -04:00