#!/bin/bash # verify-mvt.sh — review a completed movement montage and manage source JPEGs. # # Usage: # ./verify-mvt.sh # # Shows technical checks on the montage, lets you play it, then: # (p) play — open in ffplay / mpv / vlc (whichever is available) # (g) good — delete source JPEG folders for all movement days # (r) redo — re-run music+overlay only (keeps sped file if present) # (R) redo-all — re-run full montage from daily clips # (q) quit — exit without changes set -euo pipefail SCRIPT_DIR="$(dirname "$(realpath "$0")")" source "$SCRIPT_DIR/sky-cam.conf" export TIMEZONE # ── Args ────────────────────────────────────────────────────────────────────── ASTRO_YEAR="${1:-}" SEASON="${2:-}" MVT_NUM="${3:-}" CAM_NAME="${4:-}" if [ -z "$ASTRO_YEAR" ] || [ -z "$SEASON" ] || [ -z "$MVT_NUM" ] || [ -z "$CAM_NAME" ]; then echo "Usage: $0 " echo " e.g. $0 2025 Spring 1 east" exit 1 fi # ── Locate montage file ─────────────────────────────────────────────────────── montage_dir="$MOVIES_DIR/$CAM_NAME/$ASTRO_YEAR/$SEASON/Mvt${MVT_NUM}/montage" montage_file=$(find "$montage_dir" -maxdepth 1 -name "*-Montage.mp4" 2>/dev/null | sort | tail -n 1) if [ -z "$montage_file" ] || [ ! -f "$montage_file" ]; then echo "ERROR: No montage file found in $montage_dir" echo " Run: ./montage-mvt.sh to build it first." exit 1 fi # ── Get movement date range from season_info ────────────────────────────────── # Extract date from montage filename (format: YYYY-MM-DD_Season_MvtN-Montage.mp4) mvt_start_date=$(basename "$montage_file" | cut -d_ -f1) _info="$(python3 "$SCRIPT_DIR/season_info.py" "$mvt_start_date")" eval "$_info" # Provides: MVT_START MVT_END SEASON MVT_NUM DAYS_IN_MVT # ── Technical checks ────────────────────────────────────────────────────────── echo "" echo "══════════════════════════════════════════════════════" echo " $SEASON Mvt $MVT_NUM ($ASTRO_YEAR) [$CAM_NAME]" echo " Movement: $MVT_START → $MVT_END ($DAYS_IN_MVT days)" echo " File: $(basename "$montage_file")" echo "══════════════════════════════════════════════════════" actual_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$montage_file" 2>/dev/null || echo "?") stream_types=$(ffprobe -v error -show_entries stream=codec_type -of csv=p=0 "$montage_file" 2>/dev/null || echo "") has_video=$(echo "$stream_types" | grep -c "video" 2>/dev/null || echo 0) has_audio=$(echo "$stream_types" | grep -c "audio" 2>/dev/null || echo 0) file_size=$(du -sh "$montage_file" | cut -f1) # Count finals that went into the montage finals_dir="$MOVIES_DIR/$CAM_NAME/$ASTRO_YEAR/$SEASON/Mvt${MVT_NUM}" final_count=$(find "$finals_dir" -maxdepth 1 -name "*-final.mp4" 2>/dev/null | wc -l) # Check music file music_base_dir="$MUSIC_DIR" [ ! -d "$music_base_dir" ] && music_base_dir="$SCRIPT_DIR/music" music_file=$(find "$music_base_dir" -type f -iname "*${SEASON}*" \ | grep -iE "Mvt[^0-9]*${MVT_NUM}[^0-9]" | sort | head -n 1) music_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$music_file" 2>/dev/null || echo "?") dur_delta="?" if [ "$actual_dur" != "?" ] && [ "$music_dur" != "?" ]; then dur_delta=$(echo "scale=2; $actual_dur - $music_dur" | bc | sed 's/^-//') fi echo "" printf " Duration : %ss (music: %ss drift: %ss)\n" "$actual_dur" "$music_dur" "$dur_delta" printf " Video stream : %s\n" "$([ "$has_video" -gt 0 ] && echo "ok" || echo "MISSING")" printf " Audio stream : %s\n" "$([ "$has_audio" -gt 0 ] && echo "ok" || echo "MISSING")" printf " File size : %s\n" "$file_size" printf " Daily clips : %d / %d days\n" "$final_count" "$DAYS_IN_MVT" printf " Music : %s\n" "$(basename "${music_file:-NOT FOUND}")" # Count JPEG folders and sum their sizes jpeg_count=0 jpeg_bytes=0 d="$MVT_START" while [[ "$d" < "$MVT_END" || "$d" == "$MVT_END" ]]; do dir="$BASE_DIR/$CAM_NAME/$d" if [ -d "$dir" ]; then jpeg_count=$(( jpeg_count + 1 )) jpeg_bytes=$(( jpeg_bytes + $(du -sb "$dir" 2>/dev/null | cut -f1 || echo 0) )) fi d=$(date -d "$d + 1 day" +%Y-%m-%d) done if [ "$jpeg_count" -gt 0 ]; then jpeg_size=$(numfmt --to=iec-i --suffix=B "$jpeg_bytes" 2>/dev/null || echo "${jpeg_bytes}B") printf " JPEG folders : %d date folders, %s (%s → %s)\n" \ "$jpeg_count" "$jpeg_size" "$MVT_START" "$MVT_END" else printf " JPEG folders : none found (already deleted)\n" fi echo "" # ── Interactive menu ────────────────────────────────────────────────────────── while true; do echo " (p) play the montage" echo " (g) good — delete JPEG folders for this movement" echo " (r) redo music+overlay (keeps daily clips)" echo " (R) redo all (re-concat from daily clips)" echo " (q) quit" echo "" read -rp " Choice: " choice case "$choice" in p|P) if command -v mpv &>/dev/null; then mpv "$montage_file" & elif command -v vlc &>/dev/null; then vlc "$montage_file" & elif command -v ffplay &>/dev/null; then ffplay "$montage_file" & else echo " No player found (install mpv, vlc, or ffplay)" fi ;; g|G) if [ "$jpeg_count" -eq 0 ]; then echo " No JPEG folders found for this movement — nothing to delete." continue fi echo "" echo " Will delete $jpeg_count JPEG date folders ($MVT_START → $MVT_END) for camera [$CAM_NAME] — ${jpeg_size}." echo " The montage video and daily clips are kept." read -rp " Confirm? [yes/no]: " confirm if [ "$confirm" = "yes" ]; then deleted=0 d="$MVT_START" while [[ "$d" < "$MVT_END" || "$d" == "$MVT_END" ]]; do dir="$BASE_DIR/$CAM_NAME/$d" if [ -d "$dir" ]; then rm -rf "$dir" echo " deleted $dir" deleted=$(( deleted + 1 )) fi d=$(date -d "$d + 1 day" +%Y-%m-%d) done echo " Done — $deleted JPEG folders deleted." "$SCRIPT_DIR/notify.sh" \ "JPEGs deleted: $SEASON Mvt$MVT_NUM ($ASTRO_YEAR) [$CAM_NAME]" \ "$deleted date folders removed after manual verification" || true else echo " Cancelled." fi ;; r) echo " Re-running music+overlay step..." "$SCRIPT_DIR/montage-mvt.sh" "$CAM_NAME" "$mvt_start_date" break ;; R) echo " Re-running full montage (concat + speed + music + overlay)..." # Remove sped file so montage-mvt.sh starts from scratch find "$MOVIES_DIR/$CAM_NAME/$ASTRO_YEAR/$SEASON/Mvt${MVT_NUM}" \ -maxdepth 1 -name "*-Sped.mp4" -delete 2>/dev/null || true "$SCRIPT_DIR/montage-mvt.sh" "$CAM_NAME" "$mvt_start_date" break ;; q|Q) break ;; *) echo " Unknown choice — enter p, g, r, R, or q." ;; esac echo "" done