From 4d5da05bc9dc5dc03dd89a069ab48aa7b71f95f3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 00:36:58 +0000 Subject: [PATCH] =?UTF-8?q?README:=20full=20rewrite=20=E2=80=94=20dependen?= =?UTF-8?q?cies=20first,=20accurate=20quick=20start,=20current=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Step 1 is now dependency installation (apt + pip3) before anything else - SCRIPT_DIR removed from config table (auto-detected) - RTSP/Mattermost credentials moved to .env section (step 4) - install.sh now shown as generating .env.example before credentials are filled in - Schedule settings explained: SEASONS/FULLDAY process yesterday; SUNRISE waits internally - How it works: audio capture described as SUNRISE_TARGET_SECS centred on sunrise - daily_sunrise_video.sh pipeline updated: steps 3a/3b shown separately - Resilience section replaced with four-tier audio/overlay matrix table - Camera audio section updated: exact duration, centred split, odd-second rule - Manual operations: examples use explicit camera name argument https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ --- README.md | 138 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 006c5b1..88df55b 100644 --- a/README.md +++ b/README.md @@ -2,35 +2,30 @@ 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 +- **Daily sunrise clip** — a 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 +## Quick start -| Dependency | Notes | -|---|---| -| `ffmpeg` + `ffprobe` | Encoding, capture, audio recording, duration probing | -| `python3` | `suntime pytz` for sunrise calculation, `requests` for Mattermost upload — `pip3 install suntime pytz requests` | -| `fonts-dejavu` | DejaVu fonts for text overlays — `sudo apt install fonts-dejavu` | -| `bc` | Shell arithmetic (floating-point speed factors) | -| `fontconfig` (`fc-match`) | Font detection for overlays — optional, falls back to hardcoded paths | -| IP camera with RTSP stream | `capture.sh` pulls frames directly — no NVR software needed | -| Vivaldi Four Seasons audio | 12 MP3 files named so that `*Spring*Mvt*1*`, `*Summer*Mvt*2*`, etc. match with `find -iname` | - -Install Python dependencies: +### 1. Install dependencies ```bash +sudo apt install ffmpeg bc fonts-dejavu curl python3-pip pip3 install suntime pytz requests ``` ---- +| Package | Purpose | +|---|---| +| `ffmpeg` + `ffprobe` | Encoding, RTSP capture, audio recording, duration probing | +| `bc` | Floating-point arithmetic for speed factors | +| `fonts-dejavu` | Text overlays (sunrise time, attribution) | +| `python3` + `suntime pytz` | Astronomical sunrise calculation — pure math, no internet, works indefinitely | +| `python3` + `requests` | Mattermost upload | -## Quick start - -### 1. Download +### 2. Download ```bash bash <(curl -fsSL https://raw.githubusercontent.com/outis1one/sky-cam/main/bootstrap.sh) @@ -44,42 +39,65 @@ curl -fsSL https://raw.githubusercontent.com/outis1one/sky-cam/main/bootstrap.sh cd /opt/sky-cam ``` -### 2. Configure +### 3. Configure ```bash $EDITOR sky-cam.conf ``` -Minimum settings to fill in (everything else has sensible defaults): +`SCRIPT_DIR` and `BASE_DIR` are auto-detected — you only need to fill in settings specific to your setup: | Setting | What it is | |---|---| -| `SCRIPT_DIR` | Full path to this directory | -| `BASE_DIR` | Root where camera images live (`BASE_DIR///.jpg`) | +| `BASE_DIR` | Root where camera images live — override if storing on a separate drive (`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 | +| `MUSIC_DIR` | Directory containing the 12 Vivaldi Four Seasons MP3 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 | -| `CAM_RTSP_` | RTSP stream URL for each camera, e.g. `rtsp://admin:pass@192.168.1.100:554/stream1` | | `CAPTURE_INTERVAL` | Seconds between captured frames (default: 10) | -| `mattermost_url` / `access_token` / `channel_id` | Mattermost upload credentials | +| `SCHEDULE_SUNRISE` | When to start the sunrise job — default `03:00`, script waits internally until the capture window closes | +| `SCHEDULE_SEASONS_` | When to run the Four Seasons daily clip — processes **yesterday's** images; runs after midnight | +| `SCHEDULE_FULLDAY_` | When to run the full-day timelapse — also processes yesterday; schedule after SEASONS | -### 3. Install systemd timers +### 4. Set up credentials ```bash -./install.sh # user-level timers (~/.config/systemd/user), no root needed -# or -./install.sh --system # system-wide (/etc/systemd/system), requires sudo +./install.sh # generates .env.example alongside systemd units +cp .env.example .env +$EDITOR .env ``` -Re-run `install.sh` any time `sky-cam.conf` changes. - -### 4. Verify +`.env` holds all sensitive values and is never committed to git: ```bash +# RTSP stream URL — one per camera (variable name matches camera name) +CAM_RTSP_sunrise=rtsp://admin:password@192.168.1.100:554/stream1 + +# Mattermost upload +mattermost_url=https://your-mattermost.example.com +access_token=your-token +channel_id=your-channel-id + +# Notifications (uncomment what you use) +#NTFY_URL=https://ntfy.sh/your-topic +#EMAIL_TO=you@example.com +``` + +Re-run `./install.sh` any time `sky-cam.conf` changes (schedules, cameras, etc.). + +### 5. Verify + +```bash +# Confirm cameras are capturing +systemctl --user status sky-cam-capture-sunrise.service +ls BASE_DIR/sunrise/$(date +%Y-%m-%d)/ # images should appear within CAPTURE_INTERVAL seconds + +# Check all timers are scheduled systemctl --user list-timers 'sky-cam-*' -journalctl --user -u sky-cam-sunrise.service -f + +# Test sunrise calculation +python3 sunrise.py ``` --- @@ -92,19 +110,23 @@ Camera RTSP stream ├─ capture.sh (long-running systemd service, one per camera) │ ffmpeg pulls one frame every CAPTURE_INTERVAL seconds │ Writes: BASE_DIR//YYYY-MM-DD/HH-MM-SS.jpg - │ Restarts at midnight for the new date directory; auto-reconnects + │ Restarts at midnight for the new date directory; auto-reconnects after 30s on loss │ - ├─ sunrise-audio-capture.sh (runs at 03:00, waits for sunrise window) - │ ffmpeg records audio-only from RTSP during sunrise window + ├─ sunrise-audio-capture.sh (starts at 03:00, waits internally for sunrise) + │ Calculates today's sunrise, then records exactly SUNRISE_TARGET_SECS of audio + │ centred on the moment of sunrise (odd second goes to post-sunrise) │ Writes: BASE_DIR//YYYY-MM-DD/sunrise-audio.m4a │ Deleted automatically after being mixed into the sunrise video │ Camera JPEGs + audio │ - ├─ daily_sunrise_video.sh (runs at SCHEDULE_SUNRISE) + ├─ daily_sunrise_video.sh (starts at SCHEDULE_SUNRISE, waits for capture window) + │ Wakes at SCHEDULE_SUNRISE, sleeps until (sunrise + SUNRISE_POST_MIN) │ 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 + mix camera audio (if available) + │ Step 3a: mix audio (camera mic → library fallback → no audio) + │ Step 3b: burn sunrise time overlay (optional, set SUNRISE_OVERLAY_ENABLED=false to skip) + │ Each step degrades independently — upload always fires │ OnSuccess → sunrise2mm.py uploads to Mattermost │ ├─ 4-seasons.sh (runs at SCHEDULE_SEASONS_, processes yesterday) @@ -123,10 +145,18 @@ Camera JPEGs + audio ### Resilience -Each pipeline saves an intermediate file before the step most likely to fail, so a partial failure leaves a recoverable artifact: +Every pipeline saves an intermediate file before the riskiest step, so a partial failure always leaves something uploadable: -- **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 +**Sunrise video — four-tier fallback, upload always fires:** + +| Audio | Overlay | What gets uploaded | +|---|---|---| +| ✓ | ✓ | Final video with audio + timestamp | +| ✓ | ✗ | Audio-mixed video, no timestamp — overlay failure notified | +| ✗ | ✓ | Video with timestamp, no audio — audio failure notified | +| ✗ | ✗ | Speed-only video — both failures notified | + +**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. --- @@ -140,8 +170,8 @@ Each pipeline saves an intermediate file before the step most likely to fail, so | 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 +Notifications fire for: +- Sunrise video ready (with audio/overlay status), upload success/failure - Each daily seasons clip saved - Montage complete (or degraded if audio/overlay failed) - Year-end Four Seasons video complete @@ -151,24 +181,24 @@ You receive notifications for: ## Camera audio (optional) -If your camera has a microphone, sky-cam can mix a natural-speed 10-second audio clip (birds, rain, wind — whatever was actually happening at sunrise) into the daily sunrise video. +If your camera has a microphone, sky-cam records exactly `SUNRISE_TARGET_SECS` of natural-speed audio centred on the actual sunrise moment and mixes it into the timelapse video. 1. Set `AUDIO_ENABLED=true` in `sky-cam.conf` -2. Set `CAM_RTSP_` for the sunrise camera (needed for both image capture and audio) +2. Set `CAM_RTSP_` in `.env` for the sunrise camera 3. Re-run `./install.sh` to generate the `sky-cam-audio-capture.timer` -The audio is recorded during the same window as the images, stored alongside them, and deleted automatically after being mixed into the final video. No audio library or AI required — it's the real sound from your camera. +The recording is split evenly around sunrise (e.g. 5 s before + 5 s after for a 10 s clip; odd second goes to post-sunrise). It is deleted automatically after mixing. ### Audio fallback library (optional) -If the camera has no mic, or audio capture fails, `daily_sunrise_video.sh` picks a random ambient sound from `sunrise-sounds/` instead. The library is organised into 11 weather/season folders (`clear-spring`, `rain`, `thunder`, `windy`, etc.) — populate it once with: +If the camera has no mic, or audio capture fails, `daily_sunrise_video.sh` picks a random ambient sound from `sunrise-sounds/` instead. The library is organised into 11 weather/season folders (`clear-spring`, `rain`, `thunder`, `windy`, etc.) — populate it once with: ```bash # Get a free API key at https://freesound.org/apiv2/apply/ python3 download-sunrise-sounds.py --api-key YOUR_KEY ``` -Default: ~275 CC-licensed 128 kbps MP3 previews (~25 per category). Re-run any time to top up: +Default: ~275 CC-licensed 128 kbps MP3 previews (~25 per category). Re-run any time to top up: ```bash python3 download-sunrise-sounds.py --api-key YOUR_KEY --per-category 40 @@ -176,29 +206,29 @@ python3 download-sunrise-sounds.py --api-key YOUR_KEY --per-category 40 Attribution data for every file is written to `sunrise-sounds/manifest.json`. -**Priority order for sunrise audio:** +**Audio priority order:** 1. Camera mic recording (`sunrise-audio.m4a`) — real ambient sound at actual sunrise -2. Random file from `sunrise-sounds/` library — weather/season matched in a future update +2. Random file from `sunrise-sounds/` library 3. No audio — overlay-only video (always produced regardless) --- ## Manual operations -**Re-run today's sunrise** (e.g. after fixing a font issue): +**Re-run today's sunrise** (e.g. after a config fix): ```bash ./daily_sunrise_video.sh ``` -**Re-run a daily seasons clip** for a specific date: +**Re-run a daily seasons clip** for yesterday: ```bash -./4-seasons.sh # reprocesses yesterday +./4-seasons.sh sunrise ``` **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 +./montage-mvt.sh # uses today's movement +./montage-mvt.sh 2025-06-15 sunrise # specific date + camera ``` **Rebuild the year-end video**: