use std::{ convert::Infallible, net::{Ipv6Addr, SocketAddrV6}, path::PathBuf, sync::Arc, }; use app::App; use audio_control::{AudioControl, GStreamerBackend}; use pipewire::{context::Context, main_loop::MainLoop}; use serde::Deserialize; use tokio::task::spawn_blocking; use warp::{serve, Filter}; mod app; mod audio_control; mod types; #[derive(Deserialize)] struct PlayTrackParams { track_name: String, } fn with_app(app: Arc<App>) -> impl Filter<Extract = (Arc<App>,), Error = Infallible> + Clone { warp::any().map(move || app.clone()) } async fn server_main(app: Arc<App>) { 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 app = app.clone(); move || { let devices = app.audio_devices(); serde_json::to_string(&devices).unwrap() } }); /* let list_tracks = warp::path!("tracks").map({ let app = app.clone(); move || serde_json::to_string(&app.tracks()).unwrap() }); */ let enable_track = warp::put() .and(warp::path!("playing")) .and(warp::body::json()) .and(with_app(app.clone())) .then(|params: PlayTrackParams, app: Arc<App>| async move { println!("enable track"); let _ = app.enable_track(PathBuf::from(params.track_name)).await; "".to_owned() }); let disable_track = warp::delete() .and(warp::path!("playing")) .and(warp::body::json()) .and(with_app(app.clone())) .then(|params: PlayTrackParams, app: Arc<App>| async move { let _ = app.disable_track(¶ms.track_name); "".to_owned() }); let play_all = warp::post() .and(warp::path!("play")) .and(with_app(app.clone())) .then({ |app: Arc<App>| async move { println!("play_all"); let _ = app.play().await; "".to_owned() } }); let stop_all = warp::post() .and(warp::path!("stop")) .and(with_app(app.clone())) .then({ |app: Arc<App>| async move { let _ = app.stop().await; "".to_owned() } }); let pause = warp::post() .and(warp::path!("pause")) .and(with_app(app.clone())) .then({ |app: Arc<App>| async move { let _ = app.pause().await; "".to_owned() } }); let now_playing = warp::path!("playing").map({ let app = app.clone(); move || serde_json::to_string(&app.playing()).unwrap() }); let routes = root .or(list_output_devices) // .or(list_tracks) .or(enable_track) .or(disable_track) .or(play_all) .or(stop_all) .or(pause) .or(now_playing); serve(routes).run(server_addr).await; } fn handle_add_audio_device(app: App, props: &pipewire::spa::utils::dict::DictRef) { if props.get("media.class") == Some("Audio/Sink") { if let Some(device_name) = props.get("node.description") { app.add_audio(device_name.to_owned()); } } } /* fn pipewire_loop(app: App) -> Result<(), Box<dyn std::error::Error>> { 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 app = app.clone(); move |global_data| { if let Some(props) = global_data.props { handle_add_audio_device(app.clone(), props); } } }) .register(); mainloop.run(); Ok(()) } */ /* fn pipewire_main(state: App) { pipewire_loop(state).expect("pipewire should not error"); } */ #[tokio::main] async fn main() { gstreamer::init(); let (audio_control_tx, audio_control_rx) = tokio::sync::mpsc::channel(5); let (audio_status_tx, audio_status_rx) = tokio::sync::mpsc::channel(5); let app = Arc::new(App::new(audio_control_tx, audio_status_rx)); let audio_controller = Arc::new(AudioControl::new(GStreamerBackend::default())); tokio::spawn({ let audio_controller = audio_controller.clone(); async move { audio_controller.listen(audio_control_rx).await } }); tokio::spawn({ let audio_controller = audio_controller.clone(); async move { audio_controller.report(audio_status_tx).await } }); /* spawn_blocking({ let app = app.clone(); move || pipewire_main(state) }); */ server_main(app.clone()).await; }