Files
shell/plex/nuclear-plex-recovery.sh
Peter Wood 0123fc6007 feat: Add comprehensive Plex recovery validation script
- Introduced `validate-plex-recovery.sh` for validating Plex database recovery.
- Implemented checks for service status, database integrity, web interface accessibility, API functionality, and recent logs.
- Added detailed recovery summary and next steps for users.

fix: Improve Debian patching script for compatibility

- Enhanced `debian-patches.sh` to securely download and execute bootstrap scripts.
- Updated package mapping logic and ensured proper permissions for patched files.

fix: Update Docker test scripts for better permission handling

- Modified `run-docker-tests.sh` to set appropriate permissions on logs directory.
- Ensured log files have correct permissions after test runs.

fix: Enhance setup scripts for secure installations

- Updated `setup.sh` to securely download and execute installation scripts for zoxide and nvm.
- Improved error handling for failed downloads.

fix: Refine startup script for log directory permissions

- Adjusted `startup.sh` to set proper permissions for log directories and files.

chore: Revamp update-containers.sh for better error handling and logging

- Rewrote `update-containers.sh` to include detailed logging and error handling.
- Added validation for Docker image names and improved overall script robustness.
2025-06-05 07:22:28 -04:00

361 lines
12 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# Nuclear Plex Database Recovery Script
################################################################################
#
# Author: Peter Wood <peter@peterwood.dev>
# 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