read_config / _read_conf now strips trailing inline shell comments
(whitespace + # + anything) before processing the value. Without this,
a line like:
SUNRISE_CAM=east # which camera faces east
produced SUNRISE_CAM = 'east # which camera faces east',
causing sunrise2mm.py to build a path with the comment embedded in it.
The regex \s+#.*$ requires at least one whitespace before # so passwords
or URLs containing # (e.g. %23 URL-encoded) are unaffected.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Audio:
AUDIO_DIR/<cam>/YYYY-MM-DD/<cam>-HH-MM-SS.m4a
Movies — cam name now always leads the filename:
daily_sunrise_video.sh:
east-2026-04-22-daily-sunrise.mp4
east-2026-04-22-daily-sunrise-sped.mp4 (intermediate)
east-2026-04-22-daily-sunrise-test.mp4 (demo)
4-seasons.sh:
east-2026-04-21_Mvt2-Day2of31-final.mp4 (was: 2026-04-21_east_Mvt2-...)
montage-mvt.sh:
east-2026-03-20_Spring_Mvt1-Sped.mp4
east-2026-03-20_Spring_Mvt1-Montage.mp4
year-end-join.sh:
east-2026.mp4 (already had cam at front)
sunrise2mm.py updated to look for the new east-YYYY-MM-DD-daily-sunrise.mp4
filename. Retention find patterns (wildcards) already match both formats.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
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
daily_sunrise_video.sh:
- Test output now saves to DEMO_DIR/<cam>/ (default: MOVIES_DIR/demoio/<cam>/)
instead of the production sunrise-only directory
- After each test run, deletes test files older than DEMO_RETENTION_DAYS (8)
- Production mode: warns if the last selected frame is >3 intervals before
the window end, which indicates the capture service had a gap near sunrise:
WARNING: last frame is 20min 0s before window end — capture gap near sunrise
Check: systemctl --user status sky-cam-capture-east.service
sky-cam.conf: add DEMO_DIR and DEMO_RETENTION_DAYS settings
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
daily_sunrise_video.sh:
- Fix "Sunrise (UTC)" label — sunrise.py has always returned local time;
now labelled correctly as "Sunrise (local)"
- After image selection, log first and last frame timestamps alongside the
overlay time so the window can be verified at a glance:
Images: 481 (05:22:10 → 06:43:00 overlay: 06:32)
sunrise2mm.py:
- Cleanup now only deletes posts whose message matches "Sunrise YYYY-MM-DD"
(the format this script posts), so other posts with attachments in the
channel are never touched
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
sunrise2mm.py: after a successful upload, scan the channel and delete any
post older than MM_SUNRISE_RETENTION_DAYS that is not pinned and has a file
attachment. Pinned posts are always preserved regardless of age.
sky-cam.conf: add MM_SUNRISE_RETENTION_DAYS=8 (independent of the local
disk SUNRISE_RETENTION_DAYS so the two retentions can differ).
Also modernises sunrise2mm.py: uses the same ${VAR:-default} aware
read_config as sunrise.py, consistent error handling, cleaner structure.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Rule is now consistent throughout:
- *_ENABLED flags are plain true/false in sky-cam.conf — flip them there
- Private values (URLs, credentials, location) go in .env only
sky-cam.conf:
- NTFY_ENABLED, EMAIL_ENABLED, MM_NOTIFY_ENABLED are now plain false (not
using the ${VAR:-} pattern, which implied they could go in .env)
- NTFY_URL, EMAIL_TO, EMAIL_FROM, MM_NOTIFY_CHANNEL_ID use ${VAR:-} pattern
so they are set in .env
- LATITUDE/LONGITUDE comment clarified with example .env lines
install.sh .env.example: updated notification comment to match
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Adds TIMEZONE, BASE_DIR, MOVIES_DIR (commented out) so .env.example
is a complete reference — location, storage paths, RTSP URLs, Mattermost
credentials, and notification endpoints all in one place.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
sky-cam.conf:
- MUSIC_DIR now uses override pattern (MUSIC_DIR="${MUSIC_DIR:-$SCRIPT_DIR/music}")
so the real path can be set in .env without hardcoding it
- LATITUDE/LONGITUDE use override pattern; set real coords in .env to keep
location private and out of git
- NTFY_URL now uses override pattern with empty default; set in .env
- NTFY_ENABLED moved out of comments into active config with false default
sunrise.py:
- Reads .env after sky-cam.conf so .env values override (same source order
as sky-cam.conf itself)
- Handles ${VAR:-default} bash syntax when parsing sky-cam.conf values
All completion notifications now include the full output file path so you
can tell at a glance where the file landed:
- daily_sunrise_video.sh: "filename — Xs, sunrise at HH:MM | /full/path"
- 4-seasons.sh: "filename — Xs | /full/path"
- montage-mvt.sh: "filename | checks | /full/path | verify: ..."
- year-end-join.sh: "filename — Xmin | /full/path" (also adds [cam] to title)
install.sh .env.example now includes LATITUDE, LONGITUDE, MUSIC_DIR, NTFY_URL
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- chmod +x all *.sh and *.py in install.sh so permissions survive git checkouts
- Fix daily_sunrise_video.sh and Python scripts missing executable bit
- Add [CAM_NAME] prefix to all 4-seasons.sh notify.sh calls so multi-camera
setups show which camera each daily clip came from
- Include camera name in output filename: YYYY-MM-DD_<cam>_MvtN-DayXofY-final.mp4
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Per-camera capture interval:
CAPTURE_INTERVAL_<cam> in sky-cam.conf overrides the global value for
a specific camera. capture.sh and 4-seasons.sh both respect it.
Example: south at 30s saves storage; north at 5s gives smoother motion.
4-seasons.sh one-frame bug fix:
Same root cause as the sunrise scripts — concat list had no 'duration'
entries, collapsing all JPEG frames to timestamp 0. Fixed by writing
'duration INTERVAL' per frame. Added fps=25 to the speed-adjust step
so the VFR source resamples to a standard frame rate.
ambient-record.sh (new):
Continuous AAC recorder for natural sounds (birds, rain, wind).
Saves AMBIENT_CHUNK_SECS chunks to AMBIENT_DIR/<cam>/YYYY-MM-DD/HH-MM-SS.m4a.
Reconnects automatically on stream failure. Rolling retention via
AMBIENT_RETENTION_DAYS. Enabled by setting AMBIENT_ENABLED=true and
listing cameras in AMBIENT_CAMS in sky-cam.conf, then re-running install.sh.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Notification config (NTFY_URL, MM_NOTIFY_CHANNEL_ID, etc.) lives in
.env but notify.sh was only sourcing sky-cam.conf, so notifications
were silently skipped even when configured.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
enable --now only starts a service that is not yet running — it leaves
an already-running service untouched, so changes to .env credentials
are never picked up without a manual restart.
Switching to enable + restart means every install.sh run brings capture
services in sync with the current sky-cam.conf and .env: new cameras
start, reconfigured cameras restart, and nothing needs manual follow-up.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
CAM_RTSP_<cam> is set in .env, not sky-cam.conf, but capture.sh was
only sourcing sky-cam.conf — causing the service to fail with "not set"
even after credentials were added to .env.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Root cause: the concat list had no 'duration' entries, so all JPEG
frames collapsed to timestamp 0 and only one frame survived encoding.
Fix: add 'duration CAPTURE_INTERVAL' per entry in both scripts.
Also add fps=25 to the speed-adjust step so the VFR source is resampled
to a standard frame rate — without this, players see ~6fps and may drop
frames to reach the declared rate.
daily_sunrise_video.sh --test [MINUTES]:
Skips sunrise.py lookup and capture-window wait. Uses the last MINUTES
minutes of captured frames (default 2). Overlay shows the mid-point
frame's timestamp. Audio grabbed live from RTSP immediately.
Output: <date>-daily-sunrise-test.mp4 — no notifications or upload.
Usage: ./daily_sunrise_video.sh east --test 2
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- fontcolor: white → yellow (both scripts)
- y position: (h-th)/2 → h-th-18 (vertically centered → lower-right corner)
- test-sunrise.sh: reverted to reading last 60 frames from capture directory
(~10 minutes at 10s interval) instead of live RTSP capture
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Overlay fix (both scripts):
Switch from text='...\n...' to drawtext's textfile= option. Writing
each character on its own line eliminates the \n escape ambiguity in
ffmpeg's filter-string single-quote parser, which was causing 'n' to
appear literally instead of as a line break.
test-sunrise.sh redesign:
Now captures NUM_FRAMES fresh RTSP frames (120s at default interval)
at run time instead of reading pre-existing frames from the capture
directory. The RTSP URL must be set in .env — the script fails early
with a clear message if it is not.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- Overlay text was showing characters run together (e.g. '1n2n') because
inside ffmpeg single-quoted filter text, \n is consumed as just 'n'.
Fix: use \\\\n in bash double-quotes which stores \\n in the variable,
which ffmpeg then parses as \n, which drawtext renders as a newline.
Applied to both test-sunrise.sh (MID_VERT) and daily_sunrise_video.sh
(SR_VERT, replacing the awk one-liner with the same bash approach).
- test-sunrise.sh concat list was missing per-frame duration entries,
causing 12 frames to encode as 0.48s (25fps default) and then slow
down instead of compress. Fixed by writing 'duration INTERVAL' per
frame so 12 frames × 10s = 120s raw → 12× compression to 10s.
- Removed 2>/dev/null from audio ffmpeg call in test-sunrise.sh so
capture failures are visible for diagnosis.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Runs the same 4-step pipeline as daily_sunrise_video.sh but uses the
most recent ~2 minutes of captured frames instead of the sunrise window,
grabs live RTSP audio for the audio mix step, and accepts an arbitrary
output path — so it can be run at any time of day to verify the full
pipeline (speed-adjust, audio mix, vertical time overlay) without
waiting for tomorrow's sunrise.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
capture.sh creates the per-day subdirectory at runtime, but BASE_DIR/<cam>
and MOVIES_DIR/<cam> need to exist first. Create them during install so the
capture service works immediately on first start without needing a separate
manual mkdir step.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
The shell version had to pass the password through a shell variable into
a Python subprocess, which is fragile for special characters.
The Python version uses getpass.getpass() which reads directly from the
terminal at the Python level — no shell variable, no quoting issues, no
risk of special characters being mishandled.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Prompts for a password without echoing, URL-encodes it via Python's
urllib.parse.quote, and prints the result with a ready-to-paste .env
snippet. Password is passed via environment variable to avoid any
shell quoting or injection issues — never written to disk or network.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- Set XDG_RUNTIME_DIR before systemctl --user calls so daemon-reload
and enable --now work when run outside a login session (e.g. via SSH
or sudo)
- Run loginctl enable-linger so user services start at boot without a
login session
- Update .env.example to use single quotes around RTSP URLs and
document URL-encoding for special characters in passwords
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Older sky-cam.conf versions may not define these. Fall back to the
canonical defaults so make-finals.sh works without requiring the user
to update their local conf.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Allows processing a single movement without waiting for the full backup
scan, e.g.:
./make-finals.sh ~/drives/data2-main --cam north --season Autumn --mvt 1
Filters are applied as soon as the relevant variable is known (cam before
calling season_info.py, season/mvt after) so skipped files cost almost
nothing.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
The previous check used || true which masked ffprobe's non-zero exit
code. For files with a missing/partial moov atom ffprobe can print a
duration estimate then exit non-zero — the || true let that through.
Now use || raw_dur="" to honour the exit code, and add a second probe
for an actual video stream codec so files that report a duration but
have no decodable stream are also caught.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Short adagio movements (Summer Mvt 2 at 107s, Winter Mvt 2 at 132s, etc.)
compress each day to only 3-5 seconds of screen time at 1×. Looping the
music N times multiplies the per-day target duration proportionally so the
footage is less aggressively sped up and more watchable.
- sky-cam.conf: add MONTAGE_MUSIC_LOOPS and per-movement overrides
(Summer Mvt2=3×, Winter Mvt2=3×, Autumn Mvt2=2×, Summer Mvt3=2×, Spring Mvt2=2×)
- 4-seasons.sh: multiply target_per_clip by loops
- montage-mvt.sh: compute effective_duration, use -stream_loop for audio,
fix fade_out_start, -t, and post-build duration check
- make-finals.sh: multiply target by loops
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Check ffprobe output before encoding — empty/N/A duration means the
file is corrupt or truncated; skip with a clear message rather than
passing a bad input to ffmpeg.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Scans all *-temp.mp4 files under a backup root regardless of directory
structure, infers camera name from path, uses season_info.py for correct
movement sizing, writes finals to MOVIES_DIR. Handles messy nested paths
(south/south/, movies/south/, etc.) and deduplicates via EXISTS check.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Restructure find loop to iterate known CAMERAS+sunrise in the main shell
so cam_name is visible in the while body. Previously the for loop ran in
a process substitution subshell, making cam_name unavailable and causing
top-level year dirs (e.g. 2026/) to be treated as camera names.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Moves Season/MvtN mp4 files from flat camera dirs into
astro-YEAR/<cam>/Season/MvtN/ using season_info.py for correct
year assignment. Handles cross-year mixing in old backup structure.
Dry-run support; never deletes files.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Re-processes *-temp.mp4 files from a backup location using season_info.py
to compute the correct target duration per clip (music_duration / DAYS_IN_MVT),
producing properly-sized *-final.mp4 files without touching the backup source.
Skips finals that already exist. Fixes drift caused by old pipeline sizing clips
to the wrong movement structure.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- sky-cam.conf: add ENCODE_PRESET=slow (applied to all encodes);
split CRF_MONTAGE (intermediates, H.264) from CRF_MONTAGE_FINAL=22
(archive montage, H.265); add SUNRISE_RETENTION_DAYS=10; improve
CAPTURE_INTERVAL comment with storage/density table
- montage-mvt.sh: steps 1+2 use preset; step 3 final switches to
libx265 + CRF_MONTAGE_FINAL + -tag:v hvc1 for broad compatibility
- 4-seasons.sh: both encode steps use ENCODE_PRESET
- daily_sunrise_video.sh: all encode steps use ENCODE_PRESET; rolling
retention deletes sunrise videos older than SUNRISE_RETENTION_DAYS
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- verify-mvt.sh: remove dead first _info= line (used MVT_START before
defined); fix JPEG size loop to accumulate bytes correctly and display
human-readable total; show size in delete confirmation
- montage-mvt.sh: grep -c uses || echo 0 instead of || true to be
unambiguous about the no-match value under set -e
- capture.sh, capture-watchdog.sh: require camera name arg, remove
silent SUNRISE_CAM fallback
- migrate-seasons.sh: require --cam arg, error if missing
- README.md, sky-cam.conf: remove stale fullday-video.sh references;
README pipeline diagram updated to show verify-mvt.sh
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
No script defaults to SUNRISE_CAM (east) for the seasons pipeline.
Every direction is treated equally — camera name is a required arg,
not an optional override.
- 4-seasons.sh: $1 required, exits with usage if missing
- montage-mvt.sh: arg order changed to <cam> [date]; $1 required
- year-end-join.sh: $2 (cam) required
- verify-mvt.sh: $4 (cam) required
- migrate-seasons.sh: example commands updated to new arg order
- README.md: montage-mvt.sh examples updated
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- sky-cam.conf: CAMERAS now includes east, north, south with staggered
SCHEDULE_SEASONS times (01:00, 01:30, 02:00); fullday schedules and
tuning removed
- install.sh: fullday service/timer generation removed entirely
- fullday-video.sh + systemd units: deleted
- montage-mvt.sh: post-build technical checks (duration drift, video/audio
stream presence) added; notification now includes check results and the
exact verify-mvt.sh command to run
- verify-mvt.sh: new interactive CLI for montage review — shows checks,
plays video, deletes source JPEG folders on approval, or re-runs montage
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
--cam NAME overrides SUNRISE_CAM so the script can be used for any
camera (e.g. north). Summer is now included in the find targets so
any ASTRO_YEAR=2025 Summer clips (Summer 2026) are also migrated.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
The old pattern *Mvt*3* matched .mp3 in every MP3 filename, so sort|head-1
always picked track 01 (Mvt1). Fix by finding all files for the season
then filtering with grep -iE "Mvt[^0-9]*N[^0-9]", which requires the
movement number to be surrounded by non-digit characters (space, dot,
underscore) and cannot match digits embedded in .mp3 or track prefixes.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
All *-final.mp4 clips are valid footage regardless of what speed factor
the old script used — montage-mvt.sh re-adjusts the full concatenated
video to match music duration anyway, so per-clip speed is irrelevant.
Move every ASTRO_YEAR=2025 clip to the correct new movement directory
based on its date. Spring Mvt1 now gets all 30 clips (Mar 21–Apr 19).
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- Filter to ASTRO_YEAR=2025 (Winter 2025-26 onward); skip Autumn 2025 and earlier
- Move files instead of creating symlinks
- Quarantine clips matching Day<N>of92 (Spring clips from broken script with
wrong 92-day movement total) to quarantine-old-scripts/ for review + deletion
- Preserve clips with Dayof92 (no day number, old legacy format) — these are
migrated normally since montage-mvt.sh re-adjusts speed regardless
- Summary shows delete commands for temp files and quarantine folder
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
migrate-seasons.sh uses season_info.py to re-classify every old
*-final.mp4 clip by its YYYY-MM-DD filename prefix, placing a symlink
in the correct new-system directory regardless of how the old system
labelled movements. Handles the boundary mismatches between old and
new movement splits automatically.
Coverage after migration (based on available clips):
Spring 2026 Mvt1 30/31 days (missing Day 1 only — excellent)
Winter Mvt1 ~18/29 days
Winter Mvt2 ~24/29 days
Winter Mvt3 ~20/31 days
Autumn Mvt1 ~24/30 days
Autumn Mvt3 ~18/30 days
Autumn Mvt2 absent — year-end join will not fire for 2024 cycle
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
capture-watchdog.sh: long-running script that checks the newest JPEG mtime
in each camera's output directory every CAPTURE_STALE_SECS/3 seconds (min 5s).
Sends a stall notification via notify.sh if no new frame has arrived within
CAPTURE_STALE_SECS. Sends a recovery notification once new frames resume.
Only alerts if the camera was previously working (avoids false positives on
first start or overnight before the first frame of the day).
sky-cam.conf: CAPTURE_STALE_SECS=30 added next to CAPTURE_INTERVAL with a
note that it should be at least 2x the interval.
install.sh: generates sky-cam-watchdog-<cam>.service for every camera that
has a RTSP URL configured. The watchdog service starts after and alongside
the capture service, restarts on failure, and is enabled/started with the
same loop as the capture service.
README.md: note about watchdog and journalctl command for its logs.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
montage-mvt.sh, year-end-join.sh: CAM_NAME fallback referenced undefined
\$CAM_NAME — crashes under set -euo pipefail on manual runs without args.
Fixed to fall back to \$SUNRISE_CAM (same fix applied earlier to other scripts).
notify.sh: Mattermost notification guard only checked MM_NOTIFY_CHANNEL_ID,
not mattermost_url or access_token. If those are unset, curl would send a
malformed request silently. Added guards for all three required values.
install.sh: seasons/fullday service units had literal \n in After=/Wants=
lines because bash does not interpret \n in heredoc variable expansions.
Changed to \$'...' syntax so actual newlines are written, making valid
systemd unit files. Also: audio capture timer now uses \$SCHEDULE_SUNRISE
instead of hardcoded 03:00, keeping it in sync if the user changes the
schedule.
sunrise-audio-capture.sh: record_start_sec could theoretically go negative
(sunrise very early + large SUNRISE_TARGET_SECS). Added floor-at-zero guard.
daily_sunrise_video.sh: added comment explaining why audio_offset is always 0.
README.md: fix CAM_RTSP_sunrise example to CAM_RTSP_east.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
Cameras are now named by direction (east, north, south, west) throughout
config defaults, examples, and documentation. The config key SUNRISE_CAM
is unchanged — it still means 'which camera faces east and gets the sunrise
job'. Only the camera instance name value changes from 'sunrise' to 'east'.
sky-cam.conf: CAMERAS=(east), SUNRISE_CAM=east, SCHEDULE_*_east,
example comments updated to show all four compass points.
README.md: all command examples updated (4-seasons.sh east, journalctl
sky-cam-capture-east, etc.) — phenomenon references ('sunrise window',
'sunrise time') left unchanged.
bootstrap.sh: example updated to show compass directions.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
- 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
4-seasons.sh, fullday-video.sh: CAM_NAME fallback referenced undefined
\$CAM_NAME — under set -euo pipefail this crashes if called manually without
an argument. Changed to fall back to \$SUNRISE_CAM (matches capture.sh).
daily_sunrise_video.sh: add log message when the capture window has already
closed (manual re-run after the fact). Behaviour is unchanged — processing
proceeds immediately — but the operator now gets a clear confirmation rather
than silence.
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ