From 5f25e3825cd09fbac7d09b6df296864e5936edda Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Tue, 3 Feb 2026 15:49:54 +0100 Subject: [PATCH] Closes #3814 --- changedetectionio/blueprint/imports/__init__.py | 3 ++- changedetectionio/blueprint/ui/views.py | 3 ++- changedetectionio/forms.py | 6 +++--- changedetectionio/processors/__init__.py | 14 ++++++++++++++ docker-compose.yml | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/changedetectionio/blueprint/imports/__init__.py b/changedetectionio/blueprint/imports/__init__.py index 96e2360a5..b088ab688 100644 --- a/changedetectionio/blueprint/imports/__init__.py +++ b/changedetectionio/blueprint/imports/__init__.py @@ -26,8 +26,9 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe # URL List import if request.values.get('urls') and len(request.values.get('urls').strip()): # Import and push into the queue for immediate update check + from changedetectionio import processors importer_handler = import_url_list() - importer_handler.run(data=request.values.get('urls'), flash=flash, datastore=datastore, processor=request.values.get('processor', 'text_json_diff')) + importer_handler.run(data=request.values.get('urls'), flash=flash, datastore=datastore, processor=request.values.get('processor', processors.get_default_processor())) logger.debug(f"Imported {len(importer_handler.new_uuids)} new UUIDs") # Dont' add to queue because scheduler can see that they haven't been checked and will add them to the queue # for uuid in importer_handler.new_uuids: diff --git a/changedetectionio/blueprint/ui/views.py b/changedetectionio/blueprint/ui/views.py index 4673ab877..172829c94 100644 --- a/changedetectionio/blueprint/ui/views.py +++ b/changedetectionio/blueprint/ui/views.py @@ -24,7 +24,8 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe flash(gettext('Warning, URL {} already exists').format(url), "notice") add_paused = request.form.get('edit_and_watch_submit_button') != None - processor = request.form.get('processor', 'text_json_diff') + from changedetectionio import processors + processor = request.form.get('processor', processors.get_default_processor()) new_uuid = datastore.add_watch(url=url, tag=request.form.get('tags').strip(), extras={'paused': add_paused, 'processor': processor}) if new_uuid: diff --git a/changedetectionio/forms.py b/changedetectionio/forms.py index 9ffd111fe..8343dd1af 100644 --- a/changedetectionio/forms.py +++ b/changedetectionio/forms.py @@ -730,7 +730,7 @@ class quickWatchForm(Form): url = fields.URLField(_l('URL'), validators=[validateURL()]) tags = StringTagUUID(_l('Group tag'), validators=[validators.Optional()]) watch_submit_button = SubmitField(_l('Watch'), render_kw={"class": "pure-button pure-button-primary"}) - processor = RadioField(_l('Processor'), choices=lambda: processors.available_processors(), default="text_json_diff") + processor = RadioField(_l('Processor'), choices=lambda: processors.available_processors(), default=processors.get_default_processor) edit_and_watch_submit_button = SubmitField(_l('Edit > Watch'), render_kw={"class": "pure-button pure-button-primary"}) @@ -749,7 +749,7 @@ class commonSettingsForm(Form): notification_format = SelectField(_l('Notification format'), choices=list(valid_notification_formats.items())) notification_title = StringField(_l('Notification Title'), default='ChangeDetection.io Notification - {{ watch_url }}', validators=[validators.Optional(), ValidateJinja2Template()]) notification_urls = StringListField(_l('Notification URL List'), validators=[validators.Optional(), ValidateAppRiseServers(), ValidateJinja2Template()]) - processor = RadioField( label=_l("Processor - What do you want to achieve?"), choices=lambda: processors.available_processors(), default="text_json_diff") + processor = RadioField( label=_l("Processor - What do you want to achieve?"), choices=lambda: processors.available_processors(), default=processors.get_default_processor) scheduler_timezone_default = StringField(_l("Default timezone for watch check scheduler"), render_kw={"list": "timezones"}, validators=[validateTimeZoneName()]) webdriver_delay = IntegerField(_l('Wait seconds before extracting text'), validators=[validators.Optional(), validators.NumberRange(min=1, message=_l("Should contain one or more seconds"))]) @@ -763,7 +763,7 @@ class commonSettingsForm(Form): class importForm(Form): - processor = RadioField(_l('Processor'), choices=lambda: processors.available_processors(), default="text_json_diff") + processor = RadioField(_l('Processor'), choices=lambda: processors.available_processors(), default=processors.get_default_processor) urls = TextAreaField(_l('URLs')) xlsx_file = FileField(_l('Upload .xlsx file'), validators=[FileAllowed(['xlsx'], _l('Must be .xlsx file!'))]) file_mapping = SelectField(_l('File mapping'), [validators.DataRequired()], choices={('wachete', 'Wachete mapping'), ('custom','Custom mapping')}) diff --git a/changedetectionio/processors/__init__.py b/changedetectionio/processors/__init__.py index 9cb9332c1..044199987 100644 --- a/changedetectionio/processors/__init__.py +++ b/changedetectionio/processors/__init__.py @@ -256,6 +256,20 @@ def available_processors(): return [(name, desc) for name, desc, weight in available] +def get_default_processor(): + """ + Get the default processor to use when none is specified. + Returns the first available processor based on weight (lowest weight = highest priority). + This ensures forms auto-select a valid processor even when DISABLED_PROCESSORS filters the list. + + :return: The processor name string (e.g., 'text_json_diff') + """ + available = available_processors() + if available: + return available[0][0] # Return the processor name from first tuple + return 'text_json_diff' # Fallback if somehow no processors are available + + def get_processor_badge_texts(): """ Get a dictionary mapping processor names to their list_badge_text values. diff --git a/docker-compose.yml b/docker-compose.yml index 997901598..6e658e98c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,8 +16,8 @@ services: # Log output levels: TRACE, DEBUG(default), INFO, SUCCESS, WARNING, ERROR, CRITICAL # - LOGGER_LEVEL=TRACE # + # Plugins! See https://changedetection.io/plugins for more plugins. # Install additional Python packages (processor plugins, etc.) - # Packages are installed at container startup and cached to avoid reinstalling on every restart # Example: Install the OSINT reconnaissance processor plugin # - EXTRA_PACKAGES=changedetection.io-osint # Multiple packages can be installed by separating with spaces: