Audio resilience: three-tier fallback for sunrise audio; add freesound download script

daily_sunrise_video.sh: priority chain is camera mic → library random MP3 →
overlay-only → sped video promotion.  If audio mixing fails a warning
notification fires and the script retries with overlay-only so a video is
always produced and uploaded.

download-sunrise-sounds.py: downloads CC-licensed ambient sounds from
freesound.org into 11 weather/season category folders (~275 files total at
25 per category), writing a manifest.json with attribution data.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
This commit is contained in:
Claude
2026-04-19 21:22:01 +00:00
parent 9fe0051f70
commit baf886fd23
2 changed files with 342 additions and 14 deletions
+49 -14
View File
@@ -140,27 +140,62 @@ DT="${DT}:x=w-tw-18:y=(h-th)/2"
DT="${DT}:shadowcolor=black@0.55:shadowx=1:shadowy=1"
# ── Step 3: Overlay + optional audio ─────────────────────────────────────────
# With audio: DT must go into -filter_complex (can't mix -vf and -filter_complex)
# Without audio: plain -vf is simpler and avoids any filter_complex overhead.
# If overlay fails either way, the sped video is promoted so upload still fires.
# Priority: (1) camera mic recording, (2) library fallback, (3) no audio.
# If audio mixing fails, step 3 retries with overlay-only so the video is
# always made. If overlay itself fails, the sped video is promoted so upload
# still fires.
final_video="$output_dir/$current_date-daily-sunrise.mp4"
audio_file="$image_dir/sunrise-audio.m4a"
fade_out=$(echo "scale=1; $SUNRISE_TARGET_SECS - 0.5" | bc)
# ── Pick audio source ─────────────────────────────────────────────────────────
audio_src=""
audio_offset="0"
cam_audio="$image_dir/sunrise-audio.m4a"
if [ "${AUDIO_ENABLED:-false}" = "true" ]; then
if [ -f "$cam_audio" ]; then
# Camera recording: offset centres the clip on actual sunrise
buffer_sec=$(( ${AUDIO_PRE_BUFFER_MIN:-2} * 60 ))
audio_offset=$(echo "scale=1; $SUNRISE_PRE_MIN * 60 + $buffer_sec - $SUNRISE_TARGET_SECS / 2" | bc)
audio_src="$cam_audio"
echo "Audio: camera recording offset=${audio_offset}s"
else
# Library fallback: random file from sunrise-sounds/
library_dir="$SCRIPT_DIR/sunrise-sounds"
if [ -d "$library_dir" ]; then
random_file=$(find "$library_dir" -name "*.mp3" | shuf -n 1 2>/dev/null || true)
if [ -n "$random_file" ]; then
audio_src="$random_file"
echo "Audio: library fallback $(basename "$audio_src")"
fi
fi
fi
fi
# ── Attempt overlay + audio (falls back to overlay-only if audio fails) ───────
audio_mixed=false
step3_ok=true
if [ "${AUDIO_ENABLED:-false}" = "true" ] && [ -f "$audio_file" ]; then
buffer_sec=$(( ${AUDIO_PRE_BUFFER_MIN:-2} * 60 ))
audio_offset=$(echo "scale=1; $SUNRISE_PRE_MIN * 60 + $buffer_sec - $SUNRISE_TARGET_SECS / 2" | bc)
fade_out=$(echo "scale=1; $SUNRISE_TARGET_SECS - 0.5" | bc)
echo "Step 3/3: overlay + camera audio (offset ${audio_offset}s) → $final_video"
ffmpeg -loglevel warning \
if [ -n "$audio_src" ]; then
echo "Step 3/3: overlay + audio → $final_video"
if ffmpeg -loglevel warning \
-i "$sped_video" \
-ss "$audio_offset" -t "$SUNRISE_TARGET_SECS" -i "$audio_file" \
-ss "$audio_offset" -t "$SUNRISE_TARGET_SECS" -i "$audio_src" \
-filter_complex "[0:v]${DT}[vout];[1:a]afade=t=in:st=0:d=0.5,afade=t=out:st=${fade_out}:d=0.5[aout]" \
-map "[vout]" -map "[aout]" \
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" \
-c:a aac -b:a 128k \
-y "$final_video" || step3_ok=false
else
-y "$final_video"; then
audio_mixed=true
else
echo "Audio mix failed — retrying with overlay only"
"$SCRIPT_DIR/notify.sh" "WARNING: sunrise audio mix failed $current_date" \
"Audio could not be mixed — falling back to overlay-only" || true
[ "$audio_src" = "$cam_audio" ] && rm -f "$cam_audio"
fi
fi
if ! $audio_mixed; then
echo "Step 3/3: overlay (no audio) → $final_video"
ffmpeg -loglevel warning \
-i "$sped_video" \
@@ -173,7 +208,7 @@ if $step3_ok; then
actual_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$final_video")
echo "Done: $final_video (${actual_dur}s, sunrise at ${SR_TIME})"
rm -f "$sped_video"
[ -f "$audio_file" ] && rm -f "$audio_file"
[ -f "$cam_audio" ] && rm -f "$cam_audio"
"$SCRIPT_DIR/notify.sh" "Sunrise ready: $current_date" \
"$(basename "$final_video")${actual_dur}s, sunrise at ${SR_TIME}" || true
else