Archive dead/manual scripts to pipeline/lib/manual (R8)

Move six scripts that are not wired into the app, scheduler, or web UI out of
the active pipeline/lib into pipeline/lib/manual, so the scheduled scripts are
easy to see and these stay available for hands-on use: convert-m4a.sh,
fix-empty-album.sh, backfill-spotify-tags.sh, backfill-buy-url.py,
recover-azuracast-playlists.py, import-dj-collection.py. Adds a README
explaining each, and extends the Dockerfile chmod to cover the new folder.
Nothing referenced these from active code (verified), so no wiring changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
andrew
2026-07-10 10:52:44 -06:00
parent db13890fe1
commit 2d7243332d
8 changed files with 32 additions and 1 deletions
+109
View File
@@ -0,0 +1,109 @@
#!/bin/bash
# Find audio files in the library that have no ALBUM tag, set ALBUM to
# "{TITLE} - Single" — matches Spotify / Apple Music's convention for
# single-track releases. Otherwise these tracks land as [Unknown Album]
# in Navidrome.
#
# Usage:
# fix-empty-album.sh # dry run (default)
# fix-empty-album.sh --apply # actually rewrite tags
#
# Bandcamp single-track downloads are the most common source of this
# issue (Bandcamp serves single tracks with no ALBUM tag). New downloads
# via sync-bandcamp.py are auto-tagged; this script handles backfill.
set -u
APPLY=0
[[ "${1:-}" == "--apply" ]] && APPLY=1
LOG=${ALEMBIC_CONFIG_DIR:-/config}/logs/fix-empty-album-$(date +%Y%m%d-%H%M%S).log
mkdir -p "$(dirname "$LOG")"
NAVIDROME_ENV="${ALEMBIC_CONFIG_DIR:-/config}/pipeline/navidrome/admin.env"
[ -f "$NAVIDROME_ENV" ] && source "$NAVIDROME_ENV"
ND_BASE="${ND_BASE:-http://navidrome:4533}"
ND_USER="${ND_USER:-}"
ND_PASS="${ND_PASS:-}"
log() { echo "[$(date -Iseconds)] $*" | tee -a "$LOG"; }
if [[ $APPLY -eq 1 ]]; then
log "=== FIX EMPTY ALBUM (APPLY) ==="
else
log "=== FIX EMPTY ALBUM (DRY RUN — pass --apply to write) ==="
fi
FIXED=0
SKIPPED=0
while IFS= read -r -d '' f; do
ext="${f,,}"; ext="${ext##*.}"
album="" ; title=""
case "$ext" in
flac)
album=$(metaflac --show-tag=ALBUM "$f" 2>/dev/null | sed -n 's/^[^=]*=//p' | head -1)
title=$(metaflac --show-tag=TITLE "$f" 2>/dev/null | sed -n 's/^[^=]*=//p' | head -1)
;;
mp3)
album=$(id3v2 -l "$f" 2>/dev/null | sed -n 's/^TALB[^:]*: //p' | head -1)
title=$(id3v2 -l "$f" 2>/dev/null | sed -n 's/^TIT2[^:]*: //p' | head -1)
;;
*) continue ;;
esac
if [[ -n "$album" ]]; then
continue # already has an album, leave it alone
fi
if [[ -z "$title" ]]; then
log "[skip-no-title] $f"
SKIPPED=$((SKIPPED + 1))
continue
fi
# Some titles have a leading "NN " track-number prefix baked in from a
# bad filename parse (e.g. title="01 Domestic Violence"). If we use the
# title as-is, the album becomes "01 Domestic Violence - Single" which
# is broken. Strip leading 1-3 digits + space, and also fix the title
# tag in place so it isn't shown with the prefix in clients.
clean_title=$(echo "$title" | sed -E 's/^[[:space:]]*[0-9]{1,3}[[:space:]]+//')
if [[ "$clean_title" != "$title" && -n "$clean_title" ]]; then
log " trimmed leading track-number from title: \"$title\" -> \"$clean_title\""
if [[ $APPLY -eq 1 ]]; then
case "$ext" in
flac) metaflac --remove-tag=TITLE --set-tag="TITLE=${clean_title}" "$f" 2>>"$LOG" ;;
mp3) id3v2 --TIT2 "${clean_title}" "$f" 2>>"$LOG" ;;
esac
fi
title="$clean_title"
fi
new_album="${title} - Single"
log "[$( [[ $APPLY -eq 1 ]] && echo SET || echo WOULD-SET )] album=\"${new_album}\" - $f"
if [[ $APPLY -eq 1 ]]; then
case "$ext" in
flac) metaflac --set-tag="ALBUM=${new_album}" "$f" 2>>"$LOG" ;;
mp3) id3v2 --TALB "${new_album}" "$f" 2>>"$LOG" ;;
esac
fi
FIXED=$((FIXED + 1))
done < <(find ${MUSIC_DATA_DIR:-/data/music}/Library -type f \( -iname "*.flac" -o -iname "*.mp3" \) -print0 2>/dev/null)
log ""
log "=== Summary: $FIXED fixed, $SKIPPED skipped (no title) ==="
if [[ $APPLY -eq 1 && $FIXED -gt 0 ]]; then
log "Syncing beets DB from new tags..."
beet update 2>&1 | tail -3 >> "$LOG"
log "Moving files to reflect new album in path templates..."
beet move 2>&1 | tail -3 >> "$LOG"
log "Triggering Navidrome rescan..."
curl -s -G "$ND_BASE/rest/startScan.view" \
--data-urlencode "u=$ND_USER" \
--data-urlencode "p=$ND_PASS" \
--data-urlencode 'v=1.16.0' \
--data-urlencode 'c=fix-empty-album' \
--data-urlencode 'f=json' \
--data-urlencode 'fullScan=true' >> "$LOG" 2>&1
fi
log "=== Done. Log: $LOG ==="