The 2026-07-08 path rewrite changed beets' displayed paths from the
transitional /music/... mount to real /data/music/Library/... paths.
dedup-library.sh was fixed in 0.6.8; three more scripts still assumed
the old prefix, each failing silently:
- replace-with-better.sh resolved every library match to a doubled
nonexistent path, logged [stale-db], skipped the in-place replace,
and then imported the staged FLAC as a NEW track via the
leftover-import step. Net effect: the mp3-to-flac upgrade flow
produced flac+mp3 twin pairs in the library (surfacing in the dedup
queue) instead of replacing the MP3 in place.
- fix-track-metadata.py queried beets only by the legacy /music/...
form, matched nothing, and silently skipped beet update/move after
retagging.
- export-laptop-playlists.py filtered out every library track (no
displayed path starts with /music/ anymore), producing empty
exports.
All three now use the real displayed path and keep the /music/... form
only as a legacy fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds strict mode to the file-replacement script (the destructive path used by
the Bandcamp sync and the mp3->flac upgrade).
Uses `set -eu` deliberately WITHOUT pipefail: the script reads tags through
many `cmd | head -1` pipelines, and under pipefail `head` closing the pipe
early makes the producer die with SIGPIPE, turning good reads into false
failures. pipefail adds no safety here since every piped read feeds a value
that is immediately emptiness-checked.
Guards added so a single expected non-zero doesn't abort a destructive run:
- the four progressive `beet ls` match queries fall through on empty/error
- beet remove / beet import / rm / leftover-import / find -delete log a warning
instead of aborting mid-replace
- copy_tags_from_existing is called with `|| true` (also suspends set -e for its
best-effort per-tag metaflac calls)
- explicit `exit 0` so a successful run reports success
Verified: function-level tests (ranking, match fall-through under failing beet)
and a full dry-run against the real library, all exit 0 under set -e.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.