Harden fix-genre, sync-bandcamp, upgrade-mp3-to-flac under strict mode (R1)
- fix-genre.sh: set -euo pipefail; guard the tag-remove metaflac calls and the 'beet ls | head -5' verify (SIGPIPE) that would otherwise abort. - sync-bandcamp.sh: set -euo pipefail; the destructive chain already used 'if cmd; then..else WARN' (set -e exempt); guard the staging mv. - upgrade-mp3-to-flac.sh: set -euo pipefail; capture sldl/replace-with-better exit codes without aborting (was 'cmd; RC=$?', which set -e breaks); read Soulseek creds from the first available .conf instead of a hardcoded y2k.conf, guarded; guard the xargs purge and a grep -c. - pipeline-status.sh: deliberately kept at set -u (documented). It is read-only and assembles ~30 independent probes; set -e would abort the whole digest on one missing optional file, which is exactly how the 9am notification broke. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
# sudo fix-genre.sh "Aphex Twin" "IDM; Electronic; Ambient"
|
||||
# sudo fix-genre.sh --dry "Mac DeMarco" "Indie; Indie Rock"
|
||||
|
||||
set -u
|
||||
set -euo pipefail
|
||||
|
||||
DRY=0
|
||||
if [[ "${1:-}" == "--dry" ]]; then
|
||||
@@ -55,10 +55,12 @@ for cp in "${paths[@]}"; do
|
||||
fi
|
||||
case "${hp,,}" in
|
||||
*.flac)
|
||||
metaflac --remove-tag=GENRE "$hp" 2>/dev/null
|
||||
# remove-tag returns non-zero when the tag isn't present; `|| true` so
|
||||
# that expected case doesn't abort under set -e.
|
||||
metaflac --remove-tag=GENRE "$hp" 2>/dev/null || true
|
||||
metaflac --set-tag="GENRE=$GENRE" "$hp" 2>/dev/null && ok=$((ok + 1)) || fail=$((fail + 1))
|
||||
metaflac --remove-tag=GENRE_LOCK "$hp" 2>/dev/null
|
||||
metaflac --set-tag="GENRE_LOCK=1" "$hp" 2>/dev/null
|
||||
metaflac --remove-tag=GENRE_LOCK "$hp" 2>/dev/null || true
|
||||
metaflac --set-tag="GENRE_LOCK=1" "$hp" 2>/dev/null || true
|
||||
;;
|
||||
*.mp3)
|
||||
# mid3v2 / id3v2 don't have a clean way to set TCON via plain id3v2 here,
|
||||
@@ -115,7 +117,9 @@ beet update "artist:$ARTIST" 2>&1 | grep -E '^\s+[+-]' || true
|
||||
|
||||
# Verify
|
||||
echo "[fix-genre] new state:"
|
||||
beet ls -f ' $title — $genres' "artist:$ARTIST" | head -5
|
||||
# `|| true`: head closes the pipe after 5 lines, so beet exits with SIGPIPE for
|
||||
# any artist with more than 5 tracks; that's expected, not a failure.
|
||||
beet ls -f ' $title — $genres' "artist:$ARTIST" | head -5 || true
|
||||
remaining=$(beet ls "artist:$ARTIST" | wc -l)
|
||||
shown=5
|
||||
[[ "$remaining" -gt "$shown" ]] && echo " ...and $((remaining - shown)) more"
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
# Only flags SCRIPT-level failures, not per-file Soulseek errors (those are
|
||||
# normal — peers go offline, connections reset).
|
||||
|
||||
# set -u only, NOT -e/pipefail, on purpose. This script is read-only: it
|
||||
# assembles a status digest from ~30 independent probes (grep over logs, beet
|
||||
# queries, curl health checks, optional-credential file reads). Each probe is
|
||||
# allowed to fail individually and report a warn/skip line; that's the whole
|
||||
# design. Under set -e a single missing optional file (e.g. qobuz/app_id) or a
|
||||
# no-match grep would abort the entire run and send NO digest at all, which is
|
||||
# the opposite of what a health report should do. It writes no library state,
|
||||
# so there is nothing to leave half-applied.
|
||||
set -u
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# 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 -u
|
||||
set -euo pipefail
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
export PATH
|
||||
|
||||
@@ -81,7 +81,7 @@ log "$NEW_COUNT new audio file(s) staged; moving to $IMPORT_ME for import"
|
||||
shopt -s dotglob nullglob
|
||||
for entry in "$BANDCAMP_STAGING"/*; do
|
||||
[[ -e "$entry" ]] || continue
|
||||
mv "$entry" "$IMPORT_ME/" 2>>"$LOG"
|
||||
mv "$entry" "$IMPORT_ME/" 2>>"$LOG" || log "WARN: failed to move $entry into $IMPORT_ME"
|
||||
done
|
||||
shopt -u dotglob nullglob
|
||||
|
||||
|
||||
@@ -19,16 +19,18 @@
|
||||
# upgrade-mp3-to-flac.sh # run the scanner end-to-end
|
||||
# upgrade-mp3-to-flac.sh --csv-only # just write the CSV; don't run sldl
|
||||
|
||||
set -u
|
||||
set -euo pipefail
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
export PATH
|
||||
|
||||
# These helpers are the same ones import-track.sh sources and runs under its
|
||||
# own set -euo pipefail on every manual import, so they are already strict-safe.
|
||||
# shellcheck source=${PIPELINE_DIR:-/app/pipeline}/lib/tag-guard.sh
|
||||
source ${PIPELINE_DIR:-/app/pipeline}/lib/tag-guard.sh
|
||||
source "${PIPELINE_DIR:-/app/pipeline}/lib/tag-guard.sh"
|
||||
# shellcheck source=${PIPELINE_DIR:-/app/pipeline}/lib/prep-audio.sh
|
||||
source ${PIPELINE_DIR:-/app/pipeline}/lib/prep-audio.sh
|
||||
source "${PIPELINE_DIR:-/app/pipeline}/lib/prep-audio.sh"
|
||||
# shellcheck source=${PIPELINE_DIR:-/app/pipeline}/lib/mb-tags.sh
|
||||
source ${PIPELINE_DIR:-/app/pipeline}/lib/mb-tags.sh
|
||||
source "${PIPELINE_DIR:-/app/pipeline}/lib/mb-tags.sh"
|
||||
|
||||
CSV_ONLY=0
|
||||
[[ "${1:-}" == "--csv-only" ]] && CSV_ONLY=1
|
||||
@@ -72,11 +74,19 @@ if [[ $CSV_ONLY -eq 1 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Pull credentials from any playlist conf (they're all the same)
|
||||
SOULSEEK_USER=$(sed -n 's/^user *= *//p' ${ALEMBIC_CONFIG_DIR:-/config}/pipeline/y2k.conf | tr -d ' ')
|
||||
SOULSEEK_PASS=$(sed -n 's/^pass *= *//p' ${ALEMBIC_CONFIG_DIR:-/config}/pipeline/y2k.conf | tr -d ' ')
|
||||
# Pull Soulseek credentials from any rendered playlist conf (they all carry the
|
||||
# same user/pass). Don't hardcode a specific playlist name: pick the first
|
||||
# non-underscore .conf that exists. Guarded so a missing conf yields empty
|
||||
# creds (sldl then fails and is handled) rather than aborting under set -e.
|
||||
_CRED_CONF=$(ls "${ALEMBIC_CONFIG_DIR:-/config}"/pipeline/*.conf 2>/dev/null | grep -v '/_' | head -1 || true)
|
||||
SOULSEEK_USER=$(sed -n 's/^user *= *//p' "${_CRED_CONF:-/nonexistent}" 2>/dev/null | tr -d ' ' || true)
|
||||
SOULSEEK_PASS=$(sed -n 's/^pass *= *//p' "${_CRED_CONF:-/nonexistent}" 2>/dev/null | tr -d ' ' || true)
|
||||
|
||||
log "Running sldl with --format flac (HARD requirement, no MP3/WAV fallback)"
|
||||
# sldl routinely exits non-zero (skipped/partial searches), which is normal
|
||||
# here. Capture the code without letting set -e abort: init to 0, override in
|
||||
# the || branch. (`cmd; RC=$?` would abort at cmd before RC=$? ran.)
|
||||
SLDL_EXIT=0
|
||||
"$SLDL_BIN" \
|
||||
"$CSV_HOST" \
|
||||
--input-type csv \
|
||||
@@ -92,8 +102,7 @@ log "Running sldl with --format flac (HARD requirement, no MP3/WAV fallback)"
|
||||
--no-browse-folder \
|
||||
--no-skip-existing \
|
||||
--verbose \
|
||||
>> "$LOG" 2>&1
|
||||
SLDL_EXIT=$?
|
||||
>> "$LOG" 2>&1 || SLDL_EXIT=$?
|
||||
log "sldl exit code: $SLDL_EXIT"
|
||||
|
||||
# Guard: this script is FLAC-upgrade-only. If sldl ever drops anything else
|
||||
@@ -103,7 +112,7 @@ NON_FLAC=$(find "$STAGING_HOST" -type f ! -iname "*.flac" ! -name "*.incomplete"
|
||||
if [[ -n "$NON_FLAC" ]]; then
|
||||
log "Purging non-FLAC files from staging:"
|
||||
echo "$NON_FLAC" | tee -a "$LOG"
|
||||
echo "$NON_FLAC" | xargs -r rm -f
|
||||
echo "$NON_FLAC" | xargs -r rm -f || true
|
||||
fi
|
||||
|
||||
NEW_FLAC_COUNT=$(find "$STAGING_HOST" -type f -iname "*.flac" 2>/dev/null | wc -l)
|
||||
@@ -149,8 +158,8 @@ log "Pre-tagged $PREPPED file(s)"
|
||||
# the replacing FLAC before the swap, so playlist M3Us still resolve and the
|
||||
# FLAC inherits spotify-retag's canonical metadata.
|
||||
log "Running replace-with-better.sh on staging (--copy-tags-from-existing)"
|
||||
${PIPELINE_DIR:-/app/pipeline}/lib/replace-with-better.sh --apply --copy-tags-from-existing "$STAGING_HOST" >> "$LOG" 2>&1
|
||||
RBE_EXIT=$?
|
||||
RBE_EXIT=0
|
||||
${PIPELINE_DIR:-/app/pipeline}/lib/replace-with-better.sh --apply --copy-tags-from-existing "$STAGING_HOST" >> "$LOG" 2>&1 || RBE_EXIT=$?
|
||||
log "replace-with-better exit: $RBE_EXIT"
|
||||
|
||||
# Regenerate playlist M3Us. Upgraded tracks have new paths (FLAC vs MP3) but
|
||||
@@ -164,7 +173,7 @@ while IFS= read -r playlist; do
|
||||
[[ -z "$playlist" ]] && continue
|
||||
M3U_OUT="${PLAYLISTS_DIR}/${playlist}.m3u8"
|
||||
BEET_OUTPUT=$(beet ls -f '$path' "grouping:${playlist}" 2>>"$LOG" || true)
|
||||
TRACK_COUNT=$(echo -n "$BEET_OUTPUT" | grep -c '^')
|
||||
TRACK_COUNT=$(echo -n "$BEET_OUTPUT" | grep -c '^' || true)
|
||||
[[ "$TRACK_COUNT" -gt 0 ]] || continue
|
||||
{ echo "#EXTM3U"; echo "$BEET_OUTPUT"; } > "$M3U_OUT"
|
||||
log " $playlist.m3u8 — $TRACK_COUNT tracks"
|
||||
|
||||
Reference in New Issue
Block a user