Add per-step resilience and notifications across all video pipeline scripts
Each pipeline now saves intermediate files permanently before the step most likely to fail, so a partial failure leaves a recoverable artifact: - daily_sunrise_video.sh: speed-adjusted video (*-sped.mp4) survives if the drawtext overlay step fails; deleted automatically on success - montage-mvt.sh: speed-adjusted video (*-Sped.mp4) survives if the music-mux + attribution overlay step fails; deleted on success - 4-seasons.sh / montage-mvt.sh / year-end-join.sh: explicit ffmpeg error handling with targeted notify.sh calls naming the failed step and the surviving artifact path - montage-mvt.sh / 4-seasons.sh / year-end-join.sh: clip-duration-sum vs music-duration drift check warns when cumulative float error exceeds 2 s https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
This commit is contained in:
+39
-8
@@ -51,6 +51,8 @@ mkdir -p "$output_dir"
|
||||
|
||||
music_file=$(find "$music_base_dir" -type f -iname "*${SEASON}*Mvt*${MVT_NUM}*" | sort | head -n 1)
|
||||
if [ -z "$music_file" ]; then
|
||||
"$SCRIPT_DIR/notify.sh" "FAILED: montage $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
|
||||
"Music file not found in $music_base_dir" || true
|
||||
echo "ERROR: Music file not found for $SEASON Mvt $MVT_NUM in $music_base_dir"
|
||||
exit 1
|
||||
fi
|
||||
@@ -60,11 +62,28 @@ echo "Music: $(basename "$music_file") (${music_duration}s)"
|
||||
# ── Collect sorted daily clips ────────────────────────────────────────────────
|
||||
mapfile -d '' daily_clips < <(find "$video_dir" -maxdepth 1 -name "*-final.mp4" -print0 | sort -z)
|
||||
if [ "${#daily_clips[@]}" -eq 0 ]; then
|
||||
"$SCRIPT_DIR/notify.sh" "FAILED: montage $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
|
||||
"No *-final.mp4 clips found in $video_dir" || true
|
||||
echo "ERROR: No *-final.mp4 clips in $video_dir"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found ${#daily_clips[@]} daily clips"
|
||||
|
||||
# ── Validate clip duration sum against music ──────────────────────────────────
|
||||
total_clip_dur=0
|
||||
for clip in "${daily_clips[@]}"; do
|
||||
d=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$clip")
|
||||
total_clip_dur=$(echo "scale=3; $total_clip_dur + $d" | bc)
|
||||
done
|
||||
clip_drift=$(echo "scale=3; $total_clip_dur - $music_duration" | bc | sed 's/^-//')
|
||||
echo "Sum of clips: ${total_clip_dur}s Music: ${music_duration}s Drift: ${clip_drift}s"
|
||||
if awk "BEGIN{exit !($clip_drift > 2.0)}"; then
|
||||
echo "WARNING: clip total differs from music by ${clip_drift}s — speed pass will compensate"
|
||||
"$SCRIPT_DIR/notify.sh" \
|
||||
"WARNING: $SEASON Mvt$MVT_NUM clip drift ${clip_drift}s ($ASTRO_YEAR)" \
|
||||
"Clips sum ${total_clip_dur}s vs music ${music_duration}s" || true
|
||||
fi
|
||||
|
||||
# ── Source resolution ─────────────────────────────────────────────────────────
|
||||
VIDEO_W=$(ffprobe -v error -select_streams v:0 -show_entries stream=width -of csv=p=0 "${daily_clips[0]}")
|
||||
VIDEO_H=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "${daily_clips[0]}")
|
||||
@@ -105,18 +124,25 @@ pad=${VIDEO_W}:${VIDEO_H}:(ow-iw)/2:(oh-ih)/2,setsar=1" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_MONTAGE" -an \
|
||||
-y "$temp_concat"
|
||||
|
||||
# ── Step 2: Speed-adjust to match music duration exactly ──────────────────────
|
||||
# ── Step 2: Speed-adjust — saved permanently so music/overlay failure is recoverable ─
|
||||
# Deleted automatically if step 3 succeeds.
|
||||
concat_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$temp_concat")
|
||||
speed_factor=$(echo "scale=6; $concat_dur / $music_duration" | bc)
|
||||
echo "Step 2/3: speed $speed_factor (raw=${concat_dur}s → music=${music_duration}s)"
|
||||
|
||||
temp_sped=$(mktemp --suffix=.mp4); TMPFILES+=("$temp_sped")
|
||||
ffmpeg -loglevel warning \
|
||||
sped_file="$output_dir/${MVT_START}_${SEASON}_Mvt${MVT_NUM}-Sped.mp4"
|
||||
if ! ffmpeg -loglevel warning \
|
||||
-i "$temp_concat" \
|
||||
-vf "setpts=PTS/$speed_factor" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_MONTAGE" -an \
|
||||
-t "$music_duration" -y "$temp_sped"
|
||||
-t "$music_duration" -y "$sped_file"; then
|
||||
rm -f "$sped_file"
|
||||
"$SCRIPT_DIR/notify.sh" "FAILED: montage step 2/3 (speed) $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
|
||||
"speed-adjust failed — concat temp lost; re-run montage-mvt.sh" || true
|
||||
exit 1
|
||||
fi
|
||||
rm "$temp_concat"
|
||||
echo "Speed-adjusted: $sped_file (saved — music+overlay still pending)"
|
||||
|
||||
# ── Step 3: Music + fades + attribution overlay ───────────────────────────────
|
||||
# Attribution overlay: semi-transparent bar across the top for the first
|
||||
@@ -152,16 +178,21 @@ DT="${DT}:alpha='${ALPHA}':enable='${ENABLE}'"
|
||||
|
||||
output_file="$output_dir/${MVT_START}_${SEASON}_Mvt${MVT_NUM}-Montage.mp4"
|
||||
|
||||
ffmpeg -loglevel warning \
|
||||
-i "$temp_sped" -i "$music_file" \
|
||||
# If this fails, $sped_file survives for manual recovery (re-run step 3 only).
|
||||
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];\
|
||||
[1:a]afade=t=in:st=0:d=${FADE_DUR},afade=t=out:st=${fade_out_start}:d=${FADE_DUR}[aout]" \
|
||||
-map "[vout]" -map "[aout]" \
|
||||
-c:v libx264 -pix_fmt yuv420p -c:a aac -b:a "$AUDIO_BITRATE" \
|
||||
-t "$music_duration" -y "$output_file"
|
||||
rm "$temp_sped"
|
||||
-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
|
||||
fi
|
||||
|
||||
rm -f "$sped_file"
|
||||
echo "Done: $output_file"
|
||||
|
||||
# ── Notify ────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user