#!/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