CI runs pytest without root, so the new /var/lib/romm/sync default
fails when FSSyncHandler tries to mkdir its base path at import time.
The other handlers stay writable in tests because they derive from
ROMM_BASE_PATH=romm_test (relative); pin SYNC_BASE_PATH the same way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shell fallback was assigned locally but never exported, so
sync_watcher.py and the Python config layer never saw the resolved
value. They happened to land on the same /var/lib/romm/sync default by
coincidence; export it so the shell and Python defaults stay linked
through a single source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The prod Dockerfile creates /var/lib/romm/sync at build time, but if a
user overrides SYNC_BASE_PATH to a path that doesn't exist (or runs the
dev entrypoint, which never created the default), watchfiles fails to
start because its target directory is missing. Have both entrypoints
mkdir -p the resolved path before handing it to watchfiles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the sync staging folder out of ROMM_BASE_PATH so it lives on a
dedicated writable mount. This lets the container run with a read-only
root filesystem without losing in-flight save uploads, and keeps
app-owned state separate from the user-curated library volume.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The init script ran `sed -i` against /etc/gunicorn/logging.conf, which
fails both on read-only root filesystems and when the container runs
as a non-root UID (since /etc/gunicorn is not chmod'd writable). Copy
the config to /tmp/gunicorn/logging.conf at startup and edit/use that
copy instead, leaving the image file untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the tuple+derived-dict pair with PLAYMATCH_TAG_TO_ATTR as the
canonical mapping. Rename enum members to UPPER_CASE, expand
PlaymatchRomMatch to cover all provider ids, and inline the fallback
match in place of the _empty_playmatch_rom_match helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover the local-export flow end to end: redirect resource and library
base paths into a tmp_path sandbox, run export_platform_to_file, and
assert that media files land at <platform>/assets/<subdir>/<rom>.<ext>
and that gamelist.xml references them. A second test withholds source
files to verify that failed copies omit their tags from the XML while
other assets still export.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the pegasus exporter pattern: collect each ROM's media into a
canonical asset-kind dict, then either copy the files under
<platform>/assets/<subdir>/ for local exports or build absolute URLs
from request.base_url for remote exports. Exclude the generated assets/
directory from filesystem scans.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use URLPath.make_absolute_url with request.base_url to build resource
URLs for non-local exports, and simplify the local-export traversal
check with Path.is_relative_to.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a ROM is rescanned and its file IDs change, disc IDs stored in
localStorage become stale. This caused file_ids query params with invalid
IDs to be sent to /api/roms/{id}/content/{name}, resulting in 404 errors.
Validate the stored disc ID against the actual ROM files before use. If
stale, clear localStorage and fall back to the first available file.
Agent-Logs-Url: https://github.com/rommapp/romm/sessions/3579d577-13ff-4288-9a9c-909b6f891c9e
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
Covers now use object-fit: contain inside a fixed slot, so each image
keeps its own aspect ratio (letterboxed where needed) instead of being
cropped to the platform's configured ratio. The slot itself still uses
a single default aspect ratio for grid stability, so cards don't reflow
as images load.
With per-platform aspect ratio no longer doing anything visible, remove
the Cover style picker from the platform info drawer and clean up dead
plumbing: galleryViewStore.getAspectRatio no longer reads
platform.aspect_ratio, platformId is dropped from Skeleton / SearchCover
/ MatchRom / showSearchCoverDialog, and the orphaned i18n keys
(cover-style, settings, old-squared-cases, old-horizontal-cases) are
stripped from all platform.json locale files.
Backend aspect_ratio column is left in place; no client sends it anymore.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>