122 lines
5.2 KiB
Python
122 lines
5.2 KiB
Python
from flask import Flask
|
|
from flask_socketio import SocketIO
|
|
import threading
|
|
import json
|
|
import time
|
|
from loguru import logger
|
|
import blinker
|
|
|
|
from changedetectionio.flask_app import _jinja2_filter_datetime, watch_check_completed
|
|
|
|
|
|
class SignalHandler:
|
|
"""A standalone class to receive signals"""
|
|
def __init__(self, socketio_instance):
|
|
self.socketio_instance = socketio_instance
|
|
|
|
# Get signal from app config
|
|
app_signal = socketio_instance.main_app.config.get('WATCH_CHECK_COMPLETED_SIGNAL')
|
|
if app_signal:
|
|
app_signal.connect(self.handle_signal, weak=False)
|
|
logger.info("SignalHandler: Connected to signal from app config")
|
|
else:
|
|
# Fallback if not in app config
|
|
from changedetectionio.flask_app import watch_check_completed as wcc
|
|
wcc.connect(self.handle_signal, weak=False)
|
|
logger.info("SignalHandler: Connected to signal from direct import")
|
|
|
|
def handle_signal(self, *args, **kwargs):
|
|
logger.info(f"SignalHandler: Signal received with {len(args)} args and {len(kwargs)} kwargs")
|
|
# Safely extract the watch UUID from kwargs
|
|
watch_uuid = kwargs.get('watch_uuid')
|
|
if watch_uuid:
|
|
# Get the datastore from the socket instance
|
|
datastore = self.socketio_instance.datastore
|
|
# Get the watch object from the datastore
|
|
watch = datastore.data['watching'].get(watch_uuid)
|
|
if watch:
|
|
# Forward to the socket instance with the watch parameter
|
|
self.socketio_instance.handle_watch_update(watch=watch)
|
|
logger.info(f"Signal handler processed watch UUID {watch_uuid}")
|
|
else:
|
|
logger.warning(f"Watch UUID {watch_uuid} not found in datastore")
|
|
|
|
class ChangeDetectionSocketIO:
|
|
def __init__(self, app, datastore):
|
|
self.main_app = app
|
|
self.datastore = datastore
|
|
|
|
# Use threading mode instead of eventlet
|
|
self.socketio = SocketIO(self.main_app,
|
|
async_mode='threading',
|
|
cors_allowed_origins="*",
|
|
logger=False,
|
|
engineio_logger=False)
|
|
|
|
# Set up event handlers
|
|
self.socketio.on_event('connect', self.handle_connect)
|
|
self.socketio.on_event('disconnect', self.handle_disconnect)
|
|
|
|
# Don't patch the update_watch method - this was causing issues
|
|
# Just start a background thread to periodically emit watch status
|
|
self.thread = None
|
|
self.thread_lock = threading.Lock()
|
|
|
|
# Create a dedicated signal handler
|
|
self.signal_handler = SignalHandler(self)
|
|
|
|
def handle_connect(self):
|
|
"""Handle client connection"""
|
|
logger.info("Socket.IO: Client connected")
|
|
|
|
|
|
def handle_disconnect(self):
|
|
"""Handle client disconnection"""
|
|
logger.info("Socket.IO: Client disconnected")
|
|
|
|
def handle_watch_update(self, **kwargs):
|
|
"""Handle watch update signal from blinker"""
|
|
try:
|
|
watch = kwargs.get('watch')
|
|
# Emit the watch update to all connected clients
|
|
with self.main_app.app_context():
|
|
from changedetectionio.flask_app import running_update_threads, update_q
|
|
|
|
# Get list of watches that are currently running
|
|
running_uuids = []
|
|
for t in running_update_threads:
|
|
if hasattr(t, 'current_uuid') and t.current_uuid:
|
|
running_uuids.append(t.current_uuid)
|
|
|
|
# Get list of watches in the queue
|
|
queue_list = []
|
|
for q_item in update_q.queue:
|
|
if hasattr(q_item, 'item') and 'uuid' in q_item.item:
|
|
queue_list.append(q_item.item['uuid'])
|
|
|
|
# Create a simplified watch data object to send to clients
|
|
watch_data = {
|
|
'uuid': watch.get('uuid'),
|
|
'last_checked_text': _jinja2_filter_datetime(watch),
|
|
'last_checked': watch.get('last_checked'),
|
|
'last_changed': watch.get('last_changed'),
|
|
'queued': True if watch.get('uuid') in queue_list else False,
|
|
'checking_now': True if watch.get('uuid') in running_uuids else False,
|
|
'unviewed': watch.has_unviewed,
|
|
}
|
|
self.socketio.emit("watch_update", watch_data)
|
|
logger.debug(f"Socket.IO: Emitted update for watch {watch.uuid}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Socket.IO error in handle_watch_update: {str(e)}")
|
|
|
|
|
|
def run(self, host='0.0.0.0', port=5005):
|
|
"""Run the Socket.IO server on a separate port"""
|
|
# Start the background task when the server starts
|
|
#self.start_background_task()
|
|
|
|
# Run the Socket.IO server
|
|
# Use 0.0.0.0 to listen on all interfaces
|
|
logger.info(f"Starting Socket.IO server on http://{host}:{port}")
|
|
self.socketio.run(self.main_app, host=host, port=port, debug=False, use_reloader=False, allow_unsafe_werkzeug=True) |