NetDesk 1.5.0 — source release

This commit is contained in:
NetDesk committed 2026-10-18 00:10:03 +00:00
commit 37aa876908
227 files changed
+80694

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
//! NetDesk voice send-test — drives the EXACT path the native viewer's voice
//! button uses (`netdesk_host::audio::run_audio` with source = microphone, and
//! `Session::voice` per Opus packet), headlessly, so we can prove viewer->host
//! two-way voice end to end against a scratch host. Developed by Akronlogic.
//!
//! Usage: netdesk-voice-send <host_id> <password> [ws_url] [secs]
//! Watch the HOST log for `voice: rx N pkts from viewer mic, recent peak X`.
use netdesk_core::{connect, ConnectConfig, SessionEvent};
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let a: Vec<String> = std::env::args().collect();
if a.len() < 3 {
eprintln!("usage: netdesk-voice-send <host_id> <password> [ws_url] [secs]");
std::process::exit(2);
}
let secs: u64 = a.get(4).and_then(|s| s.parse().ok()).unwrap_or(6);
let cfg = ConnectConfig {
host_id: a[1].clone(),
password: a[2].clone(),
ws_url: a.get(3).cloned().unwrap_or_else(|| "wss://netdesk.space/ws".into()),
totp: String::new(),
takeover: false,
};
let (tx, mut rx) = mpsc::unbounded_channel::<SessionEvent>();
eprintln!("[voice-send] connecting to host {} …", cfg.host_id);
let session = connect(cfg, tx).await.map_err(|e| anyhow::anyhow!("connect: {e}"))?;
eprintln!("[voice-send] connected; capturing microphone and streaming CHAN_VOICE for {secs}s");
// Mirror viewer::toggle_voice exactly: share the Session behind a Mutex and
// feed each captured Opus packet to Session::voice from the capture thread.
let sess = Arc::new(Mutex::new(session));
let stop = Arc::new(AtomicBool::new(false));
let sent = Arc::new(AtomicUsize::new(0));
#[cfg(windows)]
{
let s2 = sess.clone();
let st2 = stop.clone();
let n2 = sent.clone();
std::thread::spawn(move || {
let src = Arc::new(AtomicU8::new(2)); // 2 = microphone (default capture device)
netdesk_host::audio::run_audio(st2, || true, src, move |opus| {
s2.lock().unwrap().voice(&opus);
n2.fetch_add(1, Ordering::Relaxed);
});
});
}
#[cfg(not(windows))]
{
eprintln!("[voice-send] microphone capture is Windows-only");
}
// Drain session events (ignore them) while the capture thread streams.
let deadline = tokio::time::Instant::now() + Duration::from_secs(secs);
loop {
tokio::select! {
_ = tokio::time::sleep_until(deadline) => break,
ev = rx.recv() => match ev {
Some(SessionEvent::Ended { .. }) | None => break,
Some(_) => {}
}
}
}
stop.store(true, Ordering::Relaxed);
let n = sent.load(Ordering::Relaxed);
sess.lock().unwrap().close();
println!("VOICE_SEND_DONE sent={n} packets");
if n == 0 {
eprintln!("[voice-send] WARNING: 0 packets — no microphone/capture device?");
std::process::exit(3);
}
Ok(())
}