diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index 8b3e8e39..67d2f0be 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -700,6 +700,7 @@ def changedetection_app(config=None, datastore_o=None): 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=watch.has_extra_headers_file or datastore.has_extra_headers_file, is_html_webdriver=is_html_webdriver, jq_support=jq_support, playwright_enabled=os.getenv('PLAYWRIGHT_DRIVER_URL', False), @@ -1444,6 +1445,7 @@ def check_for_new_version(): # Check daily app.config.exit.wait(86400) + def notification_runner(): global notification_debug_log from datetime import datetime diff --git a/changedetectionio/model/App.py b/changedetectionio/model/App.py index 7c7cac9f..54580b3d 100644 --- a/changedetectionio/model/App.py +++ b/changedetectionio/model/App.py @@ -49,3 +49,15 @@ class model(dict): def __init__(self, *arg, **kw): super(model, self).__init__(*arg, **kw) self.update(self.base_config) + + +def parse_headers_from_text_file(filepath): + headers = {} + with open(filepath, 'r') as f: + for l in f.readlines(): + l = l.strip() + if not l.startswith('#') and ':' in l: + (k, v) = l.split(':') + headers[k.strip()] = v.strip() + + return headers \ No newline at end of file diff --git a/changedetectionio/model/Watch.py b/changedetectionio/model/Watch.py index ca654d04..77c07497 100644 --- a/changedetectionio/model/Watch.py +++ b/changedetectionio/model/Watch.py @@ -473,6 +473,40 @@ class model(dict): # None is set return False + @property + def has_extra_headers_file(self): + if os.path.isfile(os.path.join(self.watch_data_dir, 'headers.txt')): + return True + + for f in self.all_tags: + fname = "headers-"+re.sub(r'[\W_]', '', f).lower().strip() + ".txt" + filepath = os.path.join(self.__datastore_path, fname) + if os.path.isfile(filepath): + return True + + return False + + def get_all_headers(self): + from .App import parse_headers_from_text_file + headers = self.get('headers', {}).copy() + # Available headers on the disk could 'headers.txt' in the watch data dir + filepath = os.path.join(self.watch_data_dir, 'headers.txt') + try: + if os.path.isfile(filepath): + headers.update(parse_headers_from_text_file(filepath)) + except Exception as e: + print(f"ERROR reading headers.txt at {filepath}", str(e)) + + # Or each by tag, as tagname.txt in the main datadir + for f in self.all_tags: + fname = "headers-"+re.sub(r'[\W_]', '', f).lower().strip() + ".txt" + filepath = os.path.join(self.__datastore_path, fname) + try: + if os.path.isfile(filepath): + headers.update(parse_headers_from_text_file(filepath)) + except Exception as e: + print(f"ERROR reading headers.txt at {filepath}", str(e)) + return headers def get_last_fetched_before_filters(self): import brotli diff --git a/changedetectionio/processors/text_json_diff.py b/changedetectionio/processors/text_json_diff.py index cf85522a..f767703b 100644 --- a/changedetectionio/processors/text_json_diff.py +++ b/changedetectionio/processors/text_json_diff.py @@ -70,10 +70,9 @@ class perform_site_check(difference_detection_processor): # Unset any existing notification error update_obj = {'last_notification_error': False, 'last_error': False} - extra_headers = watch.get('headers', []) - # Tweak the base config with the per-watch ones - request_headers = deepcopy(self.datastore.data['settings']['headers']) + extra_headers = watch.get_all_headers() + request_headers = self.datastore.get_all_headers() request_headers.update(extra_headers) # https://github.com/psf/requests/issues/4525 diff --git a/changedetectionio/store.py b/changedetectionio/store.py index f69eb907..5e071ce5 100644 --- a/changedetectionio/store.py +++ b/changedetectionio/store.py @@ -3,7 +3,7 @@ from flask import ( ) from . model import App, Watch -from copy import deepcopy +from copy import deepcopy, copy from os import path, unlink from threading import Lock import json @@ -474,8 +474,6 @@ class ChangeDetectionStore: return proxy_list if len(proxy_list) else None - - def get_preferred_proxy_for_watch(self, uuid): """ Returns the preferred proxy by ID key @@ -507,6 +505,25 @@ class ChangeDetectionStore: return None + @property + def has_extra_headers_file(self): + filepath = os.path.join(self.datastore_path, 'headers.txt') + return os.path.isfile(filepath) + + def get_all_headers(self): + from .model.App import parse_headers_from_text_file + headers = copy(self.data['settings'].get('headers', {})) + + filepath = os.path.join(self.datastore_path, 'headers.txt') + try: + if os.path.isfile(filepath): + headers.update(parse_headers_from_text_file(filepath)) + except Exception as e: + print(f"ERROR reading headers.txt at {filepath}", str(e)) + + return headers + + # Run all updates # IMPORTANT - Each update could be run even when they have a new install and the schema is correct # So therefor - each `update_n` should be very careful about checking if it needs to actually run diff --git a/changedetectionio/templates/edit.html b/changedetectionio/templates/edit.html index 805c79c7..40b1101f 100644 --- a/changedetectionio/templates/edit.html +++ b/changedetectionio/templates/edit.html @@ -152,6 +152,15 @@ {{ render_field(form.headers, rows=5, placeholder="Example Cookie: foobar User-Agent: wonderbra 1.0") }} + +
+