Merge pull request #18 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
This commit is contained in:
@@ -1,70 +0,0 @@
|
||||
#!/bin/bash
|
||||
# fullday-video.sh — create a natural-speed timelapse of yesterday's full day of
|
||||
# images at a fixed frame rate. Files older than RETENTION_DAYS are deleted
|
||||
# automatically. Sunrise 10-second uploads and montage videos are NOT touched.
|
||||
#
|
||||
# Run once per day from cron (e.g. 02:00 daily, after 4-seasons.sh).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||
source "$SCRIPT_DIR/sky-cam.conf"
|
||||
|
||||
# Camera name: first argument overrides conf (systemd passes it via ExecStart).
|
||||
CAM_NAME="${1:-$SUNRISE_CAM}"
|
||||
|
||||
# ── Configuration ──────────────────────────────────────────────────────────────
|
||||
base_dir="$BASE_DIR"
|
||||
# FULLDAY_FPS and RETENTION_DAYS come from sky-cam.conf
|
||||
|
||||
# ── Date / paths ──────────────────────────────────────────────────────────────
|
||||
yesterday=$(date --date="yesterday" +%Y-%m-%d)
|
||||
image_dir="$base_dir/$CAM_NAME/$yesterday"
|
||||
output_dir="$MOVIES_DIR/$CAM_NAME/fullday"
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# ── Purge old full-day videos ─────────────────────────────────────────────────
|
||||
echo "Removing full-day videos older than $RETENTION_DAYS days..."
|
||||
find "$output_dir" -maxdepth 1 -name "*-fullday.mp4" -mtime "+$RETENTION_DAYS" -delete
|
||||
|
||||
# ── Sanity checks ─────────────────────────────────────────────────────────────
|
||||
if [ ! -d "$image_dir" ]; then
|
||||
echo "WARNING: Image directory $image_dir not found — skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
num_images=$(find "$image_dir" -type f -name "*.jpg" | wc -l)
|
||||
if [ "$num_images" -lt 1 ]; then
|
||||
echo "WARNING: No images in $image_dir — skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
frame_dur=$(echo "scale=6; 1 / $FULLDAY_FPS" | bc)
|
||||
approx_min=$(echo "scale=1; $num_images / $FULLDAY_FPS / 60" | bc)
|
||||
echo "Images: $num_images at ${FULLDAY_FPS} fps ≈ ${approx_min} min"
|
||||
|
||||
# ── Build concat list with per-frame duration ─────────────────────────────────
|
||||
# The concat demuxer needs an explicit duration per image; the last file is
|
||||
# repeated without a duration to correctly anchor the final frame's pts.
|
||||
temp_list=$(mktemp --suffix=.txt)
|
||||
trap 'rm -f "$temp_list"' EXIT
|
||||
|
||||
find "$image_dir" -type f -name "*.jpg" | sort | while read -r img; do
|
||||
printf "file '%s'\nduration %s\n" "$img" "$frame_dur"
|
||||
done > "$temp_list"
|
||||
|
||||
# Append last image again (required by ffmpeg concat demuxer for images)
|
||||
last_img=$(find "$image_dir" -type f -name "*.jpg" | sort | tail -n 1)
|
||||
printf "file '%s'\n" "$last_img" >> "$temp_list"
|
||||
|
||||
# ── Encode — native camera resolution, no audio ───────────────────────────────
|
||||
output_file="$output_dir/${yesterday}-fullday.mp4"
|
||||
echo "Creating: $output_file"
|
||||
|
||||
ffmpeg -loglevel warning \
|
||||
-f concat -safe 0 -i "$temp_list" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_FULLDAY" \
|
||||
-an -y "$output_file"
|
||||
|
||||
actual_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$output_file")
|
||||
echo "Done: $output_file (${actual_dur}s / $(echo "scale=1; $actual_dur/60" | bc) min)"
|
||||
+3
-15
@@ -176,12 +176,11 @@ if [ "${AUDIO_ENABLED:-false}" = "true" ]; then
|
||||
timers+=("sky-cam-audio-capture")
|
||||
fi
|
||||
|
||||
# ── Per-camera Four Seasons and full-day jobs ─────────────────────────────────
|
||||
# Reads CAMERAS, SCHEDULE_SEASONS_<cam>, SCHEDULE_FULLDAY_<cam> from sky-cam.conf.
|
||||
# Scripts receive the camera name as $1 so they know which camera to process.
|
||||
# ── Per-camera Four Seasons jobs ─────────────────────────────────────────────
|
||||
# Reads CAMERAS and SCHEDULE_SEASONS_<cam> from sky-cam.conf.
|
||||
# Script receives the camera name as $1 so it knows which camera to process.
|
||||
for cam in "${CAMERAS[@]}"; do
|
||||
sched_seasons_var="SCHEDULE_SEASONS_${cam}"
|
||||
sched_fullday_var="SCHEDULE_FULLDAY_${cam}"
|
||||
|
||||
if [ -n "${!sched_seasons_var:-}" ]; then
|
||||
write_service "sky-cam-seasons-${cam}" \
|
||||
@@ -194,17 +193,6 @@ for cam in "${CAMERAS[@]}"; do
|
||||
else
|
||||
echo " WARNING: SCHEDULE_SEASONS_${cam} not set — skipping seasons timer for ${cam}"
|
||||
fi
|
||||
|
||||
if [ -n "${!sched_fullday_var:-}" ]; then
|
||||
write_service "sky-cam-fullday-${cam}" \
|
||||
"${cam}: full-day timelapse" \
|
||||
"fullday-video.sh ${cam}"
|
||||
write_timer "sky-cam-fullday-${cam}" "${cam}: full-day timelapse" \
|
||||
"${!sched_fullday_var}"
|
||||
timers+=("sky-cam-fullday-${cam}")
|
||||
else
|
||||
echo " WARNING: SCHEDULE_FULLDAY_${cam} not set — skipping fullday timer for ${cam}"
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Generate .env.example ────────────────────────────────────────────────────
|
||||
|
||||
+11
-6
@@ -29,15 +29,19 @@ source "$SCRIPT_DIR/sky-cam.conf"
|
||||
|
||||
DRY_RUN=false
|
||||
OLD_ROOT="$HOME/drives/local-2tb/movies/sunrise"
|
||||
CAM=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
*) OLD_ROOT="$arg" ;;
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
--cam) shift; CAM="$1" ;;
|
||||
--cam=*) CAM="${1#--cam=}" ;;
|
||||
*) OLD_ROOT="$1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
CAM="$SUNRISE_CAM"
|
||||
[ -z "$CAM" ] && CAM="$SUNRISE_CAM"
|
||||
NEW_ROOT="$MOVIES_DIR/$CAM/2025"
|
||||
|
||||
echo "Old archive : $OLD_ROOT"
|
||||
@@ -91,6 +95,7 @@ while IFS= read -r -d '' src; do
|
||||
|
||||
done < <(find \
|
||||
"$OLD_ROOT/Spring" \
|
||||
"$OLD_ROOT/Summer" \
|
||||
"$OLD_ROOT/Winter" \
|
||||
"$OLD_ROOT/Autumn" \
|
||||
-name "*-final.mp4" -print0 2>/dev/null | sort -z)
|
||||
@@ -117,7 +122,7 @@ $DRY_RUN && echo "Re-run without --dry-run to move the files." && echo ""
|
||||
|
||||
echo "Next steps:"
|
||||
echo " 1. Build montages for completed movements:"
|
||||
echo " ./montage-mvt.sh 2026-04-19 $CAM # Spring Mvt1"
|
||||
echo " ./montage-mvt.sh 2026-04-19 $CAM # Spring Mvt1 (today is last day)"
|
||||
echo " ./montage-mvt.sh 2026-01-18 $CAM # Winter Mvt1"
|
||||
echo " ./montage-mvt.sh 2026-02-16 $CAM # Winter Mvt2"
|
||||
echo " ./montage-mvt.sh 2026-03-19 $CAM # Winter Mvt3"
|
||||
|
||||
+16
-4
@@ -195,16 +195,28 @@ if ffmpeg -loglevel warning \
|
||||
-t "$music_duration" -y "$output_file"; then
|
||||
rm -f "$sped_file"
|
||||
echo "Done: $output_file"
|
||||
|
||||
# ── Post-build checks ─────────────────────────────────────────────────────
|
||||
actual_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$output_file")
|
||||
dur_delta=$(echo "scale=1; $actual_dur - $music_duration" | bc | sed 's/^-//')
|
||||
stream_types=$(ffprobe -v error -show_entries stream=codec_type -of csv=p=0 "$output_file" 2>/dev/null)
|
||||
has_video=$(echo "$stream_types" | grep -c "video" || true)
|
||||
has_audio=$(echo "$stream_types" | grep -c "audio" || true)
|
||||
checks="duration=${actual_dur}s (drift ${dur_delta}s)"
|
||||
[ "$has_video" -gt 0 ] && checks="$checks video=ok" || checks="$checks video=MISSING"
|
||||
[ "$has_audio" -gt 0 ] && checks="$checks audio=ok" || checks="$checks audio=MISSING"
|
||||
echo "Checks: $checks"
|
||||
|
||||
"$SCRIPT_DIR/notify.sh" \
|
||||
"Montage ready: $SEASON Mvt $MVT_NUM ($ASTRO_YEAR)" \
|
||||
"$(basename "$output_file") — ${music_duration}s" \
|
||||
"Montage ready: $SEASON Mvt $MVT_NUM ($ASTRO_YEAR) [$CAM_NAME]" \
|
||||
"$(basename "$output_file") | $checks | verify: ./verify-mvt.sh $ASTRO_YEAR $SEASON $MVT_NUM $CAM_NAME" \
|
||||
|| 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
|
||||
"WARNING: montage audio+overlay failed — $SEASON Mvt$MVT_NUM ($ASTRO_YEAR) [$CAM_NAME]" \
|
||||
"Silent video saved as $(basename "$output_file") — run: ./verify-mvt.sh $ASTRO_YEAR $SEASON $MVT_NUM $CAM_NAME" || true
|
||||
fi
|
||||
|
||||
# ── Trigger year-end join on last Autumn movement ────────────────────────────
|
||||
|
||||
+7
-28
@@ -48,9 +48,6 @@
|
||||
# on the last day of a movement, auto-triggers
|
||||
# montage-mvt.sh
|
||||
#
|
||||
# fullday-video.sh <cam> — daily at SCHEDULE_FULLDAY_<cam> (via systemd, per camera)
|
||||
# full-day timelapse at fixed fps; deletes videos
|
||||
# older than RETENTION_DAYS automatically
|
||||
#
|
||||
# install.sh — run once at setup, and again if this file changes
|
||||
# generates and installs systemd units from conf
|
||||
@@ -104,11 +101,6 @@
|
||||
# then re-run install.sh
|
||||
# calls → season_info.py, montage-mvt.sh
|
||||
#
|
||||
# fullday-video.sh
|
||||
# ← called by systemd sky-cam-fullday-<cam>.timer (SCHEDULE_FULLDAY_<cam>)
|
||||
# → if renamed: update install.sh (write_service call ~line 93)
|
||||
# then re-run install.sh
|
||||
#
|
||||
# montage-mvt.sh
|
||||
# ← called by 4-seasons.sh on the last day of each movement
|
||||
# → if renamed: update 4-seasons.sh (the exec line near the bottom)
|
||||
@@ -180,7 +172,7 @@ TIMEZONE=America/New_York
|
||||
# Each name becomes a subfolder under BASE_DIR (images) and BASE_DIR/movies.
|
||||
# To add a camera: add its name here, add its schedules below, re-run install.sh.
|
||||
#
|
||||
CAMERAS=(east) # e.g. CAMERAS=(east north south west)
|
||||
CAMERAS=(east north south) # add west here + uncomment its schedule below when ready
|
||||
SUNRISE_CAM=east # which camera faces east (gets the sunrise video job)
|
||||
|
||||
# ── Schedules ─────────────────────────────────────────────────────────────────
|
||||
@@ -201,24 +193,16 @@ SUNRISE_CAM=east # which camera faces east (gets the sunrise v
|
||||
# movement this automatically triggers the movement montage build.
|
||||
# Space multiple cameras at least 30 min apart to avoid disk contention.
|
||||
#
|
||||
# SCHEDULE_FULLDAY_<cam> (one per camera in CAMERAS)
|
||||
# When to run the full-day timelapse job. Also processes YESTERDAY'S images.
|
||||
# Space after SCHEDULE_SEASONS to avoid overlap.
|
||||
#
|
||||
SCHEDULE_SUNRISE=03:00:00
|
||||
|
||||
# Four Seasons daily clip — one per camera, staggered 30 min apart.
|
||||
# Processes yesterday's images; on the last day of a movement auto-triggers
|
||||
# montage-mvt.sh. Space cameras at least 30 min apart to avoid disk contention.
|
||||
SCHEDULE_SEASONS_east=01:00:00
|
||||
SCHEDULE_FULLDAY_east=02:00:00
|
||||
|
||||
# Uncomment and fill in for each camera you add to CAMERAS above:
|
||||
#SCHEDULE_SEASONS_north=01:30:00
|
||||
#SCHEDULE_FULLDAY_north=02:30:00
|
||||
#
|
||||
#SCHEDULE_SEASONS_south=02:00:00
|
||||
#SCHEDULE_FULLDAY_south=03:00:00
|
||||
#
|
||||
SCHEDULE_SEASONS_north=01:30:00
|
||||
SCHEDULE_SEASONS_south=02:00:00
|
||||
# To add west: uncomment below, add west to CAMERAS above, add CAM_RTSP_west to .env
|
||||
#SCHEDULE_SEASONS_west=02:30:00
|
||||
#SCHEDULE_FULLDAY_west=03:30:00
|
||||
|
||||
# ── Sunrise video tuning ──────────────────────────────────────────────────────
|
||||
# Capture window around sunrise:
|
||||
@@ -235,10 +219,6 @@ SUNRISE_OVERLAY_ENABLED=true
|
||||
# Opacity of the sunrise-time text (0.0 = invisible, 1.0 = fully opaque).
|
||||
SUNRISE_OVERLAY_OPACITY=0.45
|
||||
|
||||
# ── Full-day timelapse tuning ─────────────────────────────────────────────────
|
||||
FULLDAY_FPS=5 # frame rate of the timelapse video
|
||||
RETENTION_DAYS=10 # delete full-day videos older than this many days
|
||||
|
||||
# ── Video encoding quality ────────────────────────────────────────────────────
|
||||
# FFmpeg CRF: lower = higher quality / larger files. Typical range 18–30.
|
||||
# 18 ≈ visually lossless 23 = ffmpeg default 28 = smaller/lower
|
||||
@@ -248,7 +228,6 @@ RETENTION_DAYS=10 # delete full-day videos older than this many days
|
||||
# Each is independent — changing one does NOT change the others.
|
||||
#
|
||||
CRF_SUNRISE=28 # daily_sunrise_video.sh — daily Mattermost upload
|
||||
CRF_FULLDAY=28 # fullday-video.sh — full-day timelapse
|
||||
CRF_SEASONS_RAW=28 # 4-seasons.sh — raw concat before speed-adjust
|
||||
CRF_SEASONS_FINAL=26 # 4-seasons.sh — final daily clip (goes into montage)
|
||||
CRF_MONTAGE=26 # montage-mvt.sh — concat and speed-adjust and final
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Sky-Cam full-day timelapse (10-day retention)
|
||||
OnFailure=sky-cam-notify-failure@%n.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/home/motion/drives/local-2tb/sunrise-scripts/fullday-video.sh
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Sky-Cam full-day timelapse — 02:00
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 02:00:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Executable
+199
@@ -0,0 +1,199 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user