Files
beets-setup/scripts/unsorted/prune-library-old

220 lines
4.9 KiB
Bash
Executable File

#!/usr/bin/env zsh
emulate -L zsh
setopt err_exit pipefail no_unset
prune_root="${BEET_PRUNE_ROOT:-/music/pruned}"
dry_run=0
usage() {
cat >&2 <<'EOF'
usage:
beet-prune-albums [options] [beets-query...]
Interactively select albums with fzf, move them to a pruned quarantine folder,
then remove them from the beets database.
Options:
--dest DIR Quarantine root. Default: /music/pruned
--dry-run Show what would happen, but do not move or remove anything.
-h, --help Show this help.
Examples:
beet-prune-albums
beet-prune-albums albumartist:=~"Taylor Swift"
beet-prune-albums genre:soundtrack
beet-prune-albums --dest /music/pruned
beet-prune-albums --dry-run
EOF
}
while (( $# )); do
case "$1" in
--dest)
prune_root="${2:?missing argument for --dest}"
shift 2
;;
--dry-run)
dry_run=1
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
print -u2 "error: unknown option: $1"
usage
exit 2
;;
*)
break
;;
esac
done
query=( "$@" )
if ! command -v fzf >/dev/null 2>&1; then
print -u2 "error: fzf is required"
print -u2 "install with: brew install fzf"
exit 1
fi
if ! command -v beet >/dev/null 2>&1; then
print -u2 "error: beet is not in PATH"
exit 1
fi
timestamp="$(date +%Y%m%d-%H%M%S)"
batch_dir="$prune_root/$timestamp"
marker="prune_candidate_$timestamp"
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
all="$tmpdir/all.tsv"
selected="$tmpdir/selected.tsv"
# Album list:
# column 1 = beets album id, hidden from fzf display but used by the script.
# columns 2+ = human-readable fields.
fmt=$'$id\t$albumartist\t$album\t$year\t$tracktotal'
beet ls -a -f "$fmt" "${query[@]}" > "$all"
if [[ ! -s "$all" ]]; then
print "No albums matched."
exit 0
fi
fzf \
--multi \
--bind 'space:toggle' \
--delimiter=$'\t' \
--with-nth='2,3,4,5' \
--header='SPACE = select albums to PRUNE | ENTER = confirm selection | ESC = cancel' \
--preview='beet ls -f "$disc-$track $artist - $title" "album_id:{1}" | sed -n "1,200p"' \
--preview-window='down,60%,border-top' \
< "$all" > "$selected" || {
print "Cancelled."
exit 0
}
if [[ ! -s "$selected" ]]; then
print "Nothing selected."
exit 0
fi
print
print "Selected albums:"
awk -F '\t' '{ printf " [%s] %s - %s (%s, %s tracks)\n", $1, $2, $3, $4, $5 }' "$selected"
print
print "Marking selected albums with temporary marker: $marker"
while IFS=$'\t' read -r album_id _rest; do
beet modify -a -y "id:$album_id" "$marker=1" >/dev/null
done < "$selected"
cleanup_marker() {
beet modify -a -y "$marker:1" "$marker!" >/dev/null 2>&1 || true
}
trap 'cleanup_marker; rm -rf "$tmpdir"' EXIT
print
print "Albums marked for pruning:"
beet ls -a "$marker:1"
print
beet stats "$marker:1" || true
print
print "Quarantine destination:"
print " $batch_dir"
if (( dry_run )); then
print
print "Dry run. No files moved and no database rows removed."
print
print "Preview move command:"
print " beet move -a -d ${(q)batch_dir} ${(q)marker:1}"
print
print "Preview DB removal command:"
print " beet remove -a ${(q)marker:1}"
exit 0
fi
print
print "This will:"
print " 1. Move selected albums out of the active library into:"
print " $batch_dir"
print " 2. Remove those albums from the beets database."
print " 3. Leave the moved files in the quarantine folder."
print
read -q "confirm?Proceed? [y/N] "
print
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
print "Aborted. Cleaning temporary marker."
exit 1
fi
mkdir -p "$batch_dir"
print
print "Moving selected albums to quarantine..."
beet move -a -d "$batch_dir" "$marker:1"
print
print "Verifying moved files exist under quarantine..."
missing=0
while IFS=$'\t' read -r album_id _rest; do
paths="$(beet ls -f '$path' "album_id:$album_id" || true)"
if [[ -z "$paths" ]]; then
print -u2 "warning: no paths found in DB for album id $album_id after move"
missing=1
continue
fi
while IFS= read -r path; do
[[ -z "$path" ]] && continue
if [[ "$path" != "$batch_dir"/* ]]; then
print -u2 "warning: path is not under quarantine for album id $album_id:"
print -u2 " $path"
missing=1
elif [[ ! -e "$path" ]]; then
print -u2 "warning: moved path does not exist for album id $album_id:"
print -u2 " $path"
missing=1
fi
done <<< "$paths"
done < "$selected"
if (( missing )); then
print -u2
print -u2 "Refusing to remove albums from the DB because verification failed."
print -u2 "The DB may now point some selected albums at the quarantine folder."
print -u2 "Inspect with:"
print -u2 " beet ls -a '$marker:1'"
print -u2 " beet ls -f '\$path' '$marker:1'"
exit 1
fi
print
print "Removing selected albums from beets DB only..."
beet remove -a -f "$marker:1"
print
print "Done."
print "Pruned files are here:"
print " $batch_dir"