Files
shell/plex/icu-aware-recovery.sh
Peter Wood 58b5dea8b4 Refactor variable assignments and improve script readability in validate-plex-backups.sh and validate-plex-recovery.sh
- Changed inline variable assignments to separate declaration and assignment for clarity.
- Updated condition checks and log messages for better readability and consistency.
- Added a backup of validate-plex-recovery.sh for safety.
- Introduced a new script run-docker-tests.sh for testing setup in Docker containers.
- Enhanced ssh-login.sh to improve condition checks and logging functionality.
2025-06-05 17:14:02 -04:00

352 lines
11 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# ICU-Aware Plex Database Recovery Script
################################################################################
#
# Author: Peter Wood <peter@peterwood.dev>
# Description: Specialized recovery script for Plex databases that require
# ICU (International Components for Unicode) collation sequences.
# Handles complex database corruption scenarios involving Unicode
# sorting and collation issues.
#
# Features:
# - ICU collation sequence detection and repair
# - Unicode-aware database reconstruction
# - Advanced SQLite recovery techniques
# - Backup creation before recovery attempts
# - Comprehensive logging and error tracking
# - Plex service management during recovery
#
# Related Scripts:
# - backup-plex.sh: Creates backups used for recovery scenarios
# - restore-plex.sh: Standard restoration procedures
# - nuclear-plex-recovery.sh: Last-resort recovery methods
# - validate-plex-recovery.sh: Validates recovery results
# - plex.sh: General Plex service management
#
# Usage:
# ./icu-aware-recovery.sh # Interactive recovery
# ./icu-aware-recovery.sh --auto # Automated recovery
# ./icu-aware-recovery.sh --check-only # Check ICU status only
# ./icu-aware-recovery.sh --backup-first # Force backup before recovery
#
# Dependencies:
# - sqlite3 with ICU support
# - Plex Media Server
# - libicu-dev (ICU libraries)
# - systemctl (for service management)
#
# Exit Codes:
# 0 - Recovery successful
# 1 - General error
# 2 - ICU-related issues
# 3 - Database corruption beyond repair
# 4 - Service management failure
#
################################################################################
# ICU-Aware Plex Database Recovery Script
# Handles databases that require ICU collation sequences
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"
BACKUP_TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
RECOVERY_LOG="/home/acedanger/shell/plex/logs/icu-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
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 SQLite ICU support
check_sqlite_icu() {
print_status "$YELLOW" "Checking SQLite ICU collation support..."
# Try to create a test database with ICU collation
local test_db="/tmp/test_icu_$$"
if sqlite3 "$test_db" "CREATE TABLE test (id TEXT COLLATE icu_root); DROP TABLE test;" 2>/dev/null; then
print_status "$GREEN" "SQLite has ICU collation support"
rm -f "$test_db"
return 0
else
print_status "$YELLOW" "SQLite lacks ICU collation support - will use alternative verification"
rm -f "$test_db"
return 1
fi
}
# Function to verify database without ICU-dependent checks
verify_database_basic() {
local db_file="$1"
local db_name="$2"
print_status "$YELLOW" "Performing basic verification of $db_name..."
# Check if file exists and is not empty
if [[ ! -f "$db_file" ]]; then
print_status "$RED" "$db_name: File does not exist"
return 1
fi
local file_size
file_size=$(stat -c%s "$db_file" 2>/dev/null || stat -f%z "$db_file" 2>/dev/null)
if [[ $file_size -lt 1024 ]]; then
print_status "$RED" "$db_name: File is too small ($file_size bytes)"
return 1
fi
# Check if it's a valid SQLite file
if ! file "$db_file" | grep -q "SQLite"; then
print_status "$RED" "$db_name: Not a valid SQLite database"
return 1
fi
# Try basic SQLite operations that don't require ICU
if sqlite3 "$db_file" "SELECT name FROM sqlite_master WHERE type='table' LIMIT 1;" >/dev/null 2>&1; then
print_status "$GREEN" "$db_name: Basic SQLite operations successful"
# Count tables
local table_count
table_count=$(sqlite3 "$db_file" "SELECT COUNT(*) FROM sqlite_master WHERE type='table';" 2>/dev/null || echo "0")
print_status "$GREEN" "$db_name: Contains $table_count tables"
return 0
else
print_status "$RED" "$db_name: Failed basic SQLite operations"
return 1
fi
}
# Function to attempt ICU-safe integrity check
verify_database_integrity() {
local db_file="$1"
local db_name="$2"
print_status "$YELLOW" "Attempting integrity check for $db_name..."
# First try the basic verification
if ! verify_database_basic "$db_file" "$db_name"; then
return 1
fi
# Try integrity check with ICU fallback handling
local integrity_result
integrity_result=$(sqlite3 "$db_file" "PRAGMA integrity_check;" 2>&1)
local sqlite_exit_code=$?
if [[ $sqlite_exit_code -eq 0 ]] && echo "$integrity_result" | grep -q "ok"; then
print_status "$GREEN" "$db_name: Full integrity check PASSED"
return 0
elif echo "$integrity_result" | grep -q "no such collation sequence: icu"; then
print_status "$YELLOW" "$db_name: ICU collation issue detected, but database structure appears valid"
print_status "$YELLOW" "This is normal for restored databases and should resolve when Plex starts"
return 0
else
print_status "$RED" "$db_name: Integrity check failed with: $integrity_result"
return 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"
systemctl status plexmediaserver --no-pager
return 1
fi
}
# Function to validate current database state
validate_current_state() {
print_status "$YELLOW" "Validating current database state..."
local main_db="${PLEX_DB_DIR}/com.plexapp.plugins.library.db"
local blobs_db="${PLEX_DB_DIR}/com.plexapp.plugins.library.blobs.db"
local validation_passed=true
# Check main database
if ! verify_database_integrity "$main_db" "Main database"; then
validation_passed=false
fi
# Check blobs database
if ! verify_database_integrity "$blobs_db" "Blobs database"; then
validation_passed=false
fi
if [[ "$validation_passed" == "true" ]]; then
print_status "$GREEN" "Database validation completed successfully"
return 0
else
print_status "$YELLOW" "Database validation completed with warnings"
print_status "$YELLOW" "ICU collation issues are normal for restored databases"
return 0
fi
}
# Function to check database sizes
check_database_sizes() {
print_status "$YELLOW" "Checking database file sizes..."
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_size
main_size=$(du -h "$main_db" | cut -f1)
print_status "$GREEN" "Main database size: $main_size"
fi
if [[ -f "$blobs_db" ]]; then
local blobs_size
blobs_size=$(du -h "$blobs_db" | cut -f1)
print_status "$GREEN" "Blobs database size: $blobs_size"
fi
}
# Function to test Plex functionality
test_plex_functionality() {
print_status "$YELLOW" "Testing Plex functionality..."
# Wait a bit longer for Plex to fully initialize
sleep 15
# Test basic API endpoints
local max_attempts=10
local attempt=1
while [[ $attempt -le $max_attempts ]]; do
# Test the main API endpoint
if curl -s -f "http://localhost:32400/" > /dev/null 2>&1; then
print_status "$GREEN" "Plex API is responding"
# Try to get server info
local server_info=$(curl -s "http://localhost:32400/" 2>/dev/null)
if echo "$server_info" | grep -q "MediaContainer"; then
print_status "$GREEN" "Plex server is fully functional"
return 0
fi
fi
print_status "$YELLOW" "Waiting for Plex API... (attempt $attempt/$max_attempts)"
sleep 10
((attempt++))
done
print_status "$YELLOW" "Plex may still be initializing - check manually at http://localhost:32400"
return 0
}
# Main function
main() {
print_status "$BLUE" "=== ICU-AWARE PLEX DATABASE RECOVERY ==="
print_status "$BLUE" "Timestamp: $(date)"
print_status "$BLUE" "Log file: $RECOVERY_LOG"
# Check SQLite ICU support
check_sqlite_icu
# Validate current database state
validate_current_state
# Check database sizes
check_database_sizes
# Stop Plex (if running)
stop_plex
# Start Plex service
if start_plex; then
print_status "$GREEN" "Plex service started successfully"
# Test functionality
test_plex_functionality
print_status "$GREEN" "=== 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: ICU collation warnings are normal for restored databases"
print_status "$BLUE" "Recovery log saved to: $RECOVERY_LOG"
else
print_status "$RED" "Failed to start Plex service - check logs for details"
print_status "$BLUE" "Recovery log saved to: $RECOVERY_LOG"
exit 1
fi
}
# Script usage
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi