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
This commit is contained in:
Executable
+126
@@ -0,0 +1,126 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# migrate-seasons.sh — map old-format seasons clips to the new sky-cam directory
|
||||||
|
# structure using season_info.py to correctly categorize each clip by date.
|
||||||
|
#
|
||||||
|
# The old system used different movement-boundary and naming conventions;
|
||||||
|
# this script ignores the old labels and re-classifies every clip by its
|
||||||
|
# YYYY-MM-DD filename prefix, placing it exactly where montage-mvt.sh expects.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./migrate-seasons.sh [--dry-run] [OLD_ROOT]
|
||||||
|
#
|
||||||
|
# --dry-run print what would happen without creating any links
|
||||||
|
# OLD_ROOT root of old movie archive (default: ~/drives/local-2tb/movies/sunrise)
|
||||||
|
#
|
||||||
|
# Output: symlinks in $MOVIES_DIR/<cam>/<ASTRO_YEAR>/<SEASON>/Mvt<N>/
|
||||||
|
#
|
||||||
|
# After running, trigger any completed movement montages manually:
|
||||||
|
# ./montage-mvt.sh 2026-04-19 east # Spring Mvt1 (just completed)
|
||||||
|
# ./montage-mvt.sh 2026-01-18 east # Winter Mvt1
|
||||||
|
# ./montage-mvt.sh 2026-02-16 east # Winter Mvt2
|
||||||
|
# ./montage-mvt.sh 2026-03-19 east # Winter Mvt3
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||||
|
source "$SCRIPT_DIR/sky-cam.conf"
|
||||||
|
|
||||||
|
DRY_RUN=false
|
||||||
|
OLD_ROOT="$HOME/drives/local-2tb/movies/sunrise"
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--dry-run) DRY_RUN=true ;;
|
||||||
|
*) OLD_ROOT="$arg" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
CAM="$SUNRISE_CAM"
|
||||||
|
NEW_ROOT="$MOVIES_DIR/$CAM"
|
||||||
|
|
||||||
|
echo "Old archive : $OLD_ROOT"
|
||||||
|
echo "New root : $NEW_ROOT"
|
||||||
|
echo "Camera : $CAM"
|
||||||
|
$DRY_RUN && echo "(DRY RUN — no files will be created)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Counters per movement
|
||||||
|
declare -A LINKED SKIPPED ERRORS
|
||||||
|
|
||||||
|
link_clip() {
|
||||||
|
local src="$1" dst_dir="$2" key="$3"
|
||||||
|
local dst="$dst_dir/$(basename "$src")"
|
||||||
|
if [ -e "$dst" ]; then
|
||||||
|
SKIPPED["$key"]=$(( ${SKIPPED["$key"]:-0} + 1 ))
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo " LINK $(basename "$src")"
|
||||||
|
echo " -> $dst"
|
||||||
|
else
|
||||||
|
mkdir -p "$dst_dir"
|
||||||
|
ln -sf "$src" "$dst"
|
||||||
|
fi
|
||||||
|
LINKED["$key"]=$(( ${LINKED["$key"]:-0} + 1 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Search the season subdirectories for old *-final.mp4 clips
|
||||||
|
while IFS= read -r -d '' src; do
|
||||||
|
fname="$(basename "$src")"
|
||||||
|
|
||||||
|
# Extract YYYY-MM-DD from filename prefix
|
||||||
|
date_str="${fname%%_*}"
|
||||||
|
if [[ ! "$date_str" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||||
|
echo "SKIP (no date): $fname"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Classify by date using the new system's season_info.py
|
||||||
|
if ! info="$(python3 "$SCRIPT_DIR/season_info.py" "$date_str" 2>/dev/null)"; then
|
||||||
|
echo "ERROR: season_info.py failed for $date_str ($fname)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
eval "$info"
|
||||||
|
# SEASON MVT_NUM DAY_OF_MVT DAYS_IN_MVT MVT_START MVT_END ASTRO_YEAR
|
||||||
|
|
||||||
|
dst_dir="$NEW_ROOT/$ASTRO_YEAR/$SEASON/Mvt$MVT_NUM"
|
||||||
|
key="${ASTRO_YEAR}/${SEASON}/Mvt${MVT_NUM}"
|
||||||
|
|
||||||
|
$DRY_RUN && printf " %-12s %s Mvt%s Day %s/%s\n" \
|
||||||
|
"$date_str" "$SEASON" "$MVT_NUM" "$DAY_OF_MVT" "$DAYS_IN_MVT"
|
||||||
|
|
||||||
|
link_clip "$src" "$dst_dir" "$key"
|
||||||
|
|
||||||
|
done < <(find \
|
||||||
|
"$OLD_ROOT/Spring" \
|
||||||
|
"$OLD_ROOT/Winter" \
|
||||||
|
"$OLD_ROOT/Autumn" \
|
||||||
|
-name "*-final.mp4" -print0 2>/dev/null | sort -z)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Summary ==="
|
||||||
|
printf "%-35s %8s %8s\n" "Movement" "Linked" "Skipped"
|
||||||
|
printf "%-35s %8s %8s\n" "--------" "------" "-------"
|
||||||
|
|
||||||
|
# Collect all movement keys from both arrays
|
||||||
|
declare -A ALL_KEYS
|
||||||
|
for k in "${!LINKED[@]}" "${!SKIPPED[@]}"; do ALL_KEYS["$k"]=1; done
|
||||||
|
|
||||||
|
for key in $(printf '%s\n' "${!ALL_KEYS[@]}" | sort); do
|
||||||
|
linked=${LINKED["$key"]:-0}
|
||||||
|
skipped=${SKIPPED["$key"]:-0}
|
||||||
|
printf "%-35s %8d %8d\n" "$key" "$linked" "$skipped"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if $DRY_RUN; then
|
||||||
|
echo "Re-run without --dry-run to create the symlinks."
|
||||||
|
echo "Then trigger montages for completed movements:"
|
||||||
|
else
|
||||||
|
echo "Symlinks created. Trigger montages for completed movements:"
|
||||||
|
fi
|
||||||
|
echo " ./montage-mvt.sh 2026-04-19 $CAM # Spring Mvt1 (just completed, 30/31 days)"
|
||||||
|
echo " ./montage-mvt.sh 2026-01-18 $CAM # Winter Mvt1 (~18/29 days)"
|
||||||
|
echo " ./montage-mvt.sh 2026-02-16 $CAM # Winter Mvt2 (~24/29 days)"
|
||||||
|
echo " ./montage-mvt.sh 2026-03-19 $CAM # Winter Mvt3 (~20/31 days)"
|
||||||
|
echo " # Autumn Mvt1/Mvt3 migrated but Mvt2 is absent — year-end join will not fire."
|
||||||
Reference in New Issue
Block a user