refactor: streamline system control UI and persist streaming state across restarts

This commit is contained in:
pstruebi
2025-09-26 13:35:17 +02:00
parent 27f5ee3d38
commit 8f9390ae50
2 changed files with 43 additions and 25 deletions

View File

@@ -438,8 +438,8 @@ else:
if start_stream:
# Always send stop to ensure backend is in a clean state, regardless of current status
r = requests.post(f"{BACKEND_URL}/stop_audio").json()
if r['was_running']:
st.success("Stream Stopped!")
#if r['was_running']:
# st.success("Stream Stopped!")
# Small pause lets backend fully release audio devices before re-init
time.sleep(1)
@@ -551,21 +551,12 @@ else:
############################
# System expander (collapsed)
############################
with st.expander("System", expanded=False):
with st.expander("System control", expanded=False):
st.subheader("Change password")
if is_pw_disabled():
st.info("Frontend password protection is disabled via DISABLE_FRONTEND_PW.")
st.subheader("System control")
if st.button("Reboot now", type="primary"):
try:
r = requests.post(f"{BACKEND_URL}/system_reboot", timeout=1)
if r.ok:
st.success("Reboot initiated. The UI will become unreachable shortly.")
else:
st.error(f"Failed to reboot: {r.status_code} {r.text}")
except Exception as e:
st.error(f"Error calling reboot: {e}")
else:
st.subheader("Change password")
with st.form("change_pw_form"):
cur = st.text_input("Current password", type="password")
new1 = st.text_input("New password", type="password")
@@ -587,16 +578,16 @@ with st.expander("System", expanded=False):
except Exception as e:
st.error(f"Failed to update password: {e}")
st.subheader("System control")
if st.button("Reboot now", type="primary"):
try:
r = requests.post(f"{BACKEND_URL}/system_reboot", timeout=1)
if r.ok:
st.success("Reboot initiated. The UI will become unreachable shortly.")
else:
st.error(f"Failed to reboot: {r.status_code} {r.text}")
except Exception as e:
st.error(f"Error calling reboot: {e}")
st.subheader("Reboot")
if st.button("Reboot now", type="primary"):
try:
r = requests.post(f"{BACKEND_URL}/system_reboot", timeout=1)
if r.ok:
st.success("Reboot initiated. The UI will become unreachable shortly.")
else:
st.error(f"Failed to reboot: {r.status_code} {r.text}")
except Exception as e:
st.error(f"Error calling reboot: {e}")
log.basicConfig(
level=os.environ.get('LOG_LEVEL', log.DEBUG),

View File

@@ -146,6 +146,7 @@ async def initialize(conf: auracast_config.AuracastConfigGroup):
'immediate_rendering': getattr(conf, 'immediate_rendering', False),
'assisted_listening_stream': getattr(conf, 'assisted_listening_stream', False),
'stream_password': (conf.bigs[0].code if conf.bigs and getattr(conf.bigs[0], 'code', None) else None),
'is_streaming': False, # will be set to True below if we actually start
'timestamp': datetime.utcnow().isoformat()
})
global_config_group = conf
@@ -160,6 +161,11 @@ async def initialize(conf: auracast_config.AuracastConfigGroup):
if any(big.audio_source.startswith("device:") or big.audio_source.startswith("file:") for big in conf.bigs):
log.info("Auto-starting streaming on multicaster1")
await multicaster1.start_streaming()
# Mark persisted state as streaming
settings = load_stream_settings() or {}
settings['is_streaming'] = True
settings['timestamp'] = datetime.utcnow().isoformat()
save_stream_settings(settings)
except Exception as e:
log.error("Exception in /init: %s", traceback.format_exc())
raise HTTPException(status_code=500, detail=str(e))
@@ -230,6 +236,16 @@ async def stop_audio():
await multicaster2.reset() # Fully reset controller and advertising
running = True
# Persist is_streaming=False
try:
settings = load_stream_settings() or {}
if settings.get('is_streaming'):
settings['is_streaming'] = False
settings['timestamp'] = datetime.utcnow().isoformat()
save_stream_settings(settings)
except Exception:
log.warning("Failed to persist is_streaming=False during stop_audio", exc_info=True)
return {"status": "stopped", "was_running": running}
except Exception as e:
log.error("Exception in /stop_audio: %s", traceback.format_exc())
@@ -268,6 +284,7 @@ async def _autostart_from_settings():
languages = settings.get('languages') or ["deu"]
stream_password = settings.get('stream_password')
original_ts = settings.get('timestamp')
previously_streaming = bool(settings.get('is_streaming'))
try:
usb_names = {d.get('name') for _, d in list_usb_pw_inputs(refresh=False)}
@@ -279,7 +296,9 @@ async def _autostart_from_settings():
else:
os.environ.setdefault("PULSE_LATENCY_MSEC", "3")
# Only auto-start device-based inputs; Webapp and Demo require external sources/UI
# Only auto-start if the previous state was streaming and it's a device-based input.
if not previously_streaming:
return
if not input_device_name:
return
if rate is None or octets is None:
@@ -467,6 +486,14 @@ async def offer(offer: Offer):
# Yield control so the Streamer coroutine has a chance to
# create the WebRTCAudioInput before we push samples.
await asyncio.sleep(0)
# Persist is_streaming=True for Webapp mode
try:
settings = load_stream_settings() or {}
settings['is_streaming'] = True
settings['timestamp'] = datetime.utcnow().isoformat()
save_stream_settings(settings)
except Exception:
log.warning("Failed to persist is_streaming=True on WebRTC start", exc_info=True)
first = False
# in stereo case this is interleaved data format
frame_array = frame.to_ndarray()