diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index c9fff44522..fd314c0d63 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -351,6 +351,58 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "base58ck" version = "0.1.0" @@ -2193,6 +2245,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hybrid-array" version = "0.4.10" @@ -2216,6 +2274,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2841,6 +2900,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "matrixmultiply" version = "0.3.10" @@ -4866,6 +4931,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_repr" version = "0.1.20" @@ -5168,6 +5244,7 @@ version = "0.1.0" dependencies = [ "atomic-write-file", "audioadapter-buffers", + "axum", "base64 0.22.1", "bzip2 0.5.2", "chrono", @@ -6331,6 +6408,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -6369,6 +6447,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 929d955781..0fe8ed2fc5 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -44,7 +44,7 @@ serde_json = "1" nostr = "0.37" nostr-compat = { package = "nostr", version = "0.36" } zeroize = "1" -reqwest = { version = "0.13", features = ["json", "query"] } +reqwest = { version = "0.13", features = ["json", "query", "stream"] } url = "2" sprout-core = { path = "../../crates/sprout-core" } sprout-persona = { path = "../../crates/sprout-persona" } @@ -62,6 +62,7 @@ sherpa-onnx = "1.12" ort = { version = "=2.0.0-rc.12", default-features = false, features = ["std", "ndarray", "tracing", "download-binaries", "tls-native", "copy-dylibs", "api-23", "coreml"] } ndarray = { version = "0.17", features = ["rayon"] } regex = "1" +axum = "0.8" rodio = "0.22" earshot = "1.0" rubato = "2.0" diff --git a/desktop/src-tauri/src/app_state.rs b/desktop/src-tauri/src/app_state.rs index d2b05a1263..605e753132 100644 --- a/desktop/src-tauri/src/app_state.rs +++ b/desktop/src-tauri/src/app_state.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, io::Write, sync::Mutex}; +use std::{ + collections::HashMap, + io::Write, + sync::{atomic::AtomicU16, Mutex}, +}; use nostr::{Keys, ToBech32}; use tauri::{AppHandle, Manager}; @@ -23,6 +27,8 @@ pub struct AppState { /// Selected audio output device name. `None` = system default. /// Used by `connect_audio_relay` and TTS pipeline when opening sinks. pub audio_output_device: Mutex>, + /// Port of the localhost media streaming proxy (set during setup). + pub media_proxy_port: AtomicU16, } pub fn build_app_state() -> AppState { @@ -69,6 +75,7 @@ pub fn build_app_state() -> AppState { huddle_state: Mutex::new(HuddleState::default()), app_handle: Mutex::new(None), audio_output_device: Mutex::new(None), + media_proxy_port: AtomicU16::new(0), } } diff --git a/desktop/src-tauri/src/commands/identity.rs b/desktop/src-tauri/src/commands/identity.rs index 482fc4fb4b..f4ca90ce3b 100644 --- a/desktop/src-tauri/src/commands/identity.rs +++ b/desktop/src-tauri/src/commands/identity.rs @@ -37,6 +37,13 @@ pub fn get_relay_http_url() -> String { relay_api_base_url() } +#[tauri::command] +pub fn get_media_proxy_port(state: State<'_, AppState>) -> u16 { + state + .media_proxy_port + .load(std::sync::atomic::Ordering::Relaxed) +} + #[tauri::command] pub fn sign_event( kind: u16, diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 13e239a402..b43a3b8601 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -3,6 +3,7 @@ mod commands; mod events; mod huddle; mod managed_agents; +mod media_proxy; mod migration; mod models; mod relay; @@ -28,7 +29,7 @@ use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }; -use tauri::{http, Emitter, Manager, RunEvent}; +use tauri::{Emitter, Manager, RunEvent}; use tauri_plugin_window_state::StateFlags; fn shutdown_managed_agents(app: &tauri::AppHandle) -> Result<(), String> { @@ -150,133 +151,6 @@ fn shutdown_managed_agents(app: &tauri::AppHandle) -> Result<(), String> { Ok(()) } -/// Defense-in-depth cap: refuse to buffer responses larger than this into RAM. -/// Range requests (≤16 MiB from server) always fit. Full GETs for huge videos -/// get a clear 413 instead of OOM — the