use pipewire::{context::Context, main_loop::MainLoop}; use std::{ net::{Ipv6Addr, SocketAddrV6}, sync::{Arc, RwLock}, }; use tokio::task::spawn_blocking; use warp::{serve, Filter}; struct State_ { device_list: Vec, } #[derive(Clone)] struct State { internal: Arc>, } impl State { fn new() -> State { let internal = State_ { device_list: vec![], }; State { internal: Arc::new(RwLock::new(internal)), } } fn add_audio(&self, device: String) { let mut st = self.internal.write().unwrap(); (*st).device_list.push(device); } fn audio_devices(&self) -> Vec { let st = self.internal.read().unwrap(); (*st).device_list.clone() } } impl Default for State { fn default() -> State { State::new() } } async fn server_main(state: State) { let localhost: Ipv6Addr = "::1".parse().unwrap(); let server_addr = SocketAddrV6::new(localhost, 3001, 0, 0); let root = warp::path!().map(|| "ok".to_string()); let list_output_devices = warp::path!("output_devices").map({ let state = state.clone(); move || { let devices = state.audio_devices(); serde_json::to_string(&devices).unwrap() } }); let routes = root.or(list_output_devices); serve(routes).run(server_addr).await; } fn handle_add_audio_device(state: State, props: &pipewire::spa::utils::dict::DictRef) { if props.get("media.class") == Some("Audio/Sink") { if let Some(device_name) = props.get("node.description") { state.add_audio(device_name.to_owned()); } } } fn pipewire_loop(state: State) -> Result<(), Box> { let mainloop = MainLoop::new(None)?; let context = Context::new(&mainloop)?; let core = context.connect(None)?; let registry = core.get_registry()?; let _listener = registry .add_listener_local() .global({ let state = state.clone(); move |global_data| { if let Some(props) = global_data.props { handle_add_audio_device(state.clone(), props); } } }) .register(); mainloop.run(); Ok(()) } fn pipewire_main(state: State) { pipewire_loop(state).expect("pipewire should not error"); } #[tokio::main] async fn main() { let state = State::default(); spawn_blocking({ let state = state.clone(); move || pipewire_main(state) }); server_main(state.clone()).await; }