Previously sunrise-audio-capture.sh recorded the entire capture window (70+ minutes) and daily_sunrise_video.sh sought to the right position with a complex offset calculation. Now: record floor(TARGET/2) seconds before sunrise and ceil(TARGET/2) after — odd second goes to post-sunrise. The file is exactly SUNRISE_TARGET_SECS long and already centred, so no offset is needed when mixing. AUDIO_PRE_BUFFER_MIN removed — it was part of the old whole-window scheme and is no longer meaningful. https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
329 lines
17 KiB
Plaintext
329 lines
17 KiB
Plaintext
# =============================================================================
|
||
# sky-cam.conf — master configuration for all sky-cam scripts
|
||
# =============================================================================
|
||
#
|
||
# This is the ONLY file you need to edit. After editing, run:
|
||
#
|
||
# ./install.sh # installs systemd timers (no root needed)
|
||
# ./install.sh --system # system-wide install (requires sudo)
|
||
#
|
||
# Re-run install.sh any time this file changes.
|
||
#
|
||
# -----------------------------------------------------------------------------
|
||
# MANUAL SYSTEMD SETUP (if you prefer not to use install.sh)
|
||
# -----------------------------------------------------------------------------
|
||
# After editing this file, generate the unit files:
|
||
#
|
||
# mkdir -p ~/.config/systemd/user
|
||
# cp systemd/*.service systemd/*.timer ~/.config/systemd/user/
|
||
#
|
||
# Then edit ExecStart= in each .service file to match your SCRIPT_DIR, then:
|
||
#
|
||
# systemctl --user daemon-reload
|
||
# systemctl --user enable --now sky-cam-sunrise.timer
|
||
# systemctl --user enable --now sky-cam-seasons-sunrise.timer
|
||
# systemctl --user enable --now sky-cam-fullday-sunrise.timer
|
||
# # one sky-cam-seasons-<cam>.timer and sky-cam-fullday-<cam>.timer per camera
|
||
#
|
||
# Check status:
|
||
# systemctl --user list-timers 'sky-cam-*'
|
||
# journalctl --user -u sky-cam-seasons-sunrise.service -f
|
||
#
|
||
# System-wide (replace --user with no flag, prepend sudo):
|
||
# sudo cp systemd/*.service systemd/*.timer /etc/systemd/system/
|
||
# sudo systemctl daemon-reload && sudo systemctl enable --now sky-cam-*.timer
|
||
#
|
||
# -----------------------------------------------------------------------------
|
||
# SCRIPT MAP — what each file does and when it runs
|
||
# -----------------------------------------------------------------------------
|
||
#
|
||
# You run / schedule:
|
||
# daily_sunrise_video.sh — daily at SCHEDULE_SUNRISE (via systemd, SUNRISE_CAM only)
|
||
# grabs images around sunrise, makes a short video,
|
||
# uploads it to Mattermost
|
||
#
|
||
# 4-seasons.sh <cam> — daily at SCHEDULE_SEASONS_<cam> (via systemd, per camera)
|
||
# makes yesterday's daily clip sized to its share
|
||
# of the matching Vivaldi movement's music duration;
|
||
# 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
|
||
#
|
||
# stitch-cameras.py — run manually when needed
|
||
# stitches north + sunrise into a panorama
|
||
# usage: python3 stitch-cameras.py north.jpg sunrise.jpg prefix [focal]
|
||
#
|
||
# Auto-triggered (do not run directly):
|
||
# montage-mvt.sh — called by 4-seasons.sh on last day of each movement
|
||
# concatenates all daily clips for the movement,
|
||
# speed-adjusts to match music, overlays attribution,
|
||
# sends completion notification; on last Autumn
|
||
# movement also triggers year-end-join.sh
|
||
#
|
||
# year-end-join.sh — called by montage-mvt.sh at end of Autumn Mvt 3
|
||
# concatenates all 12 movement montages into one
|
||
# Four Seasons year video, sends notification
|
||
#
|
||
# Helpers (called by the scripts above, not run directly):
|
||
# notify.sh — sends notifications via ntfy / email / Mattermost
|
||
# season_info.py — outputs astronomical season/movement/year for a date
|
||
# sunrise.py — outputs today's sunrise time (used by daily_sunrise_video)
|
||
# sunrise2mm.py — uploads the sunrise video to Mattermost
|
||
# sunrise_overlay.py — burns a timestamp overlay onto the sunrise video
|
||
#
|
||
# -----------------------------------------------------------------------------
|
||
# FILE DEPENDENCIES — who calls whom, and what to update if you rename a file
|
||
# -----------------------------------------------------------------------------
|
||
#
|
||
# sky-cam.conf (this file)
|
||
# ← sourced by every .sh script; read by sunrise.py, sunrise2mm.py
|
||
# → if renamed: update the 'source' line at the top of ALL scripts
|
||
# (don't rename it — just edit it in place)
|
||
#
|
||
# install.sh
|
||
# ← run manually; reads this conf
|
||
# → if renamed: update the note at the top of this conf file only
|
||
# generates → systemd unit files (sky-cam-*.service / .timer)
|
||
# and sky-cam-notify-failure@.service
|
||
#
|
||
# daily_sunrise_video.sh
|
||
# ← called by systemd sky-cam-sunrise.timer (SCHEDULE_SUNRISE)
|
||
# → if renamed: update install.sh (write_service call ~line 70)
|
||
# then re-run install.sh to regenerate the unit
|
||
# calls → sunrise.py, sunrise_overlay.py, sunrise2mm.py
|
||
#
|
||
# 4-seasons.sh
|
||
# ← called by systemd sky-cam-seasons-<cam>.timer (SCHEDULE_SEASONS_<cam>)
|
||
# → if renamed: update install.sh (write_service call ~line 85)
|
||
# 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)
|
||
# calls → season_info.py, notify.sh, year-end-join.sh
|
||
#
|
||
# year-end-join.sh
|
||
# ← called by montage-mvt.sh at end of Autumn Mvt 3
|
||
# → if renamed: update montage-mvt.sh (the exec line at the bottom)
|
||
# calls → notify.sh
|
||
#
|
||
# notify.sh
|
||
# ← called by montage-mvt.sh, year-end-join.sh
|
||
# also referenced in install.sh for the OnFailure= failure template
|
||
# → if renamed: update montage-mvt.sh, year-end-join.sh, install.sh
|
||
#
|
||
# season_info.py
|
||
# ← called by 4-seasons.sh and montage-mvt.sh
|
||
# → if renamed: update 4-seasons.sh and montage-mvt.sh (the eval lines)
|
||
#
|
||
# sunrise.py
|
||
# ← called by daily_sunrise_video.sh
|
||
# → if renamed: update daily_sunrise_video.sh
|
||
#
|
||
# sunrise_overlay.py
|
||
# ← called by daily_sunrise_video.sh
|
||
# → if renamed: update daily_sunrise_video.sh
|
||
#
|
||
# sunrise2mm.py
|
||
# ← called by daily_sunrise_video.sh
|
||
# → if renamed: update daily_sunrise_video.sh
|
||
#
|
||
# stitch-cameras.py
|
||
# ← run manually only; no script calls it
|
||
# → safe to rename with no other changes needed
|
||
#
|
||
# =============================================================================
|
||
|
||
# ── Install location ──────────────────────────────────────────────────────────
|
||
# Auto-detected from where this file lives — override only if you move the
|
||
# scripts to a different location after install.
|
||
SCRIPT_DIR="${SCRIPT_DIR:-$(dirname "$(realpath "${BASH_SOURCE[0]}")")}"
|
||
|
||
# ── Storage ───────────────────────────────────────────────────────────────────
|
||
# BASE_DIR: root folder where camera images are stored.
|
||
# Structure created automatically: BASE_DIR/<cam>/YYYY-MM-DD/HH-MM-SS.jpg
|
||
# Defaults to a 'data' subfolder next to the scripts; override to point at
|
||
# an external drive or network mount (recommended for production).
|
||
BASE_DIR="${BASE_DIR:-$SCRIPT_DIR/data}"
|
||
MOVIES_DIR="${MOVIES_DIR:-$BASE_DIR/movies}" # override if videos live on a different drive
|
||
MUSIC_DIR=/home/motion/drives/local-2tb/music/4 Seasons
|
||
|
||
# ── Location & timezone ───────────────────────────────────────────────────────
|
||
# Used by sunrise.py to calculate sunrise time each day.
|
||
#
|
||
# To find your coordinates:
|
||
# Google Maps — right-click your location → the first item shown is "lat, lon"
|
||
# Or: maps.google.com, drop a pin, coordinates appear in the URL and sidebar
|
||
#
|
||
# TIMEZONE must be a valid IANA name. Look yours up at:
|
||
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||
# (use the value in the "TZ identifier" column, e.g. America/Chicago)
|
||
#
|
||
LATITUDE=25.0000
|
||
LONGITUDE=-38.0000
|
||
TIMEZONE=America/New_York
|
||
|
||
# ── Cameras ───────────────────────────────────────────────────────────────────
|
||
# List every camera name here (space-separated inside the parentheses).
|
||
# 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=(sunrise) # e.g. CAMERAS=(sunrise north west)
|
||
SUNRISE_CAM=sunrise # which camera faces east (gets the sunrise video job)
|
||
|
||
# ── Schedules ─────────────────────────────────────────────────────────────────
|
||
# All times are HH:MM:SS (24-hour local time).
|
||
# install.sh reads these and generates one systemd timer per camera per job.
|
||
#
|
||
# SCHEDULE_SUNRISE
|
||
# When to START the sunrise video job. The script wakes up, calculates
|
||
# today's sunrise time, then WAITS internally until the capture window is
|
||
# fully finished before touching any images. Set this early enough to
|
||
# cover your earliest possible sunrise minus SUNRISE_PRE_MIN — 03:00 works
|
||
# year-round for most locations. The video is ready a few minutes after the
|
||
# capture window closes, not at this start time.
|
||
#
|
||
# SCHEDULE_SEASONS_<cam> (one per camera in CAMERAS)
|
||
# When to run the Four Seasons daily clip job. Processes YESTERDAY'S images
|
||
# (not today's) — always runs after midnight. On the last day of a season
|
||
# 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
|
||
|
||
SCHEDULE_SEASONS_sunrise=01:00:00
|
||
SCHEDULE_FULLDAY_sunrise=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_west=02:00:00
|
||
#SCHEDULE_FULLDAY_west=03:00:00
|
||
|
||
# ── Sunrise video tuning ──────────────────────────────────────────────────────
|
||
# Capture window around sunrise:
|
||
# Images from (sunrise - SUNRISE_PRE_MIN) to (sunrise + SUNRISE_POST_MIN)
|
||
# are included in the clip. Larger windows give more context; they are all
|
||
# speed-adjusted to fit SUNRISE_TARGET_SECS regardless of window size.
|
||
SUNRISE_PRE_MIN=70 # minutes before sunrise to start capturing
|
||
SUNRISE_POST_MIN=10 # minutes after sunrise to stop capturing
|
||
# Target length of the final sunrise video (seconds).
|
||
SUNRISE_TARGET_SECS=10
|
||
# Burn the local sunrise time vertically on the right side of the video.
|
||
# Set false to skip the overlay entirely (cleaner look, faster encoding).
|
||
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
|
||
# Different scripts use different defaults by design — intermediate/draft
|
||
# encodings use a higher CRF (smaller files, quality less critical), while
|
||
# final "polished" outputs use a lower CRF for better quality.
|
||
# 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
|
||
AUDIO_BITRATE=192k # montage-mvt.sh — music track on movement montages
|
||
|
||
# ── Montage attribution overlay ───────────────────────────────────────────────
|
||
# A translucent bar across the top of each movement montage, naming the
|
||
# music source ("The Four Seasons — Antonio Vivaldi ...").
|
||
# Appears for MONTAGE_ATTR_DUR seconds from the start, with ATTR_FADE-second
|
||
# fade in and fade out. FADE_DUR controls the video/audio fade in/out at
|
||
# the very start and very end of the montage.
|
||
#
|
||
MONTAGE_FADE_DUR=2.0 # video + audio fade in/out (seconds)
|
||
MONTAGE_ATTR_DUR=6 # attribution overlay total duration (seconds)
|
||
MONTAGE_ATTR_FADE=1 # attribution fade-in and fade-out length (seconds)
|
||
|
||
# ── Direct RTSP capture (replaces MotionEye / any NVR) ────────────────────────
|
||
# capture.sh pulls frames directly from each camera's RTSP stream.
|
||
# No NVR software required — just ffmpeg and the camera's stream URL.
|
||
#
|
||
# Per-camera RTSP URL: CAM_RTSP_<cam>=rtsp://user:pass@host:port/path
|
||
# Find the URL in your camera's web UI (usually under Network → Video → RTSP)
|
||
# or check your camera's manual.
|
||
#
|
||
# CAM_RTSP_<cam> goes in .env (see bottom of this file), not here.
|
||
|
||
# Seconds between captured frames (applies to all cameras).
|
||
# 10 = one frame every 10 s → 8640 frames/day → good timelapse density.
|
||
CAPTURE_INTERVAL=10
|
||
|
||
# ── Sunrise audio capture ──────────────────────────────────────────────────────
|
||
# Records the camera's RTSP audio stream during the sunrise window so that
|
||
# daily_sunrise_video.sh can mix in natural ambient sound (birds, rain, wind)
|
||
# at real speed, while the video plays as the sped-up timelapse.
|
||
# The audio file is deleted automatically after it is mixed into the video.
|
||
#
|
||
AUDIO_ENABLED=false # set true if your camera has a working mic
|
||
CAPTURE_AUDIO_BITRATE=96k # bitrate for the sunrise audio recording
|
||
# Audio is recorded for exactly SUNRISE_TARGET_SECS, centred on actual sunrise:
|
||
# floor(TARGET/2) seconds before, ceil(TARGET/2) after (odd second → post-sunrise).
|
||
|
||
# ── Mattermost — daily sunrise upload ─────────────────────────────────────────
|
||
# mattermost_url, access_token, channel_id go in .env (see bottom of this file).
|
||
|
||
# ── Notifications — montage / year-end complete + any job failure ─────────────
|
||
# Notifications fire when a movement montage or the year video finishes,
|
||
# and via systemd OnFailure= if any daily job fails.
|
||
# Uncomment and configure one or more methods to enable.
|
||
|
||
# ntfy — push notifications, zero signup for self-hosted or free cloud tier:
|
||
# Self-hosted: https://docs.ntfy.sh/install/
|
||
# Cloud: pick any topic name at ntfy.sh (no account needed)
|
||
#NTFY_ENABLED=true
|
||
#NTFY_URL=https://ntfy.sh/your-topic-here
|
||
|
||
# Email — requires the 'mail' command (package: mailutils or s-nail).
|
||
# Configure outbound SMTP via /etc/ssmtp/ssmtp.conf or msmtp.
|
||
#EMAIL_ENABLED=true
|
||
#EMAIL_TO=you@example.com
|
||
#EMAIL_FROM=skycam@localhost
|
||
|
||
# Mattermost text post — reuses mattermost_url and access_token above.
|
||
# Can be the same daily channel or a separate admin/private channel.
|
||
#MM_NOTIFY_ENABLED=true
|
||
#MM_NOTIFY_CHANNEL_ID=your-notify-channel-id-here
|
||
|
||
# ── Credentials (.env override) ───────────────────────────────────────────────
|
||
# Sensitive values (RTSP URLs with passwords, API tokens) can be kept out of
|
||
# this file — and out of git — by placing them in a .env file in the same
|
||
# directory. install.sh generates .env.example showing exactly which variables
|
||
# to define for your camera list.
|
||
#
|
||
# To use:
|
||
# cp .env.example .env
|
||
# $EDITOR .env ← fill in real values
|
||
# (never commit .env)
|
||
#
|
||
_conf_dir="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
|
||
[ -f "$_conf_dir/.env" ] && source "$_conf_dir/.env"
|
||
unset _conf_dir
|