mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 01:10:12 -08:00
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.
This commit is contained in:
701
plex/recover-plex-database.sh.sc2086_backup
Executable file
701
plex/recover-plex-database.sh.sc2086_backup
Executable file
@@ -0,0 +1,701 @@
|
||||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
# Advanced Plex Database Recovery Script
|
||||
################################################################################
|
||||
#
|
||||
# Author: Peter Wood <peter@peterwood.dev>
|
||||
# Description: Advanced database recovery script with multiple repair strategies
|
||||
# for corrupted Plex databases. Implements progressive recovery
|
||||
# techniques from gentle repairs to aggressive reconstruction
|
||||
# methods, with comprehensive logging and rollback capabilities.
|
||||
#
|
||||
# Features:
|
||||
# - Progressive recovery strategy (gentle to aggressive)
|
||||
# - Multiple repair techniques (VACUUM, dump/restore, rebuild)
|
||||
# - Automatic backup before any recovery attempts
|
||||
# - Database integrity verification at each step
|
||||
# - Rollback capability if recovery fails
|
||||
# - Dry-run mode for safe testing
|
||||
# - Comprehensive logging and reporting
|
||||
#
|
||||
# Related Scripts:
|
||||
# - backup-plex.sh: Creates backups for recovery scenarios
|
||||
# - icu-aware-recovery.sh: ICU-specific recovery methods
|
||||
# - nuclear-plex-recovery.sh: Last-resort complete replacement
|
||||
# - validate-plex-recovery.sh: Validates recovery results
|
||||
# - restore-plex.sh: Standard restoration from backups
|
||||
# - plex.sh: General Plex service management
|
||||
#
|
||||
# Usage:
|
||||
# ./recover-plex-database.sh # Interactive recovery
|
||||
# ./recover-plex-database.sh --auto # Automated recovery
|
||||
# ./recover-plex-database.sh --dry-run # Show recovery plan
|
||||
# ./recover-plex-database.sh --gentle # Gentle repair only
|
||||
# ./recover-plex-database.sh --aggressive # Aggressive repair methods
|
||||
#
|
||||
# Dependencies:
|
||||
# - sqlite3 or Plex SQLite binary
|
||||
# - systemctl (for service management)
|
||||
# - Sufficient disk space for backups and temp files
|
||||
#
|
||||
# Exit Codes:
|
||||
# 0 - Recovery successful
|
||||
# 1 - General error
|
||||
# 2 - Database corruption beyond repair
|
||||
# 3 - Service management failure
|
||||
# 4 - Insufficient disk space
|
||||
# 5 - Recovery partially successful (manual intervention needed)
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# Advanced Plex Database Recovery Script
|
||||
# Usage: ./recover-plex-database.sh [--auto] [--dry-run]
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
PLEX_DB_DIR="/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases"
|
||||
MAIN_DB="com.plexapp.plugins.library.db"
|
||||
BLOBS_DB="com.plexapp.plugins.library.blobs.db"
|
||||
PLEX_SQLITE="/usr/lib/plexmediaserver/Plex SQLite"
|
||||
BACKUP_SUFFIX="recovery-$(date +%Y%m%d_%H%M%S)"
|
||||
RECOVERY_LOG="$SCRIPT_DIR/logs/database-recovery-$(date +%Y%m%d_%H%M%S).log"
|
||||
|
||||
# Script options
|
||||
AUTO_MODE=false
|
||||
DRY_RUN=false
|
||||
|
||||
# Ensure logs directory exists
|
||||
mkdir -p "$SCRIPT_DIR/logs"
|
||||
|
||||
# Logging function
|
||||
log_message() {
|
||||
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
||||
echo -e "$message"
|
||||
echo "$message" >> "$RECOVERY_LOG"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
log_message "${GREEN}SUCCESS: $1${NC}"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
log_message "${RED}ERROR: $1${NC}"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
log_message "${YELLOW}WARNING: $1${NC}"
|
||||
}
|
||||
|
||||
log_info() {
|
||||
log_message "${BLUE}INFO: $1${NC}"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--auto)
|
||||
AUTO_MODE=true
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: $0 [--auto] [--dry-run] [--help]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --auto Automatically attempt all recovery methods without prompts"
|
||||
echo " --dry-run Show what would be done without making changes"
|
||||
echo " --help Show this help message"
|
||||
echo ""
|
||||
echo "Recovery Methods (in order):"
|
||||
echo " 1. SQLite .recover command (modern SQLite recovery)"
|
||||
echo " 2. Partial table extraction with LIMIT"
|
||||
echo " 3. Emergency data extraction"
|
||||
echo " 4. Backup restoration from most recent good backup"
|
||||
echo ""
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies() {
|
||||
log_info "Checking dependencies..."
|
||||
|
||||
if [ ! -f "$PLEX_SQLITE" ]; then
|
||||
log_error "Plex SQLite binary not found at: $PLEX_SQLITE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v sqlite3 >/dev/null 2>&1; then
|
||||
log_error "Standard sqlite3 command not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Make Plex SQLite executable
|
||||
sudo chmod +x "$PLEX_SQLITE" 2>/dev/null || true
|
||||
|
||||
log_success "Dependencies check passed"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Stop Plex service safely
|
||||
stop_plex_service() {
|
||||
log_info "Stopping Plex Media Server..."
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "DRY RUN: Would stop Plex service"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if sudo systemctl is-active --quiet plexmediaserver; then
|
||||
sudo systemctl stop plexmediaserver
|
||||
|
||||
# Wait for service to fully stop
|
||||
local timeout=30
|
||||
while sudo systemctl is-active --quiet plexmediaserver && [ $timeout -gt 0 ]; do
|
||||
sleep 1
|
||||
timeout=$((timeout - 1))
|
||||
done
|
||||
|
||||
if sudo systemctl is-active --quiet plexmediaserver; then
|
||||
log_error "Failed to stop Plex service within timeout"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Plex service stopped successfully"
|
||||
else
|
||||
log_info "Plex service was already stopped"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Start Plex service
|
||||
start_plex_service() {
|
||||
log_info "Starting Plex Media Server..."
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "DRY RUN: Would start Plex service"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sudo systemctl start plexmediaserver
|
||||
|
||||
# Wait for service to start
|
||||
local timeout=30
|
||||
while ! sudo systemctl is-active --quiet plexmediaserver && [ $timeout -gt 0 ]; do
|
||||
sleep 1
|
||||
timeout=$((timeout - 1))
|
||||
done
|
||||
|
||||
if sudo systemctl is-active --quiet plexmediaserver; then
|
||||
log_success "Plex service started successfully"
|
||||
else
|
||||
log_warning "Plex service may not have started properly"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check database integrity
|
||||
check_database_integrity() {
|
||||
local db_file="$1"
|
||||
local db_name=$(basename "$db_file")
|
||||
|
||||
log_info "Checking integrity of $db_name..."
|
||||
|
||||
if [ ! -f "$db_file" ]; then
|
||||
log_error "Database file not found: $db_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local integrity_result
|
||||
integrity_result=$(sudo "$PLEX_SQLITE" "$db_file" "PRAGMA integrity_check;" 2>&1)
|
||||
local check_exit_code=$?
|
||||
|
||||
if [ $check_exit_code -ne 0 ]; then
|
||||
log_error "Failed to run integrity check on $db_name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if echo "$integrity_result" | grep -q "^ok$"; then
|
||||
log_success "Database integrity check passed: $db_name"
|
||||
return 0
|
||||
else
|
||||
log_warning "Database integrity issues detected in $db_name:"
|
||||
echo "$integrity_result" | while IFS= read -r line; do
|
||||
log_warning " $line"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Recovery Method 1: SQLite .recover command
|
||||
recovery_method_sqlite_recover() {
|
||||
local db_file="$1"
|
||||
local db_name=$(basename "$db_file")
|
||||
local recovered_sql="${db_file}.recovered.sql"
|
||||
local new_db="${db_file}.recovered"
|
||||
|
||||
log_info "Recovery Method 1: SQLite .recover command for $db_name"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "DRY RUN: Would attempt SQLite .recover method"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if .recover is available (SQLite 3.37.0+)
|
||||
if ! echo ".help" | sqlite3 2>/dev/null | grep -q "\.recover"; then
|
||||
log_warning "SQLite .recover command not available in this version"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Attempting SQLite .recover method..."
|
||||
|
||||
# Use standard sqlite3 for .recover as it's more reliable
|
||||
if sqlite3 "$db_file" ".recover" > "$recovered_sql" 2>/dev/null; then
|
||||
log_success "Recovery SQL generated successfully"
|
||||
|
||||
# Create new database from recovered data
|
||||
if [ -f "$recovered_sql" ] && [ -s "$recovered_sql" ]; then
|
||||
if sqlite3 "$new_db" < "$recovered_sql" 2>/dev/null; then
|
||||
log_success "New database created from recovered data"
|
||||
|
||||
# Verify new database integrity
|
||||
if sqlite3 "$new_db" "PRAGMA integrity_check;" | grep -q "ok"; then
|
||||
log_success "Recovered database integrity verified"
|
||||
|
||||
# Replace original with recovered database
|
||||
if sudo mv "$db_file" "${db_file}.corrupted" && sudo mv "$new_db" "$db_file"; then
|
||||
sudo chown plex:plex "$db_file"
|
||||
sudo chmod 644 "$db_file"
|
||||
log_success "Database successfully recovered using .recover method"
|
||||
|
||||
# Clean up
|
||||
rm -f "$recovered_sql"
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to replace original database"
|
||||
fi
|
||||
else
|
||||
log_error "Recovered database failed integrity check"
|
||||
fi
|
||||
else
|
||||
log_error "Failed to create database from recovered SQL"
|
||||
fi
|
||||
else
|
||||
log_error "Recovery SQL file is empty or not generated"
|
||||
fi
|
||||
else
|
||||
log_error "SQLite .recover command failed"
|
||||
fi
|
||||
|
||||
# Clean up on failure
|
||||
rm -f "$recovered_sql" "$new_db"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Recovery Method 2: Partial table extraction
|
||||
recovery_method_partial_extraction() {
|
||||
local db_file="$1"
|
||||
local db_name=$(basename "$db_file")
|
||||
local partial_sql="${db_file}.partial.sql"
|
||||
local new_db="${db_file}.partial"
|
||||
|
||||
log_info "Recovery Method 2: Partial table extraction for $db_name"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "DRY RUN: Would attempt partial extraction method"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Extracting schema and partial data..."
|
||||
|
||||
# Start the SQL file with schema
|
||||
{
|
||||
echo "-- Partial recovery of $db_name"
|
||||
echo "-- Generated on $(date)"
|
||||
echo ""
|
||||
} > "$partial_sql"
|
||||
|
||||
# Extract schema
|
||||
if sudo "$PLEX_SQLITE" "$db_file" ".schema" >> "$partial_sql" 2>/dev/null; then
|
||||
log_success "Schema extracted successfully"
|
||||
else
|
||||
log_warning "Schema extraction failed, trying alternative method"
|
||||
# Try with standard sqlite3
|
||||
if sqlite3 "$db_file" ".schema" >> "$partial_sql" 2>/dev/null; then
|
||||
log_success "Schema extracted with standard sqlite3"
|
||||
else
|
||||
log_error "Schema extraction failed completely"
|
||||
rm -f "$partial_sql"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Critical tables to extract (in order of importance)
|
||||
local critical_tables=(
|
||||
"accounts"
|
||||
"library_sections"
|
||||
"directories"
|
||||
"metadata_items"
|
||||
"media_items"
|
||||
"media_parts"
|
||||
"media_streams"
|
||||
"taggings"
|
||||
"tags"
|
||||
)
|
||||
|
||||
log_info "Attempting to extract critical tables..."
|
||||
|
||||
for table in "${critical_tables[@]}"; do
|
||||
log_info "Extracting table: $table"
|
||||
|
||||
# Try to extract with LIMIT to avoid hitting corrupted data
|
||||
local extract_success=false
|
||||
local limit=10000
|
||||
|
||||
while [ $limit -le 100000 ] && [ "$extract_success" = false ]; do
|
||||
if sudo "$PLEX_SQLITE" "$db_file" "SELECT COUNT(*) FROM $table;" >/dev/null 2>&1; then
|
||||
# Table exists and is readable
|
||||
{
|
||||
echo ""
|
||||
echo "-- Data for table $table (limited to $limit rows)"
|
||||
echo "DELETE FROM $table;"
|
||||
} >> "$partial_sql"
|
||||
|
||||
if sudo "$PLEX_SQLITE" "$db_file" ".mode insert $table" >>/dev/null 2>&1 && \
|
||||
sudo "$PLEX_SQLITE" "$db_file" "SELECT * FROM $table LIMIT $limit;" >> "$partial_sql" 2>/dev/null; then
|
||||
local row_count=$(tail -n +3 "$partial_sql" | grep "INSERT INTO $table" | wc -l)
|
||||
log_success "Extracted $row_count rows from $table"
|
||||
extract_success=true
|
||||
else
|
||||
log_warning "Failed to extract $table with limit $limit, trying smaller limit"
|
||||
limit=$((limit / 2))
|
||||
fi
|
||||
else
|
||||
log_warning "Table $table is not accessible or doesn't exist"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$extract_success" = false ]; then
|
||||
log_warning "Could not extract any data from table $table"
|
||||
fi
|
||||
done
|
||||
|
||||
# Create new database from partial data
|
||||
if [ -f "$partial_sql" ] && [ -s "$partial_sql" ]; then
|
||||
log_info "Creating database from partial extraction..."
|
||||
|
||||
if sqlite3 "$new_db" < "$partial_sql" 2>/dev/null; then
|
||||
log_success "Partial database created successfully"
|
||||
|
||||
# Verify basic functionality
|
||||
if sqlite3 "$new_db" "PRAGMA integrity_check;" | grep -q "ok"; then
|
||||
log_success "Partial database integrity verified"
|
||||
|
||||
# Replace original with partial database
|
||||
if sudo mv "$db_file" "${db_file}.corrupted" && sudo mv "$new_db" "$db_file"; then
|
||||
sudo chown plex:plex "$db_file"
|
||||
sudo chmod 644 "$db_file"
|
||||
log_success "Database partially recovered - some data may be lost"
|
||||
log_warning "Please verify your Plex library after recovery"
|
||||
|
||||
# Clean up
|
||||
rm -f "$partial_sql"
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to replace original database"
|
||||
fi
|
||||
else
|
||||
log_error "Partial database failed integrity check"
|
||||
fi
|
||||
else
|
||||
log_error "Failed to create database from partial extraction"
|
||||
fi
|
||||
else
|
||||
log_error "Partial extraction SQL file is empty"
|
||||
fi
|
||||
|
||||
# Clean up on failure
|
||||
rm -f "$partial_sql" "$new_db"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Recovery Method 3: Emergency data extraction
|
||||
recovery_method_emergency_extraction() {
|
||||
local db_file="$1"
|
||||
local db_name=$(basename "$db_file")
|
||||
|
||||
log_info "Recovery Method 3: Emergency data extraction for $db_name"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "DRY RUN: Would attempt emergency extraction method"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warning "This method will create a minimal database with basic library structure"
|
||||
log_warning "You will likely need to re-scan your media libraries"
|
||||
|
||||
if [ "$AUTO_MODE" = false ]; then
|
||||
read -p "Continue with emergency extraction? This will lose most metadata [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_info "Emergency extraction cancelled by user"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local emergency_db="${db_file}.emergency"
|
||||
|
||||
# Create a minimal database with essential tables
|
||||
log_info "Creating minimal emergency database..."
|
||||
|
||||
cat > "/tmp/emergency_schema.sql" << 'EOF'
|
||||
-- Emergency Plex database schema (minimal)
|
||||
CREATE TABLE accounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT,
|
||||
hashed_password TEXT,
|
||||
salt TEXT,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME
|
||||
);
|
||||
|
||||
CREATE TABLE library_sections (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT,
|
||||
section_type INTEGER,
|
||||
agent TEXT,
|
||||
scanner TEXT,
|
||||
language TEXT,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME
|
||||
);
|
||||
|
||||
CREATE TABLE directories (
|
||||
id INTEGER PRIMARY KEY,
|
||||
library_section_id INTEGER,
|
||||
path TEXT,
|
||||
created_at DATETIME,
|
||||
updated_at DATETIME
|
||||
);
|
||||
|
||||
-- Insert default admin account
|
||||
INSERT INTO accounts (id, name, created_at, updated_at)
|
||||
VALUES (1, 'plex', datetime('now'), datetime('now'));
|
||||
EOF
|
||||
|
||||
if sqlite3 "$emergency_db" < "/tmp/emergency_schema.sql" 2>/dev/null; then
|
||||
log_success "Emergency database created"
|
||||
|
||||
# Replace original with emergency database
|
||||
if sudo mv "$db_file" "${db_file}.corrupted" && sudo mv "$emergency_db" "$db_file"; then
|
||||
sudo chown plex:plex "$db_file"
|
||||
sudo chmod 644 "$db_file"
|
||||
log_success "Emergency database installed"
|
||||
log_warning "You will need to re-add library sections and re-scan media"
|
||||
|
||||
# Clean up
|
||||
rm -f "/tmp/emergency_schema.sql"
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to install emergency database"
|
||||
fi
|
||||
else
|
||||
log_error "Failed to create emergency database"
|
||||
fi
|
||||
|
||||
# Clean up on failure
|
||||
rm -f "/tmp/emergency_schema.sql" "$emergency_db"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Recovery Method 4: Restore from backup
|
||||
recovery_method_backup_restore() {
|
||||
local db_file="$1"
|
||||
local backup_dir="/mnt/share/media/backups/plex"
|
||||
|
||||
log_info "Recovery Method 4: Restore from most recent backup"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
log_info "DRY RUN: Would attempt backup restoration"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Find most recent backup
|
||||
local latest_backup=$(find "$backup_dir" -maxdepth 1 -name "plex-backup-*.tar.gz" -type f 2>/dev/null | sort -r | head -1)
|
||||
|
||||
if [ -z "$latest_backup" ]; then
|
||||
log_error "No backup files found in $backup_dir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Found latest backup: $(basename "$latest_backup")"
|
||||
|
||||
if [ "$AUTO_MODE" = false ]; then
|
||||
read -p "Restore from backup $(basename "$latest_backup")? [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
log_info "Backup restoration cancelled by user"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Extract and restore database from backup
|
||||
local temp_extract="/tmp/plex-recovery-extract-$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$temp_extract"
|
||||
|
||||
log_info "Extracting backup..."
|
||||
if tar -xzf "$latest_backup" -C "$temp_extract" 2>/dev/null; then
|
||||
local backup_db_file="$temp_extract/$(basename "$db_file")"
|
||||
|
||||
if [ -f "$backup_db_file" ]; then
|
||||
# Verify backup database integrity
|
||||
if sqlite3 "$backup_db_file" "PRAGMA integrity_check;" | grep -q "ok"; then
|
||||
log_success "Backup database integrity verified"
|
||||
|
||||
# Replace corrupted database with backup
|
||||
if sudo mv "$db_file" "${db_file}.corrupted" && sudo cp "$backup_db_file" "$db_file"; then
|
||||
sudo chown plex:plex "$db_file"
|
||||
sudo chmod 644 "$db_file"
|
||||
log_success "Database restored from backup"
|
||||
|
||||
# Clean up
|
||||
rm -rf "$temp_extract"
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to replace database with backup"
|
||||
fi
|
||||
else
|
||||
log_error "Backup database also has integrity issues"
|
||||
fi
|
||||
else
|
||||
log_error "Database file not found in backup"
|
||||
fi
|
||||
else
|
||||
log_error "Failed to extract backup"
|
||||
fi
|
||||
|
||||
# Clean up on failure
|
||||
rm -rf "$temp_extract"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Main recovery function
|
||||
main_recovery() {
|
||||
local db_file="$PLEX_DB_DIR/$MAIN_DB"
|
||||
|
||||
log_info "Starting Plex database recovery process"
|
||||
log_info "Recovery log: $RECOVERY_LOG"
|
||||
|
||||
# Check dependencies
|
||||
if ! check_dependencies; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Stop Plex service
|
||||
if ! stop_plex_service; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Change to database directory
|
||||
cd "$PLEX_DB_DIR" || {
|
||||
log_error "Failed to change to database directory"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check if database exists
|
||||
if [ ! -f "$MAIN_DB" ]; then
|
||||
log_error "Main database file not found: $MAIN_DB"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create backup of current corrupted state
|
||||
log_info "Creating backup of current corrupted database..."
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
sudo cp "$MAIN_DB" "${MAIN_DB}.${BACKUP_SUFFIX}"
|
||||
log_success "Corrupted database backed up as: ${MAIN_DB}.${BACKUP_SUFFIX}"
|
||||
fi
|
||||
|
||||
# Check current integrity
|
||||
log_info "Verifying database corruption..."
|
||||
if check_database_integrity "$MAIN_DB"; then
|
||||
log_success "Database integrity check passed - no recovery needed!"
|
||||
start_plex_service
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_warning "Database corruption confirmed, attempting recovery..."
|
||||
|
||||
# Try recovery methods in order
|
||||
local recovery_methods=(
|
||||
"recovery_method_sqlite_recover"
|
||||
"recovery_method_partial_extraction"
|
||||
"recovery_method_emergency_extraction"
|
||||
"recovery_method_backup_restore"
|
||||
)
|
||||
|
||||
for method in "${recovery_methods[@]}"; do
|
||||
log_info "Attempting: $method"
|
||||
|
||||
if $method "$MAIN_DB"; then
|
||||
log_success "Recovery successful using: $method"
|
||||
|
||||
# Verify the recovered database
|
||||
if check_database_integrity "$MAIN_DB"; then
|
||||
log_success "Recovered database integrity verified"
|
||||
start_plex_service
|
||||
log_success "Database recovery completed successfully!"
|
||||
log_info "Please check your Plex server and verify your libraries"
|
||||
exit 0
|
||||
else
|
||||
log_error "Recovered database still has integrity issues"
|
||||
# Restore backup for next attempt
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
sudo cp "${MAIN_DB}.${BACKUP_SUFFIX}" "$MAIN_DB"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_warning "Recovery method failed: $method"
|
||||
fi
|
||||
done
|
||||
|
||||
log_error "All recovery methods failed"
|
||||
log_error "Manual intervention required"
|
||||
|
||||
# Restore original corrupted database
|
||||
if [ "$DRY_RUN" = false ]; then
|
||||
sudo cp "${MAIN_DB}.${BACKUP_SUFFIX}" "$MAIN_DB"
|
||||
fi
|
||||
|
||||
start_plex_service
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Trap to ensure Plex service is restarted
|
||||
trap 'start_plex_service' EXIT
|
||||
|
||||
# Run main recovery
|
||||
main_recovery "$@"
|
||||
Reference in New Issue
Block a user