mirror of
https://github.com/acedanger/shell.git
synced 2025-12-05 21:40:12 -08:00
- 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.
352 lines
11 KiB
Bash
Executable File
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
|