Compare commits

..

44 Commits

Author SHA1 Message Date
dgtlmoon
15e0a330fe increase delay
Some checks failed
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
2025-06-03 09:27:34 +02:00
dgtlmoon
40226dbad7 add delays 2025-06-03 09:22:04 +02:00
dgtlmoon
5c7c548929 woops 2025-06-03 09:10:15 +02:00
dgtlmoon
6949a09aab test tweaks 2025-06-03 09:06:19 +02:00
dgtlmoon
cf01015601 Add delay for test stability
Some checks failed
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
ChangeDetection.io Container Build Test / test-container-build (push) Has been cancelled
2025-06-02 21:00:29 +02:00
dgtlmoon
1f710529a4 woops 2025-06-02 20:47:35 +02:00
dgtlmoon
501aaf4b77 test delay 2025-06-02 20:47:27 +02:00
dgtlmoon
8f421a43ef test improvements 2025-06-02 20:27:30 +02:00
dgtlmoon
a9cf6a4373 test speedup 2025-06-02 20:23:35 +02:00
dgtlmoon
4352e8006c Improve dockerfile 2025-06-02 19:29:26 +02:00
dgtlmoon
b90d03a78e update readme 2025-06-02 19:25:39 +02:00
dgtlmoon
03e751b57f Tidy up async worker names and cleanups when in test mode 2025-06-02 19:18:03 +02:00
dgtlmoon
6c3e88e261 test fixes due to dnspython and other changes 2025-06-02 19:09:39 +02:00
dgtlmoon
75e6fbd624 include more debug output on test 2025-06-02 18:40:03 +02:00
dgtlmoon
821c0edff4 unpin urlrequests 2025-06-02 18:39:12 +02:00
dgtlmoon
cd7dde4477 gevent/thread type cleanups 2025-06-02 18:32:10 +02:00
dgtlmoon
6866956e67 Remove eventlet go with gevent! 2025-06-02 18:25:40 +02:00
dgtlmoon
b4bfd23f98 Strip whitespace, add history/preview handling 2025-06-02 17:03:25 +02:00
dgtlmoon
817afed17d Remove old hack which is probably not compatible 2025-06-02 16:24:35 +02:00
dgtlmoon
5c0d151490 Make sure test data is not built with docker container 2025-06-02 16:23:51 +02:00
dgtlmoon
337411c16a Cross platform fixes
Some checks failed
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
2025-05-30 19:00:25 +02:00
dgtlmoon
6c1ed57032 WIP 2025-05-30 18:49:43 +02:00
dgtlmoon
3d61ce8df7 WIP 2025-05-30 18:13:31 +02:00
dgtlmoon
f7695f59d3 work on missing exceptions etc 2025-05-30 17:35:41 +02:00
dgtlmoon
40498a59b6 Adding feather-icons 2025-05-30 17:16:38 +02:00
dgtlmoon
0d332dd519 WIP 2025-05-30 16:32:18 +02:00
dgtlmoon
e9d28b810a update test handler 2025-05-30 16:22:24 +02:00
dgtlmoon
e5aba3b2f0 ensure workers are running 2025-05-30 16:14:31 +02:00
dgtlmoon
01742dd670 WIP 2025-05-30 16:09:25 +02:00
dgtlmoon
34fbfa7113 fix for browsersteps 2025-05-30 15:52:17 +02:00
dgtlmoon
a52ae11062 WIP 2025-05-30 15:49:41 +02:00
dgtlmoon
fb5e93691f Revert "WIP - The PlaywrightManager successfully isolates async operations in a dedicated thread while providing a clean synchronous interface to the worker threads, solving the original threading vs async conflict!"
Some checks failed
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
This reverts commit 6b68587bbf.
2025-05-30 10:31:38 +02:00
dgtlmoon
6b68587bbf WIP - The PlaywrightManager successfully isolates async operations in a dedicated thread while providing a clean synchronous interface to the worker threads, solving the original threading vs async conflict! 2025-05-30 10:16:35 +02:00
dgtlmoon
e891c2da42 WIP - switch to python async mode, tweak eventlet
Some checks failed
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
2025-05-29 15:03:11 +02:00
dgtlmoon
b535339e94 Undo monkey patch for eventlet
Some checks failed
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built 📦 package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
2025-05-29 12:44:44 +02:00
dgtlmoon
d40e017e29 Merge branch 'master' into socketio-tweaks 2025-05-29 11:32:33 +02:00
dgtlmoon
bf6bab6c05 more work on enable/disable 2025-05-28 11:46:37 +02:00
dgtlmoon
142f93cf88 Adding py 2025-05-28 11:35:22 +02:00
dgtlmoon
4c7395f203 use socket emit from client to handle events 2025-05-28 11:28:45 +02:00
dgtlmoon
9bc347158a WIP 2025-05-28 11:21:31 +02:00
dgtlmoon
b74eaca83f Switch to eventlet as handler, UI option to enable/disable 2025-05-28 11:13:47 +02:00
dgtlmoon
46f78f0164 Tweak script setup 2025-05-28 10:11:40 +02:00
dgtlmoon
05ab0831ef Lock to major versions 2025-05-28 10:02:40 +02:00
dgtlmoon
62653a4646 Use 3.1.3 2025-05-28 10:01:08 +02:00
12 changed files with 122 additions and 215 deletions

View File

@@ -56,8 +56,6 @@ jobs:
context: ./
file: ./.github/test/Dockerfile-alpine
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Test that the docker containers can build
id: docker_build
@@ -67,6 +65,6 @@ jobs:
context: ./
file: ./Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/arm64/v8
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache

View File

@@ -23,24 +23,13 @@ WORKDIR /install
COPY requirements.txt /requirements.txt
# Use cache mounts and multiple wheel sources for faster ARM builds
ENV PIP_CACHE_DIR=/tmp/pip-cache
RUN --mount=type=cache,target=/tmp/pip-cache \
pip install \
--extra-index-url https://www.piwheels.org/simple \
--extra-index-url https://pypi.anaconda.org/ARM-software/simple \
--cache-dir=/tmp/pip-cache \
--target=/dependencies \
-r /requirements.txt
# --extra-index-url https://www.piwheels.org/simple is for cryptography module to be prebuilt (or rustc etc needs to be installed)
RUN pip install --extra-index-url https://www.piwheels.org/simple --target=/dependencies -r /requirements.txt
# Playwright is an alternative to Selenium
# Excluded this package from requirements.txt to prevent arm/v6 and arm/v7 builds from failing
# https://github.com/dgtlmoon/changedetection.io/pull/1067 also musl/alpine (not supported)
RUN --mount=type=cache,target=/tmp/pip-cache \
pip install \
--cache-dir=/tmp/pip-cache \
--target=/dependencies \
playwright~=1.48.0 \
RUN pip install --target=/dependencies playwright~=1.48.0 \
|| echo "WARN: Failed to install Playwright. The application can still run, but the Playwright option will be disabled."
# Final image stage

View File

@@ -2,7 +2,7 @@
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
__version__ = '0.50.01'
__version__ = '0.49.18'
from changedetectionio.strtobool import strtobool
from json.decoder import JSONDecodeError

View File

@@ -7,105 +7,6 @@ from changedetectionio.blueprint.ui.edit import construct_blueprint as construct
from changedetectionio.blueprint.ui.notification import construct_blueprint as construct_notification_blueprint
from changedetectionio.blueprint.ui.views import construct_blueprint as construct_views_blueprint
def _handle_operations(op, uuids, datastore, worker_handler, update_q, queuedWatchMetaData, watch_check_update, extra_data=None, emit_flash=True):
from flask import request, flash
if op == 'delete':
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.delete(uuid)
if emit_flash:
flash(f"{len(uuids)} watches deleted")
elif op == 'pause':
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['paused'] = True
if emit_flash:
flash(f"{len(uuids)} watches paused")
elif op == 'unpause':
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid.strip()]['paused'] = False
if emit_flash:
flash(f"{len(uuids)} watches unpaused")
elif (op == 'mark-viewed'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.set_last_viewed(uuid, int(time.time()))
if emit_flash:
flash(f"{len(uuids)} watches updated")
elif (op == 'mute'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['notification_muted'] = True
if emit_flash:
flash(f"{len(uuids)} watches muted")
elif (op == 'unmute'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['notification_muted'] = False
if emit_flash:
flash(f"{len(uuids)} watches un-muted")
elif (op == 'recheck'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
# Recheck and require a full reprocessing
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
if emit_flash:
flash(f"{len(uuids)} watches queued for rechecking")
elif (op == 'clear-errors'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]["last_error"] = False
if emit_flash:
flash(f"{len(uuids)} watches errors cleared")
elif (op == 'clear-history'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.clear_watch_history(uuid)
if emit_flash:
flash(f"{len(uuids)} watches cleared/reset.")
elif (op == 'notification-default'):
from changedetectionio.notification import (
default_notification_format_for_watch
)
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['notification_title'] = None
datastore.data['watching'][uuid]['notification_body'] = None
datastore.data['watching'][uuid]['notification_urls'] = []
datastore.data['watching'][uuid]['notification_format'] = default_notification_format_for_watch
if emit_flash:
flash(f"{len(uuids)} watches set to use default notification settings")
elif (op == 'assign-tag'):
op_extradata = extra_data
if op_extradata:
tag_uuid = datastore.add_tag(title=op_extradata)
if op_extradata and tag_uuid:
for uuid in uuids:
if datastore.data['watching'].get(uuid):
# Bug in old versions caused by bad edit page/tag handler
if isinstance(datastore.data['watching'][uuid]['tags'], str):
datastore.data['watching'][uuid]['tags'] = []
datastore.data['watching'][uuid]['tags'].append(tag_uuid)
if emit_flash:
flash(f"{len(uuids)} watches were tagged")
if uuids:
for uuid in uuids:
watch_check_update.send(watch_uuid=uuid)
def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handler, queuedWatchMetaData, watch_check_update):
ui_blueprint = Blueprint('ui', __name__, template_folder="templates")
@@ -248,17 +149,93 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handle
def form_watch_list_checkbox_operations():
op = request.form['op']
uuids = [u.strip() for u in request.form.getlist('uuids') if u]
extra_data = request.form.get('op_extradata', '').strip()
_handle_operations(
datastore=datastore,
extra_data=extra_data,
queuedWatchMetaData=queuedWatchMetaData,
uuids=uuids,
worker_handler=worker_handler,
update_q=update_q,
watch_check_update=watch_check_update,
op=op,
)
if (op == 'delete'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.delete(uuid)
flash("{} watches deleted".format(len(uuids)))
elif (op == 'pause'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['paused'] = True
flash("{} watches paused".format(len(uuids)))
elif (op == 'unpause'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid.strip()]['paused'] = False
flash("{} watches unpaused".format(len(uuids)))
elif (op == 'mark-viewed'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.set_last_viewed(uuid, int(time.time()))
flash("{} watches updated".format(len(uuids)))
elif (op == 'mute'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['notification_muted'] = True
flash("{} watches muted".format(len(uuids)))
elif (op == 'unmute'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['notification_muted'] = False
flash("{} watches un-muted".format(len(uuids)))
elif (op == 'recheck'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
# Recheck and require a full reprocessing
worker_handler.queue_item_async_safe(update_q, queuedWatchMetaData.PrioritizedItem(priority=1, item={'uuid': uuid}))
flash("{} watches queued for rechecking".format(len(uuids)))
elif (op == 'clear-errors'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]["last_error"] = False
flash(f"{len(uuids)} watches errors cleared")
elif (op == 'clear-history'):
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.clear_watch_history(uuid)
flash("{} watches cleared/reset.".format(len(uuids)))
elif (op == 'notification-default'):
from changedetectionio.notification import (
default_notification_format_for_watch
)
for uuid in uuids:
if datastore.data['watching'].get(uuid):
datastore.data['watching'][uuid]['notification_title'] = None
datastore.data['watching'][uuid]['notification_body'] = None
datastore.data['watching'][uuid]['notification_urls'] = []
datastore.data['watching'][uuid]['notification_format'] = default_notification_format_for_watch
flash("{} watches set to use default notification settings".format(len(uuids)))
elif (op == 'assign-tag'):
op_extradata = request.form.get('op_extradata', '').strip()
if op_extradata:
tag_uuid = datastore.add_tag(title=op_extradata)
if op_extradata and tag_uuid:
for uuid in uuids:
if datastore.data['watching'].get(uuid):
# Bug in old versions caused by bad edit page/tag handler
if isinstance(datastore.data['watching'][uuid]['tags'], str):
datastore.data['watching'][uuid]['tags'] = []
datastore.data['watching'][uuid]['tags'].append(tag_uuid)
flash(f"{len(uuids)} watches were tagged")
if uuids:
for uuid in uuids:
# with app.app_context():
watch_check_update.send(watch_uuid=uuid)
return redirect(url_for('watchlist.index'))

View File

@@ -131,7 +131,7 @@ document.addEventListener('DOMContentLoaded', function() {
<a class="ajax-op state-on mute-toggle" data-op="mute" style="display: none" href="{{url_for('watchlist.index', op='mute', uuid=watch.uuid, tag=active_tag_uuid)}}"><img src="{{url_for('static_content', group='images', filename='bell-off.svg')}}" alt="UnMute notification" title="UnMute notification" class="icon icon-mute" ></a>
</td>
<td class="title-col inline">{{watch.title if watch.title is not none and watch.title|length > 0 else watch.url}}
<a class="external" target="_blank" rel="noopener" href="{{ watch.link.replace('source:','') }}">&nbsp;</a>
<a class="external" target="_blank" rel="noopener" href="{{ watch.link.replace('source:','') }}"><i data-feather="external-link"></i></a>
<a class="link-spread" href="{{url_for('ui.form_share_put_watch', uuid=watch.uuid)}}"><img src="{{url_for('static_content', group='images', filename='spread.svg')}}" class="status-icon icon icon-spread" title="Create a link to share watch config with others" ></a>
{%- if watch.get_fetch_backend == "html_webdriver"

View File

@@ -26,9 +26,6 @@ class SignalHandler:
queue_length_signal.connect(self.handle_queue_length, weak=False)
# logger.info("SignalHandler: Connected to queue_length signal")
watch_delete_signal = signal('watch_deleted')
watch_delete_signal.connect(self.handle_deleted_signal, weak=False)
# Create and start the queue update thread using standard threading
import threading
self.polling_emitter_thread = threading.Thread(
@@ -64,16 +61,6 @@ class SignalHandler:
else:
logger.warning(f"Watch UUID {watch_uuid} not found in datastore")
def handle_deleted_signal(self, *args, **kwargs):
watch_uuid = kwargs.get('watch_uuid')
if watch_uuid:
# Emit the queue size to all connected clients
self.socketio_instance.emit("watch_deleted", {
"uuid": watch_uuid,
"event_timestamp": time.time()
})
logger.debug(f"Watch UUID {watch_uuid} was deleted")
def handle_queue_length(self, *args, **kwargs):
"""Handle queue_length signal and emit to all clients"""
try:
@@ -180,12 +167,15 @@ def handle_watch_update(socketio, **kwargs):
if hasattr(q_item, 'item') and 'uuid' in q_item.item:
queue_list.append(q_item.item['uuid'])
error_texts = ""
# Get the error texts from the watch
error_texts = watch.compile_error_texts()
# Create a simplified watch data object to send to clients
# Create a simplified watch data object to send to clients
watch_uuid = watch.get('uuid')
watch_data = {
'checking_now': True if watch.get('uuid') in running_uuids else False,
'checking_now': True if watch_uuid in running_uuids else False,
'fetch_time': watch.get('fetch_time'),
'has_error': True if error_texts else False,
'last_changed': watch.get('last_changed'),
@@ -193,12 +183,13 @@ def handle_watch_update(socketio, **kwargs):
'error_text': error_texts,
'history_n': watch.history_n,
'last_checked_text': _jinja2_filter_datetime(watch),
'last_changed_text': timeago.format(int(watch.last_changed), time.time()) if watch.history_n >= 2 and int(watch.last_changed) > 0 else 'Not yet',
'queued': True if watch.get('uuid') in queue_list else False,
'last_changed_text': timeago.format(int(watch['last_changed']), time.time()) if watch.history_n >= 2 and int(
watch.get('last_changed', 0)) > 0 else 'Not yet',
'queued': True if watch_uuid in queue_list else False,
'paused': True if watch.get('paused') else False,
'notification_muted': True if watch.get('notification_muted') else False,
'unviewed': watch.has_unviewed,
'uuid': watch.get('uuid'),
'uuid': watch_uuid,
'event_timestamp': time.time()
}
@@ -271,29 +262,6 @@ def init_socketio(app, datastore):
# Set up event handlers
logger.info("Socket.IO: Registering connect event handler")
@socketio.on('checkbox-operation')
def event_checkbox_operations(data):
from changedetectionio.blueprint.ui import _handle_operations
from changedetectionio import queuedWatchMetaData
from changedetectionio import worker_handler
from changedetectionio.flask_app import update_q, watch_check_update
logger.trace(f"Got checkbox operations event: {data}")
datastore = socketio.datastore
_handle_operations(
op=data.get('op'),
uuids=data.get('uuids'),
datastore=datastore,
extra_data=data.get('extra_data'),
worker_handler=worker_handler,
update_q=update_q,
queuedWatchMetaData=queuedWatchMetaData,
watch_check_update=watch_check_update,
emit_flash=False
)
@socketio.on('connect')
def handle_connect():
"""Handle client connection"""

File diff suppressed because one or more lines are too long

View File

@@ -18,25 +18,6 @@ $(document).ready(function () {
return false;
});
$('#checkbox-operations button').on('click.socketHandlerNamespace', function (e) {
e.preventDefault();
const op = $(this).val();
const checkedUuids = $('input[name="uuids"]:checked').map(function () {
return this.value.trim();
}).get();
console.log(`Socket.IO: Sending watch operation '${op}' for UUIDs:`, checkedUuids);
socket.emit('checkbox-operation', {
op: op,
uuids: checkedUuids,
extra_data: $('#op_extradata').val() // Set by the alert() handler
});
$('input[name="uuids"]:checked').prop('checked', false);
$('#check-all:checked').prop('checked', false);
return false;
});
}
@@ -121,7 +102,7 @@ $(document).ready(function () {
$('td.title-col .error-text', $watchRow).html(watch.error_text)
$('td.last-changed', $watchRow).text(watch.last_changed_text)
$('td.last-changed', $watchRow).text(watch.last_checked_text)
$('td.last-checked .innertext', $watchRow).text(watch.last_checked_text)
$('td.last-checked', $watchRow).data('timestamp', watch.last_checked).data('fetchduration', watch.fetch_time);

View File

@@ -17,10 +17,6 @@
&.title-col {
word-break: break-all;
white-space: normal;
a::after {
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
margin: 0 3px 0 5px;
}
}
}
@@ -43,6 +39,16 @@
}
}
.title-col a[target="_blank"] i[data-feather],
.current-diff-url i[data-feather] {
width: 12px;
height: 12px;
stroke: #666;
margin: 0 3px 0 5px;
vertical-align: middle;
}
/* Row with 'checking-now' */
tr.checking-now {
td:first-child {

View File

@@ -537,9 +537,6 @@ body.preview-text-enabled {
.watch-table td.title-col {
word-break: break-all;
white-space: normal; }
.watch-table td.title-col a::after {
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
margin: 0 3px 0 5px; }
.watch-table th {
white-space: nowrap; }
.watch-table th a {
@@ -548,6 +545,13 @@ body.preview-text-enabled {
font-weight: bolder; }
.watch-table th a.inactive .arrow {
display: none; }
.watch-table .title-col a[target="_blank"] i[data-feather],
.watch-table .current-diff-url i[data-feather] {
width: 12px;
height: 12px;
stroke: #666;
margin: 0 3px 0 5px;
vertical-align: middle; }
.watch-table tr.checking-now td:first-child {
position: relative; }
.watch-table tr.checking-now td:first-child::before {

View File

@@ -253,9 +253,6 @@ class ChangeDetectionStore:
del self.data['watching'][uuid]
self.needs_write_urgent = True
watch_delete_signal = signal('watch_deleted')
if watch_delete_signal:
watch_delete_signal.send(watch_uuid=uuid)
# Clone a watch by UUID
def clone(self, uuid):

View File

@@ -31,9 +31,9 @@
const socketio_url="{{ get_socketio_path() }}/socket.io";
const is_authenticated = {% if current_user.is_authenticated or not has_password %}true{% else %}false{% endif %};
</script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="{{url_for('static_content', group='js', filename='jquery-3.6.0.min.js')}}"></script>
<script src="{{url_for('static_content', group='js', filename='csrf.js')}}" defer></script>
<script src="{{url_for('static_content', group='js', filename='feather-icons.min.js')}}" defer></script>
{% if socket_io_enabled %}
<script src="{{url_for('static_content', group='js', filename='socket.io.min.js')}}"></script>
<script src="{{url_for('static_content', group='js', filename='realtime.js')}}" defer></script>