diff --git a/test-sunrise.sh b/test-sunrise.sh new file mode 100755 index 0000000..d4596c5 --- /dev/null +++ b/test-sunrise.sh @@ -0,0 +1,156 @@ +#!/bin/bash +# test-sunrise.sh — smoke-test the sunrise pipeline using recent captured frames. +# +# Uses the same steps as daily_sunrise_video.sh: +# 1. raw video from the most recent ~2 min of JPEGs (12 frames @ 10s interval) +# 2. speed-adjust to SUNRISE_TARGET_SECS +# 3. grab 10s of live RTSP audio centred on the mid-point frame +# 4. mix audio + burn vertical time overlay +# +# Usage: +# ./test-sunrise.sh [output_path] +# ./test-sunrise.sh ~/drives/data2-main/test2.mp4 + +set -euo pipefail + +SCRIPT_DIR="$(dirname "$(realpath "$0")")" +source "$SCRIPT_DIR/sky-cam.conf" +source "$SCRIPT_DIR/.env" 2>/dev/null || true +export TZ="$TIMEZONE" +ENCODE_PRESET="${ENCODE_PRESET:-slow}" +CRF_SUNRISE="${CRF_SUNRISE:-28}" +SUNRISE_TARGET_SECS="${SUNRISE_TARGET_SECS:-10}" +SUNRISE_OVERLAY_OPACITY="${SUNRISE_OVERLAY_OPACITY:-0.45}" + +OUTPUT="${1:-/tmp/sunrise-test.mp4}" +CAM="$SUNRISE_CAM" +TODAY=$(date +%Y-%m-%d) +image_dir="$BASE_DIR/$CAM/$TODAY" +NUM_FRAMES=12 # ~2 minutes at 10s interval + +# ── Collect frames ──────────────────────────────────────────────────────────── +if [ ! -d "$image_dir" ]; then + echo "ERROR: no frames today at $image_dir" + echo " Is capture.sh running? systemctl --user status sky-cam-capture-${CAM}.service" + exit 1 +fi + +mapfile -t frames < <(find "$image_dir" -name "*.jpg" | sort | tail -n "$NUM_FRAMES") +if [ "${#frames[@]}" -lt 1 ]; then + echo "ERROR: no frames found in $image_dir" + exit 1 +fi +echo "Frames: ${#frames[@]} most-recent from $image_dir" + +# ── Mid-point frame → overlay time ─────────────────────────────────────────── +mid_idx=$(( ${#frames[@]} / 2 )) +mid_frame="${frames[$mid_idx]}" +mid_hms=$(basename "$mid_frame" .jpg) # HH-MM-SS +MID_TIME=$(echo "$mid_hms" | cut -c1-5 | tr '-' ':') # HH:MM +echo "Mid-point frame: $mid_hms → overlay time: $MID_TIME" + +# Vertical text: each character on its own line (matches daily_sunrise_video.sh) +MID_VERT="${MID_TIME:0:1}\\n${MID_TIME:1:1}\\n${MID_TIME:2:1}\\n${MID_TIME:3:1}\\n${MID_TIME:4:1}" + +# ── Font detection ──────────────────────────────────────────────────────────── +FONT="" +if command -v fc-match &>/dev/null; then + FONT=$(fc-match "DejaVu Sans:style=Regular" --format="%{file}" 2>/dev/null || true) +fi +if [ -z "$FONT" ]; then + for f in /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf \ + /usr/share/fonts/dejavu/DejaVuSans.ttf \ + /usr/share/fonts/TTF/DejaVuSans.ttf \ + /usr/share/fonts/truetype/freefont/FreeSans.ttf; do + [ -f "$f" ] && { FONT="$f"; break; } + done +fi + +# ── Temp files ──────────────────────────────────────────────────────────────── +TMP_LIST=$(mktemp --suffix=.txt) +TMP_RAW=$(mktemp --suffix=.mp4) +TMP_SPED=$(mktemp --suffix=.mp4) +TMP_AUDIO=$(mktemp --suffix=.m4a) +cleanup() { rm -f "$TMP_LIST" "$TMP_RAW" "$TMP_SPED" "$TMP_AUDIO" 2>/dev/null || true; } +trap cleanup EXIT + +printf "file '%s'\n" "${frames[@]}" > "$TMP_LIST" + +# ── Step 1: raw video from frames ───────────────────────────────────────────── +echo "" +echo "Step 1/4: encoding raw video from ${#frames[@]} frames..." +ffmpeg -loglevel warning \ + -f concat -safe 0 -i "$TMP_LIST" \ + -c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -vsync 2 -an \ + -y "$TMP_RAW" + +raw_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$TMP_RAW") +speed=$(echo "scale=3; $raw_dur / $SUNRISE_TARGET_SECS" | bc) +echo " Raw: ${raw_dur}s speed: ${speed}x" + +# ── Step 2: speed-adjust to SUNRISE_TARGET_SECS ─────────────────────────────── +echo "Step 2/4: speed-adjust to ${SUNRISE_TARGET_SECS}s..." +ffmpeg -loglevel warning \ + -i "$TMP_RAW" \ + -vf "setpts=PTS/$speed" \ + -c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -an \ + -t "$SUNRISE_TARGET_SECS" \ + -y "$TMP_SPED" + +# ── Step 3: grab 10s of live audio centred on mid-point ────────────────────── +# Connects to the RTSP stream briefly — audio from "right now" stands in for +# the real sunrise-audio.m4a that capture.sh would record at sunrise. +echo "Step 3/4: recording ${SUNRISE_TARGET_SECS}s of live audio from camera..." +rtsp_var="CAM_RTSP_${CAM}" +RTSP_URL="${!rtsp_var:-}" + +HAS_AUDIO=false +if [ -n "$RTSP_URL" ]; then + if ffmpeg -loglevel warning \ + -rtsp_transport tcp \ + -i "$RTSP_URL" \ + -t "$SUNRISE_TARGET_SECS" \ + -vn -c:a aac -b:a 64k \ + -y "$TMP_AUDIO" 2>/dev/null; then + HAS_AUDIO=true + echo " Audio: captured OK" + else + echo " Audio: capture failed — continuing without audio" + fi +else + echo " Audio: CAM_RTSP_${CAM} not set — skipping" +fi + +# ── Step 4: mix audio + burn overlay ───────────────────────────────────────── +echo "Step 4/4: mix audio + overlay → $OUTPUT" +mkdir -p "$(dirname "$OUTPUT")" + +FADE_OUT=$(echo "scale=1; $SUNRISE_TARGET_SECS - 0.5" | bc) +DT="drawtext=fontfile='${FONT}'" +DT="${DT}:text='${MID_VERT}'" +DT="${DT}:fontcolor=white@${SUNRISE_OVERLAY_OPACITY}" +DT="${DT}:fontsize=h/22:line_spacing=4" +DT="${DT}:x=w-tw-18:y=(h-th)/2" +DT="${DT}:shadowcolor=black@0.55:shadowx=1:shadowy=1" + +if $HAS_AUDIO; then + ffmpeg -loglevel warning \ + -i "$TMP_SPED" -i "$TMP_AUDIO" \ + -filter_complex \ +"[0:v]${DT}[vout];\ +[1:a]afade=t=in:st=0:d=0.5,afade=t=out:st=${FADE_OUT}:d=0.5[aout]" \ + -map "[vout]" -map "[aout]" \ + -c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" \ + -c:a aac -b:a 128k \ + -y "$OUTPUT" +else + ffmpeg -loglevel warning \ + -i "$TMP_SPED" \ + -vf "$DT" \ + -c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -an \ + -y "$OUTPUT" +fi + +actual=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$OUTPUT") +echo "" +echo "Done: $OUTPUT (${actual}s, overlay: $MID_TIME)"