From b9eaa579dac2ae912e3be9a3aa3e73ddcb3416e1 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 01:31:11 +0100 Subject: music: decode compressed playback without a temp file --- src/music.rs | 10 ++-------- src/voice/mod.rs | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/music.rs b/src/music.rs index ac88631..acec278 100644 --- a/src/music.rs +++ b/src/music.rs @@ -575,14 +575,8 @@ async fn build_input(request: &TrackRequest, http: &reqwest::Client) -> Result Result { +/// Decode an in-memory audio file via ffmpeg to raw f32 PCM at 48 kHz stereo. +/// +/// Used for compressed playback so the intermediate file never has to touch +/// disk (and therefore cannot leak into `TEMP_PATH`). +pub async fn ffmpeg_decode_bytes(data: Vec) -> Result { use std::process::Stdio; let mut child = tokio::process::Command::new("ffmpeg") .args([ "-i", - path, + "pipe:0", "-vn", "-f", "f32le", @@ -360,17 +362,43 @@ pub async fn ffmpeg_decode_file(path: &str) -> Result { "2", "pipe:1", ]) + .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .kill_on_drop(true) .spawn() .map_err(|e| Error::Custom(format!("ffmpeg decode spawn: {e}")))?; + let mut stdin = child + .stdin + .take() + .ok_or_else(|| Error::Custom("ffmpeg stdin not captured".into()))?; let stdout = child .stdout .take() .ok_or_else(|| Error::Custom("ffmpeg stdout not captured".into()))?; + tokio::spawn(async move { + use tokio::io::AsyncWriteExt; + let _ = stdin.write_all(&data).await; + // Dropping stdin gives ffmpeg EOF. + }); + + if let Some(stderr) = child.stderr.take() { + tokio::spawn(async move { + use tokio::io::AsyncReadExt; + let mut buf = Vec::with_capacity(4096); + let mut reader = tokio::io::BufReader::new(stderr); + let _ = reader.read_to_end(&mut buf).await; + if !buf.is_empty() { + warn!( + "ffmpeg (decode) stderr: {}", + String::from_utf8_lossy(&buf).trim_end() + ); + } + }); + } + let ffmpeg_child = FfmpegChild { _child: child, stdout, -- cgit v1.2.3