190 lines
3.8 KiB
Bash
Executable File
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
|