#!/bin/bash ################################################################################ # Nuclear Plex Database Recovery Script ################################################################################ # # Author: Peter Wood # Description: Last-resort database recovery script that completely replaces # corrupted Plex databases with known good backups. This script # is used when all other repair methods have failed and a complete # database replacement is the only remaining option. # # ⚠️ WARNING: This script will completely replace existing databases! # All data since the backup was created will be lost. # Use only when standard repair methods have failed. # # Features: # - Complete database replacement from backups # - Automatic backup of current (corrupted) databases # - Service management and safety checks # - Comprehensive logging of all operations # - Rollback capability if replacement fails # - Verification of restored database integrity # # Related Scripts: # - backup-plex.sh: Creates backups used by this recovery script # - icu-aware-recovery.sh: ICU-specific recovery methods # - restore-plex.sh: Standard restoration procedures # - validate-plex-recovery.sh: Validates recovery results # - plex.sh: General Plex service management # # Usage: # ./nuclear-plex-recovery.sh # Interactive recovery # ./nuclear-plex-recovery.sh --auto # Automated recovery # ./nuclear-plex-recovery.sh --dry-run # Show what would be done # ./nuclear-plex-recovery.sh --verify-only # Verify backup integrity # # Dependencies: # - Valid Plex backup files # - sqlite3 or Plex SQLite binary # - systemctl (for service management) # - tar (for backup extraction) # # Exit Codes: # 0 - Recovery successful # 1 - General error # 2 - Backup file issues # 3 - Database replacement failure # 4 - Service management failure # 5 - Rollback performed due to failure # ################################################################################ # Nuclear Plex Database Recovery Script # This script completely replaces corrupted databases with known good backups # Use this when standard repair methods have failed set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration PLEX_DB_DIR="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases" PLEX_USER="plex" PLEX_GROUP="plex" BACKUP_TIMESTAMP=$(date +"%Y%m%d_%H%M%S") RECOVERY_LOG="/home/acedanger/shell/plex/logs/nuclear-recovery-${BACKUP_TIMESTAMP}.log" # Ensure log directory exists mkdir -p "$(dirname "$RECOVERY_LOG")" # Function to log messages log_message() { local level="$1" local message="$2" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a "$RECOVERY_LOG" } # Function to print colored output print_status() { local color="$1" local message="$2" echo -e "${color}${message}${NC}" log_message "INFO" "$message" } # Function to check if running as root check_root() { if [[ $EUID -ne 0 ]]; then print_status "$RED" "This script must be run as root or with sudo" exit 1 fi } # Function to stop Plex service stop_plex() { print_status "$YELLOW" "Stopping Plex Media Server..." if systemctl is-active --quiet plexmediaserver; then systemctl stop plexmediaserver sleep 5 # Verify it's stopped if systemctl is-active --quiet plexmediaserver; then print_status "$RED" "Failed to stop Plex service" exit 1 fi print_status "$GREEN" "Plex service stopped successfully" else print_status "$YELLOW" "Plex service was already stopped" fi } # Function to start Plex service start_plex() { print_status "$YELLOW" "Starting Plex Media Server..." systemctl start plexmediaserver sleep 10 # Verify it's running if systemctl is-active --quiet plexmediaserver; then print_status "$GREEN" "Plex service started successfully" # Check if it's actually responding local max_attempts=30 local attempt=1 while [[ $attempt -le $max_attempts ]]; do if curl -s -f "http://localhost:32400/web/index.html" > /dev/null 2>&1; then print_status "$GREEN" "Plex web interface is responding" return 0 fi print_status "$YELLOW" "Waiting for Plex to fully start... (attempt $attempt/$max_attempts)" sleep 5 ((attempt++)) done print_status "$YELLOW" "Plex service is running but web interface may still be starting" else print_status "$RED" "Failed to start Plex service" return 1 fi } # Function to backup current corrupted databases backup_corrupted_databases() { print_status "$YELLOW" "Backing up current corrupted databases..." local backup_dir="${PLEX_DB_DIR}/corrupted-${BACKUP_TIMESTAMP}" mkdir -p "$backup_dir" # Backup main database if it exists if [[ -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" ]]; then cp "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" "$backup_dir/" print_status "$GREEN" "Backed up corrupted main database" fi # Backup blobs database if it exists if [[ -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" ]]; then cp "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" "$backup_dir/" print_status "$GREEN" "Backed up corrupted blobs database" fi print_status "$GREEN" "Corrupted databases backed up to: $backup_dir" } # Function to find best backup find_best_backup() { local backup_type="$1" local latest_backup="" # Find the most recent backup that exists and has reasonable size for backup_file in "${PLEX_DB_DIR}/${backup_type}"-????-??-??*; do if [[ -f "$backup_file" ]]; then local file_size=$(stat -f%z "$backup_file" 2>/dev/null || stat -c%s "$backup_file" 2>/dev/null) # Check if file size is reasonable (> 100MB for main DB, > 500MB for blobs) if [[ "$backup_type" == "com.plexapp.plugins.library.db" && $file_size -gt 104857600 ]] || \ [[ "$backup_type" == "com.plexapp.plugins.library.blobs.db" && $file_size -gt 524288000 ]]; then latest_backup="$backup_file" fi fi done echo "$latest_backup" } # Function to restore from backup restore_from_backup() { print_status "$YELLOW" "Finding and restoring from best available backups..." # Find best main database backup local main_backup=$(find_best_backup "com.plexapp.plugins.library.db") if [[ -n "$main_backup" ]]; then print_status "$GREEN" "Found main database backup: $(basename "$main_backup")" # Remove corrupted main database rm -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" # Copy backup to main location cp "$main_backup" "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" # Set proper ownership and permissions chown "$PLEX_USER:$PLEX_GROUP" "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" chmod 644 "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" print_status "$GREEN" "Main database restored successfully" else print_status "$RED" "No suitable main database backup found!" exit 1 fi # Find best blobs database backup local blobs_backup=$(find_best_backup "com.plexapp.plugins.library.blobs.db") if [[ -n "$blobs_backup" ]]; then print_status "$GREEN" "Found blobs database backup: $(basename "$blobs_backup")" # Remove corrupted blobs database rm -f "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" # Copy backup to main location cp "$blobs_backup" "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" # Set proper ownership and permissions chown "$PLEX_USER:$PLEX_GROUP" "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" chmod 644 "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" print_status "$GREEN" "Blobs database restored successfully" else print_status "$RED" "No suitable blobs database backup found!" exit 1 fi } # Function to verify restored databases verify_databases() { print_status "$YELLOW" "Verifying restored databases..." # Check main database if sqlite3 "${PLEX_DB_DIR}/com.plexapp.plugins.library.db" "PRAGMA integrity_check;" | grep -q "ok"; then print_status "$GREEN" "Main database integrity check: PASSED" else print_status "$RED" "Main database integrity check: FAILED" return 1 fi # Check blobs database if sqlite3 "${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" "PRAGMA integrity_check;" | grep -q "ok"; then print_status "$GREEN" "Blobs database integrity check: PASSED" else print_status "$RED" "Blobs database integrity check: FAILED" return 1 fi print_status "$GREEN" "All database integrity checks passed!" } # Function to fix ownership issues fix_ownership() { print_status "$YELLOW" "Fixing file ownership in Plex database directory..." # Fix ownership of all files in the database directory chown -R "$PLEX_USER:$PLEX_GROUP" "$PLEX_DB_DIR" # Verify critical files have correct ownership local main_db="${PLEX_DB_DIR}/com.plexapp.plugins.library.db" local blobs_db="${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db" if [[ -f "$main_db" ]]; then local main_owner=$(stat -f%Su:%Sg "$main_db" 2>/dev/null || stat -c%U:%G "$main_db" 2>/dev/null) if [[ "$main_owner" == "$PLEX_USER:$PLEX_GROUP" ]]; then print_status "$GREEN" "Main database ownership: CORRECT ($main_owner)" else print_status "$RED" "Main database ownership: INCORRECT ($main_owner)" chown "$PLEX_USER:$PLEX_GROUP" "$main_db" fi fi if [[ -f "$blobs_db" ]]; then local blobs_owner=$(stat -f%Su:%Sg "$blobs_db" 2>/dev/null || stat -c%U:%G "$blobs_db" 2>/dev/null) if [[ "$blobs_owner" == "$PLEX_USER:$PLEX_GROUP" ]]; then print_status "$GREEN" "Blobs database ownership: CORRECT ($blobs_owner)" else print_status "$RED" "Blobs database ownership: INCORRECT ($blobs_owner)" chown "$PLEX_USER:$PLEX_GROUP" "$blobs_db" fi fi } # Function to clean up temporary files cleanup_temp_files() { print_status "$YELLOW" "Cleaning up temporary and lock files..." # Remove any SQLite temporary files rm -f "${PLEX_DB_DIR}"/*.db-shm rm -f "${PLEX_DB_DIR}"/*.db-wal rm -f "${PLEX_DB_DIR}"/*.tmp print_status "$GREEN" "Temporary files cleaned up" } # Main recovery function main() { print_status "$BLUE" "=== NUCLEAR PLEX DATABASE RECOVERY STARTED ===" print_status "$BLUE" "Timestamp: $(date)" print_status "$BLUE" "Log file: $RECOVERY_LOG" # Pre-flight checks check_root # Confirm with user print_status "$YELLOW" "WARNING: This will completely replace your Plex databases with backups!" print_status "$YELLOW" "This will result in some data loss (recent changes since last backup)." read -p "Are you sure you want to continue? (yes/no): " -r if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then print_status "$YELLOW" "Recovery cancelled by user" exit 0 fi # Stop Plex service stop_plex # Backup current corrupted databases backup_corrupted_databases # Restore from backup restore_from_backup # Fix ownership issues fix_ownership # Clean up temporary files cleanup_temp_files # Verify databases verify_databases # Start Plex service start_plex print_status "$GREEN" "=== NUCLEAR RECOVERY COMPLETED SUCCESSFULLY ===" print_status "$GREEN" "Your Plex Media Server should now be functional." print_status "$GREEN" "Check the web interface at: http://localhost:32400" print_status "$YELLOW" "Note: You may need to re-scan your libraries to pick up recent changes." print_status "$BLUE" "Recovery log saved to: $RECOVERY_LOG" } # Script usage if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi