aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 01:30:24 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 01:30:24 +0100
commitfa9486650e8de2be117757b1891fa0aacc4c47cd (patch)
tree4638ed4664910600a9ce86461755a412a25df9d7
parentnet: drop unused import (diff)
downloadsenator-sperma-fa9486650e8de2be117757b1891fa0aacc4c47cd.tar.gz
senator-sperma-fa9486650e8de2be117757b1891fa0aacc4c47cd.zip
downloads: cap yt-dlp media size
-rw-r--r--src/commands/utilities.rs15
-rw-r--r--src/voice/mod.rs12
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.
19const LARGE_FILE_THRESHOLD: usize = 10 * 1024 * 1024; 19const LARGE_FILE_THRESHOLD: usize = 10 * 1024 * 1024;
20 20
21/// Hard cap for a single `/download` result (before it is read into memory).
22const MAX_DOWNLOAD_BYTES: u64 = 512 * 1024 * 1024;
23
21#[derive(Clone, Copy, PartialEq, Eq)] 24#[derive(Clone, Copy, PartialEq, Eq)]
22pub(crate) enum FileType { 25pub(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`.
27const RECORDING_SAMPLE_RATE: u32 = 48000; 27const 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.
31const MAX_MEDIA_BYTES: usize = 128 * 1024 * 1024;
32
29#[derive(Clone)] 33#[derive(Clone)]
30pub struct VoiceManager { 34pub 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}