monorepo/gm-dash/server/src/main.rs

137 lines
3.5 KiB
Rust

use pipewire::{context::Context, main_loop::MainLoop};
use serde::Deserialize;
use std::net::{Ipv6Addr, SocketAddrV6};
use tokio::task::spawn_blocking;
use warp::{serve, Filter};
mod audio_control;
mod app;
use app::App;
#[derive(Deserialize)]
struct PlayTrackParams {
track_name: String,
}
async fn server_main(state: 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 state = state.clone();
move || {
let devices = state.audio_devices();
serde_json::to_string(&devices).unwrap()
}
});
let list_tracks = warp::path!("tracks").map({
let state = state.clone();
move || serde_json::to_string(&state.tracks()).unwrap()
});
let enable_track = warp::put()
.and(warp::path!("playing"))
.and(warp::body::json())
.map({
let state = state.clone();
move |params: PlayTrackParams| {
state.enable_track(&params.track_name);
"".to_owned()
}
});
let disable_track = warp::delete()
.and(warp::path!("playing"))
.and(warp::body::json())
.map({
let state = state.clone();
move |params: PlayTrackParams| {
state.disable_track(&params.track_name);
"".to_owned()
}
});
let play_all = warp::put().and(warp::path!("playing")).map({
let state = state.clone();
move || {
state.play();
"".to_owned()
}
});
let stop_all = warp::delete().and(warp::path!("playing")).map({
let state = state.clone();
move || {
state.stop();
"".to_owned()
}
});
let now_playing = warp::path!("playing").map({
let state = state.clone();
move || serde_json::to_string(&state.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(now_playing);
serve(routes).run(server_addr).await;
}
fn handle_add_audio_device(state: App, 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: 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 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: App) {
pipewire_loop(state).expect("pipewire should not error");
}
#[tokio::main]
async fn main() {
gstreamer::init();
let state = App::default();
spawn_blocking({
let state = state.clone();
move || pipewire_main(state)
});
server_main(state.clone()).await;
}