Merge pull request #21 from outis1one/clDemoe/seasonal-sunrise-montage-nn268
ClDemoe/seasonal sunrise montage nn268
This commit is contained in:
+2
-2
@@ -88,7 +88,7 @@ done > "$temp_file"
|
|||||||
echo "Step 1/2: encoding raw video..."
|
echo "Step 1/2: encoding raw video..."
|
||||||
ffmpeg -loglevel warning \
|
ffmpeg -loglevel warning \
|
||||||
-f concat -safe 0 -i "$temp_file" \
|
-f concat -safe 0 -i "$temp_file" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SEASONS_RAW" -vsync 2 -an \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SEASONS_RAW" -vsync 2 -an \
|
||||||
-y "$temp_video"
|
-y "$temp_video"
|
||||||
|
|
||||||
raw_duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$temp_video")
|
raw_duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$temp_video")
|
||||||
@@ -102,7 +102,7 @@ final_file="$output_dir/${yesterday}_Mvt${MVT_NUM}-Day${DAY_OF_MVT}of${DAYS_IN_M
|
|||||||
if ! ffmpeg -loglevel warning \
|
if ! ffmpeg -loglevel warning \
|
||||||
-i "$temp_video" \
|
-i "$temp_video" \
|
||||||
-vf "setpts=PTS/$speed_factor" \
|
-vf "setpts=PTS/$speed_factor" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SEASONS_FINAL" \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SEASONS_FINAL" \
|
||||||
-t "$target_per_clip" -an \
|
-t "$target_per_clip" -an \
|
||||||
-y "$final_file"; then
|
-y "$final_file"; then
|
||||||
rm -f "$final_file"
|
rm -f "$final_file"
|
||||||
|
|||||||
+13
-3
@@ -110,7 +110,7 @@ raw_video=$(mktemp --suffix=.mp4)
|
|||||||
echo "Step 1/3: encoding raw video..."
|
echo "Step 1/3: encoding raw video..."
|
||||||
if ! ffmpeg -loglevel warning \
|
if ! ffmpeg -loglevel warning \
|
||||||
-f concat -safe 0 -i "$temp_list" \
|
-f concat -safe 0 -i "$temp_list" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" -vsync 2 -an \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -vsync 2 -an \
|
||||||
-y "$raw_video"; then
|
-y "$raw_video"; then
|
||||||
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise step 1/3 (encode) $current_date" \
|
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise step 1/3 (encode) $current_date" \
|
||||||
"ffmpeg raw encode from images failed — check: journalctl -u sky-cam-sunrise.service" || true
|
"ffmpeg raw encode from images failed — check: journalctl -u sky-cam-sunrise.service" || true
|
||||||
@@ -128,7 +128,7 @@ echo "Step 2/3: speed-adjust → $sped_video"
|
|||||||
if ! ffmpeg -loglevel warning \
|
if ! ffmpeg -loglevel warning \
|
||||||
-i "$raw_video" \
|
-i "$raw_video" \
|
||||||
-vf "setpts=PTS/${speed_factor}" \
|
-vf "setpts=PTS/${speed_factor}" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" -an \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -an \
|
||||||
-y "$sped_video"; then
|
-y "$sped_video"; then
|
||||||
rm -f "$sped_video"
|
rm -f "$sped_video"
|
||||||
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise step 2/3 (speed) $current_date" \
|
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise step 2/3 (speed) $current_date" \
|
||||||
@@ -223,7 +223,7 @@ if [ -n "$DT" ]; then
|
|||||||
if ffmpeg -loglevel warning \
|
if ffmpeg -loglevel warning \
|
||||||
-i "$work_video" \
|
-i "$work_video" \
|
||||||
-vf "${DT}" \
|
-vf "${DT}" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" \
|
||||||
"${audio_out_flags[@]}" \
|
"${audio_out_flags[@]}" \
|
||||||
-y "$final_video"; then
|
-y "$final_video"; then
|
||||||
step3b_ok=true
|
step3b_ok=true
|
||||||
@@ -253,4 +253,14 @@ if $step3b_ok; then
|
|||||||
[ -f "$cam_audio" ] && rm -f "$cam_audio"
|
[ -f "$cam_audio" ] && rm -f "$cam_audio"
|
||||||
"$SCRIPT_DIR/notify.sh" "Sunrise ready: $current_date" \
|
"$SCRIPT_DIR/notify.sh" "Sunrise ready: $current_date" \
|
||||||
"$(basename "$final_video") — ${actual_dur}s, sunrise at ${SR_TIME}" || true
|
"$(basename "$final_video") — ${actual_dur}s, sunrise at ${SR_TIME}" || true
|
||||||
|
|
||||||
|
# ── Rolling retention: delete sunrise videos older than SUNRISE_RETENTION_DAYS ─
|
||||||
|
retain="${SUNRISE_RETENTION_DAYS:-10}"
|
||||||
|
if [ "$retain" -gt 0 ]; then
|
||||||
|
deleted_old=$(find "$MOVIES_DIR/$CAM_NAME" \
|
||||||
|
-path "*/sunrise-only/*-daily-sunrise.mp4" \
|
||||||
|
-mtime +"$retain" -print -delete 2>/dev/null | wc -l)
|
||||||
|
[ "$deleted_old" -gt 0 ] && \
|
||||||
|
echo "Retention: deleted $deleted_old sunrise video(s) older than ${retain} days"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
+4
-3
@@ -133,7 +133,7 @@ ffmpeg -loglevel warning \
|
|||||||
-f concat -safe 0 -i "$concat_list" \
|
-f concat -safe 0 -i "$concat_list" \
|
||||||
-vf "scale=${VIDEO_W}:${VIDEO_H}:force_original_aspect_ratio=decrease,\
|
-vf "scale=${VIDEO_W}:${VIDEO_H}:force_original_aspect_ratio=decrease,\
|
||||||
pad=${VIDEO_W}:${VIDEO_H}:(ow-iw)/2:(oh-ih)/2,setsar=1" \
|
pad=${VIDEO_W}:${VIDEO_H}:(ow-iw)/2:(oh-ih)/2,setsar=1" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_MONTAGE" -an \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_MONTAGE" -an \
|
||||||
-y "$temp_concat"
|
-y "$temp_concat"
|
||||||
|
|
||||||
# ── Step 2: Speed-adjust — saved permanently so music/overlay failure is recoverable ─
|
# ── Step 2: Speed-adjust — saved permanently so music/overlay failure is recoverable ─
|
||||||
@@ -146,7 +146,7 @@ sped_file="$output_dir/${MVT_START}_${SEASON}_Mvt${MVT_NUM}-Sped.mp4"
|
|||||||
if ! ffmpeg -loglevel warning \
|
if ! ffmpeg -loglevel warning \
|
||||||
-i "$temp_concat" \
|
-i "$temp_concat" \
|
||||||
-vf "setpts=PTS/$speed_factor" \
|
-vf "setpts=PTS/$speed_factor" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_MONTAGE" -an \
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_MONTAGE" -an \
|
||||||
-t "$music_duration" -y "$sped_file"; then
|
-t "$music_duration" -y "$sped_file"; then
|
||||||
rm -f "$sped_file"
|
rm -f "$sped_file"
|
||||||
"$SCRIPT_DIR/notify.sh" "FAILED: montage step 2/3 (speed) $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
|
"$SCRIPT_DIR/notify.sh" "FAILED: montage step 2/3 (speed) $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
|
||||||
@@ -198,7 +198,8 @@ if ffmpeg -loglevel warning \
|
|||||||
"[0:v]fade=t=in:st=0:d=${FADE_DUR},fade=t=out:st=${fade_out_start}:d=${FADE_DUR},${DT}[vout];\
|
"[0:v]fade=t=in:st=0:d=${FADE_DUR},fade=t=out:st=${fade_out_start}:d=${FADE_DUR},${DT}[vout];\
|
||||||
[1:a]afade=t=in:st=0:d=${FADE_DUR},afade=t=out:st=${fade_out_start}:d=${FADE_DUR}[aout]" \
|
[1:a]afade=t=in:st=0:d=${FADE_DUR},afade=t=out:st=${fade_out_start}:d=${FADE_DUR}[aout]" \
|
||||||
-map "[vout]" -map "[aout]" \
|
-map "[vout]" -map "[aout]" \
|
||||||
-c:v libx264 -pix_fmt yuv420p -c:a aac -b:a "$AUDIO_BITRATE" \
|
-c:v libx265 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_MONTAGE_FINAL" \
|
||||||
|
-tag:v hvc1 -c:a aac -b:a "$AUDIO_BITRATE" \
|
||||||
-t "$music_duration" -y "$output_file"; then
|
-t "$music_duration" -y "$output_file"; then
|
||||||
rm -f "$sped_file"
|
rm -f "$sped_file"
|
||||||
echo "Done: $output_file"
|
echo "Done: $output_file"
|
||||||
|
|||||||
Executable
+117
@@ -0,0 +1,117 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# reorg-backup.sh — reorganize backup season files into astro-YEAR folders.
|
||||||
|
#
|
||||||
|
# Moves files from:
|
||||||
|
# <backup_root>/<cam>/Season/MvtN/YYYY-MM-DD_*
|
||||||
|
# Into:
|
||||||
|
# <backup_root>/astro-YEAR/<cam>/Season/MvtN/YYYY-MM-DD_*
|
||||||
|
#
|
||||||
|
# Uses season_info.py to determine the correct ASTRO_YEAR from each file's date.
|
||||||
|
# NEVER deletes anything — only moves files.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./reorg-backup.sh <backup_root> [--dry-run]
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ./reorg-backup.sh ~/drives/data2-main --dry-run
|
||||||
|
# ./reorg-backup.sh ~/drives/data2-main
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||||
|
source "$SCRIPT_DIR/sky-cam.conf"
|
||||||
|
export TIMEZONE
|
||||||
|
|
||||||
|
BACKUP_ROOT=""
|
||||||
|
DRY_RUN=false
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
|
-*) echo "Unknown option: $1"; exit 1 ;;
|
||||||
|
*) BACKUP_ROOT="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$BACKUP_ROOT" ]; then
|
||||||
|
echo "Usage: $0 <backup_root> [--dry-run]"
|
||||||
|
echo " e.g. $0 ~/drives/data2-main --dry-run"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BACKUP_ROOT="$(realpath "$BACKUP_ROOT")"
|
||||||
|
|
||||||
|
$DRY_RUN && echo "DRY RUN — no files will be moved"
|
||||||
|
echo "Backup root: $BACKUP_ROOT"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
moved=0
|
||||||
|
skipped=0
|
||||||
|
errors=0
|
||||||
|
|
||||||
|
# Find all mp4 files in Season/MvtN directories (not already in astro-* dirs)
|
||||||
|
while IFS= read -r -d '' src_file; do
|
||||||
|
filename="$(basename "$src_file")"
|
||||||
|
|
||||||
|
# Extract date: handles YYYY-MM-DD_ (underscore) prefix
|
||||||
|
date_str="${filename%%_*}"
|
||||||
|
if ! [[ "$date_str" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||||
|
echo "SKIP (unrecognised filename): $src_file"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get ASTRO_YEAR for this date
|
||||||
|
_info="$(python3 "$SCRIPT_DIR/season_info.py" "$date_str" 2>/dev/null)" || {
|
||||||
|
echo "SKIP (season_info.py failed for $date_str): $filename"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
eval "$_info"
|
||||||
|
|
||||||
|
# Reconstruct target path:
|
||||||
|
# <backup_root>/astro-YEAR/<cam>/<Season>/Mvt<N>/<filename>
|
||||||
|
# Source path is:
|
||||||
|
# <backup_root>/<cam>/<Season>/Mvt<N>/<filename>
|
||||||
|
rel_path="${src_file#$BACKUP_ROOT/}" # e.g. north/Winter/Mvt1/2025-01-06_...
|
||||||
|
cam_and_below="${rel_path#*/}" # e.g. Winter/Mvt1/2025-01-06_... (strip cam)
|
||||||
|
cam_name="${rel_path%%/*}" # e.g. north
|
||||||
|
|
||||||
|
dest_dir="$BACKUP_ROOT/astro-${ASTRO_YEAR}/$cam_name/$(dirname "$cam_and_below")"
|
||||||
|
dest_file="$dest_dir/$filename"
|
||||||
|
|
||||||
|
if [ "$src_file" = "$dest_file" ]; then
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$dest_file" ]; then
|
||||||
|
echo "EXISTS — skip: astro-${ASTRO_YEAR}/$cam_name/$cam_and_below"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "MOVE astro-${ASTRO_YEAR}/$cam_name/$cam_and_below"
|
||||||
|
|
||||||
|
if ! $DRY_RUN; then
|
||||||
|
mkdir -p "$dest_dir"
|
||||||
|
if mv "$src_file" "$dest_file"; then
|
||||||
|
(( moved++ )) || true
|
||||||
|
else
|
||||||
|
echo " ERROR: mv failed"
|
||||||
|
(( errors++ )) || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
(( moved++ )) || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
done < <(find "$BACKUP_ROOT" \
|
||||||
|
-not -path "*/astro-*" \
|
||||||
|
-regextype posix-extended \
|
||||||
|
-regex ".*/[A-Z][a-z]+/Mvt[0-9]+/[0-9]{4}-[0-9]{2}-[0-9]{2}_.*\.mp4" \
|
||||||
|
-print0 | sort -z)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "────────────────────────────────────────"
|
||||||
|
echo "Moved: $moved Skipped: $skipped Errors: $errors"
|
||||||
|
$DRY_RUN && echo "(dry run — re-run without --dry-run to move files)"
|
||||||
Executable
+161
@@ -0,0 +1,161 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# resize-temps.sh — re-size *-temp.mp4 files from a backup location into
|
||||||
|
# correctly-proportioned *-final.mp4 files for use with montage-mvt.sh.
|
||||||
|
#
|
||||||
|
# Reads source temp files from BACKUP_ROOT (never modifies them).
|
||||||
|
# Writes finals to MOVIES_DIR (from sky-cam.conf / .env).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./resize-temps.sh --cam <camera-name> <backup_root> [--dry-run]
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ./resize-temps.sh --cam east /home/user/drives/data2/other-pictures/longterm/movies/movies
|
||||||
|
# ./resize-temps.sh --cam north /mnt/backup/movies --dry-run
|
||||||
|
#
|
||||||
|
# The backup_root should contain the old season directories:
|
||||||
|
# <backup_root>/<cam>/Spring/ Winter/ Summer/ Autumn/
|
||||||
|
# with files like: 2025-06-15_Mvt2-temp.mp4
|
||||||
|
#
|
||||||
|
# Output goes to:
|
||||||
|
# MOVIES_DIR/<cam>/<ASTRO_YEAR>/<SEASON>/Mvt<N>/<date>_Mvt<N>-Day<D>of<T>-final.mp4
|
||||||
|
#
|
||||||
|
# Already-existing finals are skipped (never overwritten).
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||||
|
source "$SCRIPT_DIR/sky-cam.conf"
|
||||||
|
export TIMEZONE
|
||||||
|
|
||||||
|
# ── Args ──────────────────────────────────────────────────────────────────────
|
||||||
|
CAM=""
|
||||||
|
BACKUP_ROOT=""
|
||||||
|
DRY_RUN=false
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--cam) CAM="$2"; shift 2 ;;
|
||||||
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
|
-*) echo "Unknown option: $1"; exit 1 ;;
|
||||||
|
*) BACKUP_ROOT="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$CAM" ] || [ -z "$BACKUP_ROOT" ]; then
|
||||||
|
echo "Usage: $0 --cam <camera-name> <backup_root> [--dry-run]"
|
||||||
|
echo " e.g. $0 --cam east /home/user/drives/data2/other-pictures/longterm/movies/movies"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$BACKUP_ROOT/$CAM" ]; then
|
||||||
|
echo "ERROR: $BACKUP_ROOT/$CAM not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
music_base_dir="$MUSIC_DIR"
|
||||||
|
[ ! -d "$music_base_dir" ] && music_base_dir="$SCRIPT_DIR/music"
|
||||||
|
|
||||||
|
echo "Camera: $CAM"
|
||||||
|
echo "Backup root: $BACKUP_ROOT/$CAM"
|
||||||
|
echo "Output root: $MOVIES_DIR/$CAM"
|
||||||
|
echo "Music dir: $music_base_dir"
|
||||||
|
$DRY_RUN && echo "DRY RUN — no files will be written"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Find all temp files ───────────────────────────────────────────────────────
|
||||||
|
mapfile -d '' temp_files < <(
|
||||||
|
find "$BACKUP_ROOT/$CAM" -type f -name '*-temp.mp4' -print0 | sort -z
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "${#temp_files[@]}" -eq 0 ]; then
|
||||||
|
echo "No *-temp.mp4 files found under $BACKUP_ROOT/$CAM"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Found ${#temp_files[@]} temp file(s)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
skipped=0
|
||||||
|
processed=0
|
||||||
|
errors=0
|
||||||
|
|
||||||
|
for temp_file in "${temp_files[@]}"; do
|
||||||
|
# Extract date from filename: 2025-06-15_Mvt2-temp.mp4 → 2025-06-15
|
||||||
|
filename="$(basename "$temp_file")"
|
||||||
|
date_str="${filename%%_*}"
|
||||||
|
|
||||||
|
# Validate date format
|
||||||
|
if ! [[ "$date_str" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||||
|
echo "SKIP (bad filename): $temp_file"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get season/movement info for this date
|
||||||
|
_info="$(python3 "$SCRIPT_DIR/season_info.py" "$date_str" 2>/dev/null)" || {
|
||||||
|
echo "SKIP (season_info.py failed for $date_str): $filename"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
eval "$_info"
|
||||||
|
# Provides: SEASON MVT_NUM DAY_OF_MVT DAYS_IN_MVT MVT_START MVT_END
|
||||||
|
# IS_LAST_DAY ASTRO_YEAR
|
||||||
|
|
||||||
|
# Locate music file
|
||||||
|
music_file=$(find "$music_base_dir" -type f -iname "*${SEASON}*" \
|
||||||
|
| grep -iE "Mvt[^0-9]*${MVT_NUM}[^0-9]" | sort | head -n 1)
|
||||||
|
if [ -z "$music_file" ]; then
|
||||||
|
echo "SKIP (no music for $SEASON Mvt$MVT_NUM): $filename"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
music_duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$music_file")
|
||||||
|
target_per_clip=$(echo "scale=6; $music_duration / $DAYS_IN_MVT" | bc)
|
||||||
|
|
||||||
|
# Determine output path
|
||||||
|
out_dir="$MOVIES_DIR/$CAM/$ASTRO_YEAR/$SEASON/Mvt$MVT_NUM"
|
||||||
|
final_file="$out_dir/${date_str}_Mvt${MVT_NUM}-Day${DAY_OF_MVT}of${DAYS_IN_MVT}-final.mp4"
|
||||||
|
|
||||||
|
# Skip if final already exists
|
||||||
|
if [ -f "$final_file" ]; then
|
||||||
|
echo "EXISTS — skip: $(basename "$final_file")"
|
||||||
|
(( skipped++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
raw_dur=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$temp_file")
|
||||||
|
speed_factor=$(echo "scale=6; $raw_dur / $target_per_clip" | bc)
|
||||||
|
|
||||||
|
echo "Processing: $filename"
|
||||||
|
echo " $SEASON Mvt$MVT_NUM Day$DAY_OF_MVT/$DAYS_IN_MVT Year=$ASTRO_YEAR"
|
||||||
|
echo " raw=${raw_dur}s target=${target_per_clip}s speed=${speed_factor}x"
|
||||||
|
echo " → $(basename "$final_file")"
|
||||||
|
|
||||||
|
if $DRY_RUN; then
|
||||||
|
(( processed++ )) || true
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$out_dir"
|
||||||
|
|
||||||
|
if ffmpeg -loglevel warning \
|
||||||
|
-i "$temp_file" \
|
||||||
|
-vf "setpts=PTS/$speed_factor" \
|
||||||
|
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SEASONS_FINAL" \
|
||||||
|
-t "$target_per_clip" -an \
|
||||||
|
-y "$final_file"; then
|
||||||
|
actual=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$final_file")
|
||||||
|
echo " Done: ${actual}s"
|
||||||
|
(( processed++ )) || true
|
||||||
|
else
|
||||||
|
rm -f "$final_file"
|
||||||
|
echo " ERROR: ffmpeg failed — no output written"
|
||||||
|
(( errors++ )) || true
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "────────────────────────────────────────"
|
||||||
|
echo "Processed: $processed Skipped: $skipped Errors: $errors"
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "(dry run — re-run without --dry-run to write files)"
|
||||||
|
fi
|
||||||
+23
-12
@@ -217,20 +217,28 @@ SUNRISE_TARGET_SECS=10
|
|||||||
SUNRISE_OVERLAY_ENABLED=true
|
SUNRISE_OVERLAY_ENABLED=true
|
||||||
# Opacity of the sunrise-time text (0.0 = invisible, 1.0 = fully opaque).
|
# Opacity of the sunrise-time text (0.0 = invisible, 1.0 = fully opaque).
|
||||||
SUNRISE_OVERLAY_OPACITY=0.45
|
SUNRISE_OVERLAY_OPACITY=0.45
|
||||||
|
# Delete daily sunrise videos older than this many days (rolling window).
|
||||||
|
# Set 0 to keep forever.
|
||||||
|
SUNRISE_RETENTION_DAYS=10
|
||||||
|
|
||||||
# ── Video encoding quality ────────────────────────────────────────────────────
|
# ── Video encoding quality ────────────────────────────────────────────────────
|
||||||
# FFmpeg CRF: lower = higher quality / larger files. Typical range 18–30.
|
# FFmpeg CRF: lower = higher quality / larger files. Typical range 18–30.
|
||||||
# 18 ≈ visually lossless 23 = ffmpeg default 28 = smaller/lower
|
# 18 ≈ visually lossless 23 = ffmpeg default 28 = smaller/lower
|
||||||
# Different scripts use different defaults by design — intermediate/draft
|
# H.265 (libx265) CRF is not directly comparable — H.265 CRF 22 ≈ H.264 CRF 26
|
||||||
# encodings use a higher CRF (smaller files, quality less critical), while
|
# in visual quality but ~40-50% smaller file. Final montages use H.265;
|
||||||
# final "polished" outputs use a lower CRF for better quality.
|
# intermediates (deleted after use) stay H.264.
|
||||||
# Each is independent — changing one does NOT change the others.
|
|
||||||
#
|
#
|
||||||
CRF_SUNRISE=28 # daily_sunrise_video.sh — daily Mattermost upload
|
# ENCODE_PRESET: slow = better compression, same CRF quality, ~2× encode time.
|
||||||
CRF_SEASONS_RAW=28 # 4-seasons.sh — raw concat before speed-adjust
|
# Use 'medium' on slower machines, 'slow' or 'slower' on fast ones.
|
||||||
CRF_SEASONS_FINAL=26 # 4-seasons.sh — final daily clip (goes into montage)
|
# All encodes run at night so 'slow' is the right default.
|
||||||
CRF_MONTAGE=26 # montage-mvt.sh — concat and speed-adjust and final
|
#
|
||||||
AUDIO_BITRATE=192k # montage-mvt.sh — music track on movement montages
|
CRF_SUNRISE=28 # daily_sunrise_video.sh — daily Mattermost upload (H.264)
|
||||||
|
CRF_SEASONS_RAW=28 # 4-seasons.sh — raw concat before speed-adjust (H.264)
|
||||||
|
CRF_SEASONS_FINAL=26 # 4-seasons.sh — final daily clip (goes into montage) (H.264)
|
||||||
|
CRF_MONTAGE=28 # montage-mvt.sh — intermediate concat + sped steps (H.264)
|
||||||
|
CRF_MONTAGE_FINAL=22 # montage-mvt.sh — final archive montage (H.265)
|
||||||
|
ENCODE_PRESET=slow # ffmpeg preset for all encodes (slow/medium/fast)
|
||||||
|
AUDIO_BITRATE=192k # montage-mvt.sh — music track on movement montages
|
||||||
|
|
||||||
# ── Montage attribution overlay ───────────────────────────────────────────────
|
# ── Montage attribution overlay ───────────────────────────────────────────────
|
||||||
# A translucent bar across the top of each movement montage, naming the
|
# A translucent bar across the top of each movement montage, naming the
|
||||||
@@ -254,12 +262,15 @@ MONTAGE_ATTR_FADE=1 # attribution fade-in and fade-out length (seconds)
|
|||||||
# CAM_RTSP_<cam> goes in .env (see bottom of this file), not here.
|
# CAM_RTSP_<cam> goes in .env (see bottom of this file), not here.
|
||||||
|
|
||||||
# Seconds between captured frames (applies to all cameras).
|
# Seconds between captured frames (applies to all cameras).
|
||||||
# 10 = one frame every 10 s → 8640 frames/day → good timelapse density.
|
# Lower = smoother timelapse + larger storage footprint:
|
||||||
|
# 10 s → 8,640 frames/day ~2.5 GB/day (default, good balance)
|
||||||
|
# 5 s → 17,280 frames/day ~5 GB/day (smoother motion)
|
||||||
|
# 2 s → 43,200 frames/day ~12 GB/day (very smooth, high storage)
|
||||||
|
# CAPTURE_STALE_SECS should always be at least 2× CAPTURE_INTERVAL.
|
||||||
CAPTURE_INTERVAL=10
|
CAPTURE_INTERVAL=10
|
||||||
|
|
||||||
# Seconds without a new frame before the watchdog sends a stall notification.
|
# Seconds without a new frame before the watchdog sends a stall notification.
|
||||||
# Should be at least 2× CAPTURE_INTERVAL. A recovery notification fires
|
# A recovery notification fires automatically when capture resumes.
|
||||||
# automatically when frames start arriving again.
|
|
||||||
CAPTURE_STALE_SECS=30
|
CAPTURE_STALE_SECS=30
|
||||||
|
|
||||||
# ── Sunrise audio capture ──────────────────────────────────────────────────────
|
# ── Sunrise audio capture ──────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user