mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2025-12-19 22:45:52 +00:00
Compare commits
10 Commits
UI-browser
...
ui-html-va
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b58b36b6ff | ||
|
|
939fa86582 | ||
|
|
b87c92b9e0 | ||
|
|
4d5535d72c | ||
|
|
ad08219d03 | ||
|
|
82211eef82 | ||
|
|
5d9380609c | ||
|
|
a8b3918fca | ||
|
|
e83fb37fb6 | ||
|
|
6b99afe0f7 |
@@ -112,6 +112,35 @@ def build_watch_json_schema(d):
|
|||||||
|
|
||||||
schema['properties']['time_between_check'] = build_time_between_check_json_schema()
|
schema['properties']['time_between_check'] = build_time_between_check_json_schema()
|
||||||
|
|
||||||
|
schema['properties']['browser_steps'] = {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"operation": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"maxLength": 5000 # Allows null and any string up to 5000 chars (including "")
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"maxLength": 5000
|
||||||
|
},
|
||||||
|
"optional_value": {
|
||||||
|
"type": ["string", "null"],
|
||||||
|
"maxLength": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["operation", "selector", "optional_value"],
|
||||||
|
"additionalProperties": False # No extra keys allowed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"type": "null"}, # Allows null for `browser_steps`
|
||||||
|
{"type": "array", "maxItems": 0} # Allows empty array []
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
# headers ?
|
# headers ?
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ class steppable_browser_interface():
|
|||||||
page = None
|
page = None
|
||||||
start_url = None
|
start_url = None
|
||||||
|
|
||||||
|
action_timeout = 10 * 1000
|
||||||
|
|
||||||
def __init__(self, start_url):
|
def __init__(self, start_url):
|
||||||
self.start_url = start_url
|
self.start_url = start_url
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ class steppable_browser_interface():
|
|||||||
return
|
return
|
||||||
elem = self.page.get_by_text(value)
|
elem = self.page.get_by_text(value)
|
||||||
if elem.count():
|
if elem.count():
|
||||||
elem.first.click(delay=randint(200, 500), timeout=3000)
|
elem.first.click(delay=randint(200, 500), timeout=self.action_timeout)
|
||||||
|
|
||||||
def action_click_element_containing_text_if_exists(self, selector=None, value=''):
|
def action_click_element_containing_text_if_exists(self, selector=None, value=''):
|
||||||
logger.debug("Clicking element containing text if exists")
|
logger.debug("Clicking element containing text if exists")
|
||||||
@@ -111,7 +113,7 @@ class steppable_browser_interface():
|
|||||||
elem = self.page.get_by_text(value)
|
elem = self.page.get_by_text(value)
|
||||||
logger.debug(f"Clicking element containing text - {elem.count()} elements found")
|
logger.debug(f"Clicking element containing text - {elem.count()} elements found")
|
||||||
if elem.count():
|
if elem.count():
|
||||||
elem.first.click(delay=randint(200, 500), timeout=3000)
|
elem.first.click(delay=randint(200, 500), timeout=self.action_timeout)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -119,7 +121,7 @@ class steppable_browser_interface():
|
|||||||
if not len(selector.strip()):
|
if not len(selector.strip()):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.page.fill(selector, value, timeout=10 * 1000)
|
self.page.fill(selector, value, timeout=self.action_timeout)
|
||||||
|
|
||||||
def action_execute_js(self, selector, value):
|
def action_execute_js(self, selector, value):
|
||||||
response = self.page.evaluate(value)
|
response = self.page.evaluate(value)
|
||||||
@@ -130,7 +132,7 @@ class steppable_browser_interface():
|
|||||||
if not len(selector.strip()):
|
if not len(selector.strip()):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.page.click(selector=selector, timeout=30 * 1000, delay=randint(200, 500))
|
self.page.click(selector=selector, timeout=self.action_timeout + 20 * 1000, delay=randint(200, 500))
|
||||||
|
|
||||||
def action_click_element_if_exists(self, selector, value):
|
def action_click_element_if_exists(self, selector, value):
|
||||||
import playwright._impl._errors as _api_types
|
import playwright._impl._errors as _api_types
|
||||||
@@ -138,7 +140,7 @@ class steppable_browser_interface():
|
|||||||
if not len(selector.strip()):
|
if not len(selector.strip()):
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.page.click(selector, timeout=10 * 1000, delay=randint(200, 500))
|
self.page.click(selector, timeout=self.action_timeout, delay=randint(200, 500))
|
||||||
except _api_types.TimeoutError as e:
|
except _api_types.TimeoutError as e:
|
||||||
return
|
return
|
||||||
except _api_types.Error as e:
|
except _api_types.Error as e:
|
||||||
@@ -185,10 +187,10 @@ class steppable_browser_interface():
|
|||||||
self.page.keyboard.press("PageDown", delay=randint(200, 500))
|
self.page.keyboard.press("PageDown", delay=randint(200, 500))
|
||||||
|
|
||||||
def action_check_checkbox(self, selector, value):
|
def action_check_checkbox(self, selector, value):
|
||||||
self.page.locator(selector).check(timeout=1000)
|
self.page.locator(selector).check(timeout=self.action_timeout)
|
||||||
|
|
||||||
def action_uncheck_checkbox(self, selector, value):
|
def action_uncheck_checkbox(self, selector, value):
|
||||||
self.page.locator(selector, timeout=1000).uncheck(timeout=1000)
|
self.page.locator(selector).uncheck(timeout=self.action_timeout)
|
||||||
|
|
||||||
|
|
||||||
# Responsible for maintaining a live 'context' with the chrome CDP
|
# Responsible for maintaining a live 'context' with the chrome CDP
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ class model(watch_base):
|
|||||||
# Iterate over all history texts and see if something new exists
|
# Iterate over all history texts and see if something new exists
|
||||||
# Always applying .strip() to start/end but optionally replace any other whitespace
|
# Always applying .strip() to start/end but optionally replace any other whitespace
|
||||||
def lines_contain_something_unique_compared_to_history(self, lines: list, ignore_whitespace=False):
|
def lines_contain_something_unique_compared_to_history(self, lines: list, ignore_whitespace=False):
|
||||||
local_lines = []
|
local_lines = set([])
|
||||||
if lines:
|
if lines:
|
||||||
if ignore_whitespace:
|
if ignore_whitespace:
|
||||||
if isinstance(lines[0], str): # Can be either str or bytes depending on what was on the disk
|
if isinstance(lines[0], str): # Can be either str or bytes depending on what was on the disk
|
||||||
|
|||||||
@@ -1,48 +1,66 @@
|
|||||||
// Rewrite this is a plugin.. is all this JS really 'worth it?'
|
(function ($) {
|
||||||
|
$.fn.hashTabs = function (options) {
|
||||||
|
var settings = $.extend({
|
||||||
|
tabContainer: ".tabs ul",
|
||||||
|
tabSelector: "li a",
|
||||||
|
tabContent: ".tab-pane-inner",
|
||||||
|
activeClass: "active",
|
||||||
|
errorClass: ".messages .error",
|
||||||
|
bodyClassToggle: "full-width"
|
||||||
|
}, options);
|
||||||
|
|
||||||
window.addEventListener('hashchange', function () {
|
var $tabs = $(settings.tabContainer).find(settings.tabSelector);
|
||||||
var tabs = document.getElementsByClassName('active');
|
|
||||||
while (tabs[0]) {
|
|
||||||
tabs[0].classList.remove('active');
|
|
||||||
document.body.classList.remove('full-width');
|
|
||||||
}
|
|
||||||
set_active_tab();
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
var has_errors = document.querySelectorAll(".messages .error");
|
function setActiveTab() {
|
||||||
if (!has_errors.length) {
|
var hash = window.location.hash;
|
||||||
if (document.location.hash == "") {
|
var $activeTab = $tabs.filter("[href='" + hash + "']");
|
||||||
location.replace(document.querySelector(".tabs ul li:first-child a").hash);
|
|
||||||
} else {
|
|
||||||
set_active_tab();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
focus_error_tab();
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_active_tab() {
|
// Remove active class from all tabs
|
||||||
document.body.classList.remove('full-width');
|
$(settings.tabContainer).find("li").removeClass(settings.activeClass);
|
||||||
var tab = document.querySelectorAll("a[href='" + location.hash + "']");
|
|
||||||
if (tab.length) {
|
|
||||||
tab[0].parentElement.className = "active";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
// Add active class to selected tab
|
||||||
|
if ($activeTab.length) {
|
||||||
|
$activeTab.parent().addClass(settings.activeClass);
|
||||||
|
}
|
||||||
|
|
||||||
function focus_error_tab() {
|
// Show the correct content
|
||||||
// time to use jquery or vuejs really,
|
$(settings.tabContent).hide();
|
||||||
// activate the tab with the error
|
if (hash) {
|
||||||
var tabs = document.querySelectorAll('.tabs li a'), i;
|
$(hash).show();
|
||||||
for (i = 0; i < tabs.length; ++i) {
|
}
|
||||||
var tab_name = tabs[i].hash.replace('#', '');
|
|
||||||
var pane_errors = document.querySelectorAll('#' + tab_name + ' .error')
|
|
||||||
if (pane_errors.length) {
|
|
||||||
document.location.hash = '#' + tab_name;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
function focusErrorTab() {
|
||||||
}
|
$tabs.each(function () {
|
||||||
|
var tabName = this.hash.replace("#", "");
|
||||||
|
if ($("#" + tabName).find(settings.errorClass).length) {
|
||||||
|
window.location.hash = "#" + tabName;
|
||||||
|
return false; // Stop loop on first error tab
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeTabs() {
|
||||||
|
if ($(settings.errorClass).length) {
|
||||||
|
focusErrorTab();
|
||||||
|
} else if (!window.location.hash) {
|
||||||
|
window.location.replace($tabs.first().attr("href"));
|
||||||
|
} else {
|
||||||
|
setActiveTab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for hash changes
|
||||||
|
$(window).on("hashchange", setActiveTab);
|
||||||
|
|
||||||
|
// Initialize on page load
|
||||||
|
initializeTabs();
|
||||||
|
|
||||||
|
return this; // Enable jQuery chaining
|
||||||
|
};
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$(".tabs").hashTabs();
|
||||||
|
});
|
||||||
@@ -945,15 +945,7 @@ $form-edge-padding: 20px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane-inner {
|
.tab-pane-inner {
|
||||||
|
display: none;
|
||||||
&:not(:target) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:target {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// doesnt need padding because theres another row of buttons/activity
|
// doesnt need padding because theres another row of buttons/activity
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1159,11 +1159,8 @@ textarea::placeholder {
|
|||||||
border-radius: 5px; }
|
border-radius: 5px; }
|
||||||
|
|
||||||
.tab-pane-inner {
|
.tab-pane-inner {
|
||||||
|
display: none;
|
||||||
padding: 0px; }
|
padding: 0px; }
|
||||||
.tab-pane-inner:not(:target) {
|
|
||||||
display: none; }
|
|
||||||
.tab-pane-inner:target {
|
|
||||||
display: block; }
|
|
||||||
|
|
||||||
.beta-logo {
|
.beta-logo {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
}}
|
}}
|
||||||
<div class="pure-form-message-inline">
|
<div class="pure-form-message-inline">
|
||||||
<p>
|
<p>
|
||||||
<strong>Tip:</strong> Use <a target=_new href="https://github.com/caronc/apprise">AppRise Notification 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>.<br>
|
<strong>Tip:</strong> Use <a target="newwindow" href="https://github.com/caronc/apprise">AppRise Notification URLs</a> for notification to just about any service! <i><a target="newwindow" href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes">Please read the notification services wiki here for important configuration notes</a></i>.<br>
|
||||||
</p>
|
</p>
|
||||||
<div data-target="#advanced-help-notifications" class="toggle-show pure-button button-tag button-xsmall">Show advanced help and tips</div>
|
<div data-target="#advanced-help-notifications" class="toggle-show pure-button button-tag button-xsmall">Show advanced help and tips</div>
|
||||||
<ul style="display: none" id="advanced-help-notifications">
|
<ul style="display: none" id="advanced-help-notifications">
|
||||||
<li><code><a target=_new href="https://github.com/caronc/apprise/wiki/Notify_discord">discord://</a></code> (or <code>https://discord.com/api/webhooks...</code>)) only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li>
|
<li><code><a target="newwindow" href="https://github.com/caronc/apprise/wiki/Notify_discord">discord://</a></code> (or <code>https://discord.com/api/webhooks...</code>)) only supports a maximum <strong>2,000 characters</strong> of notification text, including the title.</li>
|
||||||
<li><code><a target=_new href="https://github.com/caronc/apprise/wiki/Notify_telegram">tgram://</a></code> bots can't send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
<li><code><a target="newwindow" href="https://github.com/caronc/apprise/wiki/Notify_telegram">tgram://</a></code> bots can't send messages to other bots, so you should specify chat ID of non-bot user.</li>
|
||||||
<li><code><a target=_new href="https://github.com/caronc/apprise/wiki/Notify_telegram">tgram://</a></code> only supports very limited HTML and can fail when extra tags are sent, <a href="https://core.telegram.org/bots/api#html-style">read more here</a> (or use plaintext/markdown format)</li>
|
<li><code><a target="newwindow" href="https://github.com/caronc/apprise/wiki/Notify_telegram">tgram://</a></code> only supports very limited HTML and can fail when extra tags are sent, <a href="https://core.telegram.org/bots/api#html-style">read more here</a> (or use plaintext/markdown format)</li>
|
||||||
<li><code>gets://</code>, <code>posts://</code>, <code>puts://</code>, <code>deletes://</code> for direct API calls (or omit the "<code>s</code>" for non-SSL ie <code>get://</code>) <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes#postposts">more help here</a></li>
|
<li><code>gets://</code>, <code>posts://</code>, <code>puts://</code>, <code>deletes://</code> for direct API calls (or omit the "<code>s</code>" for non-SSL ie <code>get://</code>) <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Notification-configuration-notes#postposts">more help here</a></li>
|
||||||
<li>Accepts the <code>{{ '{{token}}' }}</code> placeholders listed below</li>
|
<li>Accepts the <code>{{ '{{token}}' }}</code> placeholders listed below</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
<div class="pure-form-message-inline">
|
<div class="pure-form-message-inline">
|
||||||
<p>
|
<p>
|
||||||
Warning: Contents of <code>{{ '{{diff}}' }}</code>, <code>{{ '{{diff_removed}}' }}</code>, and <code>{{ '{{diff_added}}' }}</code> depend on how the difference algorithm perceives the change. <br>
|
Warning: Contents of <code>{{ '{{diff}}' }}</code>, <code>{{ '{{diff_removed}}' }}</code>, and <code>{{ '{{diff_added}}' }}</code> depend on how the difference algorithm perceives the change. <br>
|
||||||
For example, an addition or removal could be perceived as a change in some cases. <a target="_new" href="https://github.com/dgtlmoon/changedetection.io/wiki/Using-the-%7B%7Bdiff%7D%7D,-%7B%7Bdiff_added%7D%7D,-and-%7B%7Bdiff_removed%7D%7D-notification-tokens">More Here</a> <br>
|
For example, an addition or removal could be perceived as a change in some cases. <a target="newwindow" href="https://github.com/dgtlmoon/changedetection.io/wiki/Using-the-%7B%7Bdiff%7D%7D,-%7B%7Bdiff_added%7D%7D,-and-%7B%7Bdiff_removed%7D%7D-notification-tokens">More Here</a> <br>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
For JSON payloads, use <strong>|tojson</strong> without quotes for automatic escaping, for example - <code>{ "name": {{ '{{ watch_title|tojson }}' }} }</code>
|
For JSON payloads, use <strong>|tojson</strong> without quotes for automatic escaping, for example - <code>{ "name": {{ '{{ watch_title|tojson }}' }} }</code>
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
<a id="chrome-extension-link"
|
<a id="chrome-extension-link"
|
||||||
title="Try our new Chrome Extension!"
|
title="Try our new Chrome Extension!"
|
||||||
href="https://chromewebstore.google.com/detail/changedetectionio-website/kefcfmgmlhmankjmnbijimhofdjekbop">
|
href="https://chromewebstore.google.com/detail/changedetectionio-website/kefcfmgmlhmankjmnbijimhofdjekbop">
|
||||||
<img src="{{url_for('static_content', group='images', filename='Google-Chrome-icon.png')}}">
|
<img alt="Chrome store icon" src="{{url_for('static_content', group='images', filename='Google-Chrome-icon.png')}}">
|
||||||
Chrome Webstore
|
Chrome Webstore
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -45,9 +45,8 @@
|
|||||||
{% if extra_tab_content %}
|
{% if extra_tab_content %}
|
||||||
<li class="tab"><a href="#extras_tab">{{ extra_tab_content }}</a></li>
|
<li class="tab"><a href="#extras_tab">{{ extra_tab_content }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if playwright_enabled %}
|
|
||||||
<li class="tab"><a id="browsersteps-tab" href="#browser-steps">Browser Steps</a></li>
|
<li class="tab"><a id="browsersteps-tab" href="#browser-steps">Browser Steps</a></li>
|
||||||
{% endif %}
|
<!-- should goto extra forms? -->
|
||||||
{% if watch['processor'] == 'text_json_diff' %}
|
{% if watch['processor'] == 'text_json_diff' %}
|
||||||
<li class="tab"><a id="visualselector-tab" href="#visualselector">Visual Filter Selector</a></li>
|
<li class="tab"><a id="visualselector-tab" href="#visualselector">Visual Filter Selector</a></li>
|
||||||
<li class="tab" id="filters-and-triggers-tab"><a href="#filters-and-triggers">Filters & Triggers</a></li>
|
<li class="tab" id="filters-and-triggers-tab"><a href="#filters-and-triggers">Filters & Triggers</a></li>
|
||||||
@@ -199,8 +198,9 @@ Math: {{ 1 + 1 }}") }}
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
{% if playwright_enabled %}
|
|
||||||
<div class="tab-pane-inner" id="browser-steps">
|
<div class="tab-pane-inner" id="browser-steps">
|
||||||
|
{% if playwright_enabled %}
|
||||||
<img class="beta-logo" src="{{url_for('static_content', group='images', filename='beta-logo.png')}}" alt="New beta functionality">
|
<img class="beta-logo" src="{{url_for('static_content', group='images', filename='beta-logo.png')}}" alt="New beta functionality">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
@@ -234,14 +234,22 @@ Math: {{ 1 + 1 }}") }}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="browser-steps-fieldlist" >
|
<div id="browser-steps-fieldlist" >
|
||||||
<span id="browser-seconds-remaining">Loading</span> <span style="font-size: 80%;"> (<a target=_new href="https://github.com/dgtlmoon/changedetection.io/pull/478/files#diff-1a79d924d1840c485238e66772391268a89c95b781d69091384cf1ea1ac146c9R4">?</a>) </span>
|
<span id="browser-seconds-remaining">Loading</span> <span style="font-size: 80%;"> (<a target="newwindow" href="https://github.com/dgtlmoon/changedetection.io/pull/478/files#diff-1a79d924d1840c485238e66772391268a89c95b781d69091384cf1ea1ac146c9R4">?</a>) </span>
|
||||||
{{ render_field(form.browser_steps) }}
|
{{ render_field(form.browser_steps) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
{% else %}
|
||||||
|
<span class="pure-form-message-inline">
|
||||||
|
<p>Sorry, this functionality only works with Playwright/Chrome enabled watches.</p>
|
||||||
|
<p>Enable the Playwright Chrome fetcher, or alternatively try our <a href="https://lemonade.changedetection.io/start">very affordable subscription based service</a>.</p>
|
||||||
|
<p>This is because Selenium/WebDriver can not extract full page screenshots reliably.</p>
|
||||||
|
<p>You may need to <a href="https://github.com/dgtlmoon/changedetection.io/blob/09ebc6ec6338545bdd694dc6eee57f2e9d2b8075/docker-compose.yml#L31">Enable playwright environment variable</a> and uncomment the <strong>sockpuppetbrowser</strong> from the docker-compose.yml file.</p>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="notifications">
|
<div class="tab-pane-inner" id="notifications">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -298,7 +306,7 @@ xpath://body/div/span[contains(@class, 'example-class')]",
|
|||||||
<span class="pure-form-message-inline"><strong>Note!: //text() function does not work where the <element> contains <![CDATA[]]></strong></span><br>
|
<span class="pure-form-message-inline"><strong>Note!: //text() function does not work where the <element> contains <![CDATA[]]></strong></span><br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="pure-form-message-inline">One CSS, xPath, JSON Path/JQ selector per line, <i>any</i> rules that matches will be used.<br>
|
<span class="pure-form-message-inline">One CSS, xPath, JSON Path/JQ selector per line, <i>any</i> rules that matches will be used.<br>
|
||||||
<p><div data-target="#advanced-help-selectors" class="toggle-show pure-button button-tag button-xsmall">Show advanced help and tips</div><br></p>
|
<span data-target="#advanced-help-selectors" class="toggle-show pure-button button-tag button-xsmall">Show advanced help and tips</span><br>
|
||||||
<ul id="advanced-help-selectors" style="display: none;">
|
<ul id="advanced-help-selectors" style="display: none;">
|
||||||
<li>CSS - Limit text to this CSS rule, only text matching this CSS rule is included.</li>
|
<li>CSS - Limit text to this CSS rule, only text matching this CSS rule is included.</li>
|
||||||
<li>JSON - Limit text to this JSON rule, using either <a href="https://pypi.org/project/jsonpath-ng/" target="new">JSONPath</a> or <a href="https://stedolan.github.io/jq/" target="new">jq</a> (if installed).
|
<li>JSON - Limit text to this JSON rule, using either <a href="https://pypi.org/project/jsonpath-ng/" target="new">JSONPath</a> or <a href="https://stedolan.github.io/jq/" target="new">jq</a> (if installed).
|
||||||
@@ -493,6 +501,7 @@ keyword") }}
|
|||||||
<p>Sorry, this functionality only works with Playwright/Chrome enabled watches.</p>
|
<p>Sorry, this functionality only works with Playwright/Chrome enabled watches.</p>
|
||||||
<p>Enable the Playwright Chrome fetcher, or alternatively try our <a href="https://lemonade.changedetection.io/start">very affordable subscription based service</a>.</p>
|
<p>Enable the Playwright Chrome fetcher, or alternatively try our <a href="https://lemonade.changedetection.io/start">very affordable subscription based service</a>.</p>
|
||||||
<p>This is because Selenium/WebDriver can not extract full page screenshots reliably.</p>
|
<p>This is because Selenium/WebDriver can not extract full page screenshots reliably.</p>
|
||||||
|
<p>You may need to <a href="https://github.com/dgtlmoon/changedetection.io/blob/09ebc6ec6338545bdd694dc6eee57f2e9d2b8075/docker-compose.yml#L31">Enable playwright environment variable</a> and uncomment the <strong>sockpuppetbrowser</strong> from the docker-compose.yml file.</p>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ nav
|
|||||||
<a id="chrome-extension-link"
|
<a id="chrome-extension-link"
|
||||||
title="Try our new Chrome Extension!"
|
title="Try our new Chrome Extension!"
|
||||||
href="https://chromewebstore.google.com/detail/changedetectionio-website/kefcfmgmlhmankjmnbijimhofdjekbop">
|
href="https://chromewebstore.google.com/detail/changedetectionio-website/kefcfmgmlhmankjmnbijimhofdjekbop">
|
||||||
<img src="{{ url_for('static_content', group='images', filename='Google-Chrome-icon.png') }}" alt="Chrome">
|
<img alt="Chrome store icon" src="{{ url_for('static_content', group='images', filename='Google-Chrome-icon.png') }}" alt="Chrome">
|
||||||
Chrome Webstore
|
Chrome Webstore
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -280,9 +280,7 @@ nav
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
|
||||||
Your proxy provider may need to whitelist our IP of <code>204.15.192.195</code>
|
|
||||||
</p>
|
|
||||||
<p><strong>Tip</strong>: "Residential" and "Mobile" proxy type can be more successfull than "Data Center" for blocked websites.
|
<p><strong>Tip</strong>: "Residential" and "Mobile" proxy type can be more successfull than "Data Center" for blocked websites.
|
||||||
|
|
||||||
<div class="pure-control-group" id="extra-proxies-setting">
|
<div class="pure-control-group" id="extra-proxies-setting">
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
or ( watch.get_fetch_backend == "system" and system_default_fetcher == 'html_webdriver' )
|
or ( watch.get_fetch_backend == "system" and system_default_fetcher == 'html_webdriver' )
|
||||||
or "extra_browser_" in watch.get_fetch_backend
|
or "extra_browser_" in watch.get_fetch_backend
|
||||||
%}
|
%}
|
||||||
<img class="status-icon" src="{{url_for('static_content', group='images', filename='Google-Chrome-icon.png')}}" title="Using a Chrome browser" >
|
<img class="status-icon" src="{{url_for('static_content', group='images', filename='Google-Chrome-icon.png')}}" alt="Using a Chrome browser" title="Using a Chrome browser" >
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{%if watch.is_pdf %}<img class="status-icon" src="{{url_for('static_content', group='images', filename='pdf-icon.svg')}}" title="Converting PDF to text" >{% endif %}
|
{%if watch.is_pdf %}<img class="status-icon" src="{{url_for('static_content', group='images', filename='pdf-icon.svg')}}" title="Converting PDF to text" >{% endif %}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.2'
|
|
||||||
services:
|
services:
|
||||||
changedetection:
|
changedetection:
|
||||||
image: ghcr.io/dgtlmoon/changedetection.io
|
image: ghcr.io/dgtlmoon/changedetection.io
|
||||||
|
|||||||
Reference in New Issue
Block a user