Files
alembic/pipeline/lib/navidrome-scan.sh
T
andrew 68cb007e4c Stage 0: migrate pipeline scripts from /opt/sldl, scaffold repo
Moves all ~30 pipeline scripts, configs, and the vendored sldl binary into
this repo (source /opt/sldl left untouched). Removes all docker exec/docker
compose dependencies now that beets and sldl run in-process/as a subprocess
of this container instead of via soulbeet/on-demand sldl containers.
Replaces hardcoded host paths, Navidrome credentials, and Spotify credential
sourcing with env-var-driven paths and shared credential loaders. Adds
Dockerfile, entrypoint.sh, requirements.txt, docker-compose.snippet.yml, and
the initial app DB schema.
2026-07-08 13:10:55 -06:00

61 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# navidrome-scan.sh — trigger a Navidrome full scan ONLY if the music library
# share is healthy.
#
# Why this exists: with ND_SCANNER_PURGEMISSING=always (which we keep, so
# on-disk deletions propagate to playlists and out to Traktor), a full scan
# that runs while the QNAP NFS share is unavailable makes Navidrome see the
# entire library as "missing" and purge it — wiping every playlist. That is
# exactly what happened on 2026-06-01 and lost the manual dj-* playlists.
#
# This wrapper refuses to trigger the scan unless the share is mounted, a
# canary file is readable, and the library has a sane number of artist dirs.
# Skipping a scan is harmless (the next healthy run picks up changes); running
# one against a dead mount is catastrophic.
set -euo pipefail
: "${MUSIC_DATA_DIR:=/data/music}"
: "${ALEMBIC_CONFIG_DIR:=/config}"
NAVIDROME_ENV="$ALEMBIC_CONFIG_DIR/pipeline/navidrome/admin.env"
[ -f "$NAVIDROME_ENV" ] && source "$NAVIDROME_ENV"
LIB="$MUSIC_DATA_DIR/Library"
CANARY="$LIB/.navidrome-canary"
MIN_ARTIST_DIRS=500 # library has ~1500; 500 is "clearly not wiped"
ND="${ND_BASE:-http://navidrome:4533}"
ND_USER="${ND_USER:-andrew}"
ND_PASS="${ND_PASS:-}"
log() { echo "[$(date -Iseconds)] navidrome-scan: $*"; }
# `mountpoint -q` on the host detected an unmounted/dead NFS share directly.
# Running inside a container against a bind-mounted view, that check doesn't
# apply (a bind mount has no distinct device number to detect) — the canary
# file + artist-dir-count checks below are what actually carry the
# 2026-06-01 protection now: an unhealthy/empty share fails both of them.
# (Confirmed as an intentional adaptation, not a silent drop — see alembic
# migration plan / MIGRATION.md.)
if [ ! -r "$CANARY" ]; then
log "ABORT: canary $CANARY missing/unreadable — share degraded — skipping scan"
exit 0
fi
n=$(find "$LIB" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l)
if [ "$n" -lt "$MIN_ARTIST_DIRS" ]; then
log "ABORT: only $n artist dirs under $LIB (< $MIN_ARTIST_DIRS) — library looks truncated — skipping scan"
exit 0
fi
log "library healthy ($n artist dirs) — triggering full scan"
resp=$(curl -s -G "$ND/rest/startScan.view" \
--data-urlencode "u=$ND_USER" --data-urlencode "p=$ND_PASS" \
--data-urlencode 'v=1.16.0' --data-urlencode 'c=cron' \
--data-urlencode 'f=json' --data-urlencode 'fullScan=true' || true)
# Don't log "scan triggered" on faith: a rotated password or Navidrome error
# would otherwise report success forever while no scan ever runs.
if echo "$resp" | grep -q '"status":"ok"'; then
log "scan triggered"
else
log "ERROR: scan trigger failed — response: ${resp:-<empty/no response>}"
exit 1
fi