diff --git a/src/bot/dl.rs b/src/bot/dl.rs index 52a69b6..1cd84fd 100644 --- a/src/bot/dl.rs +++ b/src/bot/dl.rs @@ -3,12 +3,11 @@ use teloxide::types::InputFile; use tracing::{event, Level}; use super::types::HandlerResult; -use crate::dl::delete_if_exists; use crate::dl::download; async fn bot_download(bot: Bot, msg: Message, url: String) -> HandlerResult { - let output_path = match download(url.as_str()).await { - Ok(path) => path, + let output = match download(url.as_str()).await { + Ok(file) => file, Err(e) => { event!(Level::ERROR, "{}", e.to_string()); bot.send_message(msg.chat.id, e.to_string()).await?; @@ -16,14 +15,8 @@ async fn bot_download(bot: Bot, msg: Message, url: String) -> HandlerResult { } }; - if let Err(e) = bot - .send_video(msg.chat.id, InputFile::file(&output_path)) - .await - { - delete_if_exists(&output_path); - return Err(Box::new(e)); - } - + bot.send_video(msg.chat.id, InputFile::file(&output.path)) + .await?; Ok(()) } diff --git a/src/dl.rs b/src/dl.rs index fd1ad5f..6be367f 100644 --- a/src/dl.rs +++ b/src/dl.rs @@ -5,6 +5,7 @@ use tracing::{event, Level}; use crate::dl::ffmpeg::FFMpeg; use self::spawn::SpawnError; +use self::tmpfile::{TmpFile, TmpFileError}; use self::yt_dlp::{YtDlp, YtDlpError, YtDlpFormat, YtDlpInfo}; pub mod ffmpeg; @@ -30,6 +31,14 @@ impl From for DownloadError { } } +impl From for DownloadError { + fn from(value: TmpFileError) -> Self { + match value { + TmpFileError::MakePathError => DownloadError::MakePathError, + } + } +} + impl fmt::Display for DownloadError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use DownloadError as DE; @@ -76,7 +85,7 @@ pub fn delete_if_exists(path: &str) { } } -async fn download_fallback(url: &str, info: YtDlpInfo) -> Result { +async fn download_fallback(url: &str, info: YtDlpInfo) -> Result { let av = match info.best_av_format() { Some(av) => av, None => { @@ -95,16 +104,10 @@ async fn download_fallback(url: &str, info: YtDlpInfo) -> Result Result { +pub async fn download(url: &str) -> Result { event!(Level::INFO, "url {}", url); let info = YtDlp::load_info(url).await?; @@ -117,19 +120,8 @@ pub async fn download(url: &str) -> Result { None => return download_fallback(url, info).await, }; - // TODO: I should wrap those temp files in a impl Drop for defer deletion - let video_path = make_download_path(&info, Some("video"), &vf)?; - if let Err(e) = YtDlp::download(url, &vf.format_id, video_path.as_str()).await { - delete_if_exists(&video_path); - return Err(DownloadError::Message(e.to_string())); - } - - let audio_path = make_download_path(&info, Some("audio"), &af)?; - if let Err(e) = YtDlp::download(url, &af.format_id, audio_path.as_str()).await { - delete_if_exists(&video_path); - delete_if_exists(&audio_path); - return Err(DownloadError::Message(e.to_string())); - } + let video = YtDlp::download(url, &info, &vf).await?; + let audio = YtDlp::download(url, &info, &af).await?; let abr = if let Some(abr) = af.abr { FFMpeg::round_mp3_bitrate(abr) @@ -144,8 +136,7 @@ pub async fn download(url: &str) -> Result { 192 }; - let output_path = make_download_path(&info, None, &vf)?; - + let output = TmpFile::new(format!("{}.{}", &info.id, &vf.ext).as_str())?; event!( Level::INFO, "for {} we joining video {} and audio {}", @@ -154,18 +145,10 @@ pub async fn download(url: &str) -> Result { af.format_id ); - let res = FFMpeg::join_video_audio( - video_path.as_str(), - audio_path.as_str(), - abr, - output_path.as_str(), - ) - .await; - delete_if_exists(&video_path); - delete_if_exists(&audio_path); + let res = FFMpeg::join_video_audio(&video.path, &audio.path, abr, &output.path).await; match res { - Ok(()) => Ok(output_path), + Ok(()) => Ok(output), Err(e) => Err(DownloadError::Message(e.to_string())), } } diff --git a/src/dl/tmpfile.rs b/src/dl/tmpfile.rs index 38da162..9c6e1b6 100644 --- a/src/dl/tmpfile.rs +++ b/src/dl/tmpfile.rs @@ -2,7 +2,7 @@ use std::fs; use tracing::{event, Level}; pub enum TmpFileError { - MakePathError + MakePathError, } pub struct TmpFile { @@ -10,20 +10,20 @@ pub struct TmpFile { } impl TmpFile { - pub fn new(filename: String) -> Result { + pub fn new(filename: &str) -> Result { let path = std::env::temp_dir() .join(filename) .into_os_string() .into_string() .map_err(|_| TmpFileError::MakePathError)?; - + Ok(Self { path }) } pub fn exists(&self) -> bool { match fs::metadata(&self.path) { Ok(_) => true, - Err(_) => false + Err(_) => false, } } @@ -41,4 +41,4 @@ impl Drop for TmpFile { fn drop(&mut self) { self.delete_if_exists(); } -} \ No newline at end of file +} diff --git a/src/dl/yt_dlp.rs b/src/dl/yt_dlp.rs index 80a7377..8f23ed1 100644 --- a/src/dl/yt_dlp.rs +++ b/src/dl/yt_dlp.rs @@ -200,6 +200,7 @@ pub enum YtDlpError { ErrorMessage(String), // keep it separate type if we ever plan to parse yt-dlp errors JsonError, NoFormats, + MakePathError, NoFilePresent, } // ^(?:ERROR: \[.*\] \S* )(.*$) - regex for matching yt-dlp's youtube errors @@ -213,6 +214,12 @@ impl From for YtDlpError { } } +impl From for YtDlpError { + fn from(_value: TmpFileError) -> Self { + Self::MakePathError + } +} + impl From for YtDlpError { fn from(_value: serde_json::Error) -> Self { Self::JsonError @@ -227,6 +234,7 @@ impl fmt::Display for YtDlpError { YTE::ErrorMessage(msg) => write!(f, "yt-dlp error - {}", msg), YTE::JsonError => write!(f, "json parsing error"), YTE::NoFormats => write!(f, "no formats were parsed"), + YTE::MakePathError => write!(f, "make path error"), YTE::NoFilePresent => write!(f, "downloaded file doesn't exists"), } } @@ -247,7 +255,14 @@ impl YtDlp { Ok(info) } - pub async fn download(url: &str, format_id: &str, output_path: &str) -> Result<(), YtDlpError> { + pub async fn download( + url: &str, + info: &YtDlpInfo, + format: &YtDlpFormat, + ) -> Result { + let file = + TmpFile::new(format!("{}_{}.{}", info.id, format.format_id, format.ext).as_str())?; + spawn( "python", &[ @@ -255,17 +270,17 @@ impl YtDlp { "yt_dlp", url, "-f", - format_id, + &format.format_id, "-o", - output_path, + &file.path, "--force-overwrites", ], ) .await?; - match fs::metadata(output_path) { - Ok(_) => Ok(()), - Err(_) => Err(YtDlpError::NoFilePresent), + match file.exists() { + true => Ok(file), + false => Err(YtDlpError::NoFilePresent), } } }