Files
beets-setup/scripts/postprocess/postprocess-set-audit-status
2026-05-18 15:33:48 -04:00

190 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
force=0
dry_run=0
batch_size=250
usage() {
cat >&2 <<'EOF'
usage:
fix-audit.sh [--force] [--dry-run] [--batch-size N]
Marks album-level library_audited=1 when every track in that album has
track-level library_audited=1.
By default, albums that already have album-level library_audited=1 are skipped.
Options:
--force Re-apply the album-level marker even if already set.
--dry-run, -n Show what would be modified without modifying anything.
--batch-size N Number of album IDs per beet modify call. Default: 250.
-h, --help Show this help.
EOF
}
while (($#)); do
case "$1" in
--force)
force=1
;;
--dry-run|-n)
dry_run=1
;;
--batch-size)
shift
[[ $# -gt 0 ]] || {
echo "error: --batch-size requires a value" >&2
exit 2
}
batch_size="$1"
[[ "$batch_size" =~ ^[0-9]+$ && "$batch_size" -gt 0 ]] || {
echo "error: --batch-size must be a positive integer" >&2
exit 2
}
;;
-h|--help)
usage
exit 0
;;
*)
echo "error: unknown argument: $1" >&2
usage
exit 2
;;
esac
shift
done
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
fully_audited="$tmpdir/fully-audited-albums"
already_marked="$tmpdir/already-marked-albums"
todo="$tmpdir/todo"
# Find albums where every item has item-level library_audited exactly equal to 1.
#
# Output format:
# album_id<TAB>library_audited
#
# Any track with missing, empty, or non-1 library_audited disqualifies the album.
beet ls -f $'$album_id\t$library_audited' |
awk -F '\t' '
$1 == "" { next }
{
album_id = $1
audited = $2
seen[album_id] = 1
if (audited !~ /^1$/) {
not_fully_audited[album_id] = 1
}
}
END {
for (album_id in seen) {
if (!(album_id in not_fully_audited)) {
print album_id
}
}
}
' |
LC_ALL=C sort -u > "$fully_audited"
if (( force )); then
cp "$fully_audited" "$todo"
: > "$already_marked"
else
# Find albums that already have album-level library_audited exactly equal to 1.
beet ls -a -f $'$id\t$library_audited' |
awk -F '\t' '
$1 != "" && $2 ~ /^1$/ {
print $1
}
' |
LC_ALL=C sort -u > "$already_marked"
# comm requires both inputs to be sorted in the same lexical order.
LC_ALL=C comm -23 "$fully_audited" "$already_marked" > "$todo"
fi
fully_count="$(wc -l < "$fully_audited" | tr -d ' ')"
already_count="$(wc -l < "$already_marked" | tr -d ' ')"
todo_count="$(wc -l < "$todo" | tr -d ' ')"
echo "Fully audited albums: $fully_count"
if (( force )); then
echo "Already-marked albums: skipped check due to --force"
else
echo "Already-marked albums: $already_count"
fi
echo "Albums to mark/update: $todo_count"
echo "Batch size: $batch_size"
if (( todo_count == 0 )); then
exit 0
fi
mapfile -t ids < "$todo"
batch=()
batch_num=0
processed_total=0
flush_batch() {
((${#batch[@]} > 0)) || return 0
batch_num=$((batch_num + 1))
local joined
joined="$(IFS='|'; echo "${batch[*]}")"
local query
query="id::^(${joined})$"
echo
echo "Batch $batch_num: ${#batch[@]} album(s)"
if (( dry_run )); then
beet ls -a "$query"
else
beet modify -a -I -M -W -y "$query" library_audited=1
fi
processed_total=$((processed_total + ${#batch[@]}))
batch=()
}
for album_id in "${ids[@]}"; do
[[ -z "$album_id" ]] && continue
if [[ ! "$album_id" =~ ^[0-9]+$ ]]; then
echo "warning: skipping non-numeric album id: $album_id" >&2
continue
fi
batch+=("$album_id")
if ((${#batch[@]} >= batch_size)); then
flush_batch
fi
done
flush_batch
echo
if (( dry_run )); then
echo "Dry run complete. Albums that would be marked/updated: $processed_total"
else
echo "Done. Albums marked/updated: $processed_total"
fi