🤬
  • ■ ■ ■ ■ ■ ■
    changedetectionio/__init__.py
    skipped 781 lines
    782 782   
    783 783   return redirect(url_for('index'))
    784 784   
     785 + 
     786 + @app.route("/diff/image/<string:uuid>", methods=['GET'])
     787 + @login_required
     788 + def diff_image_history_page(uuid):
     789 + 
     790 + # More for testing, possible to return the first/only
     791 + if uuid == 'first':
     792 + uuid = list(datastore.data['watching'].keys()).pop()
     793 + 
     794 + extra_stylesheets = [url_for('static_content', group='styles', filename='diff.css')]
     795 + try:
     796 + watch = datastore.data['watching'][uuid]
     797 + except KeyError:
     798 + flash("No history found for the specified link, bad link?", "error")
     799 + return redirect(url_for('index'))
     800 + 
     801 + history = watch.history
     802 + dates = list(history.keys())
     803 + 
     804 + if len(dates) < 2:
     805 + flash("Not enough saved change detection snapshots to produce a report.", "error")
     806 + return redirect(url_for('index'))
     807 + 
     808 + previous_version = dates[-2]
     809 + 
     810 + output = render_template("diff-image.html",
     811 + watch=watch,
     812 + extra_stylesheets=extra_stylesheets,
     813 + versions=dates[:-1], # All except current/last
     814 + uuid=uuid,
     815 + newest_version_timestamp=dates[-1],
     816 + current_previous_version=str(previous_version),
     817 + current_diff_url=watch['url'],
     818 + extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']),
     819 + left_sticky=True,
     820 + last_error=watch['last_error'],
     821 + last_error_text=watch.get_error_text(),
     822 + last_error_screenshot=watch.get_error_snapshot()
     823 + )
     824 + return output
     825 + 
     826 + 
    785 827   @app.route("/diff/<string:uuid>", methods=['GET'])
    786 828   @login_required
    787 829   def diff_history_page(uuid):
    skipped 159 lines
    947 989   
    948 990   return output
    949 991   
     992 + @app.route("/preview/image/<string:uuid>/<string:history_timestamp>")
     993 + def render_single_image(uuid, history_timestamp):
     994 + 
     995 + watch = datastore.data['watching'].get(uuid)
     996 + dates = list(watch.history.keys())
     997 + 
     998 + 
     999 + if not history_timestamp or history_timestamp == 'None':
     1000 + history_timestamp = dates[-2]
     1001 + 
     1002 + 
     1003 + filename = watch.history[history_timestamp]
     1004 + with open(filename, 'rb') as f:
     1005 + img = f.read()
     1006 + 
     1007 + response = make_response(img)
     1008 + 
     1009 + response.headers['Content-type'] = 'image/png'
     1010 + response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
     1011 + response.headers['Pragma'] = 'no-cache'
     1012 + response.headers['Expires'] = 0
     1013 + 
     1014 + return response
     1015 + 
     1016 + 
     1017 + 
     1018 + # Diff renderer for images
     1019 + # Renders the diff which includes the red box around what changes
     1020 + # We always compare the newest against whatever compare_date we are given
     1021 + @app.route("/diff/image/<string:uuid>/<string:compare_date>")
     1022 + def render_diff_image(uuid, compare_date):
     1023 + from changedetectionio import image_diff
     1024 + 
     1025 + from flask import make_response
     1026 + watch = datastore.data['watching'].get(uuid)
     1027 + 
     1028 + dates = list(watch.history.keys())
     1029 + if len(dates) < 2:
     1030 + flash("Not enough saved change detection snapshots to produce a report.", "error")
     1031 + return redirect(url_for('index'))
     1032 + 
     1033 + if not compare_date or compare_date == 'None':
     1034 + compare_date = dates[-2]
     1035 + 
     1036 + new_img = watch.history[watch.newest_history_key]
     1037 + prev_img = watch.history[compare_date]
     1038 + 
     1039 + img = image_diff.render_diff(new_img, prev_img)
     1040 + 
     1041 + resp = make_response(img)
     1042 + resp.headers['Content-Type'] = 'image/jpeg'
     1043 + return resp
     1044 + 
     1045 + 
     1046 + 
    950 1047   @app.route("/settings/notification-logs", methods=['GET'])
    951 1048   @login_required
    952 1049   def notification_logs():
    skipped 142 lines
    1095 1192   return redirect(url_for('index'))
    1096 1193   
    1097 1194   url = request.form.get('url').strip()
     1195 + fetch_processor =request.form.get('fetch_processor').strip()
    1098 1196   if datastore.url_exists(url):
    1099 1197   flash('The URL {} already exists'.format(url), "error")
    1100 1198   return redirect(url_for('index'))
    1101 1199   
    1102 1200   add_paused = request.form.get('edit_and_watch_submit_button') != None
    1103  - new_uuid = datastore.add_watch(url=url, tag=request.form.get('tag').strip(), extras={'paused': add_paused})
     1201 + new_uuid = datastore.add_watch(url=url,
     1202 + tag=request.form.get('tag').strip(),
     1203 + extras={'paused': add_paused, 'fetch_processor': fetch_processor}
     1204 + )
    1104 1205   
    1105 1206   
    1106 1207   if not add_paused and new_uuid:
    skipped 134 lines
    1241 1342   
    1242 1343   return redirect(url_for('index'))
    1243 1344   
    1244  - @app.route("/api/share-url", methods=['GET'])
     1345 + @app.route("/api/r-url", methods=['GET'])
    1245 1346   @login_required
    1246 1347   def form_share_put_watch():
    1247 1348   """Given a watch UUID, upload the info and return a share-link
    skipped 217 lines
  • ■ ■ ■ ■ ■ ■
    changedetectionio/image_diff.py
     1 +from skimage.metrics import structural_similarity as compare_ssim
     2 +import argparse
     3 +import imutils
     4 +import cv2
     5 + 
     6 +# From https://www.pyimagesearch.com/2017/06/19/image-difference-with-opencv-and-python/
     7 +def render_diff(fpath_imageA, fpath_imageB):
     8 + 
     9 + imageA = cv2.imread(fpath_imageA)
     10 + imageB = cv2.imread(fpath_imageB)
     11 + 
     12 + # convert the images to grayscale
     13 + grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
     14 + grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
     15 + 
     16 + # compute the Structural Similarity Index (SSIM) between the two
     17 + # images, ensuring that the difference image is returned
     18 + (score, diff) = compare_ssim(grayA, grayB, full=True)
     19 + diff = (diff * 255).astype("uint8")
     20 + print("SSIM: {}".format(score))
     21 + 
     22 + # threshold the difference image, followed by finding contours to
     23 + # obtain the regions of the two input images that differ
     24 + thresh = cv2.threshold(diff, 0, 255,
     25 + cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
     26 + cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
     27 + cv2.CHAIN_APPROX_SIMPLE)
     28 + cnts = imutils.grab_contours(cnts)
     29 + 
     30 + # loop over the contours
     31 + for c in cnts:
     32 + # compute the bounding box of the contour and then draw the
     33 + # bounding box on both input images to represent where the two
     34 + # images differ
     35 + (x, y, w, h) = cv2.boundingRect(c)
     36 + cv2.rectangle(imageA, (x, y), (x + w, y + h), (0, 0, 255), 2)
     37 + cv2.rectangle(imageB, (x, y), (x + w, y + h), (0, 0, 255), 2)
     38 + 
     39 + #return cv2.imencode('.jpg', imageB)[1].tobytes()
     40 + return cv2.imencode('.jpg', imageA)[1].tobytes()
     41 + 
  • ■ ■ ■ ■
    changedetectionio/model/Watch.py
    skipped 26 lines
    27 27   'extract_text': [], # Extract text by regex after filters
    28 28   'extract_title_as_title': False,
    29 29   'fetch_backend': None,
    30  - 'fetch_processor': None, # default None, json_html_plaintext, image
     30 + 'fetch_processor': 'json_html_plaintext', # json_html_plaintext, image
    31 31   'filter_failure_notification_send': strtobool(os.getenv('FILTER_FAILURE_NOTIFICATION_SEND_DEFAULT', 'True')),
    32 32   'headers': {}, # Extra headers to send
    33 33   'ignore_text': [], # List of text to ignore when calculating the comparison checksum
    skipped 229 lines
  • ■ ■ ■ ■ ■ ■
    changedetectionio/templates/diff-image.html
     1 +{% extends 'base.html' %}
     2 + 
     3 +{% block content %}
     4 + 
     5 +<div id="settings">
     6 + <h1>Differences</h1>
     7 + <form class="pure-form " action="" method="GET">
     8 + <fieldset>
     9 + {% if versions|length >= 1 %}
     10 + <label for="diff-version">Compare newest (<span id="current-v-date"></span>) with</label>
     11 + <select id="diff-version" name="previous_version">
     12 + {% for version in versions %}
     13 + <option value="{{version}}" {% if version== current_previous_version %} selected="" {% endif %}>
     14 + {{version}}
     15 + </option>
     16 + {% endfor %}
     17 + </select>
     18 + <button type="submit" class="pure-button pure-button-primary">Go</button>
     19 + {% endif %}
     20 + </fieldset>
     21 + </form>
     22 + 
     23 +</div>
     24 + 
     25 +<div id="diff-ui">
     26 + <img style="max-width: 100%" src="{{ url_for('render_diff_image', uuid=uuid, compare_date=current_previous_version) }}" />
     27 + <img style="max-width: 100%" src="{{ url_for('render_single_image', uuid=uuid, history_timestamp=current_previous_version) }}" />
     28 +</div>
     29 + 
     30 + 
     31 +<script type="text/javascript" src="{{url_for('static_content', group='js', filename='diff.js')}}"></script>
     32 + 
     33 +<script defer="">
     34 +window.onload = function() {
     35 + /* Set current version date as local time in the browser also */
     36 + var current_v = document.getElementById("current-v-date");
     37 + var dateObject = new Date({{ newest_version_timestamp }}*1000);
     38 + current_v.innerHTML=dateObject.toLocaleString();
     39 + 
     40 + /* Convert what is options from UTC time.time() to local browser time */
     41 + var diffList=document.getElementById("diff-version");
     42 + if (typeof(diffList) != 'undefined' && diffList != null) {
     43 + for (var option of diffList.options) {
     44 + var dateObject = new Date(option.value*1000);
     45 + option.label=dateObject.toLocaleString();
     46 + }
     47 + }
     48 +}
     49 +</script>
     50 + 
     51 +{% endblock %}
  • ■ ■ ■ ■ ■
    changedetectionio/templates/edit.html
    skipped 30 lines
    31 31   {% if 'text-filters-and-triggers' in enabled_tabs %}
    32 32   <li class="tab"><a href="#filters-and-triggers">Filters &amp; Triggers</a></li>
    33 33   {%endif%}
    34  - 
    35 34   <li class="tab"><a href="#notifications">Notifications</a></li>
    36 35   </ul>
    37 36   </div>
    skipped 301 lines
  • ■ ■ ■ ■ ■ ■
    changedetectionio/templates/preview-image.html
     1 +{% extends 'base.html' %}
     2 +{% block content %}
     3 +<div id="settings">
     4 + <h1>Preview</h1>
     5 + </div>
     6 + 
     7 +<div id="diff-ui">
     8 + <img style="max-width: 100%" src="{{ url_for('render_single_image', uuid=uuid, date=current_previous_version) }}" />
     9 +</div>
     10 + 
     11 +{% endblock %}
  • ■ ■ ■ ■ ■
    changedetectionio/templates/watch-overview.html
    skipped 115 lines
    116 116   class="recheck pure-button button-small pure-button-primary">{% if watch.uuid in queued_uuids %}Queued{% else %}Recheck{% endif %}</a>
    117 117   <a href="{{ url_for('edit_page', uuid=watch.uuid)}}" class="pure-button button-small pure-button-primary">Edit</a>
    118 118   {% if watch.history_n >= 2 %}
    119  - <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>
     119 + {% if watch.fetch_processor == "image" %}
     120 + <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>
     121 + {% else %}
     122 + <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>
     123 + {% endif %}
     124 + 
    120 125   {% else %}
    121 126   {% if watch.history_n == 1 or (watch.history_n ==0 and watch.error_text_ctime )%}
    122 127   <a href="{{ url_for('preview_page', uuid=watch.uuid)}}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary">Preview</a>
    skipped 30 lines
  • ■ ■ ■ ■ ■ ■
    requirements.txt
    skipped 45 lines
    46 46   
    47 47  imagehash ~= 4.3.0
    48 48  pillow
     49 +scikit-image
     50 +imutils
     51 +opencv-python
    49 52  python-magic
    50 53   
Please wait...
Page is in error, reload to recover