Skip to content

Commit

Permalink
0.5.74 - add database health verification and periodic checks, implem…
Browse files Browse the repository at this point in the history
…ented database health verification in main.py, added periodic database health check task in run_program.py, updated initialization progress ranges and durations, fixed missing notification notify_ons on new add, updated debug routes and settings routes to support new database health task, fixed missing get_setting import
  • Loading branch information
godver3 committed Feb 5, 2025
1 parent 3099795 commit 103e9f4
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 14 deletions.
1 change: 1 addition & 0 deletions database/maintenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def update_show_ids():
if tmdb_id:
logging.info(f"Attempting to get IMDb ID from TMDB (ID: {tmdb_id})")
# TMDB API requires an API key - we should get this from config
from settings import get_setting
tmdb_api_key = get_setting('TMDB','api_key')
if tmdb_api_key:
import requests
Expand Down
18 changes: 11 additions & 7 deletions initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@

# Progress ranges for each phase
PROGRESS_RANGES = {
'reset': (0, 5), # 5 seconds
'plex': (5, 50), # 2 minutes
'sources': (50, 90), # 2 minutes
'release': (90, 100) # 30 seconds
'reset': (0, 5), # 5 seconds
'plex': (5, 40), # 2 minutes
'sources': (40, 70), # 2 minutes
'release': (70, 85), # 30 seconds
'show_ids': (85, 92), # 1 minute
'show_titles': (92, 100) # 1 minute
}

# Duration for each phase in seconds
PHASE_DURATIONS = {
'reset': 5,
'plex': 120, # 2 minutes
'sources': 120, # 2 minutes
'release': 30
'plex': 120, # 2 minutes
'sources': 120, # 2 minutes
'release': 30, # 30 seconds
'show_ids': 60, # 1 minute
'show_titles': 60 # 1 minute
}

# Global variable to track initialization progress
Expand Down
82 changes: 81 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import psutil
import webbrowser
import socket
import sqlite3
from datetime import datetime

# Import Windows-specific modules only on Windows
Expand Down Expand Up @@ -834,7 +835,81 @@ def fix_notification_settings():
except Exception as e:
logging.error(f"Error checking notification settings: {e}")

# Update the main function to use a single thread for the metadata battery
def verify_database_health():
"""
Verifies the health of both media_items.db and cli_battery.db databases.
If corruption is detected, backs up the corrupted database and creates a new one.
"""
logging.info("Verifying database health...")

# Get database paths
db_content_dir = os.environ.get('USER_DB_CONTENT', '/user/db_content')
media_items_path = os.path.join(db_content_dir, 'media_items.db')
cli_battery_path = os.path.join(db_content_dir, 'cli_battery.db')

def check_db_health(db_path, db_name):
if not os.path.exists(db_path):
logging.warning(f"{db_name} does not exist, will be created during initialization")
return True

try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Try to perform a simple query
cursor.execute("SELECT 1")
cursor.fetchone()

# Verify database integrity
cursor.execute("PRAGMA integrity_check")
result = cursor.fetchone()

cursor.close()
conn.close()

if result[0] != "ok":
raise sqlite3.DatabaseError(f"Integrity check failed: {result[0]}")

logging.info(f"{db_name} health check passed")
return True

except sqlite3.DatabaseError as e:
logging.error(f"{db_name} is corrupted: {str(e)}")

# Create backup of corrupted database
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_path = f"{db_path}.corrupted_{timestamp}"
try:
shutil.copy2(db_path, backup_path)
logging.info(f"Created backup of corrupted {db_name} at {backup_path}")
except Exception as backup_error:
logging.error(f"Failed to create backup of corrupted {db_name}: {str(backup_error)}")

# Delete corrupted database
try:
os.remove(db_path)
logging.info(f"Removed corrupted {db_name}")
except Exception as del_error:
logging.error(f"Failed to remove corrupted {db_name}: {str(del_error)}")
return False

return True

except Exception as e:
logging.error(f"Error checking {db_name} health: {str(e)}")
return False

# Check both databases
media_items_ok = check_db_health(media_items_path, "media_items.db")
cli_battery_ok = check_db_health(cli_battery_path, "cli_battery.db")

if not media_items_ok or not cli_battery_ok:
logging.error("Database health check failed")
return False

logging.info("Database health check completed successfully")
return True

def main():
global program_runner, metadata_process
metadata_process = None
Expand All @@ -845,6 +920,11 @@ def main():
backup_config()
backup_database()

# Verify database health before proceeding
if not verify_database_health():
logging.error("Database health check failed. Please check the logs and resolve any issues.")
return False

# Set up notification handlers
setup_crash_handler()
register_shutdown_handler()
Expand Down
4 changes: 3 additions & 1 deletion routes/debug_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ def run_task():
'task_update_show_ids': program_runner.task_update_show_ids,
'task_update_show_titles': program_runner.task_update_show_titles,
'task_get_plex_watch_history': program_runner.task_get_plex_watch_history,
'task_check_database_health': program_runner.task_check_database_health,
}

if task_name not in tasks:
Expand All @@ -765,7 +766,8 @@ def get_available_tasks():
'task_refresh_release_dates', 'task_purge_not_wanted_magnets_file',
'task_generate_airtime_report', 'task_check_service_connectivity', 'task_send_notifications',
'task_check_trakt_early_releases', 'task_reconcile_queues', 'task_check_plex_files',
'task_update_show_ids', 'task_update_show_titles', 'task_get_plex_watch_history'
'task_update_show_ids', 'task_update_show_titles', 'task_get_plex_watch_history',
'task_check_database_health'
]
return jsonify({'tasks': tasks}), 200

Expand Down
22 changes: 20 additions & 2 deletions routes/settings_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,16 @@ def add_notification():
'unreleased': False,
'blacklisted': False,
'pending_uncached': False,
'upgrading': False
'upgrading': False,
'program_stop': True,
'program_crash': True,
'program_start': True,
'program_pause': True,
'program_resume': True,
'queue_pause': True,
'queue_resume': True,
'queue_start': True,
'queue_stop': True
}
}

Expand Down Expand Up @@ -252,7 +261,16 @@ def ensure_notification_defaults(notification_config):
'unreleased': False,
'blacklisted': False,
'pending_uncached': False,
'upgrading': False
'upgrading': False,
'program_stop': True,
'program_crash': True,
'program_start': True,
'program_pause': True,
'program_resume': True,
'queue_pause': True,
'queue_resume': True,
'queue_start': True,
'queue_stop': True
}

# If notify_on is missing or empty, set it to the default values
Expand Down
27 changes: 26 additions & 1 deletion run_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def __init__(self):
'task_update_show_titles': 3600, # Run every hour
'task_get_plex_watch_history': 24 * 60 * 60, # Run every 24 hours
'task_refresh_plex_tokens': 24 * 60 * 60, # Run every 24 hours
'task_check_database_health': 3600, # Run every hour
}
self.start_time = time.time()
self.last_run_times = {task: self.start_time for task in self.task_intervals}
Expand All @@ -128,7 +129,8 @@ def __init__(self):
'task_refresh_download_stats',
'task_update_show_ids',
'task_update_show_titles',
'task_refresh_plex_tokens'
'task_refresh_plex_tokens',
'task_check_database_health'
}

if get_setting('File Management', 'file_collection_management') == 'Plex':
Expand Down Expand Up @@ -1160,6 +1162,29 @@ def trigger_task(self, task_name):
logging.error(f"Error running task {task_name}: {str(e)}")
raise

def task_check_database_health(self):
"""Periodic task to verify database health and handle any corruption."""
from main import verify_database_health

try:
if not verify_database_health():
logging.error("Database health check failed during periodic check")
# Pause the queue if database is corrupted
self.pause_reason = "Database corruption detected - check logs for details"
self.pause_queue()

# Send notification about database corruption
try:
from notifications import send_program_crash_notification
send_program_crash_notification("Database corruption detected - program must be restarted to recreate databases")

except Exception as e:
logging.error(f"Failed to send database corruption notification: {str(e)}")
else:
logging.info("Periodic database health check passed")
except Exception as e:
logging.error(f"Error during periodic database health check: {str(e)}")

def process_overseerr_webhook(data):
notification_type = data.get('notification_type')

Expand Down
2 changes: 1 addition & 1 deletion templates/settings_tabs/notifications.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ <h4>{{ config.title }}_{{ notification_id.split('_')[-1] }}</h4>
<div class="settings-form-group">
<label class="settings-title">Notify On:</label>
<div class="notification-categories">
{% set allowed_categories = ['checking', 'sleeping', 'collected', 'upgrading', 'program_stop', 'program_crash', 'program_start', 'queue_pause', 'queue_resume', 'queue_start', 'queue_stop'] %}
{% set allowed_categories = ['collected', 'wanted', 'scraping', 'adding', 'checking', 'sleeping', 'unreleased', 'blacklisted', 'pending_uncached', 'upgrading', 'program_stop', 'program_crash', 'program_start', 'program_pause', 'program_resume', 'queue_pause', 'queue_resume', 'queue_start', 'queue_stop'] %}
{% for category, enabled in value.items() %}
{% if category.lower() in allowed_categories %}
<div class="notification-category">
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.5.73
0.5.74

0 comments on commit 103e9f4

Please sign in to comment.