mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-09-26 23:35:51 +00:00
WIP
This commit is contained in:
@@ -782,6 +782,48 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
@app.route("/diff/image/<string:uuid>", methods=['GET'])
|
||||
@login_required
|
||||
def diff_image_history_page(uuid):
|
||||
|
||||
# More for testing, possible to return the first/only
|
||||
if uuid == 'first':
|
||||
uuid = list(datastore.data['watching'].keys()).pop()
|
||||
|
||||
extra_stylesheets = [url_for('static_content', group='styles', filename='diff.css')]
|
||||
try:
|
||||
watch = datastore.data['watching'][uuid]
|
||||
except KeyError:
|
||||
flash("No history found for the specified link, bad link?", "error")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
history = watch.history
|
||||
dates = list(history.keys())
|
||||
|
||||
if len(dates) < 2:
|
||||
flash("Not enough saved change detection snapshots to produce a report.", "error")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
previous_version = dates[-2]
|
||||
|
||||
output = render_template("diff-image.html",
|
||||
watch=watch,
|
||||
extra_stylesheets=extra_stylesheets,
|
||||
versions=dates[:-1], # All except current/last
|
||||
uuid=uuid,
|
||||
newest_version_timestamp=dates[-1],
|
||||
current_previous_version=str(previous_version),
|
||||
current_diff_url=watch['url'],
|
||||
extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']),
|
||||
left_sticky=True,
|
||||
last_error=watch['last_error'],
|
||||
last_error_text=watch.get_error_text(),
|
||||
last_error_screenshot=watch.get_error_snapshot()
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
@app.route("/diff/<string:uuid>", methods=['GET'])
|
||||
@login_required
|
||||
def diff_history_page(uuid):
|
||||
@@ -947,6 +989,61 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
return output
|
||||
|
||||
@app.route("/preview/image/<string:uuid>/<string:history_timestamp>")
|
||||
def render_single_image(uuid, history_timestamp):
|
||||
|
||||
watch = datastore.data['watching'].get(uuid)
|
||||
dates = list(watch.history.keys())
|
||||
|
||||
|
||||
if not history_timestamp or history_timestamp == 'None':
|
||||
history_timestamp = dates[-2]
|
||||
|
||||
|
||||
filename = watch.history[history_timestamp]
|
||||
with open(filename, 'rb') as f:
|
||||
img = f.read()
|
||||
|
||||
response = make_response(img)
|
||||
|
||||
response.headers['Content-type'] = 'image/png'
|
||||
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
response.headers['Expires'] = 0
|
||||
|
||||
return response
|
||||
|
||||
|
||||
|
||||
# Diff renderer for images
|
||||
# Renders the diff which includes the red box around what changes
|
||||
# We always compare the newest against whatever compare_date we are given
|
||||
@app.route("/diff/image/<string:uuid>/<string:compare_date>")
|
||||
def render_diff_image(uuid, compare_date):
|
||||
from changedetectionio import image_diff
|
||||
|
||||
from flask import make_response
|
||||
watch = datastore.data['watching'].get(uuid)
|
||||
|
||||
dates = list(watch.history.keys())
|
||||
if len(dates) < 2:
|
||||
flash("Not enough saved change detection snapshots to produce a report.", "error")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
if not compare_date or compare_date == 'None':
|
||||
compare_date = dates[-2]
|
||||
|
||||
new_img = watch.history[watch.newest_history_key]
|
||||
prev_img = watch.history[compare_date]
|
||||
|
||||
img = image_diff.render_diff(new_img, prev_img)
|
||||
|
||||
resp = make_response(img)
|
||||
resp.headers['Content-Type'] = 'image/jpeg'
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
@app.route("/settings/notification-logs", methods=['GET'])
|
||||
@login_required
|
||||
def notification_logs():
|
||||
@@ -1095,12 +1192,16 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
return redirect(url_for('index'))
|
||||
|
||||
url = request.form.get('url').strip()
|
||||
fetch_processor =request.form.get('fetch_processor').strip()
|
||||
if datastore.url_exists(url):
|
||||
flash('The URL {} already exists'.format(url), "error")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
add_paused = request.form.get('edit_and_watch_submit_button') != None
|
||||
new_uuid = datastore.add_watch(url=url, tag=request.form.get('tag').strip(), extras={'paused': add_paused})
|
||||
new_uuid = datastore.add_watch(url=url,
|
||||
tag=request.form.get('tag').strip(),
|
||||
extras={'paused': add_paused, 'fetch_processor': fetch_processor}
|
||||
)
|
||||
|
||||
|
||||
if not add_paused and new_uuid:
|
||||
@@ -1241,7 +1342,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@app.route("/api/share-url", methods=['GET'])
|
||||
@app.route("/api/r-url", methods=['GET'])
|
||||
@login_required
|
||||
def form_share_put_watch():
|
||||
"""Given a watch UUID, upload the info and return a share-link
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from skimage.metrics import structural_similarity as compare_ssim
|
||||
import argparse
|
||||
import imutils
|
||||
import cv2
|
||||
|
||||
# From https://www.pyimagesearch.com/2017/06/19/image-difference-with-opencv-and-python/
|
||||
def render_diff(fpath_imageA, fpath_imageB):
|
||||
|
||||
imageA = cv2.imread(fpath_imageA)
|
||||
imageB = cv2.imread(fpath_imageB)
|
||||
|
||||
# convert the images to grayscale
|
||||
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
|
||||
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# compute the Structural Similarity Index (SSIM) between the two
|
||||
# images, ensuring that the difference image is returned
|
||||
(score, diff) = compare_ssim(grayA, grayB, full=True)
|
||||
diff = (diff * 255).astype("uint8")
|
||||
print("SSIM: {}".format(score))
|
||||
|
||||
# threshold the difference image, followed by finding contours to
|
||||
# obtain the regions of the two input images that differ
|
||||
thresh = cv2.threshold(diff, 0, 255,
|
||||
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
|
||||
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
|
||||
cv2.CHAIN_APPROX_SIMPLE)
|
||||
cnts = imutils.grab_contours(cnts)
|
||||
|
||||
# loop over the contours
|
||||
for c in cnts:
|
||||
# compute the bounding box of the contour and then draw the
|
||||
# bounding box on both input images to represent where the two
|
||||
# images differ
|
||||
(x, y, w, h) = cv2.boundingRect(c)
|
||||
cv2.rectangle(imageA, (x, y), (x + w, y + h), (0, 0, 255), 2)
|
||||
cv2.rectangle(imageB, (x, y), (x + w, y + h), (0, 0, 255), 2)
|
||||
|
||||
#return cv2.imencode('.jpg', imageB)[1].tobytes()
|
||||
return cv2.imencode('.jpg', imageA)[1].tobytes()
|
||||
@@ -27,7 +27,7 @@ class model(dict):
|
||||
'extract_text': [], # Extract text by regex after filters
|
||||
'extract_title_as_title': False,
|
||||
'fetch_backend': None,
|
||||
'fetch_processor': None, # default None, json_html_plaintext, image
|
||||
'fetch_processor': 'json_html_plaintext', # json_html_plaintext, image
|
||||
'filter_failure_notification_send': strtobool(os.getenv('FILTER_FAILURE_NOTIFICATION_SEND_DEFAULT', 'True')),
|
||||
'headers': {}, # Extra headers to send
|
||||
'ignore_text': [], # List of text to ignore when calculating the comparison checksum
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="settings">
|
||||
<h1>Differences</h1>
|
||||
<form class="pure-form " action="" method="GET">
|
||||
<fieldset>
|
||||
{% if versions|length >= 1 %}
|
||||
<label for="diff-version">Compare newest (<span id="current-v-date"></span>) with</label>
|
||||
<select id="diff-version" name="previous_version">
|
||||
{% for version in versions %}
|
||||
<option value="{{version}}" {% if version== current_previous_version %} selected="" {% endif %}>
|
||||
{{version}}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit" class="pure-button pure-button-primary">Go</button>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="diff-ui">
|
||||
<img style="max-width: 100%" src="{{ url_for('render_diff_image', uuid=uuid, compare_date=current_previous_version) }}" />
|
||||
<img style="max-width: 100%" src="{{ url_for('render_single_image', uuid=uuid, history_timestamp=current_previous_version) }}" />
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='diff.js')}}"></script>
|
||||
|
||||
<script defer="">
|
||||
window.onload = function() {
|
||||
/* Set current version date as local time in the browser also */
|
||||
var current_v = document.getElementById("current-v-date");
|
||||
var dateObject = new Date({{ newest_version_timestamp }}*1000);
|
||||
current_v.innerHTML=dateObject.toLocaleString();
|
||||
|
||||
/* Convert what is options from UTC time.time() to local browser time */
|
||||
var diffList=document.getElementById("diff-version");
|
||||
if (typeof(diffList) != 'undefined' && diffList != null) {
|
||||
for (var option of diffList.options) {
|
||||
var dateObject = new Date(option.value*1000);
|
||||
option.label=dateObject.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@@ -31,7 +31,6 @@
|
||||
{% if 'text-filters-and-triggers' in enabled_tabs %}
|
||||
<li class="tab"><a href="#filters-and-triggers">Filters & Triggers</a></li>
|
||||
{%endif%}
|
||||
|
||||
<li class="tab"><a href="#notifications">Notifications</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<div id="settings">
|
||||
<h1>Preview</h1>
|
||||
</div>
|
||||
|
||||
<div id="diff-ui">
|
||||
<img style="max-width: 100%" src="{{ url_for('render_single_image', uuid=uuid, date=current_previous_version) }}" />
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -116,7 +116,12 @@
|
||||
class="recheck pure-button button-small pure-button-primary">{% if watch.uuid in queued_uuids %}Queued{% else %}Recheck{% endif %}</a>
|
||||
<a href="{{ url_for('edit_page', uuid=watch.uuid)}}" class="pure-button button-small pure-button-primary">Edit</a>
|
||||
{% if watch.history_n >= 2 %}
|
||||
<a href="{{ url_for('diff_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary diff-link">Diff</a>
|
||||
{% if watch.fetch_processor == "image" %}
|
||||
<a href="{{ url_for('diff_image_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary diff-link">Diff</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('diff_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary diff-link">Diff</a>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
{% if watch.history_n == 1 or (watch.history_n ==0 and watch.error_text_ctime )%}
|
||||
<a href="{{ url_for('preview_page', uuid=watch.uuid)}}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary">Preview</a>
|
||||
|
||||
@@ -46,4 +46,7 @@ werkzeug ~= 2.0.0
|
||||
|
||||
imagehash ~= 4.3.0
|
||||
pillow
|
||||
scikit-image
|
||||
imutils
|
||||
opencv-python
|
||||
python-magic
|
||||
|
||||
Reference in New Issue
Block a user