diff --git a/resize-temps.sh b/resize-temps.sh new file mode 100755 index 0000000..426f99d --- /dev/null +++ b/resize-temps.sh @@ -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 [--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: +# //Spring/ Winter/ Summer/ Autumn/ +# with files like: 2025-06-15_Mvt2-temp.mp4 +# +# Output goes to: +# MOVIES_DIR////Mvt/_Mvt-Dayof-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 [--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