Commit Graph
149 Commits
Author SHA1 Message Date
Outis c92626e9df Merge pull request #43 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-22 12:23:24 -04:00
Claude 9d34240ac0 sky-cam.conf: comment out grey filter — must be explicitly enabled
The SEASONS_GREY_STDDEV_MIN and SEASONS_GREY_DARK_FLOOR settings are
now commented out so the filter is fully inert by default and requires
a deliberate edit to activate.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 16:22:25 +00:00
Claude ec8eb7aebf 4-seasons: protect night frames from grey filter with brightness floor
A stddev-only filter would wrongly drop near-black night frames.
Now only skips a frame when BOTH stddev < GREY_MIN AND mean brightness
> GREY_DARK_FLOOR (default 30/255).  Night frames are dark so they
always pass; bad-signal grey frames are mid-bright so they are caught.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 16:17:22 +00:00
Outis 3ed9f76f2e Merge pull request #42 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
4-seasons: validate JPEGs with ffprobe, add optional grey-frame filter
2026-04-22 12:10:47 -04:00
Claude 4aeee6b3d5 4-seasons: validate JPEGs with ffprobe, add optional grey-frame filter
The previous size check caught empty files but not corrupt JPEGs with
a partial header (common first frame after a camera reconnect).
Replace with ffprobe codec detection — if ffprobe can't identify a
video stream in the file it's skipped with a clear "corrupt" message.

Also adds SEASONS_GREY_STDDEV_MIN (default 0 = off): when enabled,
frames with near-zero pixel standard deviation (uniform grey = bad
RTSP signal) are silently dropped before encoding.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 16:07:29 +00:00
Outis 029840cbe0 Merge pull request #41 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
4-seasons: skip empty/unreadable frames, warn on low day coverage
2026-04-22 11:54:41 -04:00
Claude 292824b48d 4-seasons: skip empty/unreadable frames, warn on low day coverage
Corrupt or 0-byte JPEGs (e.g. first frame after a camera reconnect)
caused ffmpeg concat to silently truncate the output, missing all
images after the bad file.  Now filters those out before building the
concat list and logs a warning + notification when raw video covers
less than 50% of the expected image count.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 15:51:59 +00:00
Outis 9b01e4323f Update README with XDG_RUNTIME_DIR export instructions
Added instructions to set XDG_RUNTIME_DIR in ~/.bashrc
2026-04-22 11:33:09 -04:00
Outis 9126e62a92 Update README.md 2026-04-22 11:32:39 -04:00
Outis 1f9f001755 Update README with environment variable setup 2026-04-22 11:30:37 -04:00
Outis 4e50bfd3b3 Merge pull request #40 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Fix Python config parser swallowing inline comments as part of values
2026-04-22 11:05:21 -04:00
Claude 45af48e46d Fix Python config parser swallowing inline comments as part of values
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
2026-04-22 15:01:53 +00:00
Outis 31b16b5221 Merge pull request #39 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-22 10:55:03 -04:00
Claude e56663209e Prepend <cam> to all output filenames consistently
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
2026-04-22 14:45:36 +00:00
Claude 11d4630197 Restructure audio into AUDIO_DIR with per-camera retention
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
2026-04-22 14:43:05 +00:00
Claude a63a42ee46 Add Demoio directory for test videos with 8-day retention; capture gap warning
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
2026-04-22 14:23:30 +00:00
Claude 9fefe698cb Fix sunrise window logging and Mattermost cleanup scope
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
2026-04-22 14:16:15 +00:00
Claude 1cf51eaa26 Auto-delete old unpinned sunrise posts from Mattermost after each upload
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
2026-04-22 14:09:27 +00:00
Outis 363ccf02e5 Merge pull request #38 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Clarify .env vs sky-cam.conf split for toggles and private values
2026-04-22 09:58:32 -04:00
Claude 3c50e5dad2 Clarify .env vs sky-cam.conf split for toggles and private values
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
2026-04-22 13:56:58 +00:00
Outis 14c9178361 Merge pull request #37 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Expand .env.example to cover all machine-specific config
2026-04-22 09:43:57 -04:00
Claude 2a28c8e5ea Expand .env.example to cover all machine-specific config
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
2026-04-22 13:43:02 +00:00
Outis b92e7650b3 Merge pull request #36 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Move location/music/ntfy config to .env; add paths to notifications
2026-04-22 09:34:03 -04:00
Claude 74a74570fc Move location/music/ntfy config to .env; add paths to notifications
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
2026-04-22 13:30:02 +00:00
Outis b642cb820f Merge pull request #35 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-22 09:08:25 -04:00
Claude ae40b898f6 Fix executable bits and add camera name to 4-seasons notifications
- 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
2026-04-22 12:56:37 +00:00
Claude dbdd59677f Per-camera capture interval; ambient recorder; fix 4-seasons one-frame bug
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
2026-04-21 21:08:10 +00:00
Outis 914458ca40 Merge pull request #34 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
notify.sh: source .env so NTFY_URL and other creds are available
2026-04-21 14:43:16 -04:00
Claude cdaca67d31 notify.sh: source .env so NTFY_URL and other creds are available
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
2026-04-21 18:42:08 +00:00
Outis 36babe8d5f Merge pull request #33 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
install.sh: restart capture services instead of enable --now
2026-04-21 14:32:36 -04:00
Claude c5474714be install.sh: restart capture services instead of enable --now
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
2026-04-21 18:28:44 +00:00
Outis 613c88c837 Merge pull request #32 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-21 14:25:32 -04:00
Claude 9707e5ca37 capture.sh: source .env so RTSP credentials are available
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
2026-04-21 18:24:29 +00:00
Claude 1a1ff1a041 Fix one-frame bug; add --test mode to daily_sunrise_video.sh
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
2026-04-21 17:52:29 +00:00
Claude 4c02de5fd4 Overlay: yellow text, lower-right position; test-sunrise uses captured frames
- 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
2026-04-21 17:32:31 +00:00
Claude f3839ce1cf Fix overlay vertical text; test-sunrise captures fresh RTSP frames
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
2026-04-21 17:14:17 +00:00
Outis c25aa1fdf5 Merge pull request #31 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Fix sunrise overlay vertical text and test-sunrise.sh pipeline bugs
2026-04-21 13:03:26 -04:00
Claude eba8ddd550 Fix sunrise overlay vertical text and test-sunrise.sh pipeline bugs
- 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
2026-04-21 16:35:25 +00:00
Outis fac7ef0309 Merge pull request #30 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Add test-sunrise.sh pipeline smoke test
2026-04-21 12:21:29 -04:00
Claude 84fa60b857 Add test-sunrise.sh pipeline smoke test
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
2026-04-21 16:20:15 +00:00
Outis 73ebfa0f87 Merge pull request #29 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Pre-create camera data directories in install.sh
2026-04-21 12:13:44 -04:00
Claude 5c88b12b9f Pre-create camera data directories in install.sh
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
2026-04-21 16:00:48 +00:00
Outis 91d90a9ad0 Merge pull request #28 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Replace url-encode-password.sh with Python script
2026-04-21 11:35:08 -04:00
Claude 21463c22fc Replace url-encode-password.sh with Python script
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
2026-04-21 15:34:02 +00:00
Outis 60bbff18ba Merge pull request #27 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Default ENCODE_PRESET/CRF_SEASONS_FINAL in make-finals.sh
2026-04-21 11:27:35 -04:00
Claude 98cef1bcd8 Add url-encode-password.sh helper
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
2026-04-21 15:26:42 +00:00
Claude 3c2e413ea2 Expand URL-encoding reference in .env.example
Full number-row special character table with percent codes, safe
characters called out, and a note on : and / which break URL parsing.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-21 15:25:12 +00:00
Claude 9d4d50b91c Fix install.sh for non-login shell environments
- 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
2026-04-21 15:21:12 +00:00
Claude 37ad990af9 Default ENCODE_PRESET/CRF_SEASONS_FINAL in make-finals.sh
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
2026-04-21 11:27:37 +00:00
Outis 15435d2c20 Merge pull request #26 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Add --cam/--season/--mvt filters to make-finals.sh
2026-04-21 07:15:17 -04:00