Commit Graph
156 Commits
Author SHA1 Message Date
Outis 283c36979a Merge pull request #46 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
4-seasons: add optional date argument
2026-04-22 16:42:55 -04:00
Claude d00d4bf15e 4-seasons: add optional date argument
Allows processing any past date instead of always yesterday:
  ./4-seasons.sh east 2026-04-19

Useful for backfilling days with existing images.
Systemd (no date arg) continues to default to yesterday.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 20:41:45 +00:00
Outis 55681abc9f Merge pull request #45 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
4-seasons: restore original concat approach — no duration per frame
2026-04-22 14:38:17 -04:00
Claude d0399fb72d 4-seasons: restore original concat approach — no duration per frame
duration=INTERVAL in the concat file made a 0.1fps raw video, which
x264 couldn't speed up 4000x cleanly and produced 20s of the last
frame instead.  The original script used plain 'file path' entries
with no duration, letting ffmpeg default to 25fps internally.  The
speed_factor calculation against raw_duration compensates correctly
either way, but only the standard-fps raw video survives the extreme
setpts transform.  Also removed the coverage_pct check which was
based on images*INTERVAL and became meaningless without duration.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 18:29:58 +00:00
Outis 334e8b85e7 Merge pull request #44 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-22 13:00:45 -04:00
Claude 32bd64459e 4-seasons: exit cleanly when all frames fail validation
Accessing ${images[-1]} on an empty array under set -u causes a bash
error.  Guard with an explicit check so a fully-corrupt image directory
exits with a clear message instead of a cryptic crash.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 16:58:16 +00:00
Claude 8b79261b81 4-seasons: remove fps=25 from speed-adjust — keep all source frames
fps=25 caused ffmpeg to discard 15 of every 16 source frames, making
the final clip jump by ~2.5 minutes per frame.  The original script
had no fps filter, so all source frames were preserved.  Without it,
the setpts speed-adjust keeps every frame; the effective framerate in
the output rises proportionally (e.g. ~400fps for a full day at 10s
intervals into an 11s clip), which is fine for H.264 and all
downstream montage steps.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-22 16:32:47 +00:00
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