From b1885518851925a7d396a133e89437e7352c14f1 Mon Sep 17 00:00:00 2001 From: mykola2312 <49044616+mykola2312@users.noreply.github.com> Date: Tue, 20 Feb 2024 21:01:41 +0200 Subject: [PATCH] make separate process spawn function with error types to avoid code duplication --- src/dl.rs | 3 ++- src/dl/ffmpeg.rs | 63 +++++++++++++++--------------------------------- src/dl/spawn.rs | 50 ++++++++++++++++++++++++++++++++++++++ src/dl/yt_dlp.rs | 48 +++++++++++++----------------------- 4 files changed, 89 insertions(+), 75 deletions(-) create mode 100644 src/dl/spawn.rs diff --git a/src/dl.rs b/src/dl.rs index ac3aebe..c8be4c3 100644 --- a/src/dl.rs +++ b/src/dl.rs @@ -1,2 +1,3 @@ +pub mod ffmpeg; +mod spawn; pub mod yt_dlp; -pub mod ffmpeg; \ No newline at end of file diff --git a/src/dl/ffmpeg.rs b/src/dl/ffmpeg.rs index a833dbf..3ce14e1 100644 --- a/src/dl/ffmpeg.rs +++ b/src/dl/ffmpeg.rs @@ -1,50 +1,27 @@ -use core::fmt; -use std::{path::Path, str::Utf8Error}; -use tokio::process::Command; - -pub enum FFMpegError { - CommandError(std::io::Error), - UtfError(Utf8Error), - ErrorMessage(String) -} - -impl From for FFMpegError { - fn from(value: std::io::Error) -> Self { - Self::CommandError(value) - } -} - -impl From for FFMpegError { - fn from(value: Utf8Error) -> Self { - Self::UtfError(value) - } -} - -impl fmt::Display for FFMpegError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use FFMpegError as FE; - match self { - FE::CommandError(e) => write!(f, "Command::new - {}", e), - FE::UtfError(_) => write!(f, "Error while decoding UTF8"), - FE::ErrorMessage(msg) => write!(f, "ffmpeg error - {}", msg) - } - } -} +use super::spawn::{spawn, SpawnError}; pub struct FFMpeg {} impl FFMpeg { - pub async fn convert_to_mp3(input_path: &str, output_path: &str, bitrate: u16) -> Result<(), FFMpegError> { - let output = Command::new("ffmpeg") - .args(["-i", input_path, "-codec:a", "libmp3lame", "-b:a", "32k", output_path]) - .output() - .await?; + pub async fn convert_to_mp3( + input_path: &str, + output_path: &str, + bitrate: u16, + ) -> Result<(), SpawnError> { + let output = spawn( + "ffmpeg", + [ + "-i", + input_path, + "-codec:a", + "libmp3lame", + "-b:a", + "32k", + output_path, + ], + ) + .await?; - if !output.status.success() { - let message = std::str::from_utf8(&output.stderr)?; - return Err(FFMpegError::ErrorMessage(message.to_string())); - } - Ok(()) } -} \ No newline at end of file +} diff --git a/src/dl/spawn.rs b/src/dl/spawn.rs new file mode 100644 index 0000000..ba2f2f6 --- /dev/null +++ b/src/dl/spawn.rs @@ -0,0 +1,50 @@ +use core::fmt; +use std::ffi::OsStr; +use std::process::Output; +use std::str::Utf8Error; +use tokio::process::Command; + +#[derive(Debug)] +pub enum SpawnError { + CommandError(std::io::Error), + UtfError(Utf8Error), + ErrorMessage(String), +} + +impl From for SpawnError { + fn from(value: std::io::Error) -> Self { + Self::CommandError(value) + } +} + +impl From for SpawnError { + fn from(value: Utf8Error) -> Self { + Self::UtfError(value) + } +} + +impl fmt::Display for SpawnError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use SpawnError as FE; + match self { + FE::CommandError(e) => write!(f, "Command::new - {}", e), + FE::UtfError(_) => write!(f, "Error while decoding UTF8"), + FE::ErrorMessage(msg) => write!(f, "ffmpeg error - {}", msg), + } + } +} + +pub async fn spawn(program: &str, args: I) -> Result +where + I: IntoIterator, + S: AsRef, +{ + let output = Command::new(program).args(args).output().await?; + + if !output.status.success() { + let message = std::str::from_utf8(&output.stderr)?; + return Err(SpawnError::ErrorMessage(message.to_string())); + } + + Ok(output) +} diff --git a/src/dl/yt_dlp.rs b/src/dl/yt_dlp.rs index 2d3f53d..614d3bb 100644 --- a/src/dl/yt_dlp.rs +++ b/src/dl/yt_dlp.rs @@ -1,9 +1,8 @@ +use super::spawn::{spawn, SpawnError}; use core::fmt; +use ordered_float::OrderedFloat; use serde::Deserialize; use serde_json; -use std::str::Utf8Error; -use tokio::process::Command; -use ordered_float::OrderedFloat; #[derive(Deserialize, Debug)] pub struct YtDlpFormat { @@ -27,7 +26,7 @@ struct VideoFormat<'a> { struct AudioFormat<'a> { pub format: &'a YtDlpFormat, - pub abr: f32 + pub abr: f32, } impl YtDlpFormat { @@ -103,7 +102,7 @@ impl YtDlpInfo { } }) .max_by_key(|f| (f.width, f.height)); - + match format { Some(vf) => Some(vf.format), None => None, @@ -114,9 +113,14 @@ impl YtDlpInfo { let format = self .formats .iter() - .filter_map(|f| Some(AudioFormat { format: f, abr: f.abr? })) + .filter_map(|f| { + Some(AudioFormat { + format: f, + abr: f.abr?, + }) + }) .max_by_key(|f| OrderedFloat(f.abr)); - + match format { Some(af) => Some(af.format), None => None, @@ -126,21 +130,13 @@ impl YtDlpInfo { #[derive(Debug)] pub enum YtDlpError { - CommandError(std::io::Error), - UtfError(Utf8Error), - ErrorMessage(String), + SpawnError(SpawnError), JsonError, } -impl From for YtDlpError { - fn from(value: std::io::Error) -> Self { - Self::CommandError(value) - } -} - -impl From for YtDlpError { - fn from(value: Utf8Error) -> Self { - Self::UtfError(value) +impl From for YtDlpError { + fn from(value: SpawnError) -> Self { + Self::SpawnError(value) } } @@ -154,9 +150,7 @@ impl fmt::Display for YtDlpError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use YtDlpError as YTE; match self { - YTE::CommandError(e) => write!(f, "Command::new - {}", e), - YTE::UtfError(_) => write!(f, "Error while decoding UTF8"), - YTE::ErrorMessage(msg) => write!(f, "yt-dlp error - {}", msg), + YTE::SpawnError(e) => write!(f, "{}", e), YTE::JsonError => write!(f, "json parsing error"), } } @@ -166,15 +160,7 @@ pub struct YtDlp {} impl YtDlp { pub async fn load_info(url: &str) -> Result { - let output = Command::new("python") - .args(["-m", "yt_dlp", url, "-j"]) - .output() - .await?; - - if !output.status.success() { - let message = std::str::from_utf8(&output.stderr)?; - return Err(YtDlpError::ErrorMessage(message.to_string())); - } + let output = spawn("python", ["-m", "yt_dlp", url, "-j"]).await?; Ok(YtDlpInfo::parse(&output.stdout)?) }