diff --git a/src/auracast/server/multicast_frontend.py b/src/auracast/server/multicast_frontend.py index da2de01..345918d 100644 --- a/src/auracast/server/multicast_frontend.py +++ b/src/auracast/server/multicast_frontend.py @@ -433,13 +433,43 @@ else: 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" - default_lang = "deu" - quality_options = list(QUALITY_MAP.keys()) - default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] + + # Use saved settings if audio_mode matches, otherwise use analog-specific defaults + saved_audio_mode = saved_settings.get('audio_mode') + if saved_audio_mode == 'Analog': + default_name = saved_settings.get('channel_names', ["Analog_Radio_1"])[0] + raw_program_info = saved_settings.get('program_info', default_name) + if isinstance(raw_program_info, list) and raw_program_info: + default_program_info = raw_program_info[0] + else: + default_program_info = raw_program_info + default_lang = saved_settings.get('languages', ["deu"])[0] + + # Map saved sampling rate to quality label + saved_rate = saved_settings.get('auracast_sampling_rate_hz') + if saved_rate == 48000: + default_quality = "High (48kHz)" + elif saved_rate == 32000: + default_quality = "Good (32kHz)" + elif saved_rate == 24000: + default_quality = "Medium (24kHz)" + elif saved_rate == 16000: + default_quality = "Fair (16kHz)" + else: + default_quality = "Medium (24kHz)" + + saved_pwd = saved_settings.get('stream_password', '') + else: + # Use analog-specific defaults when switching from another mode + default_name = "Analog_Radio_1" + default_program_info = "Analog Radio Broadcast" + default_lang = "deu" + default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] + saved_pwd = '' + + if default_quality not in quality_options: + default_quality = quality_options[0] quality1 = st.selectbox( "Stream Quality (Radio 1)", quality_options, @@ -450,7 +480,7 @@ else: stream_passwort1 = st.text_input( "Stream Passwort (Radio 1)", - value="", + value=saved_pwd, type="password", disabled=is_streaming, help="Optional: Set a broadcast code for Radio 1." @@ -557,7 +587,10 @@ else: input_device1 = None else: # Mono mode: show all available channels + saved_input_device = saved_settings.get('input_device') default_r1_idx = 0 + if saved_input_device in analog_names: + default_r1_idx = analog_names.index(saved_input_device) input_device1 = st.selectbox( "Input Device (Radio 1)", analog_names, @@ -606,22 +639,53 @@ else: ) if radio2_enabled and not stereo_enabled: - # Use analog-specific defaults for Radio 2 - default_name_r2 = "Analog_Radio_2" - default_program_info_r2 = "Analog Radio Broadcast" - default_lang_r2 = "deu" + # Use saved settings if audio_mode matches, otherwise use analog-specific defaults for Radio 2 + secondary_settings = saved_settings.get('secondary', {}) + saved_audio_mode = saved_settings.get('audio_mode') + if saved_audio_mode == 'Analog' and secondary_settings: + default_name_r2 = secondary_settings.get('channel_names', ["Analog_Radio_2"])[0] if isinstance(secondary_settings.get('channel_names'), list) else secondary_settings.get('channel_names', "Analog_Radio_2") + raw_program_info_r2 = secondary_settings.get('program_info', default_name_r2) + if isinstance(raw_program_info_r2, list) and raw_program_info_r2: + default_program_info_r2 = raw_program_info_r2[0] + else: + default_program_info_r2 = raw_program_info_r2 + default_lang_r2 = secondary_settings.get('languages', ["deu"])[0] if isinstance(secondary_settings.get('languages'), list) else secondary_settings.get('languages', 'deu') + + # Map saved sampling rate to quality label + saved_rate_r2 = secondary_settings.get('auracast_sampling_rate_hz') + if saved_rate_r2 == 48000: + default_quality_r2 = "High (48kHz)" + elif saved_rate_r2 == 32000: + default_quality_r2 = "Good (32kHz)" + elif saved_rate_r2 == 24000: + default_quality_r2 = "Medium (24kHz)" + elif saved_rate_r2 == 16000: + default_quality_r2 = "Fair (16kHz)" + else: + default_quality_r2 = "Medium (24kHz)" + + saved_pwd_r2 = secondary_settings.get('stream_password', '') + else: + # Use analog-specific defaults when switching from another mode + default_name_r2 = "Analog_Radio_2" + default_program_info_r2 = "Analog Radio Broadcast" + default_lang_r2 = "deu" + default_quality_r2 = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] + saved_pwd_r2 = '' + if default_quality_r2 not in quality_options: + default_quality_r2 = quality_options[0] quality2 = st.selectbox( "Stream Quality (Radio 2)", quality_options, - index=quality_options.index(default_quality), + index=quality_options.index(default_quality_r2), disabled=is_streaming, help="Select the audio sampling rate for Radio 2." ) stream_passwort2 = st.text_input( "Stream Passwort (Radio 2)", - value="", + value=saved_pwd_r2, type="password", disabled=is_streaming, help="Optional: Set a broadcast code for Radio 2." @@ -682,7 +746,11 @@ else: if not is_streaming: if analog_names: + secondary_settings = saved_settings.get('secondary', {}) + saved_input_device2 = secondary_settings.get('input_device') default_r2_idx = 1 if len(analog_names) > 1 else 0 + if saved_input_device2 in analog_names: + default_r2_idx = analog_names.index(saved_input_device2) input_device2 = st.selectbox( "Input Device (Radio 2)", analog_names, @@ -774,10 +842,15 @@ else: ) # Dante stereo mode toggle - saved_r1_config = saved_settings.get('dante_radio1', {}) + saved_audio_mode = saved_settings.get('audio_mode') + dante_stereo_enabled = False + if saved_audio_mode == 'Network - Dante': + # Check if any input device starts with dante_stereo_ to detect stereo mode + input_device = saved_settings.get('input_device', '') + dante_stereo_enabled = input_device.startswith('dante_stereo_') dante_stereo_enabled = st.checkbox( "🎧 Stereo Mode", - value=bool(saved_r1_config.get('dante_stereo_mode', False)), + value=dante_stereo_enabled, help="Enable stereo streaming for Dante inputs. Select left and right channels from ASRC channels 1-6. Radio 2 and multi-stream configurations will be disabled in stereo mode.", disabled=is_streaming ) @@ -786,13 +859,23 @@ else: dante_left_channel = None dante_right_channel = None if dante_stereo_enabled: - dante_channel_options = ["dante_asrc_ch1", "dante_asrc_ch2", "dante_asrc_ch3", + dante_channel_options = ["dante_asrc_ch1", "dante_asrc_ch2", "dante_asrc_ch3", "dante_asrc_ch4", "dante_asrc_ch5", "dante_asrc_ch6"] dante_channel_labels = ["CH1", "CH2", "CH3", "CH4", "CH5", "CH6"] - + + # Parse saved stereo device name to extract left and right channels + input_device = saved_settings.get('input_device', '') + saved_left = 'dante_asrc_ch1' + saved_right = 'dante_asrc_ch2' + if input_device.startswith('dante_stereo_'): + # Format: dante_stereo__ + parts = input_device.split('_') + if len(parts) >= 4: + saved_left = f"dante_asrc_ch{parts[2]}" + saved_right = f"dante_asrc_ch{parts[3]}" + col_left, col_right = st.columns(2) with col_left: - saved_left = saved_r1_config.get('dante_stereo_left', 'dante_asrc_ch1') left_idx = dante_channel_options.index(saved_left) if saved_left in dante_channel_options else 0 dante_left_channel = st.selectbox( "Left Channel", @@ -803,7 +886,6 @@ else: help="Select the Dante ASRC channel for the left stereo channel" ) with col_right: - saved_right = saved_r1_config.get('dante_stereo_right', 'dante_asrc_ch2') right_idx = dante_channel_options.index(saved_right) if saved_right in dante_channel_options else 1 dante_right_channel = st.selectbox( "Right Channel", @@ -813,7 +895,7 @@ else: disabled=is_streaming, help="Select the Dante ASRC channel for the right stereo channel" ) - + if dante_left_channel == dante_right_channel: st.warning("⚠️ Left and right channels are the same. Select different channels for true stereo.") else: @@ -821,7 +903,22 @@ else: # Stream count dropdown for Radio 1 (disabled in stereo mode - forced to 1 stream at 48kHz) r1_stream_options = list(dante_stream_options.keys()) - saved_r1_streams = saved_r1_config.get('stream_config', '1x48') + # Infer stream configuration from saved sampling rate + saved_rate = saved_settings.get('auracast_sampling_rate_hz') + saved_r1_streams = '1 × 48kHz' # default + if saved_rate: + if saved_rate == 48000: + channel_names = saved_settings.get('channel_names', []) + if len(channel_names) == 2: + saved_r1_streams = '2 × 24kHz' + elif len(channel_names) == 3: + saved_r1_streams = '3 × 16kHz' + else: + saved_r1_streams = '1 × 48kHz' + elif saved_rate == 24000: + saved_r1_streams = '2 × 24kHz' + elif saved_rate == 16000: + saved_r1_streams = '3 × 16kHz' default_r1_idx = r1_stream_options.index(saved_r1_streams) if saved_r1_streams in r1_stream_options else 0 if dante_stereo_enabled: @@ -851,15 +948,25 @@ else: r1_available_qualities = [] for quality in ["High (48kHz)", "Good (32kHz)", "Medium (24kHz)", "Fair (16kHz)"]: # Check if this quality is equal to or lower than the max - if (r1_max_quality == "High (48kHz)" or + if (r1_max_quality == "High (48kHz)" or (r1_max_quality == "Medium (24kHz)" and quality in ["Medium (24kHz)", "Fair (16kHz)"]) or (r1_max_quality == "Fair (16kHz)" and quality == "Fair (16kHz)")): r1_available_qualities.append(quality) - - saved_r1_quality = saved_r1_config.get('radio_quality', r1_max_quality) + + # Map saved sampling rate to quality label + saved_r1_quality = r1_max_quality + saved_rate = saved_settings.get('auracast_sampling_rate_hz') + if saved_rate == 48000: + saved_r1_quality = "High (48kHz)" + elif saved_rate == 32000: + saved_r1_quality = "Good (32kHz)" + elif saved_rate == 24000: + saved_r1_quality = "Medium (24kHz)" + elif saved_rate == 16000: + saved_r1_quality = "Fair (16kHz)" if saved_r1_quality not in r1_available_qualities: saved_r1_quality = r1_max_quality - + r1_radio_quality = st.selectbox( "Stream Quality (Radio 1)", r1_available_qualities, @@ -867,29 +974,29 @@ else: disabled=is_streaming, help=f"Select stream quality for Radio 1. Maximum quality based on configuration: {r1_max_quality}" ) - + # Radio-level settings for Radio 1 # First row: Assistive listening, immediate rendering, presentation delay, QoS col_r1_flags1, col_r1_flags2, col_r1_pdelay, col_r1_qos = st.columns([1, 1, 0.7, 0.6], gap="small") - + with col_r1_flags1: r1_assisted_listening = st.checkbox( "Assistive (R1)", - value=bool(saved_r1_config.get('assisted_listening', False)), + value=bool(saved_settings.get('assisted_listening_stream', False)), disabled=is_streaming, help="Assistive listening stream" ) - + with col_r1_flags2: r1_immediate_rendering = st.checkbox( "Immediate (R1)", - value=bool(saved_r1_config.get('immediate_rendering', False)), + value=bool(saved_settings.get('immediate_rendering', False)), disabled=is_streaming, help="Ignore presentation delay" ) with col_r1_pdelay: - default_pdelay = int(saved_r1_config.get('presentation_delay_us', 40000) or 40000) + default_pdelay = int(saved_settings.get('presentation_delay_us', 40000) or 40000) default_pdelay_ms = max(10, min(200, default_pdelay // 1000)) r1_presentation_delay_ms = st.number_input( "Delay (ms, R1)", @@ -897,10 +1004,10 @@ else: disabled=is_streaming, help="Presentation delay for Radio 1" ) - + with col_r1_qos: qos_options = list(QOS_PRESET_MAP.keys()) - saved_qos = saved_r1_config.get('qos_preset', 'Fast') + saved_qos = saved_settings.get('qos_preset', 'Fast') 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, @@ -918,24 +1025,31 @@ else: if dante_stereo_enabled: # Stereo mode: single stream with combined L+R channels with st.expander("Stereo Stream - Radio 1", expanded=True): - saved_streams = saved_r1_config.get('streams', []) - saved_stream = saved_streams[0] if saved_streams else {} - + # Read from flat settings structure + channel_names = saved_settings.get('channel_names', []) + program_infos = saved_settings.get('program_info', []) + languages = saved_settings.get('languages', []) + + saved_name = channel_names[0] if channel_names else 'Dante_Stereo' + saved_program_info = program_infos[0] if program_infos else saved_name + saved_language = languages[0] if languages else 'eng' + saved_password = saved_settings.get('stream_password', '') + # First row: Channel name and password col_name, col_pwd = st.columns([2, 1]) - + with col_name: stream_name = st.text_input( "Channel Name", - value=saved_stream.get('name', 'Dante_Stereo'), + value=saved_name, disabled=is_streaming, key="r1_stereo_name" ) - + with col_pwd: stream_password = st.text_input( "Stream Password", - value=saved_stream.get('stream_password', ''), + value=saved_password, type="password", disabled=is_streaming, key="r1_stereo_password", @@ -944,19 +1058,19 @@ else: # Second row: Program info and language col_prog, col_lang_code = st.columns([2, 1]) - + with col_prog: program_info = st.text_input( "Program Info", - value=saved_stream.get('program_info', 'Dante Stereo Broadcast'), + value=saved_program_info, disabled=is_streaming, key="r1_stereo_program" ) - + with col_lang_code: language = st.text_input( "Language", - value=saved_stream.get('language', 'eng'), + value=saved_language, disabled=is_streaming, key="r1_stereo_lang", help="ISO 639-3 language code" @@ -990,47 +1104,58 @@ else: }) else: # Normal mono mode: multiple streams with individual channels + # Read from flat settings structure + channel_names = saved_settings.get('channel_names', []) + program_infos = saved_settings.get('program_info', []) + languages = saved_settings.get('languages', []) + input_devices = saved_settings.get('input_devices', []) + stream_passwords = saved_settings.get('stream_passwords', []) if 'stream_passwords' in saved_settings else [] + for i in range(r1_num_streams): with st.expander(f"Stream {i+1} - Radio 1", expanded=True): - saved_streams = saved_r1_config.get('streams', []) - saved_stream = saved_streams[i] if i < len(saved_streams) else {} - + # Get saved values from flat structure + saved_name = channel_names[i] if i < len(channel_names) else f'Dante_R1_S{i+1}' + saved_program_info = program_infos[i] if i < len(program_infos) else f'Dante Radio 1 Stream {i+1}' + saved_language = languages[i] if i < len(languages) else 'eng' + saved_password = stream_passwords[i] if i < len(stream_passwords) else '' + saved_input_device = input_devices[i] if i < len(input_devices) else None + # First row: Channel name and language col_name, col_lang = st.columns([2, 1]) - + with col_name: stream_name = st.text_input( f"Channel Name", - value=saved_stream.get('name', f'Dante_R1_S{i+1}'), + value=saved_name, disabled=is_streaming, key=f"r1_stream_{i}_name" ) - + with col_lang: stream_password = st.text_input( f"Stream Password", - value=saved_stream.get('stream_password', ''), + value=saved_password, type="password", disabled=is_streaming, key=f"r1_stream_{i}_password", help="Optional: Set a broadcast code for this stream" ) - + # Second row: Program info and language col_prog, col_lang_code = st.columns([2, 1]) - + with col_prog: program_info = st.text_input( f"Program Info", - value=saved_stream.get('program_info', f'Dante Radio 1 Stream {i+1}'), + value=saved_program_info, disabled=is_streaming, key=f"r1_stream_{i}_program" ) - + with col_lang_code: language = st.text_input( f"Language", - value=saved_stream.get('language', 'eng'), + value=saved_language, disabled=is_streaming, key=f"r1_stream_{i}_lang", help="ISO 639-3 language code" @@ -1042,10 +1167,10 @@ else: with col_device: # Session state key for persisting the selection device_session_key = f"r1_stream_{i}_device_saved" - + if not is_streaming and input_options: # Get default from session state first, then from saved settings - default_input_name = st.session_state.get(device_session_key, saved_stream.get('input_device')) + default_input_name = st.session_state.get(device_session_key, saved_input_device) default_input_label = None for label, name in option_name_map.items(): if name == default_input_name: @@ -1053,7 +1178,7 @@ else: break if default_input_label not in input_options and input_options: default_input_label = input_options[0] - + selected_option = st.selectbox( f"Input Device", input_options, @@ -1066,7 +1191,7 @@ else: st.session_state[device_session_key] = input_device else: # When streaming, get the device from session state - current_device = st.session_state.get(device_session_key, saved_stream.get('input_device', 'No device')) + current_device = st.session_state.get(device_session_key, saved_input_device or 'No device') # Convert internal name to display label display_label = current_device @@ -1095,26 +1220,45 @@ else: # --- Radio 2 Section --- with st.container(border=True): st.subheader("Radio 2") - + # Disable Radio 2 in stereo mode - saved_r2_config = saved_settings.get('dante_radio2', {}) + secondary_settings = saved_settings.get('secondary', {}) if dante_stereo_enabled: st.info("🎧 Radio 2 is automatically disabled in stereo mode") radio2_enabled = False else: # Enable/disable checkbox for Radio 2 + # Use saved settings or streaming state to determine default radio2_enabled_default = secondary_is_streaming + # Check if secondary radio has saved settings (indicates it was enabled) + if secondary_settings.get('auracast_sampling_rate_hz') or secondary_settings.get('channel_names'): + radio2_enabled_default = True 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." ) - + if radio2_enabled: # Stream count dropdown for Radio 2 r2_stream_options = r1_stream_options - saved_r2_streams = saved_r2_config.get('stream_config', '1x48') + # Infer stream configuration from saved secondary sampling rate + saved_rate2 = secondary_settings.get('auracast_sampling_rate_hz') + saved_r2_streams = '1 × 48kHz' # default + if saved_rate2: + if saved_rate2 == 48000: + channel_names2 = secondary_settings.get('channel_names', []) + if len(channel_names2) == 2: + saved_r2_streams = '2 × 24kHz' + elif len(channel_names2) == 3: + saved_r2_streams = '3 × 16kHz' + else: + saved_r2_streams = '1 × 48kHz' + elif saved_rate2 == 24000: + saved_r2_streams = '2 × 24kHz' + elif saved_rate2 == 16000: + saved_r2_streams = '3 × 16kHz' default_r2_idx = r2_stream_options.index(saved_r2_streams) if saved_r2_streams in r2_stream_options else 0 r2_stream_config = st.selectbox( @@ -1136,11 +1280,20 @@ else: (r2_max_quality == "Medium (24kHz)" and quality in ["Medium (24kHz)", "Fair (16kHz)"]) or (r2_max_quality == "Fair (16kHz)" and quality == "Fair (16kHz)")): r2_available_qualities.append(quality) - - saved_r2_quality = saved_r2_config.get('radio_quality', r2_max_quality) + + # Map saved secondary sampling rate to quality label + saved_r2_quality = r2_max_quality + if saved_rate2 == 48000: + saved_r2_quality = "High (48kHz)" + elif saved_rate2 == 32000: + saved_r2_quality = "Good (32kHz)" + elif saved_rate2 == 24000: + saved_r2_quality = "Medium (24kHz)" + elif saved_rate2 == 16000: + saved_r2_quality = "Fair (16kHz)" if saved_r2_quality not in r2_available_qualities: saved_r2_quality = r2_max_quality - + r2_radio_quality = st.selectbox( "Stream Quality (Radio 2)", r2_available_qualities, @@ -1148,29 +1301,28 @@ else: disabled=is_streaming, help=f"Select stream quality for Radio 2. Maximum quality based on configuration: {r2_max_quality}" ) - + # Radio-level settings for Radio 2 - # First row: Assistive listening, immediate rendering, presentation delay, QoS col_r2_flags1, col_r2_flags2, col_r2_pdelay, col_r2_qos = st.columns([1, 1, 0.7, 0.6], gap="small") - + with col_r2_flags1: r2_assisted_listening = st.checkbox( "Assistive (R2)", - value=bool(saved_r2_config.get('assisted_listening', False)), + value=bool(secondary_settings.get('assisted_listening_stream', False)), disabled=is_streaming, help="Assistive listening stream" ) - + with col_r2_flags2: r2_immediate_rendering = st.checkbox( "Immediate (R2)", - value=bool(saved_r2_config.get('immediate_rendering', False)), + value=bool(secondary_settings.get('immediate_rendering', False)), disabled=is_streaming, help="Ignore presentation delay" ) - + with col_r2_pdelay: - default_pdelay = int(saved_r2_config.get('presentation_delay_us', 40000) or 40000) + default_pdelay = int(secondary_settings.get('presentation_delay_us', 40000) or 40000) default_pdelay_ms = max(10, min(200, default_pdelay // 1000)) r2_presentation_delay_ms = st.number_input( "Delay (ms, R2)", @@ -1178,13 +1330,13 @@ else: disabled=is_streaming, help="Presentation delay for Radio 2" ) - + with col_r2_qos: qos_options = list(QOS_PRESET_MAP.keys()) - saved_qos = saved_r2_config.get('qos_preset', 'Fast') - default_qos_idx = qos_options.index(saved_qos) if saved_qos in qos_options else 0 + saved_qos = secondary_settings.get('qos_preset', 'Fast') + default_qos_idx2 = 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, + "QoS (R2)", options=qos_options, index=default_qos_idx2, disabled=is_streaming, help="Quality of Service preset for Radio 2" ) @@ -1192,48 +1344,59 @@ else: # Per-stream configuration for Radio 2 st.write("**Stream Configuration (Radio 2)**") r2_streams = [] - + + # Read from flat secondary settings structure + channel_names2 = secondary_settings.get('channel_names', []) + program_infos2 = secondary_settings.get('program_info', []) + languages2 = secondary_settings.get('languages', []) + input_devices2 = secondary_settings.get('input_devices', []) + stream_passwords2 = secondary_settings.get('stream_passwords', []) if 'stream_passwords' in secondary_settings else [] + for i in range(r2_num_streams): with st.expander(f"Stream {i+1} - Radio 2", expanded=True): - saved_streams = saved_r2_config.get('streams', []) - saved_stream = saved_streams[i] if i < len(saved_streams) else {} + # Get saved values from flat secondary structure + saved_name2 = channel_names2[i] if i < len(channel_names2) else f'Dante_R2_S{i+1}' + saved_program_info2 = program_infos2[i] if i < len(program_infos2) else f'Dante Radio 2 Stream {i+1}' + saved_language2 = languages2[i] if i < len(languages2) else 'eng' + saved_password2 = stream_passwords2[i] if i < len(stream_passwords2) else '' + saved_input_device2 = input_devices2[i] if i < len(input_devices2) else None # First row: Channel name and password col_name, col_pwd = st.columns([2, 1]) - + with col_name: stream_name = st.text_input( f"Channel Name", - value=saved_stream.get('name', f'Dante_R2_S{i+1}'), + value=saved_name2, disabled=is_streaming, key=f"r2_stream_{i}_name" ) - + with col_pwd: stream_password = st.text_input( f"Stream Password", - value=saved_stream.get('stream_password', ''), + value=saved_password2, type="password", disabled=is_streaming, key=f"r2_stream_{i}_password", help="Optional: Set a broadcast code for this stream" ) - + # Second row: Program info and language col_prog, col_lang = st.columns([2, 1]) - + with col_prog: program_info = st.text_input( f"Program Info", - value=saved_stream.get('program_info', f'Dante Radio 2 Stream {i+1}'), + value=saved_program_info2, disabled=is_streaming, key=f"r2_stream_{i}_program" ) - + with col_lang: language = st.text_input( f"Language", - value=saved_stream.get('language', 'eng'), + value=saved_language2, disabled=is_streaming, key=f"r2_stream_{i}_lang", help="ISO 639-3 language code" @@ -1245,10 +1408,10 @@ else: with col_device: # Session state key for persisting the selection device_session_key = f"r2_stream_{i}_device_saved" - + if not is_streaming and input_options: # Get default from session state first, then from saved settings - default_input_name = st.session_state.get(device_session_key, saved_stream.get('input_device')) + default_input_name = st.session_state.get(device_session_key, saved_input_device2) default_input_label = None for label, name in option_name_map.items(): if name == default_input_name: @@ -1256,7 +1419,7 @@ else: break if default_input_label not in input_options and input_options: default_input_label = input_options[0] - + selected_option = st.selectbox( f"Input Device", input_options, @@ -1269,7 +1432,7 @@ else: st.session_state[device_session_key] = input_device else: # When streaming, get the device from session state - current_device = st.session_state.get(device_session_key, saved_stream.get('input_device', 'No device')) + current_device = st.session_state.get(device_session_key, saved_input_device2 or 'No device') # Convert internal name to display label display_label = current_device @@ -1354,8 +1517,30 @@ else: if audio_mode in ("USB", "Network"): # USB/Network: single set of controls shared with the single channel + # Use saved settings if audio_mode matches, otherwise use defaults + saved_audio_mode = saved_settings.get('audio_mode') + if saved_audio_mode in ("USB", "Network"): + # Map saved sampling rate to quality label + saved_rate = saved_settings.get('auracast_sampling_rate_hz') + if saved_rate == 48000: + default_quality = "High (48kHz)" + elif saved_rate == 32000: + default_quality = "Good (32kHz)" + elif saved_rate == 24000: + default_quality = "Medium (24kHz)" + elif saved_rate == 16000: + default_quality = "Fair (16kHz)" + else: + default_quality = "Medium (24kHz)" + saved_pwd = saved_settings.get('stream_password', '') + else: + # Use defaults when switching from another mode + default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] + saved_pwd = '' + quality_options = list(QUALITY_MAP.keys()) - default_quality = "Medium (24kHz)" if "Medium (24kHz)" in quality_options else quality_options[0] + if default_quality not in quality_options: + default_quality = quality_options[0] quality = st.selectbox( "Stream Quality (Sampling Rate)", quality_options, @@ -1366,7 +1551,7 @@ else: stream_passwort = st.text_input( "Stream Passwort", - value="", + 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." @@ -1618,7 +1803,7 @@ if start_stream: analog_gain_db_right=cfg.get('analog_gain_db_right', 0.0), bigs=[ auracast_config.AuracastBigConfig( - code=(cfg['stream_passwort'].strip() or None), + code=((cfg['stream_passwort'] or '').strip() or None), name=cfg['name'], program_info=cfg['program_info'], language=cfg['language'], diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index d230aaa..84c4b6e 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -445,6 +445,11 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, first_source = conf.bigs[0].audio_source if conf.bigs else '' input_device_name = None audio_mode_persist = 'Demo' + # Capture original per-BIG device names before transformation + original_input_devices = [ + big.audio_source.split(':', 1)[1] if (isinstance(big.audio_source, str) and big.audio_source.startswith('device:')) else None + for big in conf.bigs + ] if any(isinstance(b.audio_source, str) and b.audio_source.startswith('device:') for b in conf.bigs): if isinstance(first_source, str) and first_source.startswith('device:'): input_device_name = first_source.split(':', 1)[1] if ':' in first_source else None @@ -597,6 +602,7 @@ async def init_radio(transport: str, conf: auracast_config.AuracastConfigGroup, 'languages': [big.language for big in conf.bigs], 'audio_mode': audio_mode_persist, 'input_device': input_device_name, + 'input_devices': original_input_devices, 'program_info': [getattr(big, 'program_info', None) for big in conf.bigs], 'gain': [getattr(big, 'input_gain', 1.0) for big in conf.bigs], 'auracast_sampling_rate_hz': conf.auracast_sampling_rate_hz,