From a29769e0f46aa2e2b9ed3662d58d1395a665b5d5 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 20 Jul 2026 15:56:26 -0500 Subject: fix(sounds): make normalize-notify-sounds write atomically and clean up The script re-encoded each notify sound with `cat "$tmp" > "$f"`, which truncates the target first -- a short or empty encode left a corrupt, repo-tracked sound file. The mktemp also had no trap, so an interrupted encode leaked a temp. Now it resolves the real target with `readlink -f` (SOUND_DIR is usually the stow-symlinked ~/.local copy), stages the temp beside it, guards on a non-empty encode, and moves it into place. The mv is atomic on one filesystem and replaces the real file, so the stow symlink still points at it. An EXIT trap removes the in-flight temp when a failed encode aborts the run. --- scripts/normalize-notify-sounds.sh | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/normalize-notify-sounds.sh b/scripts/normalize-notify-sounds.sh index 72c4c33..7dffbbc 100755 --- a/scripts/normalize-notify-sounds.sh +++ b/scripts/normalize-notify-sounds.sh @@ -28,6 +28,12 @@ shopt -s nullglob files=("$SOUND_DIR"/*.ogg) (( ${#files[@]} )) || { echo "No .ogg files in $SOUND_DIR" >&2; exit 1; } +# Clean up the in-flight temp file on any exit (a failed encode under set -e +# aborts mid-loop, so the trap is what stops the temp from leaking). +tmp="" +cleanup() { [ -n "${tmp:-}" ] && rm -f "$tmp"; return 0; } +trap cleanup EXIT + for f in "${files[@]}"; do mean=$(ffmpeg -hide_banner -nostats -i "$f" -af volumedetect -f null /dev/null 2>&1 \ | grep -oP 'mean_volume: \K[-0-9.]+' || true) @@ -36,14 +42,22 @@ for f in "${files[@]}"; do continue fi gain=$(awk -v t="$TARGET_DB" -v m="$mean" 'BEGIN { printf "%.1f", t - m }') - tmp=$(mktemp --suffix=.ogg) + # Resolve the real target (SOUND_DIR is often the stow-symlinked ~/.local + # copy) and stage the temp beside it, so the mv is atomic on the same + # filesystem and replacing the real file leaves the stow symlink pointing + # at it. A truncate-in-place (cat > "$f") would have corrupted the tracked + # file if the encode produced a short or empty output. + target=$(readlink -f "$f") + tmp=$(mktemp --suffix=.ogg --tmpdir="$(dirname "$target")") ffmpeg -hide_banner -loglevel error -y -i "$f" \ -af "volume=${gain}dB" -c:a libvorbis -q:a 6 "$tmp" - # Write through the file rather than mv over it: when SOUND_DIR is the - # stow-symlinked ~/.local copy, mv would replace the symlink with a real - # file and decouple it from the repo. cat preserves the symlink target. - cat "$tmp" > "$f" - rm -f "$tmp" + if [ ! -s "$tmp" ]; then + echo "skip (empty re-encode): $f" >&2 + rm -f "$tmp"; tmp="" + continue + fi + mv -f "$tmp" "$target" + tmp="" printf "%-14s mean %7s dB gain %+6s dB -> target %s dB\n" \ "$(basename "$f")" "$mean" "$gain" "$TARGET_DB" done -- cgit v1.2.3