From 1c701d1e619fff7cfd27158d6576dc73c9c43064 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Sun, 22 Mar 2026 12:37:01 +0100 Subject: [PATCH] refactor: improve error structure and add custom executable path --- src/error.rs | 7 +++++-- src/lib.rs | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/error.rs b/src/error.rs index b94527d..290b608 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,6 +8,9 @@ pub enum YtDlpError { UTF8Error(#[from] std::string::FromUtf8Error), #[error("failed to parse JSON: {0}")] JsonError(#[from] serde_json::Error), - #[error("yt-dlp exited with error: {0}")] - Failure(String), + #[error("yt-dlp exited with status {code:?}: {stderr}")] + Failure { + code: Option, + stderr: String, + }, } diff --git a/src/lib.rs b/src/lib.rs index b88f44f..0d45438 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,10 @@ impl YtDlpChild { if status.success() { Ok(()) } else { - Err(YtDlpError::Failure("Process failed".to_string())) + Err(YtDlpError::Failure { + code: status.code(), + stderr: "Process failed".to_string(), + }) } } } @@ -113,7 +116,10 @@ impl YtDlpStream { if status.success() { Ok(()) } else { - Err(YtDlpError::Failure("Process failed".to_string())) + Err(YtDlpError::Failure { + code: status.code(), + stderr: "Process failed".to_string(), + }) } } } @@ -124,6 +130,7 @@ pub struct YtDlp { links: Vec, args: Vec<(String, Option)>, output_dir: Option, + executable_path: Option, } impl YtDlp { @@ -143,6 +150,12 @@ impl YtDlp { } } + /// Set a custom path to the yt-dlp executable. + pub fn yt_dlp_path(mut self, path: impl Into) -> Self { + self.executable_path = Some(path.into()); + self + } + /// Set the output directory for the download. pub fn output_dir(mut self, path: PathBuf) -> Self { self.output_dir = Some(path); @@ -298,7 +311,10 @@ impl YtDlp { if !output.status.success() { let err_msg = String::from_utf8(output.stderr)?; error!("yt-dlp download failed: {}", err_msg); - return Err(YtDlpError::Failure(err_msg)); + return Err(YtDlpError::Failure { + code: output.status.code(), + stderr: err_msg, + }); } info!("yt-dlp download finished successfully"); @@ -328,7 +344,10 @@ impl YtDlp { let stdout = child.stdout.take().ok_or_else(|| { error!("Failed to capture stdout for binary stream"); - YtDlpError::Failure("Failed to capture standard output".to_string()) + YtDlpError::Failure { + code: None, + stderr: "Failed to capture standard output".to_string(), + } })?; Ok(YtDlpStream { child, stdout }) @@ -342,7 +361,10 @@ impl YtDlp { if !output.status.success() { let err_msg = String::from_utf8(output.stderr)?; error!("yt-dlp get_info failed: {}", err_msg); - return Err(YtDlpError::Failure(err_msg)); + return Err(YtDlpError::Failure { + code: output.status.code(), + stderr: err_msg, + }); } let stdout = String::from_utf8(output.stdout)?; @@ -360,14 +382,13 @@ impl YtDlp { } async fn spawn_yt_dlp(&self, dump_json: bool) -> Result { - if let Some(ref path) = self.output_dir { - if !path.exists() { - debug!("Creating output directory: {:?}", path); - fs::create_dir_all(path).await?; - } + if let Some(path) = self.output_dir.as_ref().filter(|p| !p.exists()) { + debug!("Creating output directory: {:?}", path); + fs::create_dir_all(path).await?; } - let mut cmd = Command::new("yt-dlp"); + let cmd_path = self.executable_path.as_deref().unwrap_or("yt-dlp"); + let mut cmd = Command::new(cmd_path); if let Some(ref path) = self.output_dir { cmd.current_dir(path);