diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 01:30:24 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 01:30:24 +0100 |
| commit | fa9486650e8de2be117757b1891fa0aacc4c47cd (patch) | |
| tree | 4638ed4664910600a9ce86461755a412a25df9d7 | |
| parent | net: drop unused import (diff) | |
| download | senator-sperma-fa9486650e8de2be117757b1891fa0aacc4c47cd.tar.gz senator-sperma-fa9486650e8de2be117757b1891fa0aacc4c47cd.zip | |
downloads: cap yt-dlp media size
| -rw-r--r-- | src/commands/utilities.rs | 15 | ||||
| -rw-r--r-- | src/voice/mod.rs | 12 |
2 files changed, 27 insertions, 0 deletions
diff --git a/src/commands/utilities.rs b/src/commands/utilities.rs index 7a2e65d..88829b2 100644 --- a/src/commands/utilities.rs +++ b/src/commands/utilities.rs | |||
| @@ -18,6 +18,9 @@ type Context<'a> = poise::Context<'a, Data, Error>; | |||
| 18 | // original and keeps replies snappy. | 18 | // original and keeps replies snappy. |
| 19 | const LARGE_FILE_THRESHOLD: usize = 10 * 1024 * 1024; | 19 | const LARGE_FILE_THRESHOLD: usize = 10 * 1024 * 1024; |
| 20 | 20 | ||
| 21 | /// Hard cap for a single `/download` result (before it is read into memory). | ||
| 22 | const MAX_DOWNLOAD_BYTES: u64 = 512 * 1024 * 1024; | ||
| 23 | |||
| 21 | #[derive(Clone, Copy, PartialEq, Eq)] | 24 | #[derive(Clone, Copy, PartialEq, Eq)] |
| 22 | pub(crate) enum FileType { | 25 | pub(crate) enum FileType { |
| 23 | Video, | 26 | Video, |
| @@ -524,6 +527,8 @@ async fn run_download( | |||
| 524 | .arg("bestaudio/bestvideo+bestaudio") | 527 | .arg("bestaudio/bestvideo+bestaudio") |
| 525 | .arg("--merge-output-format") | 528 | .arg("--merge-output-format") |
| 526 | .arg("mp4") | 529 | .arg("mp4") |
| 530 | .arg("--max-filesize") | ||
| 531 | .arg("512M") | ||
| 527 | .arg("-o") | 532 | .arg("-o") |
| 528 | .arg(&out_template); | 533 | .arg(&out_template); |
| 529 | if is_audio { | 534 | if is_audio { |
| @@ -574,6 +579,16 @@ async fn run_download( | |||
| 574 | None => basename, | 579 | None => basename, |
| 575 | }; | 580 | }; |
| 576 | 581 | ||
| 582 | match tokio::fs::metadata(&actual_path).await { | ||
| 583 | Ok(meta) if meta.len() > MAX_DOWNLOAD_BYTES => { | ||
| 584 | let _ = tokio::fs::remove_dir_all(&dl_dir).await; | ||
| 585 | ctx.say(lang::fmt_download_failed("file is larger than 512 MiB")) | ||
| 586 | .await?; | ||
| 587 | return Ok(()); | ||
| 588 | } | ||
| 589 | _ => {} | ||
| 590 | } | ||
| 591 | |||
| 577 | let bytes = match tokio::fs::read(&actual_path).await { | 592 | let bytes = match tokio::fs::read(&actual_path).await { |
| 578 | Ok(b) => b, | 593 | Ok(b) => b, |
| 579 | Err(e) => { | 594 | Err(e) => { |
diff --git a/src/voice/mod.rs b/src/voice/mod.rs index 5043508..d8308eb 100644 --- a/src/voice/mod.rs +++ b/src/voice/mod.rs | |||
| @@ -26,6 +26,10 @@ use crate::recording::{Recorder, RecorderEventHandler}; | |||
| 26 | /// and the ffmpeg `s16le` encode. Must match the `DecodeConfig` set in `main.rs`. | 26 | /// and the ffmpeg `s16le` encode. Must match the `DecodeConfig` set in `main.rs`. |
| 27 | const RECORDING_SAMPLE_RATE: u32 = 48000; | 27 | const RECORDING_SAMPLE_RATE: u32 = 48000; |
| 28 | 28 | ||
| 29 | /// Upper bound for yt-dlp media buffered in memory. Audio is decoded to f32 | ||
| 30 | /// PCM afterwards (another 4x), so a multi-hundred-MB download would OOM. | ||
| 31 | const MAX_MEDIA_BYTES: usize = 128 * 1024 * 1024; | ||
| 32 | |||
| 29 | #[derive(Clone)] | 33 | #[derive(Clone)] |
| 30 | pub struct VoiceManager { | 34 | pub struct VoiceManager { |
| 31 | pub songbird: Arc<Songbird>, | 35 | pub songbird: Arc<Songbird>, |
| @@ -247,6 +251,8 @@ pub async fn ytdlp_download(url: &str) -> Result<Vec<u8>> { | |||
| 247 | "-o", | 251 | "-o", |
| 248 | "-", | 252 | "-", |
| 249 | "--no-playlist", | 253 | "--no-playlist", |
| 254 | "--max-filesize", | ||
| 255 | "128M", | ||
| 250 | "--", | 256 | "--", |
| 251 | url, | 257 | url, |
| 252 | ]) | 258 | ]) |
| @@ -269,6 +275,12 @@ pub async fn ytdlp_download(url: &str) -> Result<Vec<u8>> { | |||
| 269 | ); | 275 | ); |
| 270 | return Err(Error::Custom(lang::fmt_ytdlp_stderr(&stderr))); | 276 | return Err(Error::Custom(lang::fmt_ytdlp_stderr(&stderr))); |
| 271 | } | 277 | } |
| 278 | if output.stdout.len() > MAX_MEDIA_BYTES { | ||
| 279 | return Err(Error::Custom(format!( | ||
| 280 | "media is too large (limit {} MiB)", | ||
| 281 | MAX_MEDIA_BYTES / (1024 * 1024) | ||
| 282 | ))); | ||
| 283 | } | ||
| 272 | 284 | ||
| 273 | Ok(output.stdout) | 285 | Ok(output.stdout) |
| 274 | } | 286 | } |
