jellyfin-media/scripts/media-archive.sh

241 lines
7.1 KiB
Bash
Executable File

#!/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 <movies|shows|music> <name>"
echo " media-archive.sh archive <movies|shows|music> --older-than <days>"
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 <movies|shows|music> <name>"
echo ""
echo "List available archives:"
echo " ls /mnt/hetzner-media/archive/<type>/"
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 <command> [options]"
echo ""
echo "Commands:"
echo " status Show storage status and usage"
echo " list-old List content older than $DAYS_OLD days"
echo " archive <type> <name> Archive specific content to Hetzner"
echo " archive <type> --older-than <days> Archive all content older than N days"
echo " restore <type> <name> 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