Server-side events (SSE) namesto pollinga #14
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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');
|
||||||
Reference in New Issue
Block a user