Degrade gracefully on overlay/audio failure; notify on upload success

Sunrise:
- Overlay failure no longer exits 1; the speed-only video is renamed to the
  expected final path so OnSuccess still fires and the upload still happens
- sunrise2mm.py now notifies on every upload outcome (success, post failure,
  file-not-found) via notify.sh so the user always knows whether the video
  reached Mattermost

Montage:
- Music+overlay failure no longer exits 1; the sped video is promoted to the
  *-Montage.mp4 path so year-end-join can still include this movement (as a
  silent, no-overlay clip) and the user is notified of the degraded output
- Year-end join trigger is now outside the success/failure branch so it fires
  in both cases

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
This commit is contained in:
Claude
2026-04-19 18:42:46 +00:00
parent 9c929f28d8
commit b911b228bb
3 changed files with 53 additions and 26 deletions
+13 -12
View File
@@ -140,22 +140,23 @@ DT="${DT}:x=w-tw-18:y=(h-th)/2"
DT="${DT}:shadowcolor=black@0.55:shadowx=1:shadowy=1"
# ── Step 3: Overlay — sunrise time burned in ──────────────────────────────────
# If this fails, $sped_video survives at its permanent path for manual recovery.
# If overlay fails, the sped video is promoted to the upload target so
# OnSuccess still fires and the upload happens (without timestamp overlay).
final_video="$output_dir/$current_date-daily-sunrise.mp4"
echo "Step 3/3: overlay → $final_video"
if ! ffmpeg -loglevel warning \
if ffmpeg -loglevel warning \
-i "$sped_video" \
-vf "${DT}" \
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" -an \
-y "$final_video"; then
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise overlay $current_date" \
"Overlay failed — speed-only video saved: $(basename "$sped_video")" || true
exit 1
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"
"$SCRIPT_DIR/notify.sh" "Sunrise ready: $current_date" \
"$(basename "$final_video")${actual_dur}s, sunrise at ${SR_TIME}" || true
else
mv "$sped_video" "$final_video"
echo "Overlay failed — promoting speed-only video as upload target"
"$SCRIPT_DIR/notify.sh" "WARNING: sunrise overlay failed $current_date" \
"Overlay step failed — uploading speed-only video (no timestamp)" || true
fi
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"
"$SCRIPT_DIR/notify.sh" "Sunrise ready: $current_date" \
"$(basename "$final_video")${actual_dur}s, sunrise at ${SR_TIME}" || true
+15 -14
View File
@@ -178,8 +178,9 @@ DT="${DT}:alpha='${ALPHA}':enable='${ENABLE}'"
output_file="$output_dir/${MVT_START}_${SEASON}_Mvt${MVT_NUM}-Montage.mp4"
# If this fails, $sped_file survives for manual recovery (re-run step 3 only).
if ! ffmpeg -loglevel warning \
# If music/overlay fails, the sped video is promoted to the output path so
# year-end-join can still include this movement (as a silent, no-overlay clip).
if ffmpeg -loglevel warning \
-i "$sped_file" -i "$music_file" \
-filter_complex \
"[0:v]fade=t=in:st=0:d=${FADE_DUR},fade=t=out:st=${fade_out_start}:d=${FADE_DUR},${DT}[vout];\
@@ -187,20 +188,20 @@ if ! ffmpeg -loglevel warning \
-map "[vout]" -map "[aout]" \
-c:v libx264 -pix_fmt yuv420p -c:a aac -b:a "$AUDIO_BITRATE" \
-t "$music_duration" -y "$output_file"; then
"$SCRIPT_DIR/notify.sh" "FAILED: montage step 3/3 (music+overlay) $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
"Music/overlay failed — sped video saved: $(basename "$sped_file")" || true
exit 1
rm -f "$sped_file"
echo "Done: $output_file"
"$SCRIPT_DIR/notify.sh" \
"Montage ready: $SEASON Mvt $MVT_NUM ($ASTRO_YEAR)" \
"$(basename "$output_file")${music_duration}s" \
|| true
else
mv "$sped_file" "$output_file"
echo "Music+overlay failed — silent video promoted to: $output_file"
"$SCRIPT_DIR/notify.sh" \
"WARNING: montage audio+overlay failed — $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
"Silent video saved as $(basename "$output_file") — re-run montage-mvt.sh to retry" || true
fi
rm -f "$sped_file"
echo "Done: $output_file"
# ── Notify ────────────────────────────────────────────────────────────────────
"$SCRIPT_DIR/notify.sh" \
"Montage ready: $SEASON Mvt $MVT_NUM ($ASTRO_YEAR)" \
"$(basename "$output_file")${music_duration}s" \
|| true # notifications are best-effort
# ── Trigger year-end join on last Autumn movement ────────────────────────────
if [ "$SEASON" = "Autumn" ] && [ "$MVT_NUM" = "3" ]; then
echo "Last movement of astronomical year $ASTRO_YEAR — triggering year-end join..."
+25
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import requests
import os
import subprocess
from datetime import datetime
import pathlib
@@ -91,9 +92,33 @@ if os.path.isfile(final_video_file):
if message_response.status_code == 201:
print("Message posted successfully.")
subprocess.run(
[str(_here / 'notify.sh'),
f"Sunrise uploaded: {today_date_str}",
f"Video posted to Mattermost successfully"],
check=False
)
else:
print(f"Failed to post message: {message_response.text}")
subprocess.run(
[str(_here / 'notify.sh'),
f"FAILED: sunrise Mattermost post {today_date_str}",
f"File uploaded but post failed: {message_response.status_code}"],
check=False
)
else:
print(f"Failed to upload {final_video_file}: {response.text}")
subprocess.run(
[str(_here / 'notify.sh'),
f"FAILED: sunrise upload {today_date_str}",
f"Mattermost file upload failed: {response.status_code}"],
check=False
)
else:
print(f"No video file found at {final_video_file}.")
subprocess.run(
[str(_here / 'notify.sh'),
f"FAILED: sunrise upload {today_date_str}",
f"Video file not found: {final_video_file}"],
check=False
)