Commit graph

19 commits

Author SHA1 Message Date
Broque Thomas
a33faaeb38 fix(docker): pre-bake /app/Stream so basic-search playback works on rootless Docker
`core/streaming/prepare.py:94-97` creates /app/Stream lazily via
`os.makedirs(stream_folder, exist_ok=True)` on first playback. Under
standard Docker this works because the container's `root` writes /app
without restriction. Under rootless Docker / Podman the in-container
soulsync UID maps to a host UID that can't write to /app, so the
mkdir silently fails and the streaming "Play" flow errors out with
no obvious user-facing cause.

Same root cause + same fix shape as the May 2026 /app/Staging restart-
loop fix — pre-bake the directory at image build time (when the layer
is owned by root), and thread it through every entrypoint.sh spot that
touches the canonical app-dir list.

Not added to VOLUME — /app/Stream is a transient single-file cache
(cleared on every new playback), no persistence value.

Touched lines:

- Dockerfile: mkdir + chown line that pre-bakes runtime dirs.
- entrypoint.sh: the recursive chown gated on UID change, the always-runs
  mkdir + chown, and the writability audit loop.

No code change. Streaming tests pass unchanged (they use tmp_path, not
/app/Stream).
2026-05-19 19:30:54 -07:00
Antti Kettunen
ed8ff46c9c
Build webui assets in multi-stage Docker image
Add a Node-based webui builder stage that installs frontend dependencies and runs the Vite production build, then copies the generated static/dist assets into the final runtime image.

Move Python dependency installation into a separate venv build stage so compiler and development packages stay out of the runtime image. Also ignore local webui node_modules, Vite cache, and dist output from the Docker build context.
2026-05-13 22:26:21 +03:00
Broque Thomas
decb62dcc9 Docker: pre-bake /app/Staging + writability audit (fix restart loop)
Discord report: container refused to start after pulling latest.
Logs showed `mkdir: cannot create directory '/app/Staging':
Permission denied`. `set -e` in entrypoint.sh then aborted the script
and the container restart-looped.

Root cause traced to commit 70e1750 (2026-05-08, image-bloat fix):
the Dockerfile chown was changed from `chown -R /app` to a scoped
chown on specific subdirs to avoid a redundant layer that was
duplicating the entire /app tree. Side effects:

1. `/app` itself went from soulsync:soulsync (via the recursive walk)
   to root:root (Docker WORKDIR default — never re-chowned).
2. `/app/Staging` was the only runtime mount-point dir NOT pre-baked
   into the image — every other bind-mountable dir (config, logs,
   downloads, Transfer, MusicVideos, scripts) was in the Dockerfile's
   `mkdir -p` + `chown` list. Staging was left to the entrypoint.

On rootless Docker / Podman where in-container "root" maps to a host
UID, the entrypoint mkdir on `/app/Staging` could fail with EACCES
depending on the bind-mount path's host ownership.

Fix has three parts:

1. **Dockerfile** — added `/app/Staging` to the runtime mkdir +
   scoped chown list. Closes the asymmetry with the other bind-
   mountable dirs. Image now ships with the directory pre-baked
   owned soulsync:soulsync so the entrypoint mkdir is a guaranteed
   no-op even when bind-mount perms are weird.

2. **entrypoint.sh mkdir + chown** — both now have `|| true` so any
   future bind-mount permission quirk surfaces as a log line, not
   a `set -e` crash + restart loop. Previously only the chown had
   the `|| true` suffix; mkdir was bare.

3. **entrypoint.sh writability audit** — new loop at the end of
   the setup phase runs `gosu soulsync test -w "$dir"` against
   every bind-mountable dir. When a dir isn't writable by the
   soulsync user, logs a loud warning with the exact host-side
   `chown` command needed to fix it. Catches the underlying bind-
   mount perm issue that the restart-loop fix would otherwise mask
   (container starts but auto-import / downloads write into
   unwritable dirs and fail silently). This is the diagnostic that
   would have surfaced the root cause without needing the user to
   share a container-restart screenshot.

Zero behavior change for users whose containers were already
starting fine. Defensive against the rootless/podman config that
broke after the image-bloat refactor.

Verified shell syntax with `bash -n entrypoint.sh`. Full pytest
2693 passed (no Python touched).
2026-05-11 13:35:31 -07:00
Broque Thomas
70e1750948 Stop docker image bloat from auto-downloaded ffmpeg
kettui reported the dev image roughly doubled in size after a recent
nightly build. codex investigation traced it back to:

1. nightly workflow runs `python -m pytest` before docker build
2. one of the new tests imports web_server (test_tidal_auth_instructions.py)
3. importing web_server constructs YouTubeClient
4. YouTubeClient.__init__ called _check_ffmpeg() — which auto-downloads
   a ~388 MB ffmpeg/ffprobe bundle into ./tools/ when system ffmpeg
   isn't on PATH (CI runner doesn't have it)
5. .dockerignore didn't exclude tools/ffmpeg or tools/ffprobe
6. docker `COPY . .` shipped the binaries
7. the immediately-following `chown -R /app` rewrote every file into
   a new layer — so the 388 MB payload got counted twice in image
   size

three fixes:

1. .dockerignore — block the auto-downloaded binaries even if they
   leak into the workspace (tools/ffmpeg, tools/ffprobe, .exe variants,
   .zip and .tar.xz download archives). Defense-in-depth so a future
   regression in the test/import path can't bloat the image again.

2. youtube_client — split _check_ffmpeg into a side-effect-free
   _locate_ffmpeg (pure existence check) and the original auto-
   download _check_ffmpeg. __init__ now calls _locate_ffmpeg + logs
   a warning when missing instead of triggering download. is_available()
   and the actual download dispatch paths still call _check_ffmpeg —
   so end users still get auto-download on first YouTube use, but
   `import web_server` doesn't drag a 388 MB binary into the workspace.

3. Dockerfile — replaced `COPY . .` + `chown -R /app` with
   `COPY --chown=soulsync:soulsync . .` + a scoped chown on just the
   runtime mount-point dirs. eliminates the layer that duplicated
   the entire /app tree just to flip ownership bits, so even legit
   workspace content isn't double-counted in the image.

Combined effect: image size returns to baseline + future ffmpeg leaks
can't bloat it. Inside the container nothing changes — the Dockerfile
already installs system ffmpeg via apt, so YouTube downloads find it
on PATH on first use and the auto-download path never fires.

2259 passed, 1 skipped, 0 failed.
2026-05-08 15:28:51 -07:00
Antti Kettunen
cb9a4b23b6 Clean up legacy env vars, print a warning log if running web_server directly 2026-04-18 19:22:03 +03:00
Antti Kettunen
14bc9b6fad Introduce Gunicorn production runner
Switch the web UI from Werkzeug's built-in server to Gunicorn for a more stable production deployment path.

Keep a separate dev config so local runs still reload quickly, while the production path uses a dedicated WSGI entrypoint and cleaner startup behavior.

The main motivation is to reduce the websocket teardown noise and make the server behavior more predictable under the app's mostly background-driven workload.
2026-04-18 19:21:53 +03:00
Antti Kettunen
c34fb10881 Rename requirements-webui -> requirements.txt 2026-04-17 20:08:21 +03:00
Broque Thomas
b3d3c017ed Use multi-stage Docker build to reduce image size
Builder stage compiles Python dependencies with gcc/build tools.
Runtime stage only includes curl, gosu, ffmpeg, and libchromaprint-tools.
Build tools are not shipped in the final image, reducing size and
attack surface.

Inspired by kettui's PR #273.
2026-04-11 13:39:16 -07:00
Broque Thomas
1f0ef08b48 Add Music Videos directory setting for Plex music video support
New configurable path for storing music videos separately from audio
files, following Plex's global music video folder convention.

- Settings: library.music_videos_path (default: ./MusicVideos)
- UI: Music Videos Dir field on Settings Downloads tab with lock/unlock
- Docker: /app/MusicVideos volume mount in Dockerfile and docker-compose
- Added 'library' to settings save whitelist (was missing — music_paths
  also wasn't persisting through main settings save)
- No download functionality yet — path infrastructure only
2026-04-10 22:01:27 -07:00
Broque Thomas
959bca2b8d Add Run Script action to automation engine
New automation action that executes user scripts from a dedicated
scripts/ directory. Available as both a DO action and THEN action.
Scripts are selected from a dropdown populated by /api/scripts.

Security: only scripts in the scripts dir can run, path traversal
blocked, no shell=True, stdout/stderr capped, configurable timeout
(max 300s). Scripts receive SOULSYNC_EVENT, SOULSYNC_AUTOMATION,
and SOULSYNC_SCRIPTS_DIR environment variables.

Includes Dockerfile + docker-compose.yml changes for the scripts
volume mount, and three example scripts (hello_world.sh,
system_info.py, notify_ntfy.sh).
2026-04-09 18:20:29 -07:00
Broque Thomas
114af496c7 Track version 2026-03-04 11:32:40 -08:00
Broque Thomas
4d47ccf99a Fix downloads halting when browser is closed by moving post-processing trigger from browser polling to background monitor 2026-02-19 21:26:36 -08:00
Broque Thomas
d9efcbdf99 feat: AcoustID audio verification, MusicBrainz enrichment UI, v1.5
Add optional post-download audio fingerprint verification using AcoustID.
  Downloads are verified against expected track/artist using fuzzy string
  matching on AcoustID results. Mismatched files are quarantined and
  automatically added to the wishlist for retry.

  - AcoustID verification with title/artist fuzzy matching (not MBID comparison)
  - Quarantine system with JSON metadata sidecars for failed verifications
  - fpcalc binary auto-download for Windows, macOS (universal), and Linux
  - MusicBrainz enrichment worker with live status UI and track badges
  - Settings page AcoustID section with real-fingerprint connection test
  - Source reuse for album downloads to keep tracks from same Soulseek user
  - Enhanced search queries for better track matching
  - Bug fixes: wishlist tracking, album splitting, regex & handling, log rotation
2026-02-05 16:33:07 -08:00
Broque Thomas
eaf33f6f6d just testing things 2026-01-03 13:10:37 -08:00
Broque Thomas
61a698aefa Move database files to /app/data and use env var for path
Updated Dockerfile, entrypoint.sh, and Python code to store database files in /app/data instead of /app/database, avoiding conflicts with the Python package. The database path is now configurable via the DATABASE_PATH environment variable, improving flexibility for container deployments.
2026-01-01 09:53:39 -08:00
Broque Thomas
14cfbd01f4 unraid fix 2025-11-17 12:58:49 -08:00
Broque Thomas
2f8bad23a8 Add manual track matching and Docker permission docs
Implements manual track matching (discovery fix modal) for YouTube, Tidal, and Beatport platforms, allowing users to search and select Spotify tracks for unmatched results. Adds backend endpoints and frontend logic for updating matches, improves conversion of discovery results for sync/download, and updates Dockerfile/entrypoint for dynamic PUID/PGID/UMASK support. Includes a new DOCKER_PERMISSIONS.md guide.
2025-10-03 10:48:25 -07:00
Broque Thomas
f10c27c125 Update Docker setup and config paths
Improves Docker integration by copying config.example.json as the default config.json and updating ownership in the Dockerfile. Adjusts config paths in config.example.json for container compatibility and updates docker-compose.yml to build the image and comment out the config volume for baked-in config testing. Also updates .dockerignore to allow config.example.json.
2025-09-13 15:12:23 -07:00
Broque Thomas
287d2fd2ec Add Docker support and improve headless compatibility
Introduces Docker deployment files (.dockerignore, Dockerfile, docker-compose.yml, docker-setup.sh, requirements-webui.txt, and README-Docker.md) for SoulSync WebUI. Refactors core/database_update_worker.py and core/media_scan_manager.py to support headless operation without PyQt6, enabling signal/callback compatibility for both GUI and non-GUI environments. Removes logs/app.log file.
2025-09-11 08:26:25 -07:00