Compare commits

...

9 Commits

10 changed files with 128 additions and 20 deletions

View File

@@ -21,7 +21,7 @@ Steps to reproduce the behavior:
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
! ALWAYS INCLUDE AN EXAMPLE URL WHERE IT IS POSSIBLE TO RE-CREATE THE ISSUE ! ! ALWAYS INCLUDE AN EXAMPLE URL WHERE IT IS POSSIBLE TO RE-CREATE THE ISSUE - USE THE 'SHARE WATCH' FEATURE AND PASTE IN THE SHARE-LINK!
**Expected behavior** **Expected behavior**
A clear and concise description of what you expected to happen. A clear and concise description of what you expected to happen.

View File

@@ -20,6 +20,7 @@ from copy import deepcopy
from threading import Event from threading import Event
import flask_login import flask_login
import logging
import pytz import pytz
import timeago import timeago
from feedgen.feed import FeedGenerator from feedgen.feed import FeedGenerator
@@ -351,9 +352,8 @@ def changedetection_app(config=None, datastore_o=None):
latest_fname = watch.history[dates[-1]] latest_fname = watch.history[dates[-1]]
html_diff = diff.render_diff(prev_fname, latest_fname, include_equal=False, line_feed_sep="</br>") html_diff = diff.render_diff(prev_fname, latest_fname, include_equal=False, line_feed_sep="</br>")
fe.description(description="<![CDATA[" fe.content(content="<html><body><h4>{}</h4>{}</body></html>".format(watch_title, html_diff),
"<html><body><h4>{}</h4>{}</body></html>" type='CDATA')
"]]>".format(watch_title, html_diff))
fe.guid(guid, permalink=False) fe.guid(guid, permalink=False)
dt = datetime.datetime.fromtimestamp(int(watch.newest_history_key)) dt = datetime.datetime.fromtimestamp(int(watch.newest_history_key))
@@ -458,6 +458,19 @@ def changedetection_app(config=None, datastore_o=None):
return 'OK' return 'OK'
@app.route("/scrub/<string:uuid>", methods=['GET'])
@login_required
def scrub_watch(uuid):
try:
datastore.scrub_watch(uuid)
except KeyError:
flash('Watch not found', 'error')
else:
flash("Scrubbed watch {}".format(uuid))
return redirect(url_for('index'))
@app.route("/scrub", methods=['GET', 'POST']) @app.route("/scrub", methods=['GET', 'POST'])
@login_required @login_required
def scrub_page(): def scrub_page():
@@ -809,7 +822,13 @@ def changedetection_app(config=None, datastore_o=None):
screenshot_url = datastore.get_screenshot(uuid) screenshot_url = datastore.get_screenshot(uuid)
output = render_template("diff.html", watch_a=watch, system_uses_webdriver = datastore.data['settings']['application']['fetch_backend'] == 'html_webdriver'
is_html_webdriver = True if watch.get('fetch_backend') == 'html_webdriver' or (
watch.get('fetch_backend', None) is None and system_uses_webdriver) else False
output = render_template("diff.html",
watch_a=watch,
newest=newest_version_file_contents, newest=newest_version_file_contents,
previous=previous_version_file_contents, previous=previous_version_file_contents,
extra_stylesheets=extra_stylesheets, extra_stylesheets=extra_stylesheets,
@@ -820,7 +839,8 @@ def changedetection_app(config=None, datastore_o=None):
current_diff_url=watch['url'], current_diff_url=watch['url'],
extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']), extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']),
left_sticky=True, left_sticky=True,
screenshot=screenshot_url) screenshot=screenshot_url,
is_html_webdriver=is_html_webdriver)
return output return output
@@ -881,6 +901,11 @@ def changedetection_app(config=None, datastore_o=None):
content.append({'line': "No history found", 'classes': ''}) content.append({'line': "No history found", 'classes': ''})
screenshot_url = datastore.get_screenshot(uuid) screenshot_url = datastore.get_screenshot(uuid)
system_uses_webdriver = datastore.data['settings']['application']['fetch_backend'] == 'html_webdriver'
is_html_webdriver = True if watch.get('fetch_backend') == 'html_webdriver' or (
watch.get('fetch_backend', None) is None and system_uses_webdriver) else False
output = render_template("preview.html", output = render_template("preview.html",
content=content, content=content,
extra_stylesheets=extra_stylesheets, extra_stylesheets=extra_stylesheets,
@@ -889,8 +914,9 @@ def changedetection_app(config=None, datastore_o=None):
current_diff_url=watch['url'], current_diff_url=watch['url'],
screenshot=screenshot_url, screenshot=screenshot_url,
watch=watch, watch=watch,
uuid=uuid) uuid=uuid,
is_html_webdriver=is_html_webdriver)
return output return output
@app.route("/settings/notification-logs", methods=['GET']) @app.route("/settings/notification-logs", methods=['GET'])
@@ -898,7 +924,7 @@ def changedetection_app(config=None, datastore_o=None):
def notification_logs(): def notification_logs():
global notification_debug_log global notification_debug_log
output = render_template("notification-log.html", output = render_template("notification-log.html",
logs=notification_debug_log if len(notification_debug_log) else ["No errors or warnings detected"]) logs=notification_debug_log if len(notification_debug_log) else ["Notification logs are empty - no notifications sent yet."])
return output return output
@@ -1169,7 +1195,8 @@ def changedetection_app(config=None, datastore_o=None):
except Exception as e: except Exception as e:
flash("Could not share, something went wrong while communicating with the share server.", 'error') logging.error("Error sharing -{}".format(str(e)))
flash("Could not share, something went wrong while communicating with the share server - {}".format(str(e)), 'error')
# https://changedetection.io/share/VrMv05wpXyQa # https://changedetection.io/share/VrMv05wpXyQa
# in the browser - should give you a nice info page - wtf # in the browser - should give you a nice info page - wtf
@@ -1217,6 +1244,9 @@ def check_for_new_version():
def notification_runner(): def notification_runner():
global notification_debug_log global notification_debug_log
from datetime import datetime
import json
while not app.config.exit.is_set(): while not app.config.exit.is_set():
try: try:
# At the moment only one thread runs (single runner) # At the moment only one thread runs (single runner)
@@ -1225,13 +1255,16 @@ def notification_runner():
time.sleep(1) time.sleep(1)
else: else:
# Process notifications
now = datetime.now()
try: try:
from changedetectionio import notification from changedetectionio import notification
notification.process_notification(n_object, datastore) notification.process_notification(n_object, datastore)
except Exception as e: except Exception as e:
print("Watch URL: {} Error {}".format(n_object['watch_url'], str(e))) logging.error("Watch URL: {} Error {}".format(n_object['watch_url'], str(e)))
# UUID wont be present when we submit a 'test' from the global settings # UUID wont be present when we submit a 'test' from the global settings
if 'uuid' in n_object: if 'uuid' in n_object:
@@ -1241,9 +1274,10 @@ def notification_runner():
log_lines = str(e).splitlines() log_lines = str(e).splitlines()
notification_debug_log += log_lines notification_debug_log += log_lines
# Trim the log length # Process notifications
notification_debug_log = notification_debug_log[-100:] notification_debug_log+= ["{} - SENDING {}".format(now.strftime("%Y/%m/%d %H:%M:%S,000"), json.dumps(n_object))]
# 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. # Thread runner to check every minute, look for new watches to feed into the Queue.
def ticker_thread_check_time_launch_checks(): def ticker_thread_check_time_launch_checks():

View File

@@ -97,6 +97,8 @@ def process_notification(n_object, datastore):
if log_value and 'WARNING' in log_value or 'ERROR' in log_value: if log_value and 'WARNING' in log_value or 'ERROR' in log_value:
raise Exception(log_value) raise Exception(log_value)
# Notification title + body content parameters get created here. # Notification title + body content parameters get created here.
def create_notification_parameters(n_object, datastore): def create_notification_parameters(n_object, datastore):
from copy import deepcopy from copy import deepcopy

View File

@@ -0,0 +1,17 @@
$(document).ready(function () {
// 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);
}, false);
toggle(location.hash);
function toggle(hash_name) {
if (hash_name === '#screenshot') {
$("img#screenshot-img").attr('src', screenshot_url);
$("#settings").hide();
} else {
$("#settings").show();
}
}
});

View File

@@ -254,12 +254,23 @@ class ChangeDetectionStore:
def scrub_watch(self, uuid): def scrub_watch(self, uuid):
import pathlib import pathlib
self.__data['watching'][uuid].update({'history': {}, 'last_checked': 0, 'last_changed': 0, 'previous_md5': False}) self.__data['watching'][uuid].update(
self.needs_write_urgent = True {'last_checked': 0,
'last_changed': 0,
'last_viewed': 0,
'previous_md5': False,
'last_notification_error': False,
'last_error': False})
for item in pathlib.Path(self.datastore_path).rglob(uuid+"/*.txt"): # JSON Data, Screenshots, Textfiles (history index and snapshots), HTML in the future etc
for item in pathlib.Path(os.path.join(self.datastore_path, uuid)).rglob("*.*"):
unlink(item) unlink(item)
# Force the attr to recalculate
bump = self.__data['watching'][uuid].history
self.needs_write_urgent = True
def add_watch(self, url, tag="", extras=None, write_to_disk_now=True): def add_watch(self, url, tag="", extras=None, write_to_disk_now=True):
if extras is None: if extras is None:

View File

@@ -14,7 +14,7 @@
<li>Use <a target=_new href="https://github.com/caronc/apprise">AppRise URLs</a> for notification to just about any service! <i><a target=_new href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes">Please read the notification services wiki here for important configuration notes</a></i>.</li> <li>Use <a target=_new href="https://github.com/caronc/apprise">AppRise URLs</a> for notification to just about any service! <i><a target=_new href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes">Please read the notification services wiki here for important configuration notes</a></i>.</li>
<li><code>discord://</code> only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li> <li><code>discord://</code> only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li>
<li><code>tgram://</code> bots cant send messages to other bots, so you should specify chat ID of non-bot user.</li> <li><code>tgram://</code> bots cant send messages to other bots, so you should specify chat ID of non-bot user.</li>
<li>Go here for <a href="{{url_for('notification_logs')}}">notification debug logs</a></li>
</ul> </ul>
</div> </div>
<br/> <br/>
@@ -22,6 +22,7 @@
{% if emailprefix %} {% if emailprefix %}
<a id="add-email-helper" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Add email</a> <a id="add-email-helper" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Add email</a>
{% endif %} {% endif %}
<a href="{{url_for('notification_logs')}}" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Notification debug logs</a>
</div> </div>
<div id="notification-customisation" class="pure-control-group"> <div id="notification-customisation" class="pure-control-group">
<div class="pure-control-group"> <div class="pure-control-group">

View File

@@ -1,6 +1,11 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<script>
const screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid)}}";
</script>
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
<div id="settings"> <div id="settings">
<h1>Differences</h1> <h1>Differences</h1>
<form class="pure-form " action="" method="GET"> <form class="pure-form " action="" method="GET">
@@ -39,6 +44,7 @@
<div class="tabs"> <div class="tabs">
<ul> <ul>
<li class="tab" id="default-tab"><a href="#text">Text</a></li> <li class="tab" id="default-tab"><a href="#text">Text</a></li>
<li class="tab" id="screenshot-tab"><a href="#screenshot">Screenshot</a></li>
</ul> </ul>
</div> </div>
@@ -60,6 +66,21 @@
</table> </table>
Diff algorithm from the amazing <a href="https://github.com/kpdecker/jsdiff">github.com/kpdecker/jsdiff</a> Diff algorithm from the amazing <a href="https://github.com/kpdecker/jsdiff">github.com/kpdecker/jsdiff</a>
</div> </div>
<div class="tab-pane-inner" id="screenshot">
<div class="tip">
For now, Differences are performed on text, not graphically, only the latest screenshot is available.
</div>
</br>
{% if is_html_webdriver %}
{% if screenshot %}
<img style="max-width: 80%" id="screenshot-img" alt="Current screenshot from most recent request"/>
{% else %}
No screenshot available just yet! Try rechecking the page.
{% endif %}
{% else %}
<strong>Screenshot requires Playwright/WebDriver enabled</strong>
{% endif %}
</div>
</div> </div>

View File

@@ -259,6 +259,8 @@ nav
<a href="{{url_for('form_delete', uuid=uuid)}}" <a href="{{url_for('form_delete', uuid=uuid)}}"
class="pure-button button-small button-error ">Delete</a> class="pure-button button-small button-error ">Delete</a>
<a href="{{url_for('scrub_watch', uuid=uuid)}}"
class="pure-button button-small button-error ">Scrub</a>
<a href="{{url_for('form_clone', uuid=uuid)}}" <a href="{{url_for('form_clone', uuid=uuid)}}"
class="pure-button button-small ">Create Copy</a> class="pure-button button-small ">Create Copy</a>
</div> </div>

View File

@@ -4,7 +4,7 @@
<div class="edit-form"> <div class="edit-form">
<div class="inner"> <div class="inner">
<h4 style="margin-top: 0px;">The following issues were detected when sending notifications</h4> <h4 style="margin-top: 0px;">Notification debug log</h4>
<div id="notification-error-log"> <div id="notification-error-log">
<ul style="font-size: 80%; margin:0px; padding: 0 0 0 7px"> <ul style="font-size: 80%; margin:0px; padding: 0 0 0 7px">
{% for log in logs|reverse %} {% for log in logs|reverse %}

View File

@@ -1,6 +1,10 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<script>
const screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid)}}";
</script>
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
<div id="settings"> <div id="settings">
<h1>Current - {{watch.last_checked|format_timestamp_timeago}}</h1> <h1>Current - {{watch.last_checked|format_timestamp_timeago}}</h1>
@@ -10,6 +14,7 @@
<div class="tabs"> <div class="tabs">
<ul> <ul>
<li class="tab" id="default-tab"><a href="#text">Text</a></li> <li class="tab" id="default-tab"><a href="#text">Text</a></li>
<li class="tab" id="screenshot-tab"><a href="#screenshot">Screenshot</a></li>
</ul> </ul>
</div> </div>
@@ -28,5 +33,20 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="tab-pane-inner" id="screenshot">
<div class="tip">
For now, Differences are performed on text, not graphically, only the latest screenshot is available.
</div>
</br>
{% if is_html_webdriver %}
{% if screenshot %}
<img style="max-width: 80%" id="screenshot-img" alt="Current screenshot from most recent request"/>
{% else %}
No screenshot available just yet! Try rechecking the page.
{% endif %}
{% else %}
<strong>Screenshot requires Playwright/WebDriver enabled</strong>
{% endif %}
</div>
</div> </div>
{% endblock %} {% endblock %}