kopia lustrzana https://github.com/dgtlmoon/changedetection.io
UI - Real time - checkbox operations now realtime without reload
rodzic
d5af91d8f7
commit
30e84f1030
|
@ -7,6 +7,105 @@ 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.notification import construct_blueprint as construct_notification_blueprint
|
||||||
from changedetectionio.blueprint.ui.views import construct_blueprint as construct_views_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):
|
def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handler, queuedWatchMetaData, watch_check_update):
|
||||||
ui_blueprint = Blueprint('ui', __name__, template_folder="templates")
|
ui_blueprint = Blueprint('ui', __name__, template_folder="templates")
|
||||||
|
|
||||||
|
@ -149,93 +248,17 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, worker_handle
|
||||||
def form_watch_list_checkbox_operations():
|
def form_watch_list_checkbox_operations():
|
||||||
op = request.form['op']
|
op = request.form['op']
|
||||||
uuids = [u.strip() for u in request.form.getlist('uuids') if u]
|
uuids = [u.strip() for u in request.form.getlist('uuids') if u]
|
||||||
|
extra_data = request.form.get('op_extradata', '').strip()
|
||||||
if (op == 'delete'):
|
_handle_operations(
|
||||||
for uuid in uuids:
|
datastore=datastore,
|
||||||
if datastore.data['watching'].get(uuid):
|
extra_data=extra_data,
|
||||||
datastore.delete(uuid)
|
queuedWatchMetaData=queuedWatchMetaData,
|
||||||
flash("{} watches deleted".format(len(uuids)))
|
uuids=uuids,
|
||||||
|
worker_handler=worker_handler,
|
||||||
elif (op == 'pause'):
|
update_q=update_q,
|
||||||
for uuid in uuids:
|
watch_check_update=watch_check_update,
|
||||||
if datastore.data['watching'].get(uuid):
|
op=op,
|
||||||
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'))
|
return redirect(url_for('watchlist.index'))
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@ class SignalHandler:
|
||||||
queue_length_signal.connect(self.handle_queue_length, weak=False)
|
queue_length_signal.connect(self.handle_queue_length, weak=False)
|
||||||
# logger.info("SignalHandler: Connected to queue_length signal")
|
# 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
|
# Create and start the queue update thread using standard threading
|
||||||
import threading
|
import threading
|
||||||
self.polling_emitter_thread = threading.Thread(
|
self.polling_emitter_thread = threading.Thread(
|
||||||
|
@ -61,6 +64,16 @@ class SignalHandler:
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Watch UUID {watch_uuid} not found in datastore")
|
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):
|
def handle_queue_length(self, *args, **kwargs):
|
||||||
"""Handle queue_length signal and emit to all clients"""
|
"""Handle queue_length signal and emit to all clients"""
|
||||||
try:
|
try:
|
||||||
|
@ -167,7 +180,6 @@ def handle_watch_update(socketio, **kwargs):
|
||||||
if hasattr(q_item, 'item') and 'uuid' in q_item.item:
|
if hasattr(q_item, 'item') and 'uuid' in q_item.item:
|
||||||
queue_list.append(q_item.item['uuid'])
|
queue_list.append(q_item.item['uuid'])
|
||||||
|
|
||||||
error_texts = ""
|
|
||||||
# Get the error texts from the watch
|
# Get the error texts from the watch
|
||||||
error_texts = watch.compile_error_texts()
|
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
|
||||||
|
@ -259,6 +271,29 @@ def init_socketio(app, datastore):
|
||||||
# Set up event handlers
|
# Set up event handlers
|
||||||
logger.info("Socket.IO: Registering connect event handler")
|
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')
|
@socketio.on('connect')
|
||||||
def handle_connect():
|
def handle_connect():
|
||||||
"""Handle client connection"""
|
"""Handle client connection"""
|
||||||
|
|
|
@ -18,6 +18,24 @@ $(document).ready(function () {
|
||||||
|
|
||||||
return false;
|
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);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,9 @@ class ChangeDetectionStore:
|
||||||
del self.data['watching'][uuid]
|
del self.data['watching'][uuid]
|
||||||
|
|
||||||
self.needs_write_urgent = True
|
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
|
# Clone a watch by UUID
|
||||||
def clone(self, uuid):
|
def clone(self, uuid):
|
||||||
|
|
Ładowanie…
Reference in New Issue