Add year-based folders, attribution overlay, notifications, crontab/systemd

season_info.py
- Add ASTRO_YEAR: year of the Winter solstice that started the current cycle
  (Spring/Summer/Autumn 2026 → 2025; Winter Dec-2026 → 2026)

4-seasons.sh
- Daily clip output path now includes ASTRO_YEAR:
  movies/$name/$ASTRO_YEAR/$SEASON/Mvt$N/

montage-mvt.sh (full rewrite)
- Attribution is now a drawtext overlay (top 6 s of the video, fade in/out)
  instead of an appended black card — no frame-size matching needed, and
  year-end join requires no special handling
- Output path mirrors the new year-based folder structure
- Sends notification via notify.sh when done
- Automatically triggers year-end-join.sh after Autumn Mvt 3

year-end-join.sh (new)
- Concatenates all 12 movement montages for a given ASTRO_YEAR
- Output: movies/$name/$ASTRO_YEAR/$name-$ASTRO_YEAR.mp4
- Sends notification when done

notify.sh + notify_config.txt (new)
- Generic best-effort notification helper: ntfy, email, Mattermost text post
- All methods disabled by default; user fills in notify_config.txt

sky-cam.crontab + systemd/ (new)
- Crontab file: sunrise at 09:00, 4-seasons at 01:00, fullday at 02:00
- systemd .service + .timer units for all three daily jobs
- montage/year-end triggered internally, not by cron

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
This commit is contained in:
Claude
2026-04-18 13:59:48 +00:00
parent b827c58eda
commit 8617248cc0
14 changed files with 416 additions and 98 deletions
+9
View File
@@ -28,6 +28,9 @@ Outputs (shell-sourceable):
SEASON_START YYYY-MM-DD of season start (local date)
SEASON_END YYYY-MM-DD of season end (local date)
IS_LAST_DAY true | false
ASTRO_YEAR year when the current cycle's Winter solstice occurred
(Spring/Summer/Autumn of 2026 all return 2025 if Winter
started Dec 2025; Winter 2025 itself also returns 2025)
"""
import sys
@@ -148,6 +151,11 @@ def get_info(date: datetime.date, tz_name: str = DEFAULT_TZ) -> dict:
else:
mvt, mvt_start, mvt_end, days_in_mvt = 3, m2_end + datetime.timedelta(1), m3_end, m3
# Astronomical year: the calendar year in which the current cycle's Winter
# solstice occurred. Spring/Summer/Autumn belong to the Winter that preceded
# them; Winter belongs to the solstice that started it.
astro_year = s_start.year if season == "Winter" else prev["Winter"].year
return {
"SEASON": season,
"MVT_NUM": mvt,
@@ -158,6 +166,7 @@ def get_info(date: datetime.date, tz_name: str = DEFAULT_TZ) -> dict:
"SEASON_START": s_start.isoformat(),
"SEASON_END": s_end.isoformat(),
"IS_LAST_DAY": str(date == mvt_end).lower(),
"ASTRO_YEAR": str(astro_year),
}