Server-side events (SSE) namesto pollinga #14

This commit is contained in:
2026-03-27 21:03:46 +01:00
parent 8c18e3fa6b
commit f75a6f04dd
3 changed files with 63 additions and 6 deletions

View File

@@ -28,8 +28,10 @@ than being generated inside Python code.
import threading
import os
import json
import queue
import time
from flask import Flask, render_template, request, jsonify
from flask import Flask, render_template, request, jsonify, Response
# create Flask app with proper folders relative to this file
app = Flask(__name__, static_folder="static", template_folder="templates")
@@ -37,6 +39,17 @@ app = Flask(__name__, static_folder="static", template_folder="templates")
# Globalna referenca na SongProjector aplikaciju
_projector_app = None
# List of queues for SSE clients
_sse_clients = []
_sse_lock = threading.Lock()
def notify_clients():
"""Notify all connected SSE clients to refresh content"""
with _sse_lock:
for q in _sse_clients:
q.put("refresh content")
def set_projector_app(projector_app):
"""Postavi referenco na SongProjector aplikacijo"""
@@ -97,6 +110,26 @@ def get_state():
})
@app.route('/api/events')
def sse_events():
"""SSE endpoint for real-time updates"""
def event_stream():
q = queue.Queue()
with _sse_lock:
_sse_clients.append(q)
try:
# Send initial refresh command on connection
yield "data: refresh content\n\n"
while True:
msg = q.get()
yield f"data: {msg}\n\n"
finally:
with _sse_lock:
_sse_clients.remove(q)
return Response(event_stream(), mimetype="text/event-stream")
@app.route('/api/load_song', methods=['POST'])
def load_song():
"""Naloži pesem po številki"""
@@ -111,6 +144,7 @@ def load_song():
_projector_app.load_song()
if hasattr(_projector_app, 'show_page'):
_projector_app.show_page()
notify_clients()
return jsonify({'status': 'ok'})
@@ -120,6 +154,7 @@ def next_page():
"""Naslednja stran"""
if _projector_app is not None:
_projector_app.next_page()
notify_clients()
return jsonify({'status': 'ok'})
@@ -129,6 +164,7 @@ def prev_page():
"""Prejšnja stran"""
if _projector_app is not None:
_projector_app.prev_page()
notify_clients()
return jsonify({'status': 'ok'})
@@ -138,6 +174,7 @@ def clear_screen():
"""Zatemniti ekran"""
if _projector_app is not None:
_projector_app.clear_screen()
notify_clients()
return jsonify({'status': 'ok'})
@@ -148,6 +185,7 @@ def toggle_caps():
if _projector_app is not None:
_projector_app.all_caps_mode = not _projector_app.all_caps_mode
_projector_app.show_page()
notify_clients()
return jsonify({'status': 'ok'})
@@ -157,6 +195,7 @@ def toggle_split():
"""Preklop med načinom preloma po kiticah in prostim prelomom"""
if _projector_app is not None:
_projector_app.toggle_split_mode()
notify_clients()
return jsonify({'status': 'ok'})
@@ -283,6 +322,7 @@ def update_song():
if str(_projector_app.song_number_last) == str(song_id):
_projector_app.song_number = str(song_id)
_projector_app.load_song()
notify_clients()
return jsonify({'status': 'ok'})
except Exception as e:

View File

@@ -648,9 +648,24 @@ document.addEventListener('keydown', (e) => {
updateState(true);
requestWakeLock();
// osveževanje za sinhronizacijo med več napravami
setInterval(() => {
updateState(false);
}, 1000);
// SSE osveževanje za sinhronizacijo med več napravami
function setupSSE() {
const evtSource = new EventSource("/api/events");
evtSource.onmessage = function(event) {
console.log("SSE dogodek:", event.data);
if (event.data === "refresh content") {
updateState(false);
}
};
evtSource.onerror = function(err) {
console.error("SSE napaka, ponovni poskus čez 5s...", err);
evtSource.close();
setTimeout(setupSSE, 5000);
};
}
setupSSE();
console.log('JavaScript inicializacija zaključena');