atob() decodes base64 to a JS string, which r.return() then re-encodes
as UTF-8. For filenames with non-ASCII characters (e.g. Pokémon), bytes
above 0x7F get double-encoded — serving different content than what the
backend computed the CRC32 over, causing mod_zip to report CRC failure
on the .m3u file.
Buffer.from(value, 'base64') decodes directly to a byte array and
r.return() sends it verbatim, matching the CRC exactly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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.
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>
The gunicorn logging config hard-coded level=INFO, ignoring LOGLEVEL.
Patch it at startup via sed. Also pass --logging_level to rq worker
and rqscheduler so their framework logging respects LOGLEVEL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When OTEL_SDK_DISABLED=true (set automatically when no OTEL_ env vars
are present), the opentelemetry-instrument wrapper does not properly
pass through the WATCHFILES_CHANGES environment variable to watcher.py.
This causes the filesystem watcher to silently fail - watchfiles detects
changes but watcher.py receives an empty WATCHFILES_CHANGES and exits
immediately without scheduling any rescans.
The fix skips the opentelemetry-instrument wrapper when OTEL is disabled,
allowing watchfiles to pass WATCHFILES_CHANGES directly to watcher.py.
Fixes automatic rescan on filesystem change for users who don't configure
OpenTelemetry (the majority of self-hosted deployments).