diff --git a/src/auracast/server/multicast_frontend.py b/src/auracast/server/multicast_frontend.py index be22113..25bfd21 100644 --- a/src/auracast/server/multicast_frontend.py +++ b/src/auracast/server/multicast_frontend.py @@ -163,6 +163,7 @@ audio_mode = st.selectbox( "Audio Mode", options, index=options.index(saved_audio_mode) if saved_audio_mode in options else options.index("Demo"), + disabled=is_streaming, help=( "Select the audio input source. Choose 'USB' for a connected USB audio device (via PipeWire), " "'Network' (AES67) for network RTP/AES67 sources, " @@ -192,10 +193,10 @@ else: running_mode = backend_mode_mapped if (is_streaming and backend_mode_mapped) else audio_mode # Start/Stop buttons and status (moved to top) -if audio_mode != "Demo": - start_stream, stop_stream = render_stream_controls(is_streaming, "Start Auracast", "Stop Auracast", running_mode, secondary_is_streaming) +if audio_mode == "Demo": + start_stream, stop_stream = render_stream_controls(is_streaming, "Start Demo", "Stop Demo", running_mode, secondary_is_streaming) else: - start_stream, stop_stream = False, False + start_stream, stop_stream = render_stream_controls(is_streaming, "Start Auracast", "Stop Auracast", running_mode, secondary_is_streaming) # Placeholder for validation errors (will be filled in later) validation_error_placeholder = st.empty() @@ -238,6 +239,7 @@ if audio_mode == "Demo": "Demo Stream Type", demo_options, index=default_index, + disabled=is_streaming, help="Select the demo stream configuration." ) # Stream password and flags (same as USB/AES67) @@ -246,6 +248,7 @@ if audio_mode == "Demo": "Stream Passwort", value=saved_pwd, type=("password"), + disabled=is_streaming, help="Optional: Set a broadcast code to protect your stream. Leave empty for an open (uncoded) broadcast." ) col_flags1, col_flags2, col_pdelay, col_qos = st.columns([1, 1, 0.7, 0.6], gap="small", vertical_alignment="center") @@ -253,12 +256,14 @@ if audio_mode == "Demo": assisted_listening = st.checkbox( "Assistive listening", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) with col_flags2: immediate_rendering = st.checkbox( "Immediate rendering", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) # QoS/presentation controls inline with flags @@ -268,6 +273,7 @@ if audio_mode == "Demo": presentation_delay_ms = st.number_input( "Delay (ms)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Delay between capture and presentation for receivers." ) with col_qos: @@ -276,103 +282,10 @@ if audio_mode == "Demo": default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 qos_preset = st.selectbox( "QoS", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) #st.info(f"Demo mode selected: {demo_selected} (Streams: {demo_stream_map[demo_selected]['streams']}, Rate: {demo_stream_map[demo_selected]['rate']} Hz)") - # Start/Stop buttons for demo mode - if 'demo_stream_started' not in st.session_state: - st.session_state['demo_stream_started'] = False - start_demo, stop_demo = render_stream_controls(is_streaming, "Start Demo", "Stop Demo", running_mode, secondary_is_streaming) - if start_demo: - # Always stop any running stream for clean state - try: - requests.post(f"{BACKEND_URL}/stop_audio").json() - except Exception: - pass - time.sleep(1) - demo_cfg = demo_stream_map[demo_selected] - # Octets per frame logic matches quality_map - q = QUALITY_MAP[demo_cfg['quality']] - - # Language configs and test files - lang_cfgs = [ - (auracast_config.AuracastBigConfigDeu, 'de'), - (auracast_config.AuracastBigConfigEng, 'en'), - (auracast_config.AuracastBigConfigFra, 'fr'), - (auracast_config.AuracastBigConfigSpa, 'es'), - (auracast_config.AuracastBigConfigIta, 'it'), - (auracast_config.AuracastBigConfigPol, 'pl'), - ] - bigs1 = [] - for i in range(demo_cfg['streams']): - cfg_cls, lang = lang_cfgs[i % len(lang_cfgs)] - bigs1.append(cfg_cls( - code=(stream_passwort.strip() or None), - audio_source=f'file:../testdata/wave_particle_5min_{lang}_{int(q["rate"]/1000)}kHz_mono.wav', - iso_que_len=32, - sampling_frequency=q['rate'], - octets_per_frame=q['octets'], - )) - - # Split bigs into two configs if needed - max_per_mc = {48000: 1, 24000: 2, 16000: 3} - max_streams = max_per_mc.get(q['rate'], 3) - bigs2 = [] - if len(bigs1) > max_streams: - bigs2 = bigs1[max_streams:] - bigs1 = bigs1[:max_streams] - config1 = auracast_config.AuracastConfigGroup( - auracast_sampling_rate_hz=q['rate'], - octets_per_frame=q['octets'], - transport='', # is set in baccol_qoskend - assisted_listening_stream=assisted_listening, - immediate_rendering=immediate_rendering, - presentation_delay_us=int(presentation_delay_ms * 1000), - qos_config=QOS_PRESET_MAP[qos_preset], - bigs=bigs1 - ) - config2 = None - if bigs2: - config2 = auracast_config.AuracastConfigGroup( - auracast_sampling_rate_hz=q['rate'], - octets_per_frame=q['octets'], - transport='', # is set in backend - assisted_listening_stream=assisted_listening, - immediate_rendering=immediate_rendering, - presentation_delay_us=int(presentation_delay_ms * 1000), - qos_config=QOS_PRESET_MAP[qos_preset], - bigs=bigs2 - ) - # Call /init and /init2 - is_started = False - try: - r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) - if r1.status_code == 200: - st.session_state['demo_stream_started'] = True - is_started = True - else: - st.session_state['demo_stream_started'] = False - st.error(f"Failed to initialize multicaster 1: {r1.text}") - if config2: - r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) - if r2.status_code == 200: - is_started = True - else: - st.error(f"Failed to initialize multicaster 2: {r2.text}") - except Exception as e: - st.session_state['demo_stream_started'] = False - st.error(f"Error: {e}") - if is_started: - pass - elif stop_demo: - try: - r = requests.post(f"{BACKEND_URL}/stop_audio").json() - st.session_state['demo_stream_started'] = False - if r.get('was_running'): - is_stopped = True - except Exception as e: - st.error(f"Error: {e}") - quality = None # Not used in demo mode else: # --- Mode-specific configuration --- @@ -395,14 +308,6 @@ else: with st.container(border=True): st.subheader("Radio 1") - # Stereo mode toggle for analog - stereo_enabled = st.checkbox( - "🎧 Stereo Mode", - value=bool(saved_settings.get('analog_stereo_mode', False)), - help="Enable stereo streaming for analog inputs. When enabled, ch1 becomes left channel and ch2 becomes right channel in a single stereo stream. Radio 2 will be disabled in stereo mode.", - disabled=is_streaming - ) - # Always-enabled checkbox for Radio 1 st.checkbox( "Radio 1 always enabled", @@ -411,6 +316,14 @@ else: help="Radio 1 is always enabled, Radio 2 can be turned on or off." ) + # Stereo mode toggle for analog + stereo_enabled = st.checkbox( + "🎧 Stereo Mode", + value=bool(saved_settings.get('analog_stereo_mode', False)), + help="Enable stereo streaming for analog inputs. When enabled, ch1 becomes left channel and ch2 becomes right channel in a single stereo stream. Radio 2 will be disabled in stereo mode.", + disabled=is_streaming + ) + # Use analog-specific defaults (not from saved settings which may have Dante values) default_name = "Analog_Radio_1" default_program_info = "Analog Radio Broadcast" @@ -422,6 +335,7 @@ else: "Stream Quality (Radio 1)", quality_options, index=quality_options.index(default_quality), + disabled=is_streaming, help="Select the audio sampling rate for Radio 1." ) @@ -429,6 +343,7 @@ else: "Stream Passwort (Radio 1)", value="", type="password", + disabled=is_streaming, help="Optional: Set a broadcast code for Radio 1." ) @@ -437,12 +352,14 @@ else: assisted_listening1 = st.checkbox( "Assistive listening (R1)", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) with col_r1_flags2: immediate_rendering1 = st.checkbox( "Immediate rendering (R1)", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) default_pdelay = int(saved_settings.get('presentation_delay_us', 40000) or 40000) @@ -451,6 +368,7 @@ else: presentation_delay_ms1 = st.number_input( "Delay (ms, R1)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Delay between capture and presentation for Radio 1." ) with col_r1_qos: @@ -459,6 +377,7 @@ else: default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 qos_preset1 = st.selectbox( "QoS (R1)", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) @@ -467,17 +386,20 @@ else: stream_name1 = st.text_input( "Channel Name (Radio 1)", value=default_name, + disabled=is_streaming, help="Name for the first analog radio (Radio 1)." ) with col_r1_lang: language1 = st.text_input( "Language (ISO 639-3) (Radio 1)", value=default_lang, + disabled=is_streaming, help="Language code for Radio 1." ) program_info1 = st.text_input( "Program Info (Radio 1)", value=default_program_info, + disabled=is_streaming, help="Program information for Radio 1." ) @@ -517,6 +439,7 @@ else: "Input Device (Radio 1) - Stereo", ['ch1 + ch2 (Stereo: Left+Right channels)'], index=0, + disabled=is_streaming, help="Stereo mode: Captures both ch1 (left) and ch2 (right) as a single stereo stream" ) st.info("🎧 Stereo mode enabled - both ch1 and ch2 will be captured as left/right channels") @@ -530,6 +453,7 @@ else: "Input Device (Radio 1)", analog_names, index=default_r1_idx, + disabled=is_streaming, ) else: input_device1 = None @@ -568,6 +492,7 @@ else: radio2_enabled = st.checkbox( "Enable Radio 2", value=radio2_enabled_default, + disabled=is_streaming, help="Activate a second analog radio with its own quality and timing settings." ) @@ -581,6 +506,7 @@ else: "Stream Quality (Radio 2)", quality_options, index=quality_options.index(default_quality), + disabled=is_streaming, help="Select the audio sampling rate for Radio 2." ) @@ -588,6 +514,7 @@ else: "Stream Passwort (Radio 2)", value="", type="password", + disabled=is_streaming, help="Optional: Set a broadcast code for Radio 2." ) @@ -596,18 +523,21 @@ else: assisted_listening2 = st.checkbox( "Assistive listening (R2)", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) with col_r2_flags2: immediate_rendering2 = st.checkbox( "Immediate rendering (R2)", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) with col_r2_pdelay: presentation_delay_ms2 = st.number_input( "Delay (ms, R2)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Delay between capture and presentation for Radio 2." ) with col_r2_qos: @@ -615,6 +545,7 @@ else: default_qos_idx2 = qos_options.index(saved_qos2) if saved_qos2 in qos_options else 0 qos_preset2 = st.selectbox( "QoS (R2)", options=qos_options, index=default_qos_idx2, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) @@ -623,17 +554,20 @@ else: stream_name2 = st.text_input( "Channel Name (Radio 2)", value=default_name_r2, + disabled=is_streaming, help="Name for the second analog radio (Radio 2)." ) with col_r2_lang: language2 = st.text_input( "Language (ISO 639-3) (Radio 2)", value=default_lang_r2, + disabled=is_streaming, help="Language code for Radio 2." ) program_info2 = st.text_input( "Program Info (Radio 2)", value=default_program_info_r2, + disabled=is_streaming, help="Program information for Radio 2." ) @@ -644,6 +578,7 @@ else: "Input Device (Radio 2)", analog_names, index=default_r2_idx, + disabled=is_streaming, ) else: input_device2 = None @@ -791,6 +726,7 @@ else: "Stream Configuration (Radio 1)", r1_stream_options, index=default_r1_idx, + disabled=is_streaming, help="Select the number and quality of streams for Radio 1" ) @@ -815,6 +751,7 @@ else: "Stream Quality (Radio 1)", r1_available_qualities, index=r1_available_qualities.index(saved_r1_quality), + disabled=is_streaming, help=f"Select stream quality for Radio 1. Maximum quality based on configuration: {r1_max_quality}" ) @@ -826,6 +763,7 @@ else: r1_assisted_listening = st.checkbox( "Assistive (R1)", value=bool(saved_r1_config.get('assisted_listening', False)), + disabled=is_streaming, help="Assistive listening stream" ) @@ -833,6 +771,7 @@ else: r1_immediate_rendering = st.checkbox( "Immediate (R1)", value=bool(saved_r1_config.get('immediate_rendering', False)), + disabled=is_streaming, help="Ignore presentation delay" ) @@ -842,6 +781,7 @@ else: r1_presentation_delay_ms = st.number_input( "Delay (ms, R1)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Presentation delay for Radio 1" ) @@ -851,6 +791,7 @@ else: default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 r1_qos_preset = st.selectbox( "QoS (R1)", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Quality of Service preset for Radio 1" ) @@ -874,6 +815,7 @@ else: stream_name = st.text_input( "Channel Name", value=saved_stream.get('name', 'Dante_Stereo'), + disabled=is_streaming, key="r1_stereo_name" ) @@ -882,6 +824,7 @@ else: "Stream Password", value=saved_stream.get('stream_password', ''), type="password", + disabled=is_streaming, key="r1_stereo_password", help="Optional: Set a broadcast code for this stream" ) @@ -893,6 +836,7 @@ else: program_info = st.text_input( "Program Info", value=saved_stream.get('program_info', 'Dante Stereo Broadcast'), + disabled=is_streaming, key="r1_stereo_program" ) @@ -900,6 +844,7 @@ else: language = st.text_input( "Language", value=saved_stream.get('language', 'eng'), + disabled=is_streaming, key="r1_stereo_lang", help="ISO 639-3 language code" ) @@ -944,6 +889,7 @@ else: stream_name = st.text_input( f"Channel Name", value=saved_stream.get('name', f'Dante_R1_S{i+1}'), + disabled=is_streaming, key=f"r1_stream_{i}_name" ) @@ -952,6 +898,7 @@ else: f"Stream Password", value=saved_stream.get('stream_password', ''), type="password", + disabled=is_streaming, key=f"r1_stream_{i}_password", help="Optional: Set a broadcast code for this stream" ) @@ -963,6 +910,7 @@ else: program_info = st.text_input( f"Program Info", value=saved_stream.get('program_info', f'Dante Radio 1 Stream {i+1}'), + disabled=is_streaming, key=f"r1_stream_{i}_program" ) @@ -970,6 +918,7 @@ else: language = st.text_input( f"Language", value=saved_stream.get('language', 'eng'), + disabled=is_streaming, key=f"r1_stream_{i}_lang", help="ISO 639-3 language code" ) @@ -996,6 +945,7 @@ else: f"Input Device", input_options, index=input_options.index(default_input_label) if default_input_label in input_options else 0, + disabled=is_streaming, key=f"r1_stream_{i}_device" ) input_device = option_name_map.get(selected_option) @@ -1044,6 +994,7 @@ else: radio2_enabled = st.checkbox( "Enable Radio 2", value=radio2_enabled_default, + disabled=is_streaming, help="Activate a second Dante radio with its own quality and timing settings." ) @@ -1057,6 +1008,7 @@ else: "Stream Configuration (Radio 2)", r2_stream_options, index=default_r2_idx, + disabled=is_streaming, help="Select the number and quality of streams for Radio 2" ) r2_num_streams = dante_stream_options[r2_stream_config]["streams"] @@ -1080,6 +1032,7 @@ else: "Stream Quality (Radio 2)", r2_available_qualities, index=r2_available_qualities.index(saved_r2_quality), + disabled=is_streaming, help=f"Select stream quality for Radio 2. Maximum quality based on configuration: {r2_max_quality}" ) @@ -1091,6 +1044,7 @@ else: r2_assisted_listening = st.checkbox( "Assistive (R2)", value=bool(saved_r2_config.get('assisted_listening', False)), + disabled=is_streaming, help="Assistive listening stream" ) @@ -1098,6 +1052,7 @@ else: r2_immediate_rendering = st.checkbox( "Immediate (R2)", value=bool(saved_r2_config.get('immediate_rendering', False)), + disabled=is_streaming, help="Ignore presentation delay" ) @@ -1107,6 +1062,7 @@ else: r2_presentation_delay_ms = st.number_input( "Delay (ms, R2)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Presentation delay for Radio 2" ) @@ -1116,6 +1072,7 @@ else: default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 r2_qos_preset = st.selectbox( "QoS (R2)", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Quality of Service preset for Radio 2" ) @@ -1135,6 +1092,7 @@ else: stream_name = st.text_input( f"Channel Name", value=saved_stream.get('name', f'Dante_R2_S{i+1}'), + disabled=is_streaming, key=f"r2_stream_{i}_name" ) @@ -1143,6 +1101,7 @@ else: f"Stream Password", value=saved_stream.get('stream_password', ''), type="password", + disabled=is_streaming, key=f"r2_stream_{i}_password", help="Optional: Set a broadcast code for this stream" ) @@ -1154,6 +1113,7 @@ else: program_info = st.text_input( f"Program Info", value=saved_stream.get('program_info', f'Dante Radio 2 Stream {i+1}'), + disabled=is_streaming, key=f"r2_stream_{i}_program" ) @@ -1161,6 +1121,7 @@ else: language = st.text_input( f"Language", value=saved_stream.get('language', 'eng'), + disabled=is_streaming, key=f"r2_stream_{i}_lang", help="ISO 639-3 language code" ) @@ -1187,6 +1148,7 @@ else: f"Input Device", input_options, index=input_options.index(default_input_label) if default_input_label in input_options else 0, + disabled=is_streaming, key=f"r2_stream_{i}_device" ) input_device = option_name_map.get(selected_option) @@ -1285,6 +1247,7 @@ else: "Stream Quality (Sampling Rate)", quality_options, index=quality_options.index(default_quality), + disabled=is_streaming, help="Select the audio sampling rate for the stream. Lower rates may improve compatibility." ) @@ -1292,6 +1255,7 @@ else: "Stream Passwort", value="", type="password", + disabled=is_streaming, help="Optional: Set a broadcast code to protect your stream. Leave empty for an open (uncoded) broadcast." ) @@ -1300,12 +1264,14 @@ else: assisted_listening = st.checkbox( "Assistive listening", value=bool(saved_settings.get('assisted_listening_stream', False)), + disabled=is_streaming, help="tells the receiver that this is an assistive listening stream" ) with col_flags2: immediate_rendering = st.checkbox( "Immediate rendering", value=bool(saved_settings.get('immediate_rendering', False)), + disabled=is_streaming, help="tells the receiver to ignore presentation delay and render immediately if possible." ) default_pdelay = int(saved_settings.get('presentation_delay_us', 40000) or 40000) @@ -1314,6 +1280,7 @@ else: presentation_delay_ms = st.number_input( "Delay (ms)", min_value=10, max_value=200, step=5, value=default_pdelay_ms, + disabled=is_streaming, help="Delay between capture and presentation for receivers." ) with col_qos: @@ -1322,22 +1289,26 @@ else: default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 qos_preset = st.selectbox( "QoS", options=qos_options, index=default_qos_idx, + disabled=is_streaming, help="Fast: 2 retransmissions, lower latency. Robust: 4 retransmissions, better reliability." ) stream_name = st.text_input( "Channel Name", value=default_name, + disabled=is_streaming, help="The primary name for your broadcast. Like the SSID of a WLAN, it identifies your stream for receivers." ) program_info = st.text_input( "Program Info", value=default_program_info, + disabled=is_streaming, help="Additional details about the broadcast program, such as its content or purpose. Shown to receivers for more context." ) language = st.text_input( "Language (ISO 639-3)", value=default_lang, + disabled=is_streaming, help="Three-letter language code (e.g., 'eng' for English, 'deu' for German). Used by receivers to display the language of the stream. See: https://en.wikipedia.org/wiki/List_of_ISO_639-3_codes" ) @@ -1424,6 +1395,8 @@ if stop_stream: st.session_state['stream_started'] = False try: r = requests.post(f"{BACKEND_URL}/stop_audio").json() + if audio_mode == "Demo": + st.session_state['demo_stream_started'] = False if r['was_running']: is_stopped = True except Exception as e: @@ -1436,6 +1409,77 @@ if start_stream: # Small pause lets backend fully release audio devices before re-init time.sleep(1) + if audio_mode == "Demo": + demo_cfg = demo_stream_map[demo_selected] + q = QUALITY_MAP[demo_cfg['quality']] + + lang_cfgs = [ + (auracast_config.AuracastBigConfigDeu, 'de'), + (auracast_config.AuracastBigConfigEng, 'en'), + (auracast_config.AuracastBigConfigFra, 'fr'), + (auracast_config.AuracastBigConfigSpa, 'es'), + (auracast_config.AuracastBigConfigIta, 'it'), + (auracast_config.AuracastBigConfigPol, 'pl'), + ] + bigs1 = [] + for i in range(demo_cfg['streams']): + cfg_cls, lang = lang_cfgs[i % len(lang_cfgs)] + bigs1.append(cfg_cls( + code=(stream_passwort.strip() or None), + audio_source=f'file:../testdata/wave_particle_5min_{lang}_{int(q["rate"]/1000)}kHz_mono.wav', + iso_que_len=32, + sampling_frequency=q['rate'], + octets_per_frame=q['octets'], + )) + + max_per_mc = {48000: 1, 24000: 2, 16000: 3} + max_streams = max_per_mc.get(q['rate'], 3) + bigs2 = [] + if len(bigs1) > max_streams: + bigs2 = bigs1[max_streams:] + bigs1 = bigs1[:max_streams] + config1 = auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport='', + assisted_listening_stream=assisted_listening, + immediate_rendering=immediate_rendering, + presentation_delay_us=int(presentation_delay_ms * 1000), + qos_config=QOS_PRESET_MAP[qos_preset], + bigs=bigs1 + ) + config2 = None + if bigs2: + config2 = auracast_config.AuracastConfigGroup( + auracast_sampling_rate_hz=q['rate'], + octets_per_frame=q['octets'], + transport='', + assisted_listening_stream=assisted_listening, + immediate_rendering=immediate_rendering, + presentation_delay_us=int(presentation_delay_ms * 1000), + qos_config=QOS_PRESET_MAP[qos_preset], + bigs=bigs2 + ) + + is_started = False + try: + r1 = requests.post(f"{BACKEND_URL}/init", json=config1.model_dump()) + if r1.status_code == 200: + st.session_state['demo_stream_started'] = True + is_started = True + else: + st.session_state['demo_stream_started'] = False + st.error(f"Failed to initialize multicaster 1: {r1.text}") + if config2: + r2 = requests.post(f"{BACKEND_URL}/init2", json=config2.model_dump()) + if r2.status_code == 200: + is_started = True + else: + st.error(f"Failed to initialize multicaster 2: {r2.text}") + except Exception as e: + st.session_state['demo_stream_started'] = False + st.error(f"Error: {e}") + if audio_mode == "Analog": # Build separate configs per radio, each with its own quality and QoS parameters. is_started = False @@ -1570,7 +1614,7 @@ if start_stream: st.error(f"Failed to initialize Dante Radio 2: {r2.text}") except Exception as e: st.error(f"Error while starting Dante radios: {e}") - else: + if audio_mode not in ("Demo", "Analog", "Network - Dante"): # USB/Network: single config as before, using shared controls q = QUALITY_MAP[quality] config = auracast_config.AuracastConfigGroup(