make everything controllable by just one button

This commit is contained in:
2025-06-15 13:44:09 +02:00
parent 501b880931
commit d54d18987a
3 changed files with 110 additions and 54 deletions

View File

@@ -8,26 +8,44 @@ from auracast import auracast_config
PTIME = 40
BACKEND_URL = "http://localhost:5000"
# Try loading persisted settings from backend
saved_settings = {}
try:
resp = requests.get(f"{BACKEND_URL}/status", timeout=1)
if resp.status_code == 200:
saved_settings = resp.json()
except Exception:
saved_settings = {}
st.title("🎙️ Auracast Audio Mode Control")
# Audio mode selection
# Audio mode selection with persisted default
options = ["Webapp", "USB"]
saved_audio_mode = saved_settings.get("audio_mode", "Webapp")
if saved_audio_mode not in options:
saved_audio_mode = "Webapp"
audio_mode = st.selectbox(
"Audio Mode",
["Webapp", "Network", "Cloud Announcements"]
options,
index=options.index(saved_audio_mode)
)
if audio_mode in ["Webapp", "Network"]:
# Stream quality selection
quality = st.selectbox("Stream Quality", ["High (48kHz)", "Mid (24kHz)", "Fair (16kHz)"])
if audio_mode in ["Webapp", "USB"]:
# Stream quality selection (temporarily disabled)
# quality = st.selectbox("Stream Quality", ["High (48kHz)", "Mid (24kHz)", "Fair (16kHz)"])
quality_map = {
"High (48kHz)": {"rate": 48000, "octets": 120},
"Mid (24kHz)": {"rate": 24000, "octets": 60},
"Fair (16kHz)": {"rate": 16000, "octets": 40},
"High (48kHz)": {"rate": 48000, "octets": 120},
"Mid (24kHz)": {"rate": 24000, "octets": 60},
"Fair (16kHz)": {"rate": 16000, "octets": 40},
}
stream_name = st.text_input("Channel Name", value="Broadcast0")
language = st.text_input("Language (ISO 639-3)", value="deu")
start_stream = st.button("Setup Auracast")
# Default to high quality while UI is hidden
quality = "High (48kHz)"
default_name = saved_settings.get('channel_names', ["Broadcast0"])[0]
default_lang = saved_settings.get('languages', ["deu"])[0]
stream_name = st.text_input("Channel Name", value=default_name)
language = st.text_input("Language (ISO 639-3)", value=default_lang)
start_stream = st.button("Start Auracast")
if start_stream:
# Prepare config using the model (do NOT send qos_config, only relevant fields)
@@ -40,7 +58,11 @@ if audio_mode in ["Webapp", "Network"]:
name=stream_name,
program_info=f"{stream_name} {quality}",
language=language,
audio_source="webrtc" if audio_mode=="Webapp" else "network",
audio_source=(
"webrtc" if audio_mode == "Webapp" else (
"usb" if audio_mode == "USB" else "network"
)
),
input_format="auto",
iso_que_len=1, # TODO: this should be way less to decrease delay
sampling_frequency=q['rate'],
@@ -57,45 +79,42 @@ if audio_mode in ["Webapp", "Network"]:
except Exception as e:
st.error(f"Error: {e}")
if audio_mode == "Webapp":
st.markdown("Click start and speak; watch your backend logs to see incoming RTP.")
if audio_mode == "Webapp" and start_stream:
st.markdown("Starting microphone; allow access if prompted and speak.")
component = f"""
<button id='go'>Start microphone</button>
<script>
const go = document.getElementById('go');
go.onclick = async () => {{
go.disabled = true;
const pc = new RTCPeerConnection(); // No STUN needed for localhost
const stream = await navigator.mediaDevices.getUserMedia({{audio:true}});
stream.getTracks().forEach(t => pc.addTrack(t, stream));
// --- WebRTC offer/answer exchange ---
const offer = await pc.createOffer()
// Patch SDP offer to include a=ptime using global PTIME
let sdp = offer.sdp;
const ptime_line = 'a=ptime:{PTIME}';
const maxptime_line = 'a=maxptime:{PTIME}';
if (sdp.includes('a=sendrecv')) {{
sdp = sdp.replace('a=sendrecv', 'a=sendrecv\\n' + ptime_line + '\\n' + maxptime_line);
}} else {{
sdp += '\\n' + ptime_line + '\\n' + maxptime_line;
}}
const patched_offer = new RTCSessionDescription({{sdp, type: offer.type}})
await pc.setLocalDescription(patched_offer)
// Send offer to backend
const response = await fetch(
"{BACKEND_URL}/offer",
{{
method: 'POST',
headers: {{'Content-Type':'application/json'}},
body: JSON.stringify({{sdp: pc.localDescription.sdp, type: pc.localDescription.type}})
(async () => {{
const pc = new RTCPeerConnection(); // No STUN needed for localhost
const stream = await navigator.mediaDevices.getUserMedia({{audio:true}});
stream.getTracks().forEach(t => pc.addTrack(t, stream));
// --- WebRTC offer/answer exchange ---
const offer = await pc.createOffer();
// Patch SDP offer to include a=ptime using global PTIME
let sdp = offer.sdp;
const ptime_line = 'a=ptime:{PTIME}';
const maxptime_line = 'a=maxptime:{PTIME}';
if (sdp.includes('a=sendrecv')) {{
sdp = sdp.replace('a=sendrecv', 'a=sendrecv\\n' + ptime_line + '\\n' + maxptime_line);
}} else {{
sdp += '\\n' + ptime_line + '\\n' + maxptime_line;
}}
)
const answer = await response.json()
await pc.setRemoteDescription(new RTCSessionDescription({{sdp: answer.sdp, type: answer.type}}))
}};
const patched_offer = new RTCSessionDescription({{sdp, type: offer.type}});
await pc.setLocalDescription(patched_offer);
// Send offer to backend
const response = await fetch(
"{BACKEND_URL}/offer",
{{
method: 'POST',
headers: {{'Content-Type':'application/json'}},
body: JSON.stringify({{sdp: pc.localDescription.sdp, type: pc.localDescription.type}})
}}
);
const answer = await response.json();
await pc.setRemoteDescription(new RTCSessionDescription({{sdp: answer.sdp, type: answer.type}}));
}})();
</script>
"""
st.components.v1.html(component, height=80)
st.components.v1.html(component, height=0)
else:
st.header("Advertised Streams (Cloud Announcements)")
st.info("This feature requires backend support to list advertised streams.")