From 6629f9121940cd8cfad7c4fd9b9083b77c7a7709 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Apr 2026 19:35:07 +0000 Subject: [PATCH] Add README with quick-start, architecture overview, and operations guide https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ --- README.md | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed48fd1 --- /dev/null +++ b/README.md @@ -0,0 +1,165 @@ +# sky-cam + +Automated sky / timelapse camera scripts that produce: + +- **Daily sunrise clip** — a 10-second speed-adjusted video of the sunrise window, uploaded to Mattermost each morning +- **Four Seasons timelapse** — daily clips sized to each Vivaldi movement's music duration, assembled automatically into per-movement montages (with music + attribution overlay) and a full-year video +- **Full-day timelapse** — a fixed-fps timelapse of every image captured that day, kept for a configurable retention window + +--- + +## Prerequisites + +| Dependency | Notes | +|---|---| +| `ffmpeg` + `ffprobe` | Encoding and duration probing | +| `python3` | `ephem` package for sunrise calculation, `requests` for Mattermost upload | +| `bc` | Shell arithmetic (floating-point speed factors) | +| `fontconfig` (`fc-match`) | Font detection for overlays — optional, falls back to hardcoded paths | +| Camera software | e.g. `motion` — writes JPEG images named `HH-MM-SS.jpg` into `BASE_DIR///` | +| Vivaldi Four Seasons audio | 12 MP3 files named so that `*Spring*Mvt*1*`, `*Summer*Mvt*2*`, etc. match with `find -iname` | + +Install Python dependencies: + +```bash +pip3 install ephem requests +``` + +--- + +## Quick start + +### 1. Download + +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/outis1one/sky-cam/main/bootstrap.sh) +cd sky-cam +``` + +Or with a custom install directory: + +```bash +curl -fsSL https://raw.githubusercontent.com/outis1one/sky-cam/main/bootstrap.sh | bash -s -- /opt/sky-cam +cd /opt/sky-cam +``` + +### 2. Configure + +```bash +$EDITOR sky-cam.conf +``` + +Minimum settings to fill in (everything else has sensible defaults): + +| Setting | What it is | +|---|---| +| `SCRIPT_DIR` | Full path to this directory | +| `BASE_DIR` | Root where camera images live (`BASE_DIR///.jpg`) | +| `MOVIES_DIR` | Where finished videos are written (default: `BASE_DIR/movies`) | +| `MUSIC_DIR` | Directory containing the 12 Vivaldi Four Seasons audio files | +| `CAMERAS` | Space-separated list of camera names, e.g. `(sunrise north)` | +| `SUNRISE_CAM` | Which camera faces east and gets the sunrise job | +| `LATITUDE` / `LONGITUDE` / `TIMEZONE` | Your location for sunrise calculation | +| `mattermost_url` / `access_token` / `channel_id` | Mattermost upload credentials | + +### 3. Install systemd timers + +```bash +./install.sh # user-level timers (~/.config/systemd/user), no root needed +# or +./install.sh --system # system-wide (/etc/systemd/system), requires sudo +``` + +Re-run `install.sh` any time `sky-cam.conf` changes. + +### 4. Verify + +```bash +systemctl --user list-timers 'sky-cam-*' +journalctl --user -u sky-cam-sunrise.service -f +``` + +--- + +## How it works + +``` +Camera writes JPEGs + │ + ├─ daily_sunrise_video.sh (runs at SCHEDULE_SUNRISE) + │ Step 1: encode raw video from sunrise-window JPEGs + │ Step 2: speed-adjust to SUNRISE_TARGET_SECS → saved permanently + │ Step 3: burn sunrise time overlay → final video + │ OnSuccess → sunrise2mm.py uploads to Mattermost + │ + ├─ 4-seasons.sh (runs at SCHEDULE_SEASONS_, processes yesterday) + │ Step 1: encode all of yesterday's JPEGs into raw video + │ Step 2: speed-adjust to music_duration / days_in_movement + │ Last day of movement → triggers montage-mvt.sh + │ Step 1: concatenate all daily clips + │ Step 2: speed-adjust to exactly match music → saved permanently + │ Step 3: mix music + fades + attribution overlay → Montage.mp4 + │ Last movement of Autumn → triggers year-end-join.sh + │ + └─ fullday-video.sh (runs at SCHEDULE_FULLDAY_, processes yesterday) + Encode all of yesterday's JPEGs at FULLDAY_FPS + Delete videos older than RETENTION_DAYS +``` + +### Resilience + +Each pipeline saves an intermediate file before the step most likely to fail, so a partial failure leaves a recoverable artifact: + +- **Sunrise**: if the overlay (step 3) fails, the speed-only video is promoted to the upload target — the upload still happens and you get a notification of the overlay failure +- **Montage**: if music + overlay (step 3) fails, the speed-adjusted silent video is promoted to `*-Montage.mp4` — `year-end-join.sh` still includes the movement and you get a warning notification + +--- + +## Notifications + +`notify.sh` sends alerts through any combination of: + +| Channel | Config key(s) | +|---|---| +| [ntfy](https://ntfy.sh) | `NTFY_ENABLED=true`, `NTFY_URL=https://ntfy.sh/your-topic` | +| Email | `EMAIL_ENABLED=true`, `EMAIL_TO=you@example.com` | +| Mattermost text post | `MM_NOTIFY_ENABLED=true`, `MM_NOTIFY_CHANNEL_ID=` | + +You receive notifications for: +- Sunrise: video ready, upload success/failure, overlay failure +- Each daily seasons clip saved +- Montage complete (or degraded if audio/overlay failed) +- Year-end Four Seasons video complete +- Any step failure, with the surviving file path named + +--- + +## Manual operations + +**Re-run today's sunrise** (e.g. after fixing a font issue): +```bash +./daily_sunrise_video.sh +``` + +**Re-run a daily seasons clip** for a specific date: +```bash +./4-seasons.sh # reprocesses yesterday +``` + +**Rebuild a movement montage** (e.g. to retry audio after a failure): +```bash +./montage-mvt.sh # uses today's movement +./montage-mvt.sh 2025-06-15 sunrise # use a specific reference date + camera +``` + +**Rebuild the year-end video**: +```bash +./year-end-join.sh 2025 sunrise +``` + +**Check logs**: +```bash +journalctl --user -u sky-cam-sunrise.service +journalctl --user -u sky-cam-seasons-sunrise.service +journalctl --user -u sky-cam-fullday-sunrise.service +```