From 75db43fc09af69780252816c8d912218052d3d7e Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Fri, 23 Jan 2026 12:14:44 +0100 Subject: [PATCH] batch mode fixes --- changedetectionio/__init__.py | 17 +++++++++++++---- changedetectionio/flask_app.py | 24 ++++++++++++++++-------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index 84fd8483..a6d9749a 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -354,7 +354,10 @@ def main(): logging.getLogger('pyppeteer.connection.Connection').setLevel(logging.WARNING) # isnt there some @thingy to attach to each route to tell it, that this route needs a datastore - app_config = {'datastore_path': datastore_path} + app_config = { + 'datastore_path': datastore_path, + 'batch_mode': batch_mode + } if not os.path.isdir(app_config['datastore_path']): if create_datastore_dir: @@ -424,9 +427,15 @@ def main(): watches_to_queue = [] if recheck_watches == 'all': - # Queue all watches - watches_to_queue = list(datastore.data['watching'].keys()) - logger.info(f"Queuing all {len(watches_to_queue)} watches for recheck") + # Queue all watches, excluding those already queued in batch mode + all_watches = list(datastore.data['watching'].keys()) + if batch_mode and added_watch_uuids: + # Exclude newly added watches that were already queued in batch mode + watches_to_queue = [uuid for uuid in all_watches if uuid not in added_watch_uuids] + logger.info(f"Queuing {len(watches_to_queue)} existing watches for recheck ({len(added_watch_uuids)} newly added watches already queued)") + else: + watches_to_queue = all_watches + logger.info(f"Queuing all {len(watches_to_queue)} watches for recheck") else: # Queue specific UUIDs watches_to_queue = recheck_watches diff --git a/changedetectionio/flask_app.py b/changedetectionio/flask_app.py index 4333cd2c..2475c11c 100644 --- a/changedetectionio/flask_app.py +++ b/changedetectionio/flask_app.py @@ -401,7 +401,10 @@ def changedetection_app(config=None, datastore_o=None): # so far just for read-only via tests, but this will be moved eventually to be the main source # (instead of the global var) app.config['DATASTORE'] = datastore_o - + + # Store batch mode flag to skip background threads when running in batch mode + app.config['batch_mode'] = config.get('batch_mode', False) if config else False + # Store the signal in the app config to ensure it's accessible everywhere app.config['watch_check_update_SIGNAL'] = watch_check_update @@ -902,14 +905,19 @@ def changedetection_app(config=None, datastore_o=None): logger.info(f"Starting {n_workers} workers during app initialization") worker_handler.start_workers(n_workers, update_q, notification_q, app, datastore) - # @todo handle ctrl break - ticker_thread = threading.Thread(target=ticker_thread_check_time_launch_checks, daemon=True, name="TickerThread-ScheduleChecker").start() - threading.Thread(target=notification_runner, daemon=True, name="NotificationRunner").start() + # Skip background threads in batch mode (just process queue and exit) + batch_mode = app.config.get('batch_mode', False) + if not batch_mode: + # @todo handle ctrl break + ticker_thread = threading.Thread(target=ticker_thread_check_time_launch_checks, daemon=True, name="TickerThread-ScheduleChecker").start() + threading.Thread(target=notification_runner, daemon=True, name="NotificationRunner").start() - in_pytest = "pytest" in sys.modules or "PYTEST_CURRENT_TEST" in os.environ - # Check for new release version, but not when running in test/build or pytest - if not os.getenv("GITHUB_REF", False) and not strtobool(os.getenv('DISABLE_VERSION_CHECK', 'no')) and not in_pytest: - threading.Thread(target=check_for_new_version, daemon=True, name="VersionChecker").start() + in_pytest = "pytest" in sys.modules or "PYTEST_CURRENT_TEST" in os.environ + # Check for new release version, but not when running in test/build or pytest + if not os.getenv("GITHUB_REF", False) and not strtobool(os.getenv('DISABLE_VERSION_CHECK', 'no')) and not in_pytest: + threading.Thread(target=check_for_new_version, daemon=True, name="VersionChecker").start() + else: + logger.info("Batch mode: Skipping ticker thread, notification runner, and version checker") # Return the Flask app - the Socket.IO will be attached to it but initialized separately # This avoids circular dependencies