New AUDIO_DIR variable (like BASE_DIR/MOVIES_DIR) is the root for all
audio recordings. Set in .env to put audio on a separate drive.
Directory layout:
AUDIO_DIR/<cam>/YYYY-MM-DD/HH-MM-SS.m4a — ambient recordings
AUDIO_DIR/sunrise/<cam>/YYYY-MM-DD/sunrise-audio.m4a — sunrise clips
sky-cam.conf:
- Add AUDIO_DIR="${AUDIO_DIR:-$BASE_DIR/audio}" in storage section
- Remove AMBIENT_DIR (replaced by AUDIO_DIR)
- Add SUNRISE_AUDIO_RETENTION_DAYS=7 (sunrise clips kept separately from ambient)
- Add per-camera ambient retention examples:
AMBIENT_RETENTION_DAYS_south=60 (keep bird recordings longer)
ambient-record.sh:
- Use AUDIO_DIR instead of AMBIENT_DIR
- Per-camera retention: AMBIENT_RETENTION_DAYS_<cam> overrides global default
sunrise-audio-capture.sh:
- Save clips to AUDIO_DIR/sunrise/<cam>/<date>/sunrise-audio.m4a
- Rolling retention of sunrise clips (SUNRISE_AUDIO_RETENTION_DAYS)
daily_sunrise_video.sh:
- Look for cam_audio in new AUDIO_DIR/sunrise path first, fall back to
old BASE_DIR path so existing recordings keep working
install.sh:
- Pre-create AUDIO_DIR/<cam> and AUDIO_DIR/sunrise/<SUNRISE_CAM> dirs
- Add AUDIO_DIR to .env.example storage section
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
95 lines
3.6 KiB
Bash
Executable File
95 lines
3.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# sunrise-audio-capture.sh — record exactly SUNRISE_TARGET_SECS of audio
|
|
# centred on the actual sunrise moment, so daily_sunrise_video.sh can mix in
|
|
# real ambient sound (birds, rain, wind) at natural speed.
|
|
#
|
|
# Triggered at 03:00 by sky-cam-audio-capture.timer; waits internally until
|
|
# the right moment before recording. Exits immediately if AUDIO_ENABLED≠true.
|
|
#
|
|
# Recording window: floor(TARGET/2) seconds before sunrise,
|
|
# ceil(TARGET/2) seconds after (odd second goes to post).
|
|
#
|
|
# Output: BASE_DIR/<cam>/YYYY-MM-DD/sunrise-audio.m4a
|
|
# Deleted automatically by daily_sunrise_video.sh after mixing.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
|
source "$SCRIPT_DIR/sky-cam.conf"
|
|
export TZ="$TIMEZONE"
|
|
|
|
CAM="${1:-$SUNRISE_CAM}"
|
|
|
|
if [ "${AUDIO_ENABLED:-false}" != "true" ]; then
|
|
echo "AUDIO_ENABLED is not true — skipping."
|
|
exit 0
|
|
fi
|
|
|
|
rtsp_var="CAM_RTSP_${CAM}"
|
|
RTSP_URL="${!rtsp_var:-}"
|
|
if [ -z "$RTSP_URL" ]; then
|
|
echo "ERROR: CAM_RTSP_${CAM} not set in sky-cam.conf"
|
|
exit 1
|
|
fi
|
|
|
|
# ── Sunrise time ──────────────────────────────────────────────────────────────
|
|
sunrise_time=$(python3 "$SCRIPT_DIR/sunrise.py")
|
|
if [ -z "$sunrise_time" ]; then
|
|
"$SCRIPT_DIR/notify.sh" "FAILED: audio capture — no sunrise time" \
|
|
"sunrise.py returned nothing — check internet/location config" || true
|
|
exit 1
|
|
fi
|
|
sunrise_time_local=$(TZ="$TIMEZONE" date -d "$sunrise_time" +"%H:%M:%S")
|
|
echo "Sunrise (local): $sunrise_time_local"
|
|
|
|
# ── Calculate recording window centred on sunrise ─────────────────────────────
|
|
time_to_seconds() { IFS=':' read -r h m s <<< "$1"; echo $((10#$h*3600 + 10#$m*60 + 10#$s)); }
|
|
|
|
sunrise_sec=$(time_to_seconds "$sunrise_time_local")
|
|
half_pre=$(( SUNRISE_TARGET_SECS / 2 )) # floor — pre-sunrise portion
|
|
half_post=$(( SUNRISE_TARGET_SECS - half_pre )) # ceil — post-sunrise gets odd second
|
|
record_start_sec=$(( sunrise_sec - half_pre ))
|
|
[ "$record_start_sec" -lt 0 ] && record_start_sec=0
|
|
record_dur_sec=$SUNRISE_TARGET_SECS
|
|
|
|
echo "Audio window: ${half_pre}s before → ${half_post}s after sunrise (${record_dur_sec}s total)"
|
|
|
|
midnight=$(date -d "today 00:00:00" +%s)
|
|
now_day_sec=$(( $(date +%s) - midnight ))
|
|
wait_sec=$(( record_start_sec - now_day_sec ))
|
|
|
|
if [ "$wait_sec" -gt 0 ]; then
|
|
echo "Waiting ${wait_sec}s until audio window ($(date -d "@$(( midnight + record_start_sec ))" '+%H:%M:%S'))..."
|
|
sleep "$wait_sec"
|
|
fi
|
|
|
|
today=$(date +%Y-%m-%d)
|
|
dir="${AUDIO_DIR:-$BASE_DIR/audio}/sunrise/$CAM/$today"
|
|
mkdir -p "$dir"
|
|
output="$dir/sunrise-audio.m4a"
|
|
|
|
echo "Recording ${record_dur_sec}s of audio → $output"
|
|
|
|
if ! ffmpeg -loglevel warning \
|
|
-rtsp_transport tcp \
|
|
-i "$RTSP_URL" \
|
|
-t "$record_dur_sec" \
|
|
-vn \
|
|
-acodec aac -b:a "${CAPTURE_AUDIO_BITRATE:-96k}" \
|
|
-y "$output"; then
|
|
"$SCRIPT_DIR/notify.sh" "FAILED: audio capture $today" \
|
|
"ffmpeg audio recording failed — sunrise video will have no audio" || true
|
|
exit 1
|
|
fi
|
|
|
|
size=$(du -h "$output" | cut -f1)
|
|
echo "Audio capture complete: $output (${record_dur_sec}s, $size)"
|
|
|
|
# Rolling retention for sunrise audio clips
|
|
retain="${SUNRISE_AUDIO_RETENTION_DAYS:-7}"
|
|
if [ "$retain" -gt 0 ]; then
|
|
audio_root="${AUDIO_DIR:-$BASE_DIR/audio}/sunrise/$CAM"
|
|
deleted=$(find "$audio_root" -name "sunrise-audio.m4a" -mtime +"$retain" -print -delete 2>/dev/null | wc -l)
|
|
[ "$deleted" -gt 0 ] && echo "Retention: deleted $deleted sunrise audio clip(s) older than ${retain}d"
|
|
fi
|