#!/bin/bash ################################################################################ # Plex Media Server Backup Restoration Script ################################################################################ # # Author: Peter Wood # Description: Safe and reliable restoration script for Plex Media Server # backups with validation, dry-run capability, and automatic # backup of current data before restoration. # # Features: # - Interactive backup selection from available archives # - Backup validation before restoration # - Dry-run mode for testing restoration process # - Automatic backup of current data before restoration # - Service management (stop/start Plex during restoration) # - Comprehensive logging and error handling # - File ownership and permission restoration # # Related Scripts: # - backup-plex.sh: Creates backups that this script restores # - validate-plex-backups.sh: Validates backup integrity # - monitor-plex-backup.sh: Monitors backup system health # - test-plex-backup.sh: Tests backup/restore operations # - plex.sh: General Plex service management # # Usage: # ./restore-plex.sh # List available backups # ./restore-plex.sh plex-backup-20250125_143022.tar.gz # Restore specific backup # ./restore-plex.sh --dry-run backup-file.tar.gz # Test restoration process # ./restore-plex.sh --list # List all available backups # # Dependencies: # - tar (for archive extraction) # - Plex Media Server # - systemctl (for service management) # - Access to backup directory # # Exit Codes: # 0 - Success # 1 - General error # 2 - Backup file not found or invalid # 3 - Service management failure # 4 - Restoration failure # ################################################################################ # Plex Backup Restoration Script # Usage: ./restore-plex.sh [backup_date] [--dry-run] set -e RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # Configuration SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" BACKUP_ROOT="/mnt/share/media/backups/plex" PLEX_DATA_DIR="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server" # Plex file locations declare -A RESTORE_LOCATIONS=( ["com.plexapp.plugins.library.db"]="$PLEX_DATA_DIR/Plug-in Support/Databases/" ["com.plexapp.plugins.library.blobs.db"]="$PLEX_DATA_DIR/Plug-in Support/Databases/" ["Preferences.xml"]="$PLEX_DATA_DIR/" ) log_message() { echo -e "$(date '+%H:%M:%S') $1" } log_error() { log_message "${RED}ERROR: $1${NC}" } log_success() { log_message "${GREEN}SUCCESS: $1${NC}" } log_warning() { log_message "${YELLOW}WARNING: $1${NC}" } # List available backups list_backups() { log_message "Available backups:" find "$BACKUP_ROOT" -maxdepth 1 -type f -name "plex-backup-*.tar.gz" | sort -r | while read backup_file; do local backup_name=$(basename "$backup_file") local backup_date=$(echo "$backup_name" | sed 's/plex-backup-\([0-9]\{8\}\)_[0-9]\{6\}\.tar\.gz/\1/') if [[ "$backup_date" =~ ^[0-9]{8}$ ]]; then local readable_date=$(date -d "${backup_date:0:4}-${backup_date:4:2}-${backup_date:6:2}" '+%B %d, %Y' 2>/dev/null || echo "Unknown date") local file_size=$(du -h "$backup_file" 2>/dev/null | cut -f1) echo " $backup_name ($readable_date) - $file_size" else echo " $backup_name - $(du -h "$backup_file" 2>/dev/null | cut -f1)" fi done } # Validate backup integrity validate_backup() { local backup_file="$1" if [ ! -f "$backup_file" ]; then log_error "Backup file not found: $backup_file" return 1 fi log_message "Validating backup integrity for $(basename "$backup_file")..." # Test archive integrity if tar -tzf "$backup_file" >/dev/null 2>&1; then log_success "Archive integrity check passed" # List contents to verify expected files are present log_message "Archive contents:" tar -tzf "$backup_file" | while read file; do log_success " Found: $file" done return 0 else log_error "Archive integrity check failed" return 1 fi } # Create backup of current Plex data backup_current_data() { local backup_suffix=$(date '+%Y%m%d_%H%M%S') local current_backup_dir="$SCRIPT_DIR/plex_current_backup_$backup_suffix" log_message "Creating backup of current Plex data..." mkdir -p "$current_backup_dir" for file in "${!RESTORE_LOCATIONS[@]}"; do local src="${RESTORE_LOCATIONS[$file]}$file" if [ -f "$src" ]; then if sudo cp "$src" "$current_backup_dir/"; then log_success "Backed up current: $file" else log_error "Failed to backup current: $file" return 1 fi fi done log_success "Current data backed up to: $current_backup_dir" echo "$current_backup_dir" } # Restore files from backup restore_files() { local backup_file="$1" local dry_run="$2" if [ ! -f "$backup_file" ]; then log_error "Backup file not found: $backup_file" return 1 fi # Create temporary extraction directory local temp_dir="/tmp/plex-restore-$(date +%Y%m%d_%H%M%S)" mkdir -p "$temp_dir" log_message "Extracting backup archive..." if ! tar -xzf "$backup_file" -C "$temp_dir"; then log_error "Failed to extract backup archive" rm -rf "$temp_dir" return 1 fi log_message "Restoring files..." local restore_errors=0 for file in "${!RESTORE_LOCATIONS[@]}"; do local src_file="$temp_dir/$file" local dest_path="${RESTORE_LOCATIONS[$file]}" local dest_file="$dest_path$file" if [ -f "$src_file" ]; then if [ "$dry_run" == "true" ]; then log_message "Would restore: $file to $dest_file" else log_message "Restoring: $file" if sudo cp "$src_file" "$dest_file"; then sudo chown plex:plex "$dest_file" log_success "Restored: $file" else log_error "Failed to restore: $file" restore_errors=$((restore_errors + 1)) fi fi else log_warning "File not found in backup: $file" restore_errors=$((restore_errors + 1)) fi done # Clean up temporary directory rm -rf "$temp_dir" return $restore_errors } # Manage Plex service manage_plex_service() { local action="$1" log_message "$action Plex Media Server..." case "$action" in "stop") sudo systemctl stop plexmediaserver.service sleep 3 log_success "Plex stopped" ;; "start") sudo systemctl start plexmediaserver.service sleep 3 log_success "Plex started" ;; esac } # Main function main() { local backup_file="$1" local dry_run=false # Check for dry-run flag if [ "$2" = "--dry-run" ] || [ "$1" = "--dry-run" ]; then dry_run=true fi # If no backup file provided, list available backups if [ -z "$backup_file" ] || [ "$backup_file" = "--dry-run" ]; then list_backups echo echo "Usage: $0 [--dry-run]" echo "Example: $0 plex-backup-20250125_143022.tar.gz" echo " $0 /mnt/share/media/backups/plex/plex-backup-20250125_143022.tar.gz" exit 0 fi # If relative path, prepend BACKUP_ROOT if [[ "$backup_file" != /* ]]; then backup_file="$BACKUP_ROOT/$backup_file" fi # Validate backup exists and is complete if ! validate_backup "$backup_file"; then log_error "Backup validation failed" exit 1 fi if [ "$dry_run" = "true" ]; then restore_files "$backup_file" true log_message "Dry run completed. No changes were made." exit 0 fi # Confirm restoration echo log_warning "This will restore Plex data from backup $(basename "$backup_file")" log_warning "Current Plex data will be backed up before restoration" read -p "Continue? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_message "Restoration cancelled" exit 0 fi # Stop Plex service manage_plex_service stop # Backup current data local current_backup=$(backup_current_data) if [ $? -ne 0 ]; then log_error "Failed to backup current data" manage_plex_service start exit 1 fi # Restore files if restore_files "$backup_file" false; then log_success "Restoration completed successfully" log_message "Current data backup saved at: $current_backup" else log_error "Restoration failed" manage_plex_service start exit 1 fi # Start Plex service manage_plex_service start log_success "Plex restoration completed. Please verify your server is working correctly." } # Trap to ensure Plex is restarted on script exit trap 'manage_plex_service start' EXIT main "$@"