#!/bin/bash # Media Archive Manager - Move content between local and Hetzner storage # Usage: media-archive.sh [archive|restore|status|list-old] set -e # Configuration LOCAL_MEDIA="/opt/media-server/media" HETZNER_MEDIA="/mnt/hetzner-media/archive" LOG_FILE="/opt/media-server/logs/archive.log" DAYS_OLD=90 # Content older than this is considered for archiving # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Ensure log directory exists mkdir -p "$(dirname "$LOG_FILE")" log() { echo "[$(date -Iseconds)] $1" | tee -a "$LOG_FILE" } check_mount() { if ! mountpoint -q "$HETZNER_MEDIA" 2>/dev/null; then # Try to trigger automount ls "$HETZNER_MEDIA" &>/dev/null 2>&1 sleep 2 if ! mountpoint -q "$HETZNER_MEDIA" 2>/dev/null; then echo -e "${RED}Error: Hetzner storage not mounted at $HETZNER_MEDIA${NC}" echo "Run: sudo systemctl start mnt-hetzner\\x2dmedia.mount" exit 1 fi fi echo -e "${GREEN}Hetzner storage mounted${NC}" } show_status() { echo -e "${BLUE}Storage Status${NC}" echo "==============" echo "" echo -e "${YELLOW}Local Storage (/opt/media-server/media):${NC}" if [ -d "$LOCAL_MEDIA" ]; then du -sh "$LOCAL_MEDIA"/* 2>/dev/null || echo " (empty or not accessible)" else echo " Not found" fi echo "" echo -e "${YELLOW}Hetzner Storage (/mnt/hetzner-media):${NC}" if mountpoint -q "/mnt/hetzner-media" 2>/dev/null || ls "/mnt/hetzner-media" &>/dev/null 2>&1; then df -h "/mnt/hetzner-media" echo "" echo "Archive contents:" du -sh "$HETZNER_MEDIA"/* 2>/dev/null || echo " (empty)" else echo " Not mounted" fi } list_old_content() { echo -e "${BLUE}Content older than $DAYS_OLD days${NC}" echo "================================" echo "" for media_type in movies shows music; do echo -e "${YELLOW}$media_type:${NC}" if [ -d "$LOCAL_MEDIA/$media_type" ]; then find "$LOCAL_MEDIA/$media_type" -maxdepth 1 -mindepth 1 -type d -mtime +$DAYS_OLD 2>/dev/null | while read dir; do size=$(du -sh "$dir" 2>/dev/null | cut -f1) name=$(basename "$dir") echo " [$size] $name" done fi echo "" done } archive_content() { local media_type=$1 local name=$2 if [ -z "$media_type" ] || [ -z "$name" ]; then echo "Usage: media-archive.sh archive " echo " media-archive.sh archive --older-than " echo "" echo "Examples:" echo " media-archive.sh archive movies 'The Matrix (1999)'" echo " media-archive.sh archive shows --older-than 180" exit 1 fi check_mount # Handle batch archival if [ "$name" == "--older-than" ]; then local days=${3:-$DAYS_OLD} echo -e "${YELLOW}Archiving $media_type older than $days days...${NC}" find "$LOCAL_MEDIA/$media_type" -maxdepth 1 -mindepth 1 -type d -mtime +$days 2>/dev/null | while read dir; do local item_name=$(basename "$dir") archive_single "$media_type" "$item_name" done return fi archive_single "$media_type" "$name" } archive_single() { local media_type=$1 local name=$2 local source="$LOCAL_MEDIA/$media_type/$name" local dest="$HETZNER_MEDIA/$media_type/$name" if [ ! -d "$source" ]; then echo -e "${RED}Error: $source not found${NC}" return 1 fi local size=$(du -sh "$source" 2>/dev/null | cut -f1) echo -e "${BLUE}Archiving: $name ($size)${NC}" # Create destination directory mkdir -p "$HETZNER_MEDIA/$media_type" # Use rsync for reliable transfer with progress log "Archiving $media_type/$name ($size) to Hetzner" rsync -avh --progress --remove-source-files "$source/" "$dest/" # Remove empty source directory rmdir "$source" 2>/dev/null || true echo -e "${GREEN}Archived: $name${NC}" log "Completed archiving $media_type/$name" } restore_content() { local media_type=$1 local name=$2 if [ -z "$media_type" ] || [ -z "$name" ]; then echo "Usage: media-archive.sh restore " echo "" echo "List available archives:" echo " ls /mnt/hetzner-media/archive//" exit 1 fi check_mount local source="$HETZNER_MEDIA/$media_type/$name" local dest="$LOCAL_MEDIA/$media_type/$name" if [ ! -d "$source" ]; then echo -e "${RED}Error: $source not found in archive${NC}" echo "" echo "Available in $media_type archive:" ls -1 "$HETZNER_MEDIA/$media_type/" 2>/dev/null || echo " (empty)" return 1 fi local size=$(du -sh "$source" 2>/dev/null | cut -f1) echo -e "${BLUE}Restoring: $name ($size)${NC}" log "Restoring $media_type/$name ($size) from Hetzner" rsync -avh --progress "$source/" "$dest/" echo -e "${GREEN}Restored: $name${NC}" echo "Content available at: $dest" log "Completed restoring $media_type/$name" } sync_to_hetzner() { # One-way sync from local to Hetzner (backup mode) check_mount echo -e "${BLUE}Syncing local media to Hetzner (backup)...${NC}" for media_type in movies shows music; do if [ -d "$LOCAL_MEDIA/$media_type" ]; then echo -e "${YELLOW}Syncing $media_type...${NC}" mkdir -p "$HETZNER_MEDIA/$media_type" rsync -avh --progress "$LOCAL_MEDIA/$media_type/" "$HETZNER_MEDIA/$media_type/" fi done echo -e "${GREEN}Sync complete${NC}" } # Main command handler case "${1:-status}" in status) show_status ;; list-old|list) list_old_content ;; archive) archive_content "$2" "$3" "$4" ;; restore) restore_content "$2" "$3" ;; sync|backup) sync_to_hetzner ;; help|--help|-h) echo "Media Archive Manager" echo "=====================" echo "" echo "Usage: media-archive.sh [options]" echo "" echo "Commands:" echo " status Show storage status and usage" echo " list-old List content older than $DAYS_OLD days" echo " archive Archive specific content to Hetzner" echo " archive --older-than Archive all content older than N days" echo " restore Restore content from Hetzner to local" echo " sync Sync all local media to Hetzner (backup)" echo "" echo "Types: movies, shows, music" echo "" echo "Examples:" echo " media-archive.sh status" echo " media-archive.sh list-old" echo " media-archive.sh archive movies 'The Matrix (1999)'" echo " media-archive.sh archive shows --older-than 180" echo " media-archive.sh restore movies 'The Matrix (1999)'" ;; *) echo "Unknown command: $1" echo "Run 'media-archive.sh help' for usage" exit 1 ;; esac