mirror of
				https://github.com/dgtlmoon/changedetection.io.git
				synced 2025-11-03 16:17:51 +00:00 
			
		
		
		
	Compare commits
	
		
			9 Commits
		
	
	
		
			0.50.26
			...
			refactor-t
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d579e26cd6 | ||
| 
						 | 
					f8fe5c8d41 | ||
| 
						 | 
					bddf9af584 | ||
| 
						 | 
					643a45b5e2 | ||
| 
						 | 
					e9f1cc91c7 | ||
| 
						 | 
					080526eb65 | ||
| 
						 | 
					c9552d2319 | ||
| 
						 | 
					3c14273021 | ||
| 
						 | 
					547ab70ea3 | 
@@ -619,7 +619,6 @@ def changedetection_app(config=None, datastore_o=None):
 | 
			
		||||
        from .blueprint.browser_steps.browser_steps import browser_step_ui_config
 | 
			
		||||
        from . import processors
 | 
			
		||||
 | 
			
		||||
        using_default_check_time = True
 | 
			
		||||
        # More for testing, possible to return the first/only
 | 
			
		||||
        if not datastore.data['watching'].keys():
 | 
			
		||||
            flash("No watches to edit", "error")
 | 
			
		||||
@@ -644,10 +643,6 @@ def changedetection_app(config=None, datastore_o=None):
 | 
			
		||||
        # be sure we update with a copy instead of accidently editing the live object by reference
 | 
			
		||||
        default = deepcopy(datastore.data['watching'][uuid])
 | 
			
		||||
 | 
			
		||||
        # Show system wide default if nothing configured
 | 
			
		||||
        if all(value == 0 or value == None for value in datastore.data['watching'][uuid]['time_between_check'].values()):
 | 
			
		||||
            default['time_between_check'] = deepcopy(datastore.data['settings']['requests']['time_between_check'])
 | 
			
		||||
 | 
			
		||||
        # Defaults for proxy choice
 | 
			
		||||
        if datastore.proxy_list is not None:  # When enabled
 | 
			
		||||
            # @todo
 | 
			
		||||
@@ -685,18 +680,8 @@ def changedetection_app(config=None, datastore_o=None):
 | 
			
		||||
 | 
			
		||||
            if request.args.get('unpause_on_save'):
 | 
			
		||||
                extra_update_obj['paused'] = False
 | 
			
		||||
            # Re #110, if they submit the same as the default value, set it to None, so we continue to follow the default
 | 
			
		||||
            # Assume we use the default value, unless something relevant is different, then use the form value
 | 
			
		||||
            # values could be None, 0 etc.
 | 
			
		||||
            # Set to None unless the next for: says that something is different
 | 
			
		||||
            extra_update_obj['time_between_check'] = dict.fromkeys(form.time_between_check.data)
 | 
			
		||||
            for k, v in form.time_between_check.data.items():
 | 
			
		||||
                if v and v != datastore.data['settings']['requests']['time_between_check'][k]:
 | 
			
		||||
                    extra_update_obj['time_between_check'] = form.time_between_check.data
 | 
			
		||||
                    using_default_check_time = False
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            extra_update_obj['time_between_check'] = form.time_between_check.data
 | 
			
		||||
 | 
			
		||||
             # Ignore text
 | 
			
		||||
            form_ignore_text = form.ignore_text.data
 | 
			
		||||
@@ -777,7 +762,6 @@ def changedetection_app(config=None, datastore_o=None):
 | 
			
		||||
                                     extra_title=f" - Edit - {watch.label}",
 | 
			
		||||
                                     form=form,
 | 
			
		||||
                                     has_default_notification_urls=True if len(datastore.data['settings']['application']['notification_urls']) else False,
 | 
			
		||||
                                     has_empty_checktime=using_default_check_time,
 | 
			
		||||
                                     has_extra_headers_file=len(datastore.get_all_headers_in_textfile_for_watch(uuid=uuid)) > 0,
 | 
			
		||||
                                     has_special_tag_options=_watch_has_tag_options_set(watch=watch),
 | 
			
		||||
                                     is_html_webdriver=is_html_webdriver,
 | 
			
		||||
@@ -863,11 +847,13 @@ def changedetection_app(config=None, datastore_o=None):
 | 
			
		||||
                flash("An error occurred, please see below.", "error")
 | 
			
		||||
 | 
			
		||||
        output = render_template("settings.html",
 | 
			
		||||
                                 form=form,
 | 
			
		||||
                                 hide_remove_pass=os.getenv("SALTED_PASS", False),
 | 
			
		||||
                                 api_key=datastore.data['settings']['application'].get('api_access_token'),
 | 
			
		||||
                                 emailprefix=os.getenv('NOTIFICATION_MAIL_BUTTON_PREFIX', False),
 | 
			
		||||
                                 settings_application=datastore.data['settings']['application'])
 | 
			
		||||
                                 form=form,
 | 
			
		||||
                                 hide_remove_pass=os.getenv("SALTED_PASS", False),
 | 
			
		||||
                                 min_system_recheck_seconds=int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 3)),
 | 
			
		||||
                                 settings_application=datastore.data['settings']['application']
 | 
			
		||||
                                 )
 | 
			
		||||
 | 
			
		||||
        return output
 | 
			
		||||
 | 
			
		||||
@@ -1668,14 +1654,14 @@ def notification_runner():
 | 
			
		||||
            # Trim the log length
 | 
			
		||||
            notification_debug_log = notification_debug_log[-100:]
 | 
			
		||||
 | 
			
		||||
# Thread runner to check every minute, look for new watches to feed into the Queue.
 | 
			
		||||
# Threaded runner, look for new watches to feed into the Queue.
 | 
			
		||||
def ticker_thread_check_time_launch_checks():
 | 
			
		||||
    import random
 | 
			
		||||
    from changedetectionio import update_worker
 | 
			
		||||
 | 
			
		||||
    proxy_last_called_time = {}
 | 
			
		||||
 | 
			
		||||
    recheck_time_minimum_seconds = int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 20))
 | 
			
		||||
    recheck_time_minimum_seconds = int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 3))
 | 
			
		||||
    logger.debug(f"System env MINIMUM_SECONDS_RECHECK_TIME {recheck_time_minimum_seconds}")
 | 
			
		||||
 | 
			
		||||
    # Spin up Workers that do the fetching
 | 
			
		||||
@@ -1729,9 +1715,7 @@ def ticker_thread_check_time_launch_checks():
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # If they supplied an individual entry minutes to threshold.
 | 
			
		||||
 | 
			
		||||
            watch_threshold_seconds = watch.threshold_seconds()
 | 
			
		||||
            threshold = watch_threshold_seconds if watch_threshold_seconds > 0 else recheck_time_system_seconds
 | 
			
		||||
            threshold = recheck_time_system_seconds if watch.get('time_between_check_use_default') else watch.threshold_seconds()
 | 
			
		||||
 | 
			
		||||
            # #580 - Jitter plus/minus amount of time to make the check seem more random to the server
 | 
			
		||||
            jitter = datastore.data['settings']['requests'].get('jitter_seconds', 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -453,6 +453,7 @@ class watchForm(commonSettingsForm):
 | 
			
		||||
    tags = StringTagUUID('Group tag', [validators.Optional()], default='')
 | 
			
		||||
 | 
			
		||||
    time_between_check = FormField(TimeBetweenCheckForm)
 | 
			
		||||
    time_between_check_use_default = BooleanField('Use global settings for time between check', default=False)
 | 
			
		||||
 | 
			
		||||
    include_filters = StringListField('CSS/JSONPath/JQ/XPath Filters', [ValidateCSSJSONXPATHInput()], default='')
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ from loguru import logger
 | 
			
		||||
# file:// is further checked by ALLOW_FILE_URI
 | 
			
		||||
SAFE_PROTOCOL_REGEX='^(http|https|ftp|file):'
 | 
			
		||||
 | 
			
		||||
minimum_seconds_recheck_time = int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 60))
 | 
			
		||||
minimum_seconds_recheck_time = int(os.getenv('MINIMUM_SECONDS_RECHECK_TIME', 3))
 | 
			
		||||
mtable = {'seconds': 1, 'minutes': 60, 'hours': 3600, 'days': 86400, 'weeks': 86400 * 7}
 | 
			
		||||
 | 
			
		||||
from changedetectionio.notification import (
 | 
			
		||||
@@ -69,6 +69,7 @@ base_config = {
 | 
			
		||||
    # Requires setting to None on submit if it's the same as the default
 | 
			
		||||
    # Should be all None by default, so we use the system default in this case.
 | 
			
		||||
    'time_between_check': {'weeks': None, 'days': None, 'hours': None, 'minutes': None, 'seconds': None},
 | 
			
		||||
    'time_between_check_use_default': True,
 | 
			
		||||
    'title': None,
 | 
			
		||||
    'trigger_text': [],  # List of text or regex to wait for until a change is detected
 | 
			
		||||
    'url': '',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,17 @@
 | 
			
		||||
function toggleOpacity(checkboxSelector, fieldSelector) {
 | 
			
		||||
    const checkbox = document.querySelector(checkboxSelector);
 | 
			
		||||
    const fields = document.querySelectorAll(fieldSelector);
 | 
			
		||||
    function updateOpacity() {
 | 
			
		||||
        const opacityValue = checkbox.checked ? 0.6 : 1;
 | 
			
		||||
        fields.forEach(field => {
 | 
			
		||||
            field.style.opacity = opacityValue;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    // Initial setup
 | 
			
		||||
    updateOpacity();
 | 
			
		||||
    checkbox.addEventListener('change', updateOpacity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$(document).ready(function () {
 | 
			
		||||
    $('#notification-setting-reset-to-default').click(function (e) {
 | 
			
		||||
        $('#notification_title').val('');
 | 
			
		||||
@@ -10,4 +24,7 @@ $(document).ready(function () {
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        $('#notification-tokens-info').toggle();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    toggleOpacity('#time_between_check_use_default', '#time_between_check');
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -928,23 +928,26 @@ body.full-width {
 | 
			
		||||
      font-size: .875em;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  .text-filtering {
 | 
			
		||||
    h3 {
 | 
			
		||||
      margin-top: 0;
 | 
			
		||||
    }
 | 
			
		||||
    border: 1px solid #ccc;
 | 
			
		||||
    padding: 1rem;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    margin-bottom: 1rem;
 | 
			
		||||
    fieldset:last-of-type {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.border-fieldset {
 | 
			
		||||
  h3 {
 | 
			
		||||
    margin-top: 0;
 | 
			
		||||
  }
 | 
			
		||||
  border: 1px solid #ccc;
 | 
			
		||||
  padding: 1rem;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  margin-bottom: 1rem;
 | 
			
		||||
  fieldset:last-of-type {
 | 
			
		||||
    padding-bottom: 0;
 | 
			
		||||
    .pure-control-group {
 | 
			
		||||
      padding-bottom: 0;
 | 
			
		||||
      .pure-control-group {
 | 
			
		||||
        padding-bottom: 0;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ul {
 | 
			
		||||
  padding-left: 1em;
 | 
			
		||||
  padding-top: 0px;
 | 
			
		||||
 
 | 
			
		||||
@@ -1041,17 +1041,18 @@ body.full-width .edit-form {
 | 
			
		||||
    color: var(--color-text-input-description); }
 | 
			
		||||
    .edit-form .pure-form-message-inline code {
 | 
			
		||||
      font-size: .875em; }
 | 
			
		||||
  .edit-form .text-filtering {
 | 
			
		||||
    border: 1px solid #ccc;
 | 
			
		||||
    padding: 1rem;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    margin-bottom: 1rem; }
 | 
			
		||||
    .edit-form .text-filtering h3 {
 | 
			
		||||
      margin-top: 0; }
 | 
			
		||||
    .edit-form .text-filtering fieldset:last-of-type {
 | 
			
		||||
 | 
			
		||||
.border-fieldset {
 | 
			
		||||
  border: 1px solid #ccc;
 | 
			
		||||
  padding: 1rem;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
  margin-bottom: 1rem; }
 | 
			
		||||
  .border-fieldset h3 {
 | 
			
		||||
    margin-top: 0; }
 | 
			
		||||
  .border-fieldset fieldset:last-of-type {
 | 
			
		||||
    padding-bottom: 0; }
 | 
			
		||||
    .border-fieldset fieldset:last-of-type .pure-control-group {
 | 
			
		||||
      padding-bottom: 0; }
 | 
			
		||||
      .edit-form .text-filtering fieldset:last-of-type .pure-control-group {
 | 
			
		||||
        padding-bottom: 0; }
 | 
			
		||||
 | 
			
		||||
ul {
 | 
			
		||||
  padding-left: 1em;
 | 
			
		||||
 
 | 
			
		||||
@@ -872,3 +872,16 @@ class ChangeDetectionStore:
 | 
			
		||||
                        self.__data["watching"][awatch]['include_filters'][num] = 'xpath1:' + selector
 | 
			
		||||
                    if selector.startswith('xpath:'):
 | 
			
		||||
                        self.__data["watching"][awatch]['include_filters'][num] = selector.replace('xpath:', 'xpath1:', 1)
 | 
			
		||||
 | 
			
		||||
    # Use more obvious default time setting
 | 
			
		||||
    def update_15(self):
 | 
			
		||||
        for uuid in self.__data["watching"]:
 | 
			
		||||
            if self.__data["watching"][uuid]['time_between_check'] == self.__data['settings']['requests']['time_between_check']:
 | 
			
		||||
                # What the old logic was, which was pretty confusing
 | 
			
		||||
                self.__data["watching"][uuid]['time_between_check_use_default'] = True
 | 
			
		||||
            elif all(value is None or value == 0 for value in self.__data["watching"][uuid]['time_between_check'].values()):
 | 
			
		||||
                self.__data["watching"][uuid]['time_between_check_use_default'] = True
 | 
			
		||||
            else:
 | 
			
		||||
                # Something custom here
 | 
			
		||||
                self.__data["watching"][uuid]['time_between_check_use_default'] = False
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -87,15 +87,9 @@
 | 
			
		||||
                        {{ render_field(form.tags) }}
 | 
			
		||||
                        <span class="pure-form-message-inline">Organisational tag/group name used in the main listing page</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pure-control-group">
 | 
			
		||||
                    <div class="pure-control-group time-between-check border-fieldset">
 | 
			
		||||
                        {{ render_field(form.time_between_check, class="time-check-widget") }}
 | 
			
		||||
                        {% if has_empty_checktime %}
 | 
			
		||||
                        <span class="pure-form-message-inline">Currently using the <a
 | 
			
		||||
                                href="{{ url_for('settings_page', uuid=uuid) }}">default global settings</a>, change to another value if you want to be specific.</span>
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                        <span class="pure-form-message-inline">Set to blank to use the <a
 | 
			
		||||
                                href="{{ url_for('settings_page', uuid=uuid) }}">default global settings</a>.</span>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        {{ render_checkbox_field(form.time_between_check_use_default, class="use-default-timecheck") }}
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pure-control-group">
 | 
			
		||||
                        {{ render_checkbox_field(form.extract_title_as_title) }}
 | 
			
		||||
@@ -330,7 +324,7 @@ nav
 | 
			
		||||
                        </ul>
 | 
			
		||||
                      </span>
 | 
			
		||||
                </fieldset>
 | 
			
		||||
                <div class="text-filtering">
 | 
			
		||||
                <div class="text-filtering border-fieldset">
 | 
			
		||||
                <fieldset class="pure-group" id="text-filtering-type-options">
 | 
			
		||||
                    <h3>Text filtering</h3>
 | 
			
		||||
                        Limit trigger/ignore/block/extract to;<br>
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
                <fieldset>
 | 
			
		||||
                    <div class="pure-control-group">
 | 
			
		||||
                        {{ render_field(form.requests.form.time_between_check, class="time-check-widget") }}
 | 
			
		||||
                        <span class="pure-form-message-inline">Default time for all watches, when the watch does not have a specific time setting.</span>
 | 
			
		||||
                        <span class="pure-form-message-inline">Default recheck time for all watches, current system minimum is <i>{{min_system_recheck_seconds}}</i> seconds (<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Misc-system-settings#enviroment-variables">more info</a>).</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pure-control-group">
 | 
			
		||||
                        {{ render_field(form.requests.form.jitter_seconds, class="jitter_seconds") }}
 | 
			
		||||
 
 | 
			
		||||
@@ -54,102 +54,3 @@ def test_check_watch_field_storage(client, live_server):
 | 
			
		||||
    assert b"woohoo" in res.data
 | 
			
		||||
    assert b"curl: foo" in res.data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Re https://github.com/dgtlmoon/changedetection.io/issues/110
 | 
			
		||||
def test_check_recheck_global_setting(client, live_server):
 | 
			
		||||
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("settings_page"),
 | 
			
		||||
        data={
 | 
			
		||||
               "requests-time_between_check-minutes": 1566,
 | 
			
		||||
               'application-fetch_backend': "html_requests"
 | 
			
		||||
               },
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
    assert b"Settings updated." in res.data
 | 
			
		||||
 | 
			
		||||
    # Now add a record
 | 
			
		||||
 | 
			
		||||
    test_url = "http://somerandomsitewewatch.com"
 | 
			
		||||
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("import_page"),
 | 
			
		||||
        data={"urls": test_url},
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
    assert b"1 Imported" in res.data
 | 
			
		||||
 | 
			
		||||
    # Now visit the edit page, it should have the default minutes
 | 
			
		||||
 | 
			
		||||
    res = client.get(
 | 
			
		||||
        url_for("edit_page", uuid="first"),
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Should show the default minutes
 | 
			
		||||
    assert b"change to another value if you want to be specific" in res.data
 | 
			
		||||
    assert b"1566" in res.data
 | 
			
		||||
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("settings_page"),
 | 
			
		||||
        data={
 | 
			
		||||
               "requests-time_between_check-minutes": 222,
 | 
			
		||||
                'application-fetch_backend': "html_requests"
 | 
			
		||||
               },
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
    assert b"Settings updated." in res.data
 | 
			
		||||
 | 
			
		||||
    res = client.get(
 | 
			
		||||
        url_for("edit_page", uuid="first"),
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # Should show the default minutes
 | 
			
		||||
    assert b"change to another value if you want to be specific" in res.data
 | 
			
		||||
    assert b"222" in res.data
 | 
			
		||||
 | 
			
		||||
    # Now change it specifically, it should show the new minutes
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("edit_page", uuid="first"),
 | 
			
		||||
        data={"url": test_url,
 | 
			
		||||
              "time_between_check-minutes": 55,
 | 
			
		||||
              'fetch_backend': "html_requests"
 | 
			
		||||
              },
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    res = client.get(
 | 
			
		||||
        url_for("edit_page", uuid="first"),
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
    assert b"55" in res.data
 | 
			
		||||
 | 
			
		||||
    # Now submit an empty field, it should give back the default global minutes
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("settings_page"),
 | 
			
		||||
        data={
 | 
			
		||||
               "requests-time_between_check-minutes": 666,
 | 
			
		||||
                "application-fetch_backend": "html_requests"
 | 
			
		||||
               },
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
    assert b"Settings updated." in res.data
 | 
			
		||||
 | 
			
		||||
    res = client.post(
 | 
			
		||||
        url_for("edit_page", uuid="first"),
 | 
			
		||||
        data={"url": test_url,
 | 
			
		||||
              "time_between_check-minutes": "",
 | 
			
		||||
              'fetch_backend': "html_requests"
 | 
			
		||||
              },
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    assert b"Updated watch." in res.data
 | 
			
		||||
 | 
			
		||||
    res = client.get(
 | 
			
		||||
        url_for("edit_page", uuid="first"),
 | 
			
		||||
        follow_redirects=True
 | 
			
		||||
    )
 | 
			
		||||
    assert b"666" in res.data
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,9 @@ services:
 | 
			
		||||
  #        
 | 
			
		||||
  #        Default number of parallel/concurrent fetchers
 | 
			
		||||
  #      - FETCH_WORKERS=10
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  #        Absolute minimum seconds to recheck, overrides any watch minimum, change to 0 to disable
 | 
			
		||||
  #      - MINIMUM_SECONDS_RECHECK_TIME=3
 | 
			
		||||
      # Comment out ports: when using behind a reverse proxy , enable networks: etc.
 | 
			
		||||
      ports:
 | 
			
		||||
        - 5000:5000
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user