Repository navigation
Fix bugs and slow paths found in a codebase-wide review - #21
Merged
Merged
Conversation
- Screenshot dedup scans/deletes under IMAGE_PATH, not the bundle STATIC_PATH - Multi-disc scan lists each disc file once (case-insensitive FS double-count) - Default-controller auto-assign tries longest folder pattern first (snes != nes) - /api/controllers: one systems query instead of one per controller - Calculate storage: one batched write instead of a commit per game - Local search clamps negative limit; similar-games escapes LIKE wildcards - AI Fill invalidates the Library filter cache - Case-only ROM renames allowed (samefile check) - Database optimize closes its connection when VACUUM fails - ROM-tools task cleanup iterates a snapshot; browse-folders single scandir Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
services: - Clear scraped data commits the filename-title reset (close() rolled it back) - Analytics "not started" sums NULL and 'not_started' groups - query/execute helpers close connections they open outside an app context - JPEG save converts P/LA/PA/I/F modes, not only RGBA - Orphan-media scan looks basenames up in a set (was orphans x references) jobs: - Per-system RA refresh accepted system_name (TypeError -> 500 before) - Swapped/demoted bulk scrapes drop their 'queued' job_queue row - Bulk scrape settings load inside the try (no stuck 'running' job) - HLTB / alt-titles jobs commit per game, not holding the writer lock across 25 network calls - Player-core worker re-runs a request that raced its shutdown - Museum image fetch: import/connect failures end the job cleanly - Recovery dedup no longer collapses distinct multi-system scrapes - GUI installer surfaces worker errors; .desktop Exec paths are quoted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
- Card grid: one-column phone layout no longer gets inline row lines; re-layout when a resize changes the column count; measure all cards before placing (one forced layout instead of one per card) - Game detail modal: late replies no longer paint a closed/other game; error message no longer replaces the spinner for good; AI Fill only reopens the game still shown - Job toasts: a slow status reply is no longer aborted by the next poll - Stale-reply guards: museum status, version search, log viewer, official-names proposals; Kill closes the Now-playing popover - CHD converter/verify escape disk file names in innerHTML - Replace undefined CSS variables with existing tokens (skip-link focus outline, settings tab bar, rename modal backgrounds were invisible) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
- The last active admin can no longer be demoted or deactivated - /api/psn/games clamps page/per_page (per_page=0 was a 500) - Steam/Xbox/PSN library fetch: duplicate check uses one index built from one query instead of re-reading the system per fetched title - /trophies reads and cleans PlayStation titles once, not per trophy set; PSN full sync cleans each title once - PSN single-game sync writes trophies in one batch; detail-page debug logging moved from INFO to DEBUG - Collector trophies refresh: one read and one transaction (~140 round trips before); sidebar rank cached per request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
- IGDB age ratings read IGDB's single global rating enum (CERO/USK/GRAC/ ClassInd/ACB were mapped as 1-5) and the newer organization / rating_category shape; hybrid fetch requests those fields - IGDB players: null offlinemax no longer aborts the merge; no made-up 1 - ScreenScraper primary reads the flat get_game_info dict on a cache miss - ScreenScraper media extensions come from the URL path, not the query string (which carried account credentials into file names) - TGDB lookup tables: a failed fetch is retried, not cached empty; unknown IDs are dropped instead of written as bare numbers - TGDB / RAWG / ES-DE no longer invent players=1 (ES-DE overwrote curated players/modes through COALESCE) - ES-DE gamelist: full-path match wins over a same-filename match - RetroAchievements: curly apostrophes normalised; search title once - Scans keep an existing system logo; scan_roms finds SVG logos Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
- all-games-controller: the resize handler called debounce() at load, before the deferred core bundle defines it, so the Library and System pages threw and rendered no cards. Use a local timer. - The column count read from computed grid-template-columns includes the implicit tracks the cards' own inline placement created, so a grid narrowed to one column (phone) kept laying out two columns with a huge gap. A full layout clears inline placement first; a ResizeObserver on the grid re-lays out once its width settles (a window-resize timer fired mid sidebar animation and read the wrong count). - screenscraper_password / screenscraper_devpassword join SECRET_API_KEY_FIELDS: masked on GET /api/scraper-settings and the settings page, kept on a masked echo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
- /api/jobs/dismiss checks the same role / ownership as resume (_may_manage_job, shared); any signed-in user could dismiss any job - The dashboard recovery banner leaves out jobs the user may neither resume nor dismiss (a player no longer sees an interrupted bulk scrape) - Demoting or deactivating the last admin gets its own message; catalogs regenerated - CLAUDE.md: a release needs at least 5 changelog items, no upper limit (user, 2026-10-27) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013s5ztLFVCcdTjjTeVcTnJr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
This PR fixes about 55 bugs, slow code paths and cleanups found by reviewing routes, services, background jobs, scrapers, JS, CSS and templates. Each fix is small and has its own commit for its area. Nearly every behavioural fix has a regression test, in four new test files (
tests/test_*review*.py,tests/test_scraper_lookup_defaults.py) and in additions totests/test_metadata_merger.py. The changes go into the open 3.33.0 changelog entry, which now has 10 items.Why
The review asked for bugs, performance, refactoring and efficiency fixes. The most important user-facing problems fixed:
close()withoutcommit()undid it.TypeError(a 500 error) every time.<players>intoplayers=1/Single-Player, and wrote that over curated values. A missing<players>was always replaced with 1, soCOALESCEcould not keep the old value.STATIC_PATHinstead ofIMAGE_PATH, so it found nothing in the packaged app.GET /api/scraper-settingsand on the settings page.queuedrows that came back as "interrupted" after a restart;running;How
/api/controllers, the Steam/Xbox/PSN library duplicate check,/trophiesmatching, the collector-trophies refresh and the orphan-media scan.execute_manywhere the code committed once per row: storage calculation, PSN trophy sync and collector trophies.g.query/executenow close connections they open outside an app context. Inside a request nothing changes.ResizeObserveron the grid re-lays it out once its width settles. A window-resize timer fired while the sidebar was still animating and read the wrong column count.docs/specs/jobs.md§8 is updated for the multi-system scrape dedup change.Each fill-only
UPDATEin the scrapers was checked and already usedCOALESCE. The fixes here remove the made-up defaults that slipped past it.Mandatory-workflow checklist
APP_LAST_UPDATEwas bumped inconfig.example.py(config.pyis gitignored).pytest -n 8: 2269 passed (the baseline was 2225 passed, 1 skipped).requirements.txtwas not changed.Verification
check_i18n_fresh.pypassed after the JS rebuild; the only later JS change adds not()strings, andservices/js_i18n_strings.pyis unchanged./gamesin Chromium at 1400px, 900px and 375px, then back to 1400px.debounceused before the deferred core bundle loads, so no cards rendered). It is fixed in41415e2./settings,/systems,/logs,/analytics,/museumand/collector-trophiesall return 200 with no page errors. The settings tab bar now has a background and the skip-link focus outline is visible.Not verified:
organization/rating_categorytext mapping is based on IGDB's documented tables and has not been checked against live responses. The legacy code table is IGDB's documented enum.Found but not fixed (left for a follow-up):
/api/jobs/dismisshas no ownership check, unlike resume.templates/settings.htmlinserts admin-entered names viainnerHTMLwithout escaping.IMAGE_PATHare not picked up by refresh-logos.This PR touches several subsystems, so consider running
/code-review ultrabefore merging.Generated by Claude Code