From c2fd0a8c46addcdd8b65a63ede2bdbec7406cb09 Mon Sep 17 00:00:00 2001 From: pstruebi Date: Mon, 22 Sep 2025 12:30:03 +0200 Subject: [PATCH] feat: add status indicator and improve UI layout in multicast frontend --- src/auracast/server/multicast_frontend.py | 26 ++++++++++++++++++++--- src/auracast/server/multicast_server.py | 21 ++++++++++++------ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/auracast/server/multicast_frontend.py b/src/auracast/server/multicast_frontend.py index 4bff032..c8f726f 100644 --- a/src/auracast/server/multicast_frontend.py +++ b/src/auracast/server/multicast_frontend.py @@ -6,6 +6,9 @@ import requests from auracast import auracast_config import logging as log +# Set page configuration (tab title and icon) before using other Streamlit APIs +st.set_page_config(page_title="Castbox", page_icon="", layout="centered") + # Track whether WebRTC stream is active across Streamlit reruns if 'stream_started' not in st.session_state: st.session_state['stream_started'] = False @@ -30,7 +33,7 @@ try: except Exception: saved_settings = {} -st.title("🎙️ Auracast Audio Mode Control") +st.title("Auracast Audio Mode Control") # Audio mode selection with persisted default # Note: backend persists 'USB' for any device: source (including AES67). We default to 'USB' in that case. @@ -154,6 +157,7 @@ if audio_mode == "Demo": st.session_state['demo_stream_started'] = False if r.get('was_running'): st.info("Demo stream stopped.") + st.rerun() else: st.info("Demo stream was not running.") except Exception as e: @@ -277,8 +281,22 @@ else: else: input_device = None - start_stream = st.button("Start Auracast") - stop_stream = st.button("Stop Auracast") + # Buttons and status on a single row (4 columns: start, stop, spacer, status) + c_start, c_stop, c_spacer, c_status = st.columns([1, 1, 1, 2], gap="small", vertical_alignment="center") + with c_start: + start_stream = st.button("Start Auracast") + with c_stop: + stop_stream = st.button("Stop Auracast") + # c_spacer intentionally left empty to push status to the far right + with c_status: + # Fetch current status from backend and render using Streamlit widgets (no HTML) + try: + status_resp = requests.get(f"{BACKEND_URL}/status", timeout=0.8) + status_json = status_resp.json() if status_resp.ok else {} + except Exception: + status_json = {} + is_streaming = bool(status_json.get("is_streaming", False)) + st.write("🟢 Streaming" if is_streaming else "🔴 Stopped") # If gain slider moved while streaming, send update to JS without restarting if audio_mode == "Webapp" and st.session_state.get('stream_started'): @@ -295,6 +313,7 @@ else: r = requests.post(f"{BACKEND_URL}/stop_audio").json() if r['was_running']: st.success("Stream Stopped!") + st.rerun() else: st.success("Stream was not running.") except Exception as e: @@ -351,6 +370,7 @@ else: r = requests.post(f"{BACKEND_URL}/init", json=config.model_dump()) if r.status_code == 200: st.success("Stream Started!") + st.rerun() else: st.error(f"Failed to initialize: {r.text}") except Exception as e: diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index 8bd2f34..ba33fe1 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -102,8 +102,17 @@ async def initialize(conf: auracast_config.AuracastConfigGroup): # Derive audio_mode and input_device from first BIG audio_source first_source = conf.bigs[0].audio_source if conf.bigs else '' if first_source.startswith('device:'): - audio_mode_persist = 'USB' input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None + # Determine if the device is a USB or Network(AES67) PipeWire input + try: + usb_names = {d.get('name') for _, d in list_usb_pw_inputs(refresh=False)} + net_names = {d.get('name') for _, d in list_network_pw_inputs(refresh=False)} + except Exception: + usb_names, net_names = set(), set() + if input_device_name in net_names: + audio_mode_persist = 'AES67' + else: + audio_mode_persist = 'USB' # Map device name to current index for use with sounddevice device_index = get_device_index_by_name(input_device_name) if input_device_name else None # Patch config to use index for sounddevice (but persist name) @@ -307,11 +316,11 @@ async def _autostart_from_settings(): except Exception: log.warning("Autostart task failed", exc_info=True) - -# @app.on_event("startup") -# async def _startup_autostart_event(): -# # Spawn the autostart task without blocking startup -# asyncio.create_task(_autostart_from_settings()) +#TODO: enable and test this +@app.on_event("startup") +async def _startup_autostart_event(): + # Spawn the autostart task without blocking startup + asyncio.create_task(_autostart_from_settings()) @app.post("/refresh_audio_inputs")