55a059b6da
sync-bandcamp.sh set a hardcoded PATH at startup that left out /opt/venv/bin, which is where beet lives in this image. Every time the Bandcamp sync had a new purchase to import, its call into import-track.sh would run beet import with that broken PATH, fail with "command not found", and import-track.sh would just log the exit code and carry on, so sync-bandcamp.sh still reported success. The purchase sat on disk, never entered the beets library, and never showed up in Navidrome. This went unnoticed for weeks because most daily syncs have nothing new to import, so the broken code path rarely ran. Fixed the same copy-pasted PATH line in upgrade-mp3-to-flac.sh (which also calls beet directly) and notify-telegram.sh (harmless there, but fixed for consistency). Also moved the post-import duplicate cleanup (replace-with-better.sh and dedup-library.sh) out of sync-bandcamp.sh and into import-track.sh itself, so every import gets the same cleanup, not just Bandcamp purchases. A manual import or SMB drop that happens to match something already in the library no longer leaves a duplicate copy sitting there until someone runs the dedup review by hand.
111 lines
4.6 KiB
Bash
Executable File
111 lines
4.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# Wrapper: sync new Bandcamp purchases, then push them through the standard
|
|
# import-track.sh pipeline so they end up in the Navidrome library tagged
|
|
# the same way as everything else.
|
|
#
|
|
# Reads $ALEMBIC_CONFIG_DIR/pipeline/bandcamp/config.env for username, format pref, etc.
|
|
# Run as root (cron). Logs to ${ALEMBIC_CONFIG_DIR:-/config}/logs/bandcamp-YYYYMMDD.log.
|
|
|
|
set -euo pipefail
|
|
# /opt/venv/bin must lead PATH -- that's where `beet` lives (the app's own
|
|
# subprocess env puts it there too, see pipeline_runner._subprocess_env()).
|
|
# This script used to run as a bare root cron job (pre-2026-07-08 cutover to
|
|
# the app scheduler) where a minimal hardened PATH made sense; omitting the
|
|
# venv here silently broke every downstream `beet import` call inside
|
|
# import-track.sh once this started running through the app instead --
|
|
# import-track.sh swallows that failure and still reports OK, so a purchase
|
|
# would sit unimported until someone noticed it missing from Navidrome.
|
|
PATH=/opt/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
export PATH
|
|
|
|
CONFIG="${ALEMBIC_CONFIG_DIR:-/config}/pipeline/bandcamp/config.env"
|
|
LOG=${ALEMBIC_CONFIG_DIR:-/config}/logs/bandcamp-$(date +%Y%m%d).log
|
|
SYNC_PY=${PIPELINE_DIR:-/app/pipeline}/lib/sync-bandcamp.py
|
|
IMPORT_ME=${MUSIC_DATA_DIR:-/data/music}/import-me
|
|
|
|
mkdir -p "$(dirname "$LOG")"
|
|
log() { echo "[$(date -Iseconds)] $*" | tee -a "$LOG"; }
|
|
|
|
if [[ ! -f "$CONFIG" ]]; then
|
|
log "ERROR: $CONFIG not found"
|
|
exit 1
|
|
fi
|
|
# shellcheck disable=SC1090
|
|
source "$CONFIG"
|
|
|
|
if [[ ! -f "$BANDCAMP_COOKIES" ]]; then
|
|
log "ERROR: cookies file missing at $BANDCAMP_COOKIES"
|
|
log " Export Bandcamp cookies in Netscape format (Get cookies.txt LOCALLY"
|
|
log " extension, while logged into bandcamp.com), save to that path."
|
|
exit 1
|
|
fi
|
|
|
|
# Refuse to run against a dead/empty share: mkdir -p below would otherwise
|
|
# silently create shadow directories inside the container, downloads would
|
|
# land there invisibly, and state.json would mark those purchases as done —
|
|
# permanently skipping them after the mount comes back. `mountpoint -q` (the
|
|
# original host-side check) doesn't apply to a bind-mounted container path;
|
|
# a canary file + minimum artist-dir count is the container-compatible
|
|
# equivalent (same pattern as navidrome-scan.sh's share-health gate).
|
|
LIB="${MUSIC_DATA_DIR:-/data/music}/Library"
|
|
CANARY="$LIB/.navidrome-canary"
|
|
MIN_ARTIST_DIRS="${MIN_ARTIST_DIRS:-500}"
|
|
if [[ ! -r "$CANARY" ]]; then
|
|
log "ERROR: canary $CANARY missing/unreadable — share looks unhealthy — aborting sync"
|
|
exit 1
|
|
fi
|
|
n=$(find "$LIB" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l)
|
|
if [[ "$n" -lt "$MIN_ARTIST_DIRS" ]]; then
|
|
log "ERROR: only $n artist dirs under $LIB (< $MIN_ARTIST_DIRS) — library looks truncated — aborting sync"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$BANDCAMP_STAGING" "$IMPORT_ME"
|
|
|
|
log "=== Bandcamp sync starting ==="
|
|
log "user=$BANDCAMP_USERNAME staging=$BANDCAMP_STAGING format=$BANDCAMP_FORMAT_PREF"
|
|
|
|
if ! python3 "$SYNC_PY" \
|
|
"$BANDCAMP_USERNAME" \
|
|
"$BANDCAMP_COOKIES" \
|
|
"$BANDCAMP_STATE" \
|
|
"$BANDCAMP_STAGING" \
|
|
"$BANDCAMP_FORMAT_PREF" >> "$LOG" 2>&1; then
|
|
log "WARN: sync-bandcamp.py exited non-zero (likely auth issue or network)"
|
|
fi
|
|
|
|
# Anything new? If staging dir has audio, hand it off to import-track.sh.
|
|
NEW_COUNT=$(find "$BANDCAMP_STAGING" -type f \( -iname "*.flac" -o -iname "*.mp3" -o -iname "*.wav" -o -iname "*.m4a" \) 2>/dev/null | wc -l)
|
|
if [[ "$NEW_COUNT" -eq 0 ]]; then
|
|
log "No new files to import"
|
|
log "=== Bandcamp sync done (no-op) ==="
|
|
exit 0
|
|
fi
|
|
|
|
log "$NEW_COUNT new audio file(s) staged; moving to $IMPORT_ME for import"
|
|
|
|
# Move everything from staging into import-me. Preserve the band/album folder
|
|
# structure since import-track.sh handles directories fine.
|
|
shopt -s dotglob nullglob
|
|
for entry in "$BANDCAMP_STAGING"/*; do
|
|
[[ -e "$entry" ]] || continue
|
|
mv "$entry" "$IMPORT_ME/" 2>>"$LOG" || log "WARN: failed to move $entry into $IMPORT_ME"
|
|
done
|
|
shopt -u dotglob nullglob
|
|
|
|
# Hand off to the standard manual-import pipeline. No playlist tag — Bandcamp
|
|
# purchases aren't part of any Spotify playlist. The import-track.sh guard,
|
|
# albumartist fallback, beets import, straggler/dedup safety net, and
|
|
# Navidrome scan all kick in normally (import-track.sh runs the
|
|
# replace-with-better + dedup-library safety net itself now, for every
|
|
# caller, not just this one).
|
|
log "Calling import-track.sh"
|
|
if ${PIPELINE_DIR:-/app/pipeline}/bin/import-track.sh >> "$LOG" 2>&1; then
|
|
log "import-track.sh OK"
|
|
else
|
|
log "WARN: import-track.sh exited non-zero (exit $?)"
|
|
fi
|
|
|
|
log "=== Bandcamp sync done ==="
|
|
exit 0
|