diff --git a/Cargo.lock b/Cargo.lock index 5551c55..bcea2d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -387,6 +387,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -965,7 +971,7 @@ dependencies = [ "lazy_static", "libadwaita", "memorycache", - "reqwest", + "reqwest 0.11.27", "serde 1.0.209", "serde_derive", "serde_json", @@ -1950,8 +1956,10 @@ dependencies = [ "glib 0.18.5", "gstreamer", "pipewire", + "reqwest 0.12.7", "serde 1.0.209", "serde_json", + "serde_yaml", "thiserror", "tokio", "warp", @@ -2154,6 +2162,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -2319,6 +2346,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.4" @@ -2366,9 +2416,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -2380,6 +2430,43 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -2393,6 +2480,42 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -4059,11 +4182,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", + "http-body 0.4.6", "hyper 0.14.30", - "hyper-tls", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log 0.4.22", @@ -4072,12 +4195,12 @@ dependencies = [ "once_cell", "percent-encoding 2.3.1", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde 1.0.209", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", "tokio", "tokio-native-tls", "tower-service", @@ -4088,6 +4211,49 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log 0.4.22", + "mime 0.3.17", + "native-tls", + "once_cell", + "percent-encoding 2.3.1", + "pin-project-lite", + "rustls-pemfile 2.1.3", + "serde 1.0.209", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration 0.6.1", + "tokio", + "tokio-native-tls", + "tower-service", + "url 2.5.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + [[package]] name = "result-extended" version = "0.1.0" @@ -4095,6 +4261,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rp-pico" version = "0.8.0" @@ -4242,6 +4423,19 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4251,6 +4445,33 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rusty-fork" version = "0.3.0" @@ -4437,6 +4658,19 @@ dependencies = [ "serde 1.0.209", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde 1.0.209", + "unsafe-libyaml", +] + [[package]] name = "sgf" version = "0.1.0" @@ -4850,6 +5084,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -4858,7 +5101,18 @@ checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", - "system-configuration-sys", + "system-configuration-sys 0.5.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys 0.6.0", ] [[package]] @@ -4871,6 +5125,16 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -5090,6 +5354,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -5193,6 +5468,27 @@ dependencies = [ "winnow 0.6.18", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -5414,6 +5710,18 @@ dependencies = [ "traitobject", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "1.7.2" @@ -5756,6 +6064,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/gm-dash/server/Cargo.toml b/gm-dash/server/Cargo.toml index d8c0bd4..341373a 100644 --- a/gm-dash/server/Cargo.toml +++ b/gm-dash/server/Cargo.toml @@ -15,4 +15,6 @@ warp = { version = "0.3.7" } glib = { version = "0.18" } thiserror = "1.0.63" cool_asserts = "2.0.3" +serde_yaml = "0.9.34" +reqwest = { version = "0.12.7", features = ["json"] } diff --git a/gm-dash/server/config.yaml b/gm-dash/server/config.yaml new file mode 100644 index 0000000..07dac8f --- /dev/null +++ b/gm-dash/server/config.yaml @@ -0,0 +1,3 @@ +bridge_address: "192.168.1.159" +hue_api_key: "lw77sNOqZNt2YMxmXb63qEvGCgiokMyrJHfBOL1X" + diff --git a/gm-dash/server/src/app/mod.rs b/gm-dash/server/src/app/mod.rs index 3a5f43d..e4bf5d0 100644 --- a/gm-dash/server/src/app/mod.rs +++ b/gm-dash/server/src/app/mod.rs @@ -1,9 +1,8 @@ use std::{ - collections::HashMap, - path::PathBuf, - sync::{Arc, RwLock}, + collections::HashMap, fs::File, path::PathBuf, sync::{Arc, RwLock} }; +use serde::Deserialize; use tokio::{ sync::mpsc::{Receiver, Sender}, task::JoinHandle, @@ -41,12 +40,28 @@ impl Default for AppState { } } +#[derive(Deserialize, Debug, Clone)] +pub struct Configuration { + pub bridge_address: String, + pub hue_api_key: String, +} + +impl Configuration { + fn load_from_file(path: PathBuf) -> Self { + let f = File::open(path).unwrap(); + serde_yaml::from_reader(&f).expect("yaml to unwrap") + } +} + + pub struct App { state: Arc>, audio_control: Sender, listener: JoinHandle<()>, + + pub config: Configuration, } impl App { @@ -55,6 +70,7 @@ impl App { mut audio_status: Receiver, ) -> App { let state = Arc::new(RwLock::new(AppState::default())); + let config = Configuration::load_from_file(PathBuf::from("./config.yaml")); let listener = tokio::spawn({ let state = state.clone(); @@ -82,6 +98,7 @@ impl App { state, audio_control, listener, + config, } } diff --git a/gm-dash/server/src/main.rs b/gm-dash/server/src/main.rs index 7d8b7ec..06031d9 100644 --- a/gm-dash/server/src/main.rs +++ b/gm-dash/server/src/main.rs @@ -1,5 +1,7 @@ use std::{ convert::Infallible, + fs::File, + io::Read, net::{Ipv6Addr, SocketAddrV6}, path::PathBuf, sync::Arc, @@ -8,9 +10,10 @@ use std::{ use app::App; use audio_control::{AudioControl, GStreamerBackend}; use pipewire::{context::Context, main_loop::MainLoop}; +use reqwest::Method; use serde::Deserialize; use tokio::task::spawn_blocking; -use warp::{serve, Filter}; +use warp::{reject::Rejection, reply::Reply, serve, Filter}; mod app; mod audio_control; @@ -25,12 +28,8 @@ fn with_app(app: Arc) -> impl Filter,), Error = Infalli warp::any().map(move || app.clone()) } -async fn server_main(app: Arc) { - 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({ +fn sound_routes(app: Arc) -> impl Filter + Clone { + let list_output_devices = warp::path!("api" / "v1" / "output_devices").map({ let app = app.clone(); move || { let devices = app.audio_devices(); @@ -46,7 +45,7 @@ async fn server_main(app: Arc) { */ let enable_track = warp::put() - .and(warp::path!("playing")) + .and(warp::path!("api" / "v1" / "playing")) .and(warp::body::json()) .and(with_app(app.clone())) .then(|params: PlayTrackParams, app: Arc| async move { @@ -56,7 +55,7 @@ async fn server_main(app: Arc) { }); let disable_track = warp::delete() - .and(warp::path!("playing")) + .and(warp::path!("api" / "v1" / "playing")) .and(warp::body::json()) .and(with_app(app.clone())) .then(|params: PlayTrackParams, app: Arc| async move { @@ -65,7 +64,7 @@ async fn server_main(app: Arc) { }); let play_all = warp::post() - .and(warp::path!("play")) + .and(warp::path!("api" / "v1" / "play")) .and(with_app(app.clone())) .then({ |app: Arc| async move { @@ -76,7 +75,7 @@ async fn server_main(app: Arc) { }); let stop_all = warp::post() - .and(warp::path!("stop")) + .and(warp::path!("api" / "v1" / "stop")) .and(with_app(app.clone())) .then({ |app: Arc| async move { @@ -86,7 +85,7 @@ async fn server_main(app: Arc) { }); let pause = warp::post() - .and(warp::path!("pause")) + .and(warp::path!("api" / "v1" / "pause")) .and(with_app(app.clone())) .then({ |app: Arc| async move { @@ -95,20 +94,78 @@ async fn server_main(app: Arc) { } }); - let now_playing = warp::path!("playing").map({ + let now_playing = warp::path!("api" / "v1" / "playing").map({ let app = app.clone(); move || serde_json::to_string(&app.playing()).unwrap() }); - let routes = root - .or(list_output_devices) + list_output_devices // .or(list_tracks) .or(enable_track) .or(disable_track) .or(play_all) .or(stop_all) .or(pause) - .or(now_playing); + .or(now_playing) +} + +#[derive(Clone, Debug, Deserialize)] +struct RoomMetadata { + name: String, +} + +#[derive(Clone, Debug, Deserialize)] +struct Room { + id: String, + metadata: RoomMetadata, +} + +fn light_routes( + app: Arc, +) -> impl Filter + Clone { + /* + let list_lights = warp::path!("api" / "v1" / "lights").map({ + let app = app.clone(); + move || { + let devices = app.audio_devices(); + serde_json::to_string(&devices).unwrap() + } + }); + */ + + let list_rooms = warp::path!("api" / "v1" / "rooms") + .and(with_app(app.clone())) + .then({ + |app: Arc| async move { + let client = reqwest::Client::new(); + let request = client + .request(Method::GET, "https://192.168.1.159/clip/v2/resource/room") + .header("hue-application-key", app.config.hue_api_key.clone()) + .danger_disable_hostname_verification() + .build() + .unwrap(); + + let rooms: Vec = client.execute(request).await.unwrap().json().await.unwrap(); + let room_names = rooms + .into_iter() + .map(|r| r.metadata.name) + .collect::(); + serde_json::to_string(&room_names).unwrap() + } + }); + + list_rooms +} + +async fn server_main(app: Arc) { + let localhost: Ipv6Addr = "::1".parse().unwrap(); + let server_addr = SocketAddrV6::new(localhost, 3001, 0, 0); + + let root = warp::path!().map(|| "ok".to_string()); + + let routes = root + .or(sound_routes(app.clone())) + .or(light_routes(app.clone())); serve(routes).run(server_addr).await; } @@ -121,40 +178,9 @@ fn handle_add_audio_device(app: App, props: &pipewire::spa::utils::dict::DictRef } } -/* -fn pipewire_loop(app: App) -> 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 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 _ = 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); @@ -170,12 +196,5 @@ async fn main() { async move { audio_controller.report(audio_status_tx).await } }); - /* - spawn_blocking({ - let app = app.clone(); - move || pipewire_main(state) - }); - */ - server_main(app.clone()).await; }