make separate process spawn function with error types to avoid code duplication

This commit is contained in:
mykola2312 2024-02-20 21:01:41 +02:00
parent 494c00a60e
commit b188551885
4 changed files with 89 additions and 75 deletions

View file

@ -1,2 +1,3 @@
pub mod yt_dlp;
pub mod ffmpeg; pub mod ffmpeg;
mod spawn;
pub mod yt_dlp;

View file

@ -1,50 +1,27 @@
use core::fmt; use super::spawn::{spawn, SpawnError};
use std::{path::Path, str::Utf8Error};
use tokio::process::Command;
pub enum FFMpegError {
CommandError(std::io::Error),
UtfError(Utf8Error),
ErrorMessage(String)
}
impl From<std::io::Error> for FFMpegError {
fn from(value: std::io::Error) -> Self {
Self::CommandError(value)
}
}
impl From<Utf8Error> 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)
}
}
}
pub struct FFMpeg {} pub struct FFMpeg {}
impl FFMpeg { impl FFMpeg {
pub async fn convert_to_mp3(input_path: &str, output_path: &str, bitrate: u16) -> Result<(), FFMpegError> { pub async fn convert_to_mp3(
let output = Command::new("ffmpeg") input_path: &str,
.args(["-i", input_path, "-codec:a", "libmp3lame", "-b:a", "32k", output_path]) output_path: &str,
.output() bitrate: u16,
) -> Result<(), SpawnError> {
let output = spawn(
"ffmpeg",
[
"-i",
input_path,
"-codec:a",
"libmp3lame",
"-b:a",
"32k",
output_path,
],
)
.await?; .await?;
if !output.status.success() {
let message = std::str::from_utf8(&output.stderr)?;
return Err(FFMpegError::ErrorMessage(message.to_string()));
}
Ok(()) Ok(())
} }
} }

50
src/dl/spawn.rs Normal file
View file

@ -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<std::io::Error> for SpawnError {
fn from(value: std::io::Error) -> Self {
Self::CommandError(value)
}
}
impl From<Utf8Error> 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<I, S>(program: &str, args: I) -> Result<Output, SpawnError>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
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)
}

View file

@ -1,9 +1,8 @@
use super::spawn::{spawn, SpawnError};
use core::fmt; use core::fmt;
use ordered_float::OrderedFloat;
use serde::Deserialize; use serde::Deserialize;
use serde_json; use serde_json;
use std::str::Utf8Error;
use tokio::process::Command;
use ordered_float::OrderedFloat;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct YtDlpFormat { pub struct YtDlpFormat {
@ -27,7 +26,7 @@ struct VideoFormat<'a> {
struct AudioFormat<'a> { struct AudioFormat<'a> {
pub format: &'a YtDlpFormat, pub format: &'a YtDlpFormat,
pub abr: f32 pub abr: f32,
} }
impl YtDlpFormat { impl YtDlpFormat {
@ -114,7 +113,12 @@ impl YtDlpInfo {
let format = self let format = self
.formats .formats
.iter() .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)); .max_by_key(|f| OrderedFloat(f.abr));
match format { match format {
@ -126,21 +130,13 @@ impl YtDlpInfo {
#[derive(Debug)] #[derive(Debug)]
pub enum YtDlpError { pub enum YtDlpError {
CommandError(std::io::Error), SpawnError(SpawnError),
UtfError(Utf8Error),
ErrorMessage(String),
JsonError, JsonError,
} }
impl From<std::io::Error> for YtDlpError { impl From<SpawnError> for YtDlpError {
fn from(value: std::io::Error) -> Self { fn from(value: SpawnError) -> Self {
Self::CommandError(value) Self::SpawnError(value)
}
}
impl From<Utf8Error> for YtDlpError {
fn from(value: Utf8Error) -> Self {
Self::UtfError(value)
} }
} }
@ -154,9 +150,7 @@ impl fmt::Display for YtDlpError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use YtDlpError as YTE; use YtDlpError as YTE;
match self { match self {
YTE::CommandError(e) => write!(f, "Command::new - {}", e), YTE::SpawnError(e) => write!(f, "{}", e),
YTE::UtfError(_) => write!(f, "Error while decoding UTF8"),
YTE::ErrorMessage(msg) => write!(f, "yt-dlp error - {}", msg),
YTE::JsonError => write!(f, "json parsing error"), YTE::JsonError => write!(f, "json parsing error"),
} }
} }
@ -166,15 +160,7 @@ pub struct YtDlp {}
impl YtDlp { impl YtDlp {
pub async fn load_info(url: &str) -> Result<YtDlpInfo, YtDlpError> { pub async fn load_info(url: &str) -> Result<YtDlpInfo, YtDlpError> {
let output = Command::new("python") let output = spawn("python", ["-m", "yt_dlp", url, "-j"]).await?;
.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()));
}
Ok(YtDlpInfo::parse(&output.stdout)?) Ok(YtDlpInfo::parse(&output.stdout)?)
} }