diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py
index 8ec6bb8d..07492851 100644
--- a/changedetectionio/__init__.py
+++ b/changedetectionio/__init__.py
@@ -175,6 +175,7 @@ def main():
# proxy_set_header Host "localhost";
# proxy_set_header X-Forwarded-Prefix /app;
+
if os.getenv('USE_X_SETTINGS'):
logger.info("USE_X_SETTINGS is ENABLED")
from werkzeug.middleware.proxy_fix import ProxyFix
diff --git a/changedetectionio/flask_app.py b/changedetectionio/flask_app.py
index 49880b5d..fdd16294 100644
--- a/changedetectionio/flask_app.py
+++ b/changedetectionio/flask_app.py
@@ -1063,6 +1063,8 @@ def changedetection_app(config=None, datastore_o=None):
content = []
ignored_line_numbers = []
trigger_line_numbers = []
+ versions = []
+ timestamp = None
# More for testing, possible to return the first/only
if uuid == 'first':
@@ -1082,57 +1084,53 @@ def changedetection_app(config=None, datastore_o=None):
if (watch.get('fetch_backend') == 'system' and system_uses_webdriver) or watch.get('fetch_backend') == 'html_webdriver' or watch.get('fetch_backend', '').startswith('extra_browser_'):
is_html_webdriver = True
- # Never requested successfully, but we detected a fetch error
if datastore.data['watching'][uuid].history_n == 0 and (watch.get_error_text() or watch.get_error_snapshot()):
flash("Preview unavailable - No fetch/check completed or triggers not reached", "error")
- output = render_template("preview.html",
- content=content,
- history_n=watch.history_n,
- extra_stylesheets=extra_stylesheets,
-# current_diff_url=watch['url'],
- watch=watch,
- uuid=uuid,
- is_html_webdriver=is_html_webdriver,
- last_error=watch['last_error'],
- last_error_text=watch.get_error_text(),
- last_error_screenshot=watch.get_error_snapshot())
- return output
+ else:
+ # So prepare the latest preview or not
+ preferred_version = request.args.get('version')
+ versions = list(watch.history.keys())
+ timestamp = versions[-1]
+ if preferred_version and preferred_version in versions:
+ timestamp = preferred_version
- timestamp = list(watch.history.keys())[-1]
- try:
- tmp = watch.get_history_snapshot(timestamp).splitlines()
-
- # Get what needs to be highlighted
- ignore_rules = watch.get('ignore_text', []) + datastore.data['settings']['application']['global_ignore_text']
-
- # .readlines will keep the \n, but we will parse it here again, in the future tidy this up
- ignored_line_numbers = html_tools.strip_ignore_text(content="\n".join(tmp),
- wordlist=ignore_rules,
- mode='line numbers'
- )
-
- trigger_line_numbers = html_tools.strip_ignore_text(content="\n".join(tmp),
- wordlist=watch['trigger_text'],
- mode='line numbers'
- )
- # Prepare the classes and lines used in the template
- i=0
- for l in tmp:
- classes=[]
- i+=1
- if i in ignored_line_numbers:
- classes.append('ignored')
- if i in trigger_line_numbers:
- classes.append('triggered')
- content.append({'line': l, 'classes': ' '.join(classes)})
+ try:
+ versions = list(watch.history.keys())
+ tmp = watch.get_history_snapshot(timestamp).splitlines()
+
+ # Get what needs to be highlighted
+ ignore_rules = watch.get('ignore_text', []) + datastore.data['settings']['application']['global_ignore_text']
+
+ # .readlines will keep the \n, but we will parse it here again, in the future tidy this up
+ ignored_line_numbers = html_tools.strip_ignore_text(content="\n".join(tmp),
+ wordlist=ignore_rules,
+ mode='line numbers'
+ )
+
+ trigger_line_numbers = html_tools.strip_ignore_text(content="\n".join(tmp),
+ wordlist=watch['trigger_text'],
+ mode='line numbers'
+ )
+ # Prepare the classes and lines used in the template
+ i=0
+ for l in tmp:
+ classes=[]
+ i+=1
+ if i in ignored_line_numbers:
+ classes.append('ignored')
+ if i in trigger_line_numbers:
+ classes.append('triggered')
+ content.append({'line': l, 'classes': ' '.join(classes)})
- except Exception as e:
- content.append({'line': f"File doesnt exist or unable to read timestamp {timestamp}", 'classes': ''})
+ except Exception as e:
+ content.append({'line': f"File doesnt exist or unable to read timestamp {timestamp}", 'classes': ''})
output = render_template("preview.html",
content=content,
+ current_version=timestamp,
history_n=watch.history_n,
extra_stylesheets=extra_stylesheets,
+ extra_title=f" - Diff - {watch.label} @ {timestamp}",
ignored_line_numbers=ignored_line_numbers,
triggered_line_numbers=trigger_line_numbers,
current_diff_url=watch['url'],
@@ -1142,7 +1140,10 @@ def changedetection_app(config=None, datastore_o=None):
is_html_webdriver=is_html_webdriver,
last_error=watch['last_error'],
last_error_text=watch.get_error_text(),
- last_error_screenshot=watch.get_error_snapshot())
+ last_error_screenshot=watch.get_error_snapshot(),
+ versions=versions
+ )
+
return output
diff --git a/changedetectionio/static/js/diff-overview.js b/changedetectionio/static/js/diff-overview.js
index 767cf6e1..95e6dd7a 100644
--- a/changedetectionio/static/js/diff-overview.js
+++ b/changedetectionio/static/js/diff-overview.js
@@ -8,6 +8,13 @@ $(document).ready(function () {
}
})
+ $('.needs-localtime').each(function () {
+ for (var option of this.options) {
+ var dateObject = new Date(option.value * 1000);
+ option.label = dateObject.toLocaleString(undefined, {dateStyle: "full", timeStyle: "medium"});
+ }
+ });
+
// Load it when the #screenshot tab is in use, so we dont give a slow experience when waiting for the text diff to load
window.addEventListener('hashchange', function (e) {
toggle(location.hash);
diff --git a/changedetectionio/static/js/diff-render.js b/changedetectionio/static/js/diff-render.js
index 53f1d68f..ea69d364 100644
--- a/changedetectionio/static/js/diff-render.js
+++ b/changedetectionio/static/js/diff-render.js
@@ -79,12 +79,7 @@ $(document).ready(function () {
$('#jump-next-diff').click();
}
- $('.needs-localtime').each(function () {
- for (var option of this.options) {
- var dateObject = new Date(option.value * 1000);
- option.label = dateObject.toLocaleString(undefined, {dateStyle: "full", timeStyle: "medium"});
- }
- })
+
onDiffTypeChange(
document.querySelector('#settings [name="diff_type"]:checked'),
);
diff --git a/changedetectionio/static/js/preview.js b/changedetectionio/static/js/preview.js
new file mode 100644
index 00000000..a9895cb2
--- /dev/null
+++ b/changedetectionio/static/js/preview.js
@@ -0,0 +1,49 @@
+function redirect_to_version(version) {
+ var currentUrl = window.location.href;
+ var baseUrl = currentUrl.split('?')[0]; // Base URL without query parameters
+ var anchor = '';
+
+ // Check if there is an anchor
+ if (baseUrl.indexOf('#') !== -1) {
+ anchor = baseUrl.substring(baseUrl.indexOf('#'));
+ baseUrl = baseUrl.substring(0, baseUrl.indexOf('#'));
+ }
+ window.location.href = baseUrl + '?version=' + version + anchor;
+}
+
+document.addEventListener('keydown', function (event) {
+ var selectElement = document.getElementById('preview-version');
+ if (selectElement) {
+ var selectedOption = selectElement.querySelector('option:checked');
+ if (selectedOption) {
+ if (event.key === 'ArrowLeft') {
+ if (selectedOption.previousElementSibling) {
+ redirect_to_version(selectedOption.previousElementSibling.value);
+ }
+ } else if (event.key === 'ArrowRight') {
+ if (selectedOption.nextElementSibling) {
+ redirect_to_version(selectedOption.nextElementSibling.value);
+ }
+ }
+ }
+ }
+});
+
+
+document.getElementById('preview-version').addEventListener('change', function () {
+ redirect_to_version(this.value);
+});
+
+var selectElement = document.getElementById('preview-version');
+if (selectElement) {
+ var selectedOption = selectElement.querySelector('option:checked');
+ if (selectedOption) {
+ if (selectedOption.previousElementSibling) {
+ document.getElementById('btn-previous').href = "?version=" + selectedOption.previousElementSibling.value;
+ }
+ if (selectedOption.nextElementSibling) {
+ document.getElementById('btn-next').href = "?version=" + selectedOption.nextElementSibling.value;
+ }
+
+ }
+}
diff --git a/changedetectionio/templates/preview.html b/changedetectionio/templates/preview.html
index 5cc61bed..8bc231e1 100644
--- a/changedetectionio/templates/preview.html
+++ b/changedetectionio/templates/preview.html
@@ -1,72 +1,103 @@
{% extends 'base.html' %}
{% block content %}
-
+
+
+
+ {% if versions|length >= 2 %}
+
{% endif %}
- const highlight_submit_ignore_url="{{url_for('highlight_submit_ignore_url', uuid=uuid)}}";
-
-
-
-
-
-
-
-
{{watch.error_text_ctime|format_seconds_ago}} seconds ago
-
+
+
+
+
+
+
{{ watch.error_text_ctime|format_seconds_ago }} seconds ago
+
{{ last_error_text }}
-
+
-
-
{{watch.snapshot_error_screenshot_ctime|format_seconds_ago}} seconds ago
-
-
+
+
{{ watch.snapshot_error_screenshot_ctime|format_seconds_ago }} seconds ago
+
+
+
-
-
{{watch.snapshot_text_ctime|format_timestamp_timeago}}
-
Grey lines are ignored Blue lines are triggers Pro-tip: Highlight text to add to ignore filters
+
+
{{ watch.snapshot_text_ctime|format_timestamp_timeago }}
+
Grey lines are ignored Blue lines are triggers
+
Pro-tip: Highlight text to add to ignore filters
-
-
-
-
- {% for row in content %}
- {{row.line}}
- {% endfor %}
- |
-
-
-
-
+
+
+
+
+ {% for row in content %}
+ {{ row.line }}
+ {% endfor %}
+ |
+
+
+
+
-
-
- For now, Differences are performed on text, not graphically, only the latest screenshot is available.
-
-
- {% if is_html_webdriver %}
- {% if screenshot %}
-
{{watch.snapshot_screenshot_ctime|format_timestamp_timeago}}
-
- {% else %}
- No screenshot available just yet! Try rechecking the page.
- {% endif %}
- {% else %}
-
Screenshot requires Playwright/WebDriver enabled
- {% endif %}
-
-
+
+
+ For now, Differences are performed on text, not graphically, only the latest screenshot is available.
+
+
+ {% if is_html_webdriver %}
+ {% if screenshot %}
+
{{ watch.snapshot_screenshot_ctime|format_timestamp_timeago }}
+
+ {% else %}
+ No screenshot available just yet! Try rechecking the page.
+ {% endif %}
+ {% else %}
+
Screenshot requires Playwright/WebDriver enabled
+ {% endif %}
+
+
{% endblock %}