mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2025-11-25 19:03:22 +00:00
Compare commits
10 Commits
3434-detec
...
3458-fixin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0a0ec9942 | ||
|
|
86befef0cb | ||
|
|
e4baca1127 | ||
|
|
bb61a35a54 | ||
|
|
4b9ae5a97c | ||
|
|
c8caa0662d | ||
|
|
f4e8d1963f | ||
|
|
45d5e961dc | ||
|
|
45f2863966 | ||
|
|
01c1ac4c0c |
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@@ -4,11 +4,13 @@ updates:
|
|||||||
directory: /
|
directory: /
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
"caronc/apprise":
|
|
||||||
versioning-strategy: "increase"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
groups:
|
groups:
|
||||||
all:
|
all:
|
||||||
patterns:
|
patterns:
|
||||||
- "*"
|
- "*"
|
||||||
|
- package-ecosystem: pip
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
allow:
|
||||||
|
- dependency-name: "apprise"
|
||||||
|
|||||||
4
.github/workflows/containers.yml
vendored
4
.github/workflows/containers.yml
vendored
@@ -95,7 +95,7 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ secrets.DOCKER_HUB_USERNAME }}/changedetection.io:dev,ghcr.io/${{ github.repository }}:dev
|
${{ secrets.DOCKER_HUB_USERNAME }}/changedetection.io:dev,ghcr.io/${{ github.repository }}:dev
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ jobs:
|
|||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
# Looks like this was disabled
|
# Looks like this was disabled
|
||||||
|
|||||||
4
.github/workflows/test-container-build.yml
vendored
4
.github/workflows/test-container-build.yml
vendored
@@ -38,8 +38,6 @@ jobs:
|
|||||||
dockerfile: ./Dockerfile
|
dockerfile: ./Dockerfile
|
||||||
- platform: linux/arm/v8
|
- platform: linux/arm/v8
|
||||||
dockerfile: ./Dockerfile
|
dockerfile: ./Dockerfile
|
||||||
- platform: linux/arm64/v8
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
# Alpine Dockerfile platforms (musl via alpine check)
|
# Alpine Dockerfile platforms (musl via alpine check)
|
||||||
- platform: linux/amd64
|
- platform: linux/amd64
|
||||||
dockerfile: ./.github/test/Dockerfile-alpine
|
dockerfile: ./.github/test/Dockerfile-alpine
|
||||||
@@ -76,5 +74,5 @@ jobs:
|
|||||||
file: ${{ matrix.dockerfile }}
|
file: ${{ matrix.dockerfile }}
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=min
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
|
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
|
||||||
|
|
||||||
__version__ = '0.50.14'
|
__version__ = '0.50.15'
|
||||||
|
|
||||||
from changedetectionio.strtobool import strtobool
|
from changedetectionio.strtobool import strtobool
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
|
|||||||
@@ -191,6 +191,12 @@ nav
|
|||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset class="pure-group">
|
||||||
|
{{ render_checkbox_field(form.application.form.strip_ignored_lines) }}
|
||||||
|
<span class="pure-form-message-inline">Remove any text that appears in the "Ignore text" from the output (otherwise its just ignored for change-detection)<br>
|
||||||
|
<i>Note:</i> Changing this will change the status of your existing watches, possibly trigger alerts etc.
|
||||||
|
</span>
|
||||||
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="api">
|
<div class="tab-pane-inner" id="api">
|
||||||
|
|||||||
@@ -759,6 +759,7 @@ class processor_text_json_diff_form(commonSettingsForm):
|
|||||||
check_unique_lines = BooleanField('Only trigger when unique lines appear in all history', default=False)
|
check_unique_lines = BooleanField('Only trigger when unique lines appear in all history', default=False)
|
||||||
remove_duplicate_lines = BooleanField('Remove duplicate lines of text', default=False)
|
remove_duplicate_lines = BooleanField('Remove duplicate lines of text', default=False)
|
||||||
sort_text_alphabetically = BooleanField('Sort text alphabetically', default=False)
|
sort_text_alphabetically = BooleanField('Sort text alphabetically', default=False)
|
||||||
|
strip_ignored_lines = TernaryNoneBooleanField('Strip ignored lines', default=None)
|
||||||
trim_text_whitespace = BooleanField('Trim whitespace before and after text', default=False)
|
trim_text_whitespace = BooleanField('Trim whitespace before and after text', default=False)
|
||||||
|
|
||||||
filter_text_added = BooleanField('Added lines', default=True)
|
filter_text_added = BooleanField('Added lines', default=True)
|
||||||
@@ -936,6 +937,7 @@ class globalSettingsApplicationForm(commonSettingsForm):
|
|||||||
removepassword_button = SubmitField('Remove password', render_kw={"class": "pure-button pure-button-primary"})
|
removepassword_button = SubmitField('Remove password', render_kw={"class": "pure-button pure-button-primary"})
|
||||||
render_anchor_tag_content = BooleanField('Render anchor tag content', default=False)
|
render_anchor_tag_content = BooleanField('Render anchor tag content', default=False)
|
||||||
shared_diff_access = BooleanField('Allow anonymous access to watch history page when password is enabled', default=False, validators=[validators.Optional()])
|
shared_diff_access = BooleanField('Allow anonymous access to watch history page when password is enabled', default=False, validators=[validators.Optional()])
|
||||||
|
strip_ignored_lines = BooleanField('Strip ignored lines')
|
||||||
rss_hide_muted_watches = BooleanField('Hide muted watches from RSS feed', default=True,
|
rss_hide_muted_watches = BooleanField('Hide muted watches from RSS feed', default=True,
|
||||||
validators=[validators.Optional()])
|
validators=[validators.Optional()])
|
||||||
filter_failure_notification_threshold_attempts = IntegerField('Number of times the filter can be missing before sending a notification',
|
filter_failure_notification_threshold_attempts = IntegerField('Number of times the filter can be missing before sending a notification',
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class model(dict):
|
|||||||
'rss_hide_muted_watches': True,
|
'rss_hide_muted_watches': True,
|
||||||
'schema_version' : 0,
|
'schema_version' : 0,
|
||||||
'shared_diff_access': False,
|
'shared_diff_access': False,
|
||||||
|
'strip_ignored_lines': False,
|
||||||
'tags': {}, #@todo use Tag.model initialisers
|
'tags': {}, #@todo use Tag.model initialisers
|
||||||
'timezone': None, # Default IANA timezone name
|
'timezone': None, # Default IANA timezone name
|
||||||
'webdriver_delay': None , # Extra delay in seconds before extracting text
|
'webdriver_delay': None , # Extra delay in seconds before extracting text
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class watch_base(dict):
|
|||||||
'proxy': None, # Preferred proxy connection
|
'proxy': None, # Preferred proxy connection
|
||||||
'remote_server_reply': None, # From 'server' reply header
|
'remote_server_reply': None, # From 'server' reply header
|
||||||
'sort_text_alphabetically': False,
|
'sort_text_alphabetically': False,
|
||||||
|
'strip_ignored_lines': None,
|
||||||
'subtractive_selectors': [],
|
'subtractive_selectors': [],
|
||||||
'tag': '', # Old system of text name for a tag, to be removed
|
'tag': '', # Old system of text name for a tag, to be removed
|
||||||
'tags': [], # list of UUIDs to App.Tags
|
'tags': [], # list of UUIDs to App.Tags
|
||||||
|
|||||||
@@ -153,12 +153,26 @@ class perform_site_check(difference_detection_processor):
|
|||||||
# CSS Filter, extract the HTML that matches and feed that into the existing inscriptis::get_text
|
# CSS Filter, extract the HTML that matches and feed that into the existing inscriptis::get_text
|
||||||
self.fetcher.content = html_tools.workarounds_for_obfuscations(self.fetcher.content)
|
self.fetcher.content = html_tools.workarounds_for_obfuscations(self.fetcher.content)
|
||||||
html_content = self.fetcher.content
|
html_content = self.fetcher.content
|
||||||
|
content_type = self.fetcher.get_all_headers().get('content-type', '').lower()
|
||||||
|
is_attachment = 'attachment' in self.fetcher.get_all_headers().get('content-disposition', '').lower() or 'octet-stream' in content_type
|
||||||
|
|
||||||
# If not JSON, and if it's not text/plain..
|
# Try to detect better mime types if its a download or not announced as HTML
|
||||||
if 'text/plain' in self.fetcher.get_all_headers().get('content-type', '').lower():
|
if is_attachment:
|
||||||
|
logger.debug(f"Got a reply that may be a download or possibly a text attachment, checking..")
|
||||||
|
try:
|
||||||
|
import magic
|
||||||
|
mime = magic.from_buffer(html_content, mime=True)
|
||||||
|
logger.debug(f"Guessing mime type, original content_type '{content_type}', mime type detected '{mime}'")
|
||||||
|
if mime and "/" in mime: # looks valid and is a valid mime type
|
||||||
|
content_type = mime
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting a more precise mime type from 'magic' library ({str(e)}")
|
||||||
|
|
||||||
|
if 'text/' in content_type and not 'html' in content_type:
|
||||||
# Don't run get_text or xpath/css filters on plaintext
|
# Don't run get_text or xpath/css filters on plaintext
|
||||||
stripped_text_from_html = html_content
|
stripped_text_from_html = html_content
|
||||||
else:
|
else:
|
||||||
|
# If not JSON, and if it's not text/plain..
|
||||||
# Does it have some ld+json price data? used for easier monitoring
|
# Does it have some ld+json price data? used for easier monitoring
|
||||||
update_obj['has_ldjson_price_data'] = html_tools.has_ldjson_product_info(self.fetcher.content)
|
update_obj['has_ldjson_price_data'] = html_tools.has_ldjson_product_info(self.fetcher.content)
|
||||||
|
|
||||||
@@ -301,6 +315,11 @@ class perform_site_check(difference_detection_processor):
|
|||||||
text_for_checksuming = stripped_text_from_html
|
text_for_checksuming = stripped_text_from_html
|
||||||
if text_to_ignore:
|
if text_to_ignore:
|
||||||
text_for_checksuming = html_tools.strip_ignore_text(stripped_text_from_html, text_to_ignore)
|
text_for_checksuming = html_tools.strip_ignore_text(stripped_text_from_html, text_to_ignore)
|
||||||
|
# Some people prefer to also completely remove it
|
||||||
|
strip_ignored_lines = watch.get('strip_ignored_lines') if watch.get('strip_ignored_lines') is not None else self.datastore.data['settings']['application'].get('strip_ignored_lines')
|
||||||
|
if strip_ignored_lines:
|
||||||
|
# @todo add test in the 'preview' mode, check the widget works? compare to datastruct
|
||||||
|
stripped_text_from_html = text_for_checksuming
|
||||||
|
|
||||||
# Re #133 - if we should strip whitespaces from triggering the change detected comparison
|
# Re #133 - if we should strip whitespaces from triggering the change detected comparison
|
||||||
if text_for_checksuming and self.datastore.data['settings']['application'].get('ignore_whitespace', False):
|
if text_for_checksuming and self.datastore.data['settings']['application'].get('ignore_whitespace', False):
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ set -x
|
|||||||
# SOCKS5 related - start simple Socks5 proxy server
|
# SOCKS5 related - start simple Socks5 proxy server
|
||||||
# SOCKSTEST=xyz should show in the logs of this service to confirm it fetched
|
# SOCKSTEST=xyz should show in the logs of this service to confirm it fetched
|
||||||
docker run --network changedet-network -d --hostname socks5proxy --rm --name socks5proxy -p 1080:1080 -e PROXY_USER=proxy_user123 -e PROXY_PASSWORD=proxy_pass123 serjs/go-socks5-proxy
|
docker run --network changedet-network -d --hostname socks5proxy --rm --name socks5proxy -p 1080:1080 -e PROXY_USER=proxy_user123 -e PROXY_PASSWORD=proxy_pass123 serjs/go-socks5-proxy
|
||||||
docker run --network changedet-network -d --hostname socks5proxy-noauth --rm -p 1081:1080 --name socks5proxy-noauth serjs/go-socks5-proxy
|
docker run --network changedet-network -d --hostname socks5proxy-noauth --rm -p 1081:1080 --name socks5proxy-noauth -e REQUIRE_AUTH=false serjs/go-socks5-proxy
|
||||||
|
|
||||||
echo "---------------------------------- SOCKS5 -------------------"
|
echo "---------------------------------- SOCKS5 -------------------"
|
||||||
# SOCKS5 related - test from proxies.json
|
# SOCKS5 related - test from proxies.json
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
min-width: 60px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -26,7 +26,10 @@
|
|||||||
<li>Changing this will affect the comparison checksum which may trigger an alert</li>
|
<li>Changing this will affect the comparison checksum which may trigger an alert</li>
|
||||||
</ul>
|
</ul>
|
||||||
</span>
|
</span>
|
||||||
|
<br><br>
|
||||||
|
<div class="pure-control-group">
|
||||||
|
{{ render_ternary_field(form.strip_ignored_lines) }}
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|||||||
@@ -165,3 +165,130 @@ def test_check_basic_change_detection_functionality(client, live_server, measure
|
|||||||
# Cleanup everything
|
# Cleanup everything
|
||||||
res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True)
|
res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True)
|
||||||
assert b'Deleted' in res.data
|
assert b'Deleted' in res.data
|
||||||
|
|
||||||
|
def test_non_text_mime_or_downloads(client, live_server, measure_memory_usage):
|
||||||
|
"""
|
||||||
|
|
||||||
|
https://github.com/dgtlmoon/changedetection.io/issues/3434
|
||||||
|
I noticed that a watched website can be monitored fine as long as the server sends content-type: text/plain; charset=utf-8,
|
||||||
|
but once the server sends content-type: application/octet-stream (which is usually done to force the browser to show the Download dialog),
|
||||||
|
changedetection somehow ignores all line breaks and treats the document file as if everything is on one line.
|
||||||
|
|
||||||
|
:param client:
|
||||||
|
:param live_server:
|
||||||
|
:param measure_memory_usage:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
with open("test-datastore/endpoint-content.txt", "w") as f:
|
||||||
|
f.write("""some random text that should be split by line
|
||||||
|
and not parsed with html_to_text
|
||||||
|
this way we know that it correctly parsed as plain text
|
||||||
|
\r\n
|
||||||
|
ok\r\n
|
||||||
|
got it\r\n
|
||||||
|
""")
|
||||||
|
|
||||||
|
test_url = url_for('test_endpoint', content_type="application/octet-stream", _external=True)
|
||||||
|
|
||||||
|
# Add our URL to the import page
|
||||||
|
res = client.post(
|
||||||
|
url_for("imports.import_page"),
|
||||||
|
data={"urls": test_url},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert b"1 Imported" in res.data
|
||||||
|
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
|
||||||
|
### check the front end
|
||||||
|
res = client.get(
|
||||||
|
url_for("ui.ui_views.preview_page", uuid="first"),
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"some random text that should be split by line\n" in res.data
|
||||||
|
####
|
||||||
|
|
||||||
|
# Check the snapshot by API that it has linefeeds too
|
||||||
|
watch_uuid = next(iter(live_server.app.config['DATASTORE'].data['watching']))
|
||||||
|
api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
|
||||||
|
res = client.get(
|
||||||
|
url_for("watchhistory", uuid=watch_uuid),
|
||||||
|
headers={'x-api-key': api_key},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fetch a snapshot by timestamp, check the right one was found
|
||||||
|
res = client.get(
|
||||||
|
url_for("watchsinglehistory", uuid=watch_uuid, timestamp=list(res.json.keys())[-1]),
|
||||||
|
headers={'x-api-key': api_key},
|
||||||
|
)
|
||||||
|
assert b"some random text that should be split by line\n" in res.data
|
||||||
|
|
||||||
|
|
||||||
|
res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_standard_text_plain(client, live_server, measure_memory_usage):
|
||||||
|
"""
|
||||||
|
|
||||||
|
https://github.com/dgtlmoon/changedetection.io/issues/3434
|
||||||
|
I noticed that a watched website can be monitored fine as long as the server sends content-type: text/plain; charset=utf-8,
|
||||||
|
but once the server sends content-type: application/octet-stream (which is usually done to force the browser to show the Download dialog),
|
||||||
|
changedetection somehow ignores all line breaks and treats the document file as if everything is on one line.
|
||||||
|
|
||||||
|
The real bug here can be that it will try to process plain-text as HTML, losing <etc>
|
||||||
|
|
||||||
|
:param client:
|
||||||
|
:param live_server:
|
||||||
|
:param measure_memory_usage:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
with open("test-datastore/endpoint-content.txt", "w") as f:
|
||||||
|
f.write("""some random text that should be split by line
|
||||||
|
and not parsed with html_to_text
|
||||||
|
<title>Even this title should stay because we are just plain text</title>
|
||||||
|
this way we know that it correctly parsed as plain text
|
||||||
|
\r\n
|
||||||
|
ok\r\n
|
||||||
|
got it\r\n
|
||||||
|
""")
|
||||||
|
|
||||||
|
test_url = url_for('test_endpoint', content_type="text/plain", _external=True)
|
||||||
|
|
||||||
|
# Add our URL to the import page
|
||||||
|
res = client.post(
|
||||||
|
url_for("imports.import_page"),
|
||||||
|
data={"urls": test_url},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert b"1 Imported" in res.data
|
||||||
|
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
|
||||||
|
### check the front end
|
||||||
|
res = client.get(
|
||||||
|
url_for("ui.ui_views.preview_page", uuid="first"),
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"some random text that should be split by line\n" in res.data
|
||||||
|
####
|
||||||
|
|
||||||
|
# Check the snapshot by API that it has linefeeds too
|
||||||
|
watch_uuid = next(iter(live_server.app.config['DATASTORE'].data['watching']))
|
||||||
|
api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
|
||||||
|
res = client.get(
|
||||||
|
url_for("watchhistory", uuid=watch_uuid),
|
||||||
|
headers={'x-api-key': api_key},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fetch a snapshot by timestamp, check the right one was found
|
||||||
|
res = client.get(
|
||||||
|
url_for("watchsinglehistory", uuid=watch_uuid, timestamp=list(res.json.keys())[-1]),
|
||||||
|
headers={'x-api-key': api_key},
|
||||||
|
)
|
||||||
|
assert b"some random text that should be split by line\n" in res.data
|
||||||
|
assert b"<title>Even this title should stay because we are just plain text</title>" in res.data
|
||||||
|
|
||||||
|
res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True)
|
||||||
|
|
||||||
|
|||||||
@@ -58,3 +58,39 @@ def test_ignore(client, live_server, measure_memory_usage):
|
|||||||
# Should be in base.html
|
# Should be in base.html
|
||||||
assert b'csrftoken' in res.data
|
assert b'csrftoken' in res.data
|
||||||
|
|
||||||
|
|
||||||
|
def test_strip_ignore_lines(client, live_server, measure_memory_usage):
|
||||||
|
# live_server_setup(live_server) # Setup on conftest per function
|
||||||
|
set_original_ignore_response()
|
||||||
|
|
||||||
|
|
||||||
|
# Goto the settings page, add our ignore text
|
||||||
|
res = client.post(
|
||||||
|
url_for("settings.settings_page"),
|
||||||
|
data={
|
||||||
|
"requests-time_between_check-minutes": 180,
|
||||||
|
"application-ignore_whitespace": "y",
|
||||||
|
"application-strip_ignored_lines": "y",
|
||||||
|
"application-global_ignore_text": "Which is across multiple",
|
||||||
|
'application-fetch_backend': "html_requests"
|
||||||
|
},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"Settings updated." in res.data
|
||||||
|
|
||||||
|
test_url = url_for('test_endpoint', _external=True)
|
||||||
|
res = client.post(
|
||||||
|
url_for("imports.import_page"),
|
||||||
|
data={"urls": test_url},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"1 Imported" in res.data
|
||||||
|
|
||||||
|
# Give the thread time to pick it up
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
uuid = next(iter(live_server.app.config['DATASTORE'].data['watching']))
|
||||||
|
|
||||||
|
# It should not be in the preview anymore
|
||||||
|
res = client.get(url_for("ui.ui_views.preview_page", uuid=uuid))
|
||||||
|
assert b'<div class="ignored">' not in res.data
|
||||||
|
assert b'Which is across multiple' not in res.data
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ jsonpath-ng~=1.5.3
|
|||||||
# jq not available on Windows so must be installed manually
|
# jq not available on Windows so must be installed manually
|
||||||
|
|
||||||
# Notification library
|
# Notification library
|
||||||
apprise==1.9.3
|
apprise==1.9.5
|
||||||
|
|
||||||
# - Needed for apprise/spush, and maybe others? hopefully doesnt trigger a rust compile.
|
# - Needed for apprise/spush, and maybe others? hopefully doesnt trigger a rust compile.
|
||||||
# - Requires extra wheel for rPi, adds build time for arm/v8 which is not in piwheels
|
# - Requires extra wheel for rPi, adds build time for arm/v8 which is not in piwheels
|
||||||
@@ -51,8 +51,8 @@ cryptography==44.0.1
|
|||||||
# use any version other than 2.0.x due to https://github.com/eclipse/paho.mqtt.python/issues/814
|
# use any version other than 2.0.x due to https://github.com/eclipse/paho.mqtt.python/issues/814
|
||||||
paho-mqtt!=2.0.*
|
paho-mqtt!=2.0.*
|
||||||
|
|
||||||
# Used for CSS filtering
|
# Used for CSS filtering, JSON extraction from HTML
|
||||||
beautifulsoup4>=4.0.0
|
beautifulsoup4>=4.0.0,<=4.13.5
|
||||||
|
|
||||||
# XPath filtering, lxml is required by bs4 anyway, but put it here to be safe.
|
# XPath filtering, lxml is required by bs4 anyway, but put it here to be safe.
|
||||||
# #2328 - 5.2.0 and 5.2.1 had extra CPU flag CFLAGS set which was not compatible on older hardware
|
# #2328 - 5.2.0 and 5.2.1 had extra CPU flag CFLAGS set which was not compatible on older hardware
|
||||||
|
|||||||
Reference in New Issue
Block a user