diff --git a/client/src/app.rs b/client/src/app.rs index cb29551..ed9f361 100644 --- a/client/src/app.rs +++ b/client/src/app.rs @@ -210,11 +210,11 @@ impl LesavkaClientApp { let req = MonitorRequest { id: monitor_id, max_bitrate: 6_000 }; match cli.capture_video(Request::new(req)).await { Ok(mut stream) => { - debug!("πŸŽ₯ cli video{monitor_id}: stream opened"); + debug!("πŸŽ₯🏁 cli video{monitor_id}: stream opened"); while let Some(res) = stream.get_mut().message().await.transpose() { match res { Ok(pkt) => { - trace!("πŸŽ₯ cli video{monitor_id}: got {}β€―bytes", pkt.data.len()); + trace!("πŸŽ₯πŸ“₯ cli video{monitor_id}: got {}β€―bytes", pkt.data.len()); if tx.send(pkt).is_err() { warn!("⚠️πŸŽ₯ cli video{monitor_id}: GUI thread gone"); break; diff --git a/scripts/manual/audio-mic-fetch.sh b/scripts/manual/audio-mic-fetch.sh index 8d4ae24..8b7e5eb 100644 --- a/scripts/manual/audio-mic-fetch.sh +++ b/scripts/manual/audio-mic-fetch.sh @@ -1,14 +1,12 @@ #!/usr/bin/env bash -# scripts/manual/audio-clip-fetch.sh +# scripts/manual/audio-mic-fetch.sh -# Pull & play the most recent 1 s AAC clip from lesavka‑server -PI_HOST="nikto@192.168.42.253" # adjust +PI_HOST="nikto@192.168.42.253" # adjust if needed REMOTE_DIR="/tmp" -DEST="$(mktemp -u).aac" +TMPDIR=$(mktemp -d) +scp -q "${PI_HOST}:${REMOTE_DIR}/mic-*.aac" "$TMPDIR/" 2>/dev/null \ + || { echo "❌ no mic clip files found yet"; exit 1; } -scp "${PI_HOST}:${REMOTE_DIR}/ear-*.aac" "$DEST" 2>/dev/null \ - || { echo "❌ no clip files yet"; exit 1; } - -LATEST=$(ls -1t ear-*.aac | head -n1) -echo "🎧 playing ${LATEST} ..." -gst-play-1.0 --quiet "${LATEST}" +LATEST=$(ls -1t "$TMPDIR"/mic-*.aac | head -n1) +echo "🎀 playing $(basename "$LATEST") ..." +gst-play-1.0 --quiet "$LATEST" diff --git a/server/src/main.rs b/server/src/main.rs index cd3f046..5e6567f 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -2,8 +2,9 @@ // server/src/main.rs #![forbid(unsafe_code)] -use std::{panic, backtrace::Backtrace, pin::Pin, sync::Arc, time::Duration}; +use std::{panic, backtrace::Backtrace, pin::Pin, sync::Arc}; use std::sync::atomic::AtomicBool; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use anyhow::Context as _; use futures_util::{Stream, StreamExt}; use tokio::{ @@ -84,6 +85,14 @@ async fn open_with_retry(path: &str) -> anyhow::Result { Err(anyhow::anyhow!("timeout waiting for {path}")) } +fn next_minute() -> SystemTime { + let now = SystemTime::now() + .duration_since(UNIX_EPOCH).unwrap(); + let secs = now.as_secs(); + let next = (secs / 60 + 1) * 60; + UNIX_EPOCH + Duration::from_secs(next) +} + /*──────────────── Handler ───────────────────*/ struct Handler { kb: Arc>, @@ -180,10 +189,32 @@ impl Relay for Handler { // channel just to satisfy the β€œstream Empty” return type let (tx, rx) = tokio::sync::mpsc::channel(1); - // forward packets from gRPC to AppSrc + // -------- 1 clip‑tap variables ---------------------------- + use std::time::{SystemTime, UNIX_EPOCH, Duration}; + let mut capturing = Vec::with_capacity(200_000); // ~1 s @128 kbit + let mut next_min_boundary = next_minute(); + + // -------- 2 forward packets + collect for clip‑tap -------- tokio::spawn(async move { let mut inbound = req.into_inner(); while let Some(pkt) = inbound.next().await.transpose()? { + /* ---- clip‑tap: accumulate raw AAC ----- */ + capturing.extend_from_slice(&pkt.data); + if capturing.len() > 192_000 { // keep at most ~1 s + capturing.truncate(192_000); + } + if tracing::enabled!(tracing::Level::TRACE) + && SystemTime::now() >= next_min_boundary { + if !capturing.is_empty() { + let ts = chrono::Local::now() + .format("%Y%m%d-%H%M%S").to_string(); + let path = format!("/tmp/mic-{ts}.aac"); + std::fs::write(&path, &capturing).ok(); + tracing::debug!("πŸ“Ό wrote mic clip β†’ {}", path); + } + capturing.clear(); + next_min_boundary = next_minute(); + } static CNT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); let n = CNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); @@ -240,7 +271,7 @@ impl Relay for Handler { Ok(Response::new(Box::pin(s))) } - /*────────────── USB-reset RPC ───────────*/ + /*────────────── USB-reset RPC ────────────*/ async fn reset_usb( &self, _req: Request,