From 5bd8e51f0c787dd38f67bd95b8535b4aeba37ae7 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Fri, 21 Feb 2025 03:12:28 +0100 Subject: [PATCH] Simplify event stream management. --- src/templates/_base.tmpl | 62 ++++++++++++++++++++++++------------- src/templates/playlist.tmpl | 17 +++++----- src/ytplom/http.py | 27 +++++++++------- 3 files changed, 67 insertions(+), 39 deletions(-) diff --git a/src/templates/_base.tmpl b/src/templates/_base.tmpl index 48fefef..eefe444 100644 --- a/src/templates/_base.tmpl +++ b/src/templates/_base.tmpl @@ -10,39 +10,42 @@ const PATH_PLAYER = "/{{page_names.player}}"; const PATH_PLAYLIST = "/{{page_names.playlist}}"; const PATH_PREFIX_FILE = "/{{page_names.file}}/"; const PATH_FILES = "/{{page_names.files}}"; -var event_handlers = []; + var events_params = ""; var events_stream = null; +var event_handlers = {'ping': function(data) {}}; -function new_child_to(tag, parent, textContent='') { - const el = document.createElement(tag); - parent.appendChild(el); - el.textContent = textContent; - return el; -} - -window.addEventListener( - "beforeunload", function() { +window.addEventListener("beforeunload", function() { if (events_stream) { - events_stream.close(); } }); + events_stream.close(); + } +}); function connect_events() { events_stream = new EventSource(`${PATH_EVENTS}?${events_params}`); events_stream.onmessage = function(event) { - const data = JSON.parse(event.data); - if (data.ping) { - return; } - for (let i = 0; i < event_handlers.length; i++) { - event_handlers[i](data); }} + const payload = JSON.parse(event.data); + for (const [key, data] of Object.entries(payload)) { event_handlers[key](data) } + } events_stream.onerror = function(error) { const while_connecting = events_stream.readyState == events_stream.CONNECTING; console.log(`Error on ${PATH_EVENTS} connection:`, error); events_stream.close(); if (while_connecting) { console.log("Error seemed connection-related, trying reconnect."); - setTimeout(connect_events, RETRY_INTERVAL_S * 1000); } - else { - console.log("Error does not seem connection-related, therefore aborting."); }}} + setTimeout(connect_events, RETRY_INTERVAL_S * 1000); + } else { + console.log("Error does not seem connection-related, therefore aborting."); + } + } +} + +function new_child_to(tag, parent, textContent='') { + const el = document.createElement(tag); + parent.appendChild(el); + el.textContent = textContent; + return el; +} async function wrapped_fetch(target, fetch_kwargs=null, verbose=false) { if (verbose) { @@ -82,7 +85,7 @@ function father_tag_links(parent_element, tags) { }); } -event_handlers.push(function(data) { // update player state +event_handlers.player = function(data) { for (const [id, text] of [ ["a_playing", data.title], ["player_state", data.is_running ? (data.is_playing ? "playing:" : "paused:") : "stopped" + (data.title ? ':' : '')], @@ -96,7 +99,24 @@ event_handlers.push(function(data) { // update player state btn.disabled = !data.can_play; } document.getElementById("a_playing").href = data.title_digest ? `${PATH_PREFIX_FILE}${data.title_digest}` : PATH_PLAYLIST ; -}) +}; + + +// event_handlers.push(function(data) { // update player state +// for (const [id, text] of [ +// ["a_playing", data.title], +// ["player_state", data.is_running ? (data.is_playing ? "playing:" : "paused:") : "stopped" + (data.title ? ':' : '')], +// ["btn_play", data.is_playing ? "pause" : "play"]]) { +// document.getElementById(id).textContent = text; +// } +// const span_tags = document.getElementById("playing_tags"); +// span_tags.innerHTML = ""; +// father_tag_links(span_tags, data.title_tags); +// for (const btn of document.getElementsByClassName("btn_if_can_play")) { +// btn.disabled = !data.can_play; +// } +// document.getElementById("a_playing").href = data.title_digest ? `${PATH_PREFIX_FILE}${data.title_digest}` : PATH_PLAYLIST ; +// }) {% block script %} {% endblock %} diff --git a/src/templates/playlist.tmpl b/src/templates/playlist.tmpl index 3e36317..bea11b9 100644 --- a/src/templates/playlist.tmpl +++ b/src/templates/playlist.tmpl @@ -13,11 +13,10 @@ const CLS_PLAYLIST_ROW = 'playlist_row'; events_params += 'playlist=1'; var first_load = true; -event_handlers.push(function(data) { // update playlist +event_handlers.playlist = function(data) { // update playlist const table = document.getElementById('playlist_rows'); var old_rows = document.getElementsByClassName(CLS_PLAYLIST_ROW); - while (old_rows[0]) { - old_rows[0].remove(); } + while (old_rows[0]) { old_rows[0].remove(); } for (let i = 0; i < data.playlist_files.length; i++) { const file = data.playlist_files[i]; const tr = new_child_to('tr', table); @@ -33,18 +32,22 @@ event_handlers.push(function(data) { // update playlist td_entry_control.id = 'playing'; if (first_load) { // to replace anchor jump to #playing, which on first first_load = false; // load will not work yet since element not built yet - td_entry_control.scrollIntoView({block: 'center'}); } + td_entry_control.scrollIntoView({block: 'center'}); } - else { + } else { for (const [symbol, prefix] of [['>', 'jump'], ['^', 'up'], ['v', 'down'], ['x', 'rm']]) { const btn = new_child_to('button', td_entry_control, symbol); - btn.onclick = function() { player_command(`${prefix}_${i}`) }; }} + btn.onclick = function() { player_command(`${prefix}_${i}`) }; + } + } const td_link = new_child_to('td', tr); const a_file = new_child_to('a', td_link, file.rel_path); - a_file.href = `${PATH_PREFIX_FILE}${file.digest}`; }}) + a_file.href = `${PATH_PREFIX_FILE}${file.digest}`; + } +}; {% endblock %} diff --git a/src/ytplom/http.py b/src/ytplom/http.py index 85c25b4..c2a25d4 100644 --- a/src/ytplom/http.py +++ b/src/ytplom/http.py @@ -261,7 +261,7 @@ class _TaskHandler(PlomHttpHandler): time_last_write = 0.0 while True: if not payload and time_last_write < time() - _PING_INTERVAL_S: - payload['ping'] = True + payload['ping'] = {} if payload: payload_encoded = f'data: {json_dumps(payload)}\n\n'.encode() try: @@ -285,17 +285,22 @@ class _TaskHandler(PlomHttpHandler): title = str(selected.rel_path) digest = selected.digest.b64 tags = selected.tags_showable.as_str_list - payload['is_running'] = self.server.player.is_running - payload['is_playing'] = self.server.player.is_playing - payload['can_play'] = self.server.player.can_play - payload['title_tags'] = tags - payload['title_digest'] = digest - payload['title'] = title + payload['player'] = { + 'is_running': self.server.player.is_running, + 'is_playing': self.server.player.is_playing, + 'can_play': self.server.player.can_play, + 'title_tags': tags, + 'title_digest': digest, + 'title': title + } if self.params.has_key('playlist'): - payload['idx'] = self.server.player.idx - payload['playlist_files'] = [ - {'rel_path': str(f.rel_path), 'digest': f.digest.b64} - for f in self.server.player.playlist] + payload['playlist'] = { + 'idx': self.server.player.idx, + 'playlist_files': [ + {'rel_path': str(f.rel_path), + 'digest': f.digest.b64} + for f in self.server.player.playlist] + } else: sleep(_EVENTS_UPDATE_INTERVAL_S) -- 2.30.2