diff --git a/src/primary/background.py b/src/primary/background.py index d6eedb3a..7a338b88 100644 --- a/src/primary/background.py +++ b/src/primary/background.py @@ -28,7 +28,7 @@ # Removed keys_manager import as settings_manager handles API details from src.primary.state import check_state_reset, calculate_reset_time from src.primary.stats_manager import check_hourly_cap_exceeded -from src.primary.utils.instance_list_generator import generate_instance_list +# Instance list generator has been removed from src.primary.scheduler_engine import start_scheduler, stop_scheduler from src.primary.migrate_configs import migrate_json_configs # Import the migration function # from src.primary.utils.app_utils import get_ip_address # No longer used here @@ -40,8 +40,7 @@ # Hourly cap scheduler thread hourly_cap_scheduler_thread = None -# Instance list generator thread -instance_list_generator_thread = None +# Instance list generator has been removed def app_specific_loop(app_type: str) -> None: """ @@ -612,45 +611,9 @@ def hourly_cap_scheduler_loop(): logger.info("Hourly API cap scheduler stopped") -def instance_list_generator_loop(): - """ - Main loop for the instance list generator thread - Updates the instance list periodically (every 5 minutes) - """ - interval = 300 # 5 minutes in seconds - - logger.debug("Starting instance list generator loop") - - while not stop_event.is_set(): - try: - # Generate the instance list - instances = generate_instance_list() - logger.debug(f"Generated instance list with {sum(len(apps) for apps in instances.values())} total instances") - except Exception as e: - logger.error(f"Error generating instance list: {e}") - - # Wait for next interval or until stop event - stop_event.wait(interval) - - logger.debug("Instance list generator loop stopped") +# The instance list generator loop has been removed as it's no longer needed -def start_instance_list_generator(): - """Start the instance list generator thread""" - global instance_list_generator_thread - - if instance_list_generator_thread and instance_list_generator_thread.is_alive(): - logger.debug("Instance list generator already running") - return - - # Create and start the generator thread - instance_list_generator_thread = threading.Thread( - target=instance_list_generator_loop, - name="InstanceListGenerator", - daemon=True - ) - instance_list_generator_thread.start() - - logger.debug(f"Instance list generator started. Thread is alive: {instance_list_generator_thread.is_alive()}") +# The start_instance_list_generator function has been removed as it's no longer needed def start_hourly_cap_scheduler(): """Start the hourly API cap scheduler thread""" @@ -701,15 +664,8 @@ def start_huntarr(): except Exception as e: logger.error(f"Failed to start schedule action engine: {e}") - # Start the instance list generator - try: - # Generate instance list immediately - generate_instance_list() - # Then start the background thread for periodic updates - start_instance_list_generator() - logger.debug("Instance list generator started successfully") - except Exception as e: - logger.error(f"Failed to start instance list generator: {e}") + # Instance list generator has been removed + logger.debug("Instance list generator has been removed and is no longer needed") # Log initial configuration for all known apps for app_name in settings_manager.KNOWN_APP_TYPES: # Corrected attribute name diff --git a/src/primary/routes/scheduler_routes.py b/src/primary/routes/scheduler_routes.py index d9f8753a..3bacdf93 100644 --- a/src/primary/routes/scheduler_routes.py +++ b/src/primary/routes/scheduler_routes.py @@ -19,8 +19,7 @@ # Create blueprint scheduler_api = Blueprint('scheduler_api', __name__) -# Import instance list generator to access its functions -from src.primary.utils.instance_list_generator import generate_instance_list +# No longer using instance list generator # Configuration file path # Use the centralized path configuration @@ -93,21 +92,7 @@ def get_scheduler_history(): scheduler_logger.error(error_msg) return jsonify({"error": error_msg}), 500 -@scheduler_api.route('/api/scheduling/list', methods=['GET']) -def get_scheduler_instance_list(): - """Return the list of app instances for the scheduler UI""" - try: - # Generate the instance list (this will create list.json in the scheduling directory) - instances = generate_instance_list() - - # Return the generated data directly as JSON - return jsonify(instances) - except Exception as e: - scheduler_logger.error(f"Error generating instance list: {str(e)}") - return jsonify({ - 'error': f"Failed to generate instance list: {str(e)}", - 'order': ['sonarr', 'radarr', 'readarr', 'lidarr', 'whisparr', 'eros'] - }), 500 +# API route for instance list generation has been removed @scheduler_api.route('/api/scheduler/save', methods=['POST']) def save_schedules(): diff --git a/src/primary/scheduler_engine.py b/src/primary/scheduler_engine.py index d1f43ee6..b23a3f54 100644 --- a/src/primary/scheduler_engine.py +++ b/src/primary/scheduler_engine.py @@ -24,7 +24,7 @@ # Scheduler constants SCHEDULE_CHECK_INTERVAL = 60 # Check schedule every minute # Use the centralized path configuration -from src.primary.utils.config_paths import SCHEDULER_DIR, CONFIG_PATH +from src.primary.utils.config_paths import SCHEDULER_DIR, CONFIG_PATH, SETTINGS_DIR # Convert Path object to string for compatibility with os.path functions SCHEDULE_DIR = str(SCHEDULER_DIR) @@ -135,7 +135,7 @@ def execute_action(action_entry): try: apps = ['sonarr', 'radarr', 'lidarr', 'readarr', 'whisparr', 'eros'] for app in apps: - config_file = os.path.join(str(CONFIG_PATH), f"{app}.json") + config_file = os.path.join(str(SETTINGS_DIR), f"{app}.json") if os.path.exists(config_file): with open(config_file, 'r') as f: config_data = json.load(f) @@ -162,7 +162,7 @@ def execute_action(action_entry): message = f"Executing disable action for {app_type}" scheduler_logger.info(message) try: - config_file = os.path.join(str(CONFIG_PATH), f"{app_type}.json") + config_file = os.path.join(str(SETTINGS_DIR), f"{app_type}.json") if os.path.exists(config_file): with open(config_file, 'r') as f: config_data = json.load(f) @@ -195,7 +195,7 @@ def execute_action(action_entry): try: apps = ['sonarr', 'radarr', 'lidarr', 'readarr', 'whisparr', 'eros'] for app in apps: - config_file = os.path.join(str(CONFIG_PATH), f"{app}.json") + config_file = os.path.join(str(SETTINGS_DIR), f"{app}.json") if os.path.exists(config_file): with open(config_file, 'r') as f: config_data = json.load(f) @@ -222,7 +222,7 @@ def execute_action(action_entry): message = f"Executing enable action for {app_type}" scheduler_logger.info(message) try: - config_file = os.path.join(str(CONFIG_PATH), f"{app_type}.json") + config_file = os.path.join(str(SETTINGS_DIR), f"{app_type}.json") if os.path.exists(config_file): with open(config_file, 'r') as f: config_data = json.load(f) @@ -262,7 +262,7 @@ def execute_action(action_entry): try: apps = ['sonarr', 'radarr', 'lidarr', 'readarr', 'whisparr', 'eros'] for app in apps: - config_file = os.path.join(str(CONFIG_PATH), f"{app}.json") + config_file = os.path.join(str(SETTINGS_DIR), f"{app}.json") if os.path.exists(config_file): with open(config_file, 'r') as f: config_data = json.load(f) @@ -281,7 +281,7 @@ def execute_action(action_entry): message = f"Setting API cap for {app_type} to {api_limit}" scheduler_logger.info(message) try: - config_file = os.path.join(str(CONFIG_PATH), f"{app_type}.json") + config_file = os.path.join(str(SETTINGS_DIR), f"{app_type}.json") if os.path.exists(config_file): with open(config_file, 'r') as f: config_data = json.load(f) diff --git a/src/primary/utils/config_paths.py b/src/primary/utils/config_paths.py index 53f84357..cb816b87 100644 --- a/src/primary/utils/config_paths.py +++ b/src/primary/utils/config_paths.py @@ -43,7 +43,7 @@ SETTINGS_DIR = CONFIG_PATH STATEFUL_DIR = CONFIG_PATH / "stateful" RESET_DIR = CONFIG_PATH / "reset" - SCHEDULER_DIR = CONFIG_PATH / "scheduling" + SCHEDULER_DIR = CONFIG_PATH / "scheduler" SWAPARR_STATE_DIR = CONFIG_PATH / "swaparr" # Create essential directories diff --git a/src/primary/utils/instance_list_generator.py b/src/primary/utils/instance_list_generator.py deleted file mode 100644 index 0aae78c8..00000000 --- a/src/primary/utils/instance_list_generator.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Generate a consolidated list of app instances for the scheduler -This script scans all app configuration files and generates a unified list.json -for use by the scheduling UI -""" - -import os -import json -import logging -from pathlib import Path - -# Set up logging -logger = logging.getLogger("huntarr.instance_list_generator") - -def capitalize_first(string): - """Capitalize the first letter of a string""" - if not string: - return '' - return string[0].upper() + string[1:] - -def generate_instance_list(): - """ - Scan all app configuration files and generate a list.json file - containing all app instances for use by the scheduler - """ - logger.debug("Generating app instance list for scheduler") - - # Define the app types we support in the specified order - # Order: Sonarr, Radarr, Readarr, Lidarr, WhisparrV2, WhisparrV3 - app_types = ['sonarr', 'radarr', 'readarr', 'lidarr', 'whisparr', 'eros'] - - # Map order numbers for strict ordering - app_order = { - 'sonarr': 1, - 'radarr': 2, - 'readarr': 3, - 'lidarr': 4, - 'whisparr': 5, - 'eros': 6 - } - - # Ensure consistent order for data structure when returning results - instances_ordered = {'order': app_types} - for app_type in app_types: - instances_ordered[app_type] = [] - - # App type display names (for UI customization) - app_display_names = { - 'whisparr': 'WhisparrV2', - 'eros': 'WhisparrV3' - } - # Dictionary using ordered keys from above - instances = instances_ordered - - # Use the centralized path configuration - from src.primary.utils.config_paths import CONFIG_PATH - - # Base config directory (cross-platform path) - config_dir = CONFIG_PATH - - # Ensure the scheduling directory exists - scheduling_dir = config_dir / "scheduling" - os.makedirs(scheduling_dir, exist_ok=True) - - # Also try to save to a web-accessible location - try: - web_accessible_dir = Path(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) / "frontend" / "static" / "data" - os.makedirs(web_accessible_dir, exist_ok=True) - logger.debug(f"Web accessible directory: {web_accessible_dir}") - web_accessible_path_valid = True - except (PermissionError, OSError) as e: - logger.debug(f"Cannot create web-accessible directory: {e}. Will only save to config directory.") - web_accessible_path_valid = False - - # Scan each app's config file - for app_type in app_types: - config_file = config_dir / f"{app_type}.json" - - if not config_file.exists(): - logger.debug(f"No config file found for {app_type}, skipping") - continue - - try: - with open(config_file, 'r') as f: - config = json.load(f) - - # Check if we have an instances array in the config - if config and "instances" in config and isinstance(config["instances"], list): - # Add each instance to our instances object - for index, instance in enumerate(config["instances"]): - instance_name = instance.get("name") or f"{capitalize_first(app_type)} Instance {index + 1}" - instances[app_type].append({ - "id": str(index), - "name": instance_name, - "display_name": app_display_names.get(app_type, capitalize_first(app_type)), - "order": app_order.get(app_type, 999) # Use the map for strict ordering - }) - logger.debug(f"Added {len(instances[app_type])} {app_type} instances") - else: - logger.debug(f"No instances found in {app_type}.json, adding default") - # Add a default instance if none found - instances[app_type] = [ - { - "id": "0", - "name": f"{capitalize_first(app_type)} Default", - "display_name": app_display_names.get(app_type, capitalize_first(app_type)), - "order": app_order.get(app_type, 999) # Use the map for strict ordering - } - ] - except Exception as e: - logger.error(f"Error processing {app_type}.json: {str(e)}") - # Add a default instance on error - instances[app_type] = [ - { - "id": "0", - "name": f"{capitalize_first(app_type)} Default", - "display_name": app_display_names.get(app_type, capitalize_first(app_type)), - "order": app_order.get(app_type, 999) # Use the map for strict ordering - } - ] - - # Write the consolidated list to list.json in Docker config volume - list_file = scheduling_dir / "list.json" - with open(list_file, 'w') as f: - json.dump(instances, f, indent=2) - - # Also write to web-accessible location if path is valid - if web_accessible_path_valid: - try: - web_list_file = web_accessible_dir / "app_instances.json" - with open(web_list_file, 'w') as f: - json.dump(instances, f, indent=2) - logger.debug(f"Instance list generated successfully at {list_file} and {web_list_file}") - except (PermissionError, OSError) as e: - logger.debug(f"Cannot write to web-accessible file: {e}") - logger.debug(f"Instance list generated successfully at {list_file} only") - else: - logger.debug(f"Instance list generated successfully at {list_file} only") - return instances diff --git a/version.txt b/version.txt index 19300b7b..28118975 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -7.0.8 \ No newline at end of file +7.0.9 \ No newline at end of file