diff --git a/README.md b/README.md index 5fbc368..a497b9d 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,8 @@ MusicSeerr stores its config in `config/config.json` inside the mapped config vo Run `id` on your host to find your PUID and PGID values. +> **Unraid / NAS users:** Unraid defaults to `nobody:users` (PUID=99, PGID=100). If you see `chown: Operation not permitted` at startup, your volume mount is on a filesystem that rejects ownership changes (FUSE/shfs, NFS, CIFS). The container skips `chown` when the directories and their contents are already writable, so this is usually fine as long as the host paths are owned by the correct UID:GID. + ### In-App Settings | Setting | Location | diff --git a/docker-compose.example.yml b/docker-compose.example.yml index a2df07f..5f29403 100644 --- a/docker-compose.example.yml +++ b/docker-compose.example.yml @@ -5,6 +5,8 @@ services: environment: - PUID=1000 # User ID — run `id` on your host to find yours - PGID=1000 # Group ID — run `id` on your host to find yours + # Unraid: use 99/100 (nobody:users) unless you changed Unraid's defaults. + # If using Docker --user, PUID/PGID are ignored (user mapping is external). - PORT=8688 # Internal port (must match the right side of "ports" below) - TZ=Etc/UTC # Timezone — e.g. America/New_York, Europe/London ports: diff --git a/entrypoint.sh b/entrypoint.sh index b418b54..318f35e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,23 +1,77 @@ #!/bin/sh set -e +umask 027 PUID=${PUID:-1000} PGID=${PGID:-1000} -groupmod -o -g "$PGID" musicseerr 2>/dev/null || true -usermod -o -u "$PUID" musicseerr 2>/dev/null || true +case "$PUID" in ''|*[!0-9]*) echo "[init] FATAL: PUID='$PUID' is not a valid numeric UID."; exit 1;; esac +case "$PGID" in ''|*[!0-9]*) echo "[init] FATAL: PGID='$PGID' is not a valid numeric GID."; exit 1;; esac + +check_writable() { + _dir="$1" + _identity="$2" + _probe="$_dir/.musicseerr_write_test_$$" + if [ -n "$_identity" ]; then + gosu "$_identity" touch "$_probe" 2>/dev/null; _rc=$? + gosu "$_identity" rm -f "$_probe" 2>/dev/null + else + touch "$_probe" 2>/dev/null; _rc=$? + rm -f "$_probe" 2>/dev/null + fi + return "$_rc" +} + +if [ "$(id -u)" -ne 0 ]; then + echo "[init] Running as uid=$(id -u) gid=$(id -g) (non-root); skipping user/group setup." + for dir in /app/cache /app/config; do + mkdir -p "$dir" 2>/dev/null || true + if ! check_writable "$dir"; then + echo "[init] FATAL: $dir is not writable by uid=$(id -u)." + echo "[init] Ensure the host directory is owned by this UID/GID." + echo "[init] Run: chown $(id -u):$(id -g) " + exit 1 + fi + done + exec "$@" +fi + +if ! groupmod -o -g "$PGID" musicseerr 2>/dev/null; then + echo "[init] WARNING: Could not set musicseerr group to GID=$PGID." +fi +if ! usermod -o -u "$PUID" musicseerr 2>/dev/null; then + echo "[init] WARNING: Could not set musicseerr user to UID=$PUID." +fi TARGET_UID=$(id -u musicseerr) TARGET_GID=$(id -g musicseerr) +echo "[init] Runtime user: musicseerr (uid=$TARGET_UID gid=$TARGET_GID)" + +if [ "$TARGET_UID" != "$PUID" ]; then + echo "[init] WARNING: Requested PUID=$PUID but running as uid=$TARGET_UID (usermod may have failed)." +fi +if [ "$TARGET_GID" != "$PGID" ]; then + echo "[init] WARNING: Requested PGID=$PGID but running as gid=$TARGET_GID (groupmod may have failed)." +fi -# Only chown directories whose top-level ownership differs from the target. -# Nested mismatches from manual edits require a rebuild or manual chown. for dir in /app/cache /app/config; do - if [ -d "$dir" ]; then - CURRENT=$(stat -c '%u:%g' "$dir") - if [ "$CURRENT" != "$TARGET_UID:$TARGET_GID" ]; then - chown -R musicseerr:musicseerr "$dir" - fi + mkdir -p "$dir" 2>/dev/null || true + + if check_writable "$dir" "$TARGET_UID:$TARGET_GID"; then + continue + fi + + if chown musicseerr:musicseerr "$dir" 2>/dev/null; then + echo "[init] Adjusted ownership of $dir - verifying write access." + else + echo "[init] WARNING: Could not chown $dir (mount may not support ownership changes)." + fi + + if ! check_writable "$dir" "$TARGET_UID:$TARGET_GID"; then + echo "[init] FATAL: $dir is not writable by uid=$TARGET_UID gid=$TARGET_GID." + echo "[init] Common causes: FUSE/shfs (Unraid), NFS root_squash, CIFS/SMB, dropped CAP_CHOWN." + echo "[init] Fix: ensure the host directory is writable by uid=$TARGET_UID gid=$TARGET_GID." + exit 1 fi done diff --git a/templates/musicseerr.xml b/templates/musicseerr.xml new file mode 100644 index 0000000..0fe65eb --- /dev/null +++ b/templates/musicseerr.xml @@ -0,0 +1,27 @@ + + + musicseerr + ghcr.io/habirabbu/musicseerr:latest + https://github.com/habirabbu/musicseerr/pkgs/container/musicseerr + bridge + false + https://github.com/HabiRabbu/Musicseerr/issues + https://github.com/HabiRabbu/Musicseerr + MusicSeerr is a self-hosted music request and discovery app built around Lidarr. Search the full MusicBrainz catalogue, request albums, stream music from Jellyfin or Navidrome, discover new music based on your listening history, and scrobble to ListenBrainz and Last.fm. Requires a running Lidarr instance. + MediaApp:Music + http://[IP]:[PORT:8688] + https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/templates/musicseerr.xml + https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/logo_icon.png + A running Lidarr instance with an API key. Lidarr nightly build is recommended. + lidarr music request scrobble listenbrainz lastfm jellyfin navidrome + https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/HomePage.png + https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/ArtistPage.png + https://raw.githubusercontent.com/HabiRabbu/Musicseerr/main/Images/DiscoverPage.png + 8688 + /mnt/user/appdata/musicseerr/config + /mnt/user/appdata/musicseerr/cache + + 99 + 100 + Europe/London +