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

@@ -29,7 +29,7 @@ import subprocess
import sys import sys
import ctypes import ctypes
import tkinter.messagebox as messagebox import tkinter.messagebox as messagebox
from web.server import start_server_thread from web.server import start_server_thread, notify_clients
import urllib.request import urllib.request
import tempfile import tempfile
from db_schema import create_tables from db_schema import create_tables
@@ -476,6 +476,7 @@ class SongProjector:
self.song_info_label.lift() self.song_info_label.lift()
else: else:
self.song_info_label.config(text="") self.song_info_label.config(text="")
notify_clients()
# ------------------------------------------------------ # ------------------------------------------------------
# Navigacija po straneh # Navigacija po straneh
@@ -504,6 +505,7 @@ class SongProjector:
self.color_frame.config(bg="black", width=self.color_width, height=self.screen_height) self.color_frame.config(bg="black", width=self.color_width, height=self.screen_height)
self.color_frame.place(relx=0.5, rely=0.5, anchor="center") self.color_frame.place(relx=0.5, rely=0.5, anchor="center")
self.song_info_label.config(text="") self.song_info_label.config(text="")
notify_clients()
# ------------------------------------------------------ # ------------------------------------------------------
# Iskanje po naslovu # Iskanje po naslovu

View File

@@ -28,8 +28,10 @@ than being generated inside Python code.
import threading import threading
import os import os
import json 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 # create Flask app with proper folders relative to this file
app = Flask(__name__, static_folder="static", template_folder="templates") 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 # Globalna referenca na SongProjector aplikaciju
_projector_app = None _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): def set_projector_app(projector_app):
"""Postavi referenco na SongProjector aplikacijo""" """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']) @app.route('/api/load_song', methods=['POST'])
def load_song(): def load_song():
"""Naloži pesem po številki""" """Naloži pesem po številki"""
@@ -111,6 +144,7 @@ def load_song():
_projector_app.load_song() _projector_app.load_song()
if hasattr(_projector_app, 'show_page'): if hasattr(_projector_app, 'show_page'):
_projector_app.show_page() _projector_app.show_page()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
@@ -120,6 +154,7 @@ def next_page():
"""Naslednja stran""" """Naslednja stran"""
if _projector_app is not None: if _projector_app is not None:
_projector_app.next_page() _projector_app.next_page()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
@@ -129,6 +164,7 @@ def prev_page():
"""Prejšnja stran""" """Prejšnja stran"""
if _projector_app is not None: if _projector_app is not None:
_projector_app.prev_page() _projector_app.prev_page()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
@@ -138,6 +174,7 @@ def clear_screen():
"""Zatemniti ekran""" """Zatemniti ekran"""
if _projector_app is not None: if _projector_app is not None:
_projector_app.clear_screen() _projector_app.clear_screen()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
@@ -148,6 +185,7 @@ def toggle_caps():
if _projector_app is not None: if _projector_app is not None:
_projector_app.all_caps_mode = not _projector_app.all_caps_mode _projector_app.all_caps_mode = not _projector_app.all_caps_mode
_projector_app.show_page() _projector_app.show_page()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
@@ -157,6 +195,7 @@ def toggle_split():
"""Preklop med načinom preloma po kiticah in prostim prelomom""" """Preklop med načinom preloma po kiticah in prostim prelomom"""
if _projector_app is not None: if _projector_app is not None:
_projector_app.toggle_split_mode() _projector_app.toggle_split_mode()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
@@ -283,6 +322,7 @@ def update_song():
if str(_projector_app.song_number_last) == str(song_id): if str(_projector_app.song_number_last) == str(song_id):
_projector_app.song_number = str(song_id) _projector_app.song_number = str(song_id)
_projector_app.load_song() _projector_app.load_song()
notify_clients()
return jsonify({'status': 'ok'}) return jsonify({'status': 'ok'})
except Exception as e: except Exception as e:

View File

@@ -648,9 +648,24 @@ document.addEventListener('keydown', (e) => {
updateState(true); updateState(true);
requestWakeLock(); requestWakeLock();
// osveževanje za sinhronizacijo med več napravami // SSE osveževanje za sinhronizacijo med več napravami
setInterval(() => { 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); updateState(false);
}, 1000); }
};
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'); console.log('JavaScript inicializacija zaključena');