- sky-cam.conf: CAMERAS now includes east, north, south with staggered SCHEDULE_SEASONS times (01:00, 01:30, 02:00); fullday schedules and tuning removed - install.sh: fullday service/timer generation removed entirely - fullday-video.sh + systemd units: deleted - montage-mvt.sh: post-build technical checks (duration drift, video/audio stream presence) added; notification now includes check results and the exact verify-mvt.sh command to run - verify-mvt.sh: new interactive CLI for montage review — shows checks, plays video, deletes source JPEG folders on approval, or re-runs montage https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
200 lines
8.5 KiB
Bash
Executable File
200 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# verify-mvt.sh — review a completed movement montage and manage source JPEGs.
|
|
#
|
|
# Usage:
|
|
# ./verify-mvt.sh <ASTRO_YEAR> <SEASON> <MVT_NUM> [CAM_NAME]
|
|
#
|
|
# 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:-$SUNRISE_CAM}"
|
|
|
|
if [ -z "$ASTRO_YEAR" ] || [ -z "$SEASON" ] || [ -z "$MVT_NUM" ]; then
|
|
echo "Usage: $0 <ASTRO_YEAR> <SEASON> <MVT_NUM> [CAM_NAME]"
|
|
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 ──────────────────────────────────
|
|
_info="$(python3 "$SCRIPT_DIR/season_info.py" "$MVT_START" 2>/dev/null)" 2>/dev/null || true
|
|
|
|
# Use the sped file's date prefix to find a representative date in the movement
|
|
sped_file=$(find "$MOVIES_DIR/$CAM_NAME/$ASTRO_YEAR/$SEASON/Mvt${MVT_NUM}" \
|
|
-maxdepth 1 -name "*-Sped.mp4" 2>/dev/null | head -n 1 || true)
|
|
|
|
# Get MVT_START from the 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 available for deletion
|
|
jpeg_count=0
|
|
jpeg_size=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 ))
|
|
fi
|
|
d=$(date -d "$d + 1 day" +%Y-%m-%d)
|
|
done
|
|
|
|
if [ "$jpeg_count" -gt 0 ]; then
|
|
jpeg_size=$(du -sh "$BASE_DIR/$CAM_NAME" --exclude="*" 2>/dev/null || true)
|
|
# Sum only the movement date folders
|
|
total_jpeg_bytes=0
|
|
d="$MVT_START"
|
|
while [[ "$d" < "$MVT_END" || "$d" == "$MVT_END" ]]; do
|
|
dir="$BASE_DIR/$CAM_NAME/$d"
|
|
[ -d "$dir" ] && total_jpeg_bytes=$(du -sb "$dir" 2>/dev/null | cut -f1 || echo 0) || true
|
|
d=$(date -d "$d + 1 day" +%Y-%m-%d)
|
|
done
|
|
printf " JPEG folders : %d date folders (%s → %s)\n" \
|
|
"$jpeg_count" "$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]."
|
|
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" "$mvt_start_date" "$CAM_NAME"
|
|
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" "$mvt_start_date" "$CAM_NAME"
|
|
break
|
|
;;
|
|
|
|
q|Q)
|
|
break
|
|
;;
|
|
|
|
*)
|
|
echo " Unknown choice — enter p, g, r, R, or q."
|
|
;;
|
|
esac
|
|
echo ""
|
|
done
|