Commit Graph
98 Commits
Author SHA1 Message Date
Outis ebb9dafff0 Merge pull request #25 from outis1one/claude/seasonal-sunrise-montage-nn268
Fix corrupt-file detection in make-finals.sh
2026-04-21 06:52:06 -04:00
Claude 62f1330d9c Fix corrupt-file detection in make-finals.sh
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
2026-04-21 10:51:29 +00:00
Outis 288ac0dabd Merge pull request #24 from outis1one/claude/seasonal-sunrise-montage-nn268
Claude/seasonal sunrise montage nn268
2026-04-21 06:51:25 -04:00
Claude 0ccfb2dae0 Add MONTAGE_MUSIC_LOOPS per-movement music loop multiplier
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
2026-04-20 20:11:10 +00:00
Claude 387b84f367 make-finals.sh: skip corrupt temp files (moov atom not found)
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
2026-04-20 20:02:04 +00:00
Outis a7c9e038ee Merge pull request #23 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Add make-finals.sh — one-off recovery script for backup temp files
2026-04-20 15:57:51 -04:00
Claude 26414f9883 Add make-finals.sh — one-off recovery script for backup temp files
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
2026-04-20 19:57:13 +00:00
Outis 73529cb359 Merge pull request #22 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Fix reorg-backup.sh camera scoping — prevent 2026/ dir misidentified …
2026-04-20 15:41:08 -04:00
Claude 507ecf32c8 Fix reorg-backup.sh camera scoping — prevent 2026/ dir misidentified as cam
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
2026-04-20 18:49:34 +00:00
Outis 9dcb83afa8 Merge pull request #21 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-20 14:40:48 -04:00
Claude da35dc862c Add reorg-backup.sh — reorganize backup files into astro-YEAR folders
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
2026-04-20 18:39:15 +00:00
Claude d4e39a5554 Add resize-temps.sh — re-size backup temp files to correct movement targets
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
2026-04-20 18:17:20 +00:00
Claude a395f9baba Encoding: H.265 final montages, slow preset, sunrise retention
- 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
2026-04-20 17:27:57 +00:00
Outis b929589f80 Merge pull request #20 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Fix MUSIC_DIR path quoting — space in '4 Seasons' broke conf sourcing
2026-04-20 12:42:45 -04:00
Claude ab06c1cc2b Fix MUSIC_DIR path quoting — space in '4 Seasons' broke conf sourcing
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-20 16:40:55 +00:00
Outis 91d7774a4f Merge pull request #19 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-20 12:36:04 -04:00
Claude c4fb086f1d Fix bugs found in post-refactor audit
- 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
2026-04-20 16:34:05 +00:00
Claude 8c752fc165 Require camera name explicitly across all seasons scripts
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
2026-04-20 16:28:15 +00:00
Outis 0d0d3e6f24 Merge pull request #18 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-20 12:19:00 -04:00
Claude 91006bf5f6 sky-cam.conf: clean up schedule comments, west camera placeholder
Remove stale SCHEDULE_FULLDAY comment block; add commented west camera
slot with instructions so enabling it is a one-line uncomment.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-20 15:59:12 +00:00
Claude b4019ade9d Add north/south cameras, remove fullday, add verify-mvt.sh
- 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
2026-04-20 15:56:12 +00:00
Claude 04809cfe90 migrate-seasons.sh: add --cam flag, include Summer in search
--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
2026-04-20 15:05:15 +00:00
Outis bf9d67e5fd Merge pull request #17 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
Fix music file matching: Mvt3 was resolving to Mvt1 for every season
2026-04-20 10:35:57 -04:00
Claude 8ba0619ead Fix music file matching: Mvt3 was resolving to Mvt1 for every season
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
2026-04-20 14:30:32 +00:00
Outis 82a0db4dd9 Merge pull request #16 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
2026-04-20 10:16:52 -04:00
Claude 331bce38c4 Simplify migrate-seasons.sh: migrate all clips, drop quarantine logic
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
2026-04-20 14:16:13 +00:00
Claude 7affbc5c54 Revise migrate-seasons.sh: ASTRO_YEAR=2025 only, move not symlink, quarantine bad clips
- 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
2026-04-20 14:13:12 +00:00
Claude 32b82c7b7d Add one-shot migration script for old-format seasons clips
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
2026-04-20 14:01:01 +00:00
Outis f6c52e2133 Merge pull request #15 from outis1one/claude/seasonal-sunrise-montage-nn268
Add per-camera capture watchdog with stall and recovery notifications
2026-04-19 21:15:30 -04:00
Claude 3265ed80fd Add per-camera capture watchdog with stall and recovery notifications
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
2026-04-20 01:13:49 +00:00
Outis a419320c67 Merge pull request #14 from outis1one/claude/seasonal-sunrise-montage-nn268
Claude/seasonal sunrise montage nn268
2026-04-19 21:03:05 -04:00
Claude 8b9ad74c58 Fix six bugs found in final audit
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
2026-04-20 01:01:25 +00:00
Claude 64cec6b769 Rename default camera from 'sunrise' to 'east' for compass consistency
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
2026-04-20 00:53:58 +00:00
Claude 9ea03d2249 Replace \$EDITOR with nano in README and bootstrap instructions
https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-20 00:46:39 +00:00
Outis ab211174b4 Merge pull request #13 from outis1one/claude/seasonal-sunrise-montage-nn268
README: full rewrite — dependencies first, accurate quick start, curr…
2026-04-19 20:45:01 -04:00
Claude 4d5da05bc9 README: full rewrite — dependencies first, accurate quick start, current behavior
- 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
2026-04-20 00:36:58 +00:00
Outis c173eb5e18 Delete music directory 2026-04-19 20:33:15 -04:00
Outis b2d5fc0ff4 Delete images directory 2026-04-19 20:33:01 -04:00
Outis ec18c6c71a Delete 22-45-14.jpg 2026-04-19 20:32:44 -04:00
Outis 717dae88e2 Merge pull request #12 from outis1one/claude/seasonal-sunrise-montage-nn268
Claude/seasonal sunrise montage nn268
2026-04-19 20:32:15 -04:00
Claude 9039803a51 Fix undefined CAM_NAME crash; log when window already closed on re-run
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
2026-04-20 00:29:47 +00:00
Claude 4814c9e158 Audio capture records exactly SUNRISE_TARGET_SECS centred on sunrise
Previously sunrise-audio-capture.sh recorded the entire capture window
(70+ minutes) and daily_sunrise_video.sh sought to the right position with
a complex offset calculation.

Now: record floor(TARGET/2) seconds before sunrise and ceil(TARGET/2) after —
odd second goes to post-sunrise.  The file is exactly SUNRISE_TARGET_SECS long
and already centred, so no offset is needed when mixing.

AUDIO_PRE_BUFFER_MIN removed — it was part of the old whole-window scheme
and is no longer meaningful.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-20 00:22:35 +00:00
Claude be714affc0 Auto-detect SCRIPT_DIR/BASE_DIR; sunrise waits for capture window; optional overlay
sky-cam.conf:
- SCRIPT_DIR auto-detects from its own location via BASH_SOURCE — no manual
  path entry needed after bootstrap; override still works via environment.
- BASE_DIR defaults to \$SCRIPT_DIR/data; MOVIES_DIR defaults to \$BASE_DIR/movies.
  Override either to point at a drive/mount as before.
- SCHEDULE_SUNRISE changed to 03:00 — the script now waits internally until
  the capture window closes before touching images, so the schedule just needs
  to be early enough, not timed exactly to sunrise.
- Schedule section rewritten to explain what each job does and what day's data
  it processes (SEASONS/FULLDAY always process yesterday).
- SUNRISE_PRE_MIN/POST_MIN comments explain the window and its relationship to
  SUNRISE_TARGET_SECS.
- SUNRISE_OVERLAY_ENABLED=true added; set false to skip the timestamp entirely.

daily_sunrise_video.sh:
- After calculating the capture window, sleep until end_sec+30s if we fired
  before the window finished — guarantees all images are present.
- DT (drawtext filter) is only built when SUNRISE_OVERLAY_ENABLED=true.
- Step 3b skips ffmpeg entirely when overlay is disabled, moving work_video
  directly to final_video (faster, no re-encode).

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-20 00:18:43 +00:00
Outis 543dc0cc9b Merge pull request #11 from outis1one/claude/seasonal-sunrise-montage-nn268
Claude/seasonal sunrise montage nn268
2026-04-19 20:10:17 -04:00
Claude a1cf1304e4 Fix first-run blockers: MOVIES_DIR path, CAM_NAME, .env for Python, eval safety
daily_sunrise_video.sh: use \$MOVIES_DIR instead of \$BASE_DIR/movies so the
override in sky-cam.conf is respected when videos live on a separate drive.

capture.sh: default camera name falls back to \$SUNRISE_CAM (not the
undefined \$CAM_NAME) when no argument is passed.

sunrise2mm.py: also loads .env after sky-cam.conf so Mattermost credentials
moved to .env are actually visible to the upload script.

sunrise.py: raise a clear error message when LATITUDE/LONGITUDE/TIMEZONE
are missing from sky-cam.conf instead of an opaque KeyError.

4-seasons.sh, montage-mvt.sh: capture season_info.py output before eval so
a Python failure exits cleanly with a diagnostic message rather than
silently continuing with undefined variables.

bootstrap.sh: add system package install step (ffmpeg bc fonts-dejavu) and
show the .env setup step after install.sh generates .env.example.

README.md: correct Python package names and add fonts-dejavu dependency.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-19 23:37:10 +00:00
Claude a1b9ffa8ee Add .env credentials file support; gitignore .env and sunrise-sounds/
sky-cam.conf now sources .env from the same directory at the bottom of the
file, so RTSP URLs, Mattermost tokens, and notification credentials can live
outside version control.  Plaintext credential placeholders removed from conf.

install.sh generates .env.example on every run with the correct CAM_RTSP_*
variable names derived from the CAMERAS list, so users always know exactly
what to put in .env for their specific camera setup.

.gitignore: add .env and sunrise-sounds/ (downloaded audio library).

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-19 23:08:01 +00:00
Claude ba1eb70436 bootstrap.sh: fix post-install instructions (remove CAM_NAME, add pip step)
CAM_NAME is no longer a config key; replace with CAMERAS + SUNRISE_CAM.
Add pip3 install step for suntime/pytz/requests as step 1.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-19 22:45:26 +00:00
Claude 66fe1f46ca Fix CAM_NAME bug, set -x noise, wrong Python package in README
daily_sunrise_video.sh: derive CAM_NAME from $1 (defaulting to SUNRISE_CAM)
instead of an undefined variable; this was causing empty image/output paths.

install.sh: pass $SUNRISE_CAM as argument to daily_sunrise_video.sh so it
matches the pattern used by every other per-camera script.

sunrise2mm.py: use SUNRISE_CAM config key (not the undefined CAM_NAME) to
locate the output directory; fixes silent path mismatch on non-default names.

4-seasons.sh: remove stray `set -x` that was flooding systemd logs with
shell trace output.

README.md: correct Python dependencies from `ephem` to `suntime pytz` to
match what sunrise.py actually imports.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-19 21:56:33 +00:00
Outis d97740aafd Merge pull request #10 from outis1one/claude/seasonal-sunrise-montage-nn268
Claude/seasonal sunrise montage nn268
2026-04-19 17:37:45 -04:00
Claude 16af99f0a3 Split step 3 into independent audio (3a) and overlay (3b) stages
Each stage can now fail independently, giving four upload paths:
audio+overlay, audio-only, overlay-only, or speed-only video.
Step 3a copies the video stream (no re-encode) when mixing audio,
so step 3b failure still leaves a clean audio-mixed file to promote.
Overlay failure notification now reports which quality was uploaded.

https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
2026-04-19 21:34:04 +00:00