use tmp file struct instead of paths and manual file deletion

This commit is contained in:
mykola2312 2024-03-30 05:11:46 +02:00
parent 9cc888258f
commit a85e36f1f2
4 changed files with 47 additions and 56 deletions

View file

@ -3,12 +3,11 @@ use teloxide::types::InputFile;
use tracing::{event, Level}; use tracing::{event, Level};
use super::types::HandlerResult; use super::types::HandlerResult;
use crate::dl::delete_if_exists;
use crate::dl::download; use crate::dl::download;
async fn bot_download(bot: Bot, msg: Message, url: String) -> HandlerResult { async fn bot_download(bot: Bot, msg: Message, url: String) -> HandlerResult {
let output_path = match download(url.as_str()).await { let output = match download(url.as_str()).await {
Ok(path) => path, Ok(file) => file,
Err(e) => { Err(e) => {
event!(Level::ERROR, "{}", e.to_string()); event!(Level::ERROR, "{}", e.to_string());
bot.send_message(msg.chat.id, e.to_string()).await?; 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 bot.send_video(msg.chat.id, InputFile::file(&output.path))
.send_video(msg.chat.id, InputFile::file(&output_path)) .await?;
.await
{
delete_if_exists(&output_path);
return Err(Box::new(e));
}
Ok(()) Ok(())
} }

View file

@ -5,6 +5,7 @@ use tracing::{event, Level};
use crate::dl::ffmpeg::FFMpeg; use crate::dl::ffmpeg::FFMpeg;
use self::spawn::SpawnError; use self::spawn::SpawnError;
use self::tmpfile::{TmpFile, TmpFileError};
use self::yt_dlp::{YtDlp, YtDlpError, YtDlpFormat, YtDlpInfo}; use self::yt_dlp::{YtDlp, YtDlpError, YtDlpFormat, YtDlpInfo};
pub mod ffmpeg; pub mod ffmpeg;
@ -30,6 +31,14 @@ impl From<YtDlpError> for DownloadError {
} }
} }
impl From<TmpFileError> for DownloadError {
fn from(value: TmpFileError) -> Self {
match value {
TmpFileError::MakePathError => DownloadError::MakePathError,
}
}
}
impl fmt::Display for DownloadError { impl fmt::Display for DownloadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DownloadError as DE; use DownloadError as DE;
@ -76,7 +85,7 @@ pub fn delete_if_exists(path: &str) {
} }
} }
async fn download_fallback(url: &str, info: YtDlpInfo) -> Result<String, DownloadError> { async fn download_fallback(url: &str, info: YtDlpInfo) -> Result<TmpFile, DownloadError> {
let av = match info.best_av_format() { let av = match info.best_av_format() {
Some(av) => av, Some(av) => av,
None => { None => {
@ -95,16 +104,10 @@ async fn download_fallback(url: &str, info: YtDlpInfo) -> Result<String, Downloa
} }
}; };
let output_path = make_download_path(&info, None, &av)?; Ok(YtDlp::download(url, &info, &av).await?)
if let Err(e) = YtDlp::download(url, &av.format_id, output_path.as_str()).await {
delete_if_exists(&output_path);
return Err(DownloadError::Message(e.to_string()));
}
Ok(output_path)
} }
pub async fn download(url: &str) -> Result<String, DownloadError> { pub async fn download(url: &str) -> Result<TmpFile, DownloadError> {
event!(Level::INFO, "url {}", url); event!(Level::INFO, "url {}", url);
let info = YtDlp::load_info(url).await?; let info = YtDlp::load_info(url).await?;
@ -117,19 +120,8 @@ pub async fn download(url: &str) -> Result<String, DownloadError> {
None => return download_fallback(url, info).await, None => return download_fallback(url, info).await,
}; };
// TODO: I should wrap those temp files in a impl Drop for defer deletion let video = YtDlp::download(url, &info, &vf).await?;
let video_path = make_download_path(&info, Some("video"), &vf)?; let audio = YtDlp::download(url, &info, &af).await?;
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 abr = if let Some(abr) = af.abr { let abr = if let Some(abr) = af.abr {
FFMpeg::round_mp3_bitrate(abr) FFMpeg::round_mp3_bitrate(abr)
@ -144,8 +136,7 @@ pub async fn download(url: &str) -> Result<String, DownloadError> {
192 192
}; };
let output_path = make_download_path(&info, None, &vf)?; let output = TmpFile::new(format!("{}.{}", &info.id, &vf.ext).as_str())?;
event!( event!(
Level::INFO, Level::INFO,
"for {} we joining video {} and audio {}", "for {} we joining video {} and audio {}",
@ -154,18 +145,10 @@ pub async fn download(url: &str) -> Result<String, DownloadError> {
af.format_id af.format_id
); );
let res = FFMpeg::join_video_audio( let res = FFMpeg::join_video_audio(&video.path, &audio.path, abr, &output.path).await;
video_path.as_str(),
audio_path.as_str(),
abr,
output_path.as_str(),
)
.await;
delete_if_exists(&video_path);
delete_if_exists(&audio_path);
match res { match res {
Ok(()) => Ok(output_path), Ok(()) => Ok(output),
Err(e) => Err(DownloadError::Message(e.to_string())), Err(e) => Err(DownloadError::Message(e.to_string())),
} }
} }

View file

@ -2,7 +2,7 @@ use std::fs;
use tracing::{event, Level}; use tracing::{event, Level};
pub enum TmpFileError { pub enum TmpFileError {
MakePathError MakePathError,
} }
pub struct TmpFile { pub struct TmpFile {
@ -10,7 +10,7 @@ pub struct TmpFile {
} }
impl TmpFile { impl TmpFile {
pub fn new(filename: String) -> Result<Self, TmpFileError> { pub fn new(filename: &str) -> Result<Self, TmpFileError> {
let path = std::env::temp_dir() let path = std::env::temp_dir()
.join(filename) .join(filename)
.into_os_string() .into_os_string()
@ -23,7 +23,7 @@ impl TmpFile {
pub fn exists(&self) -> bool { pub fn exists(&self) -> bool {
match fs::metadata(&self.path) { match fs::metadata(&self.path) {
Ok(_) => true, Ok(_) => true,
Err(_) => false Err(_) => false,
} }
} }

View file

@ -200,6 +200,7 @@ pub enum YtDlpError {
ErrorMessage(String), // keep it separate type if we ever plan to parse yt-dlp errors ErrorMessage(String), // keep it separate type if we ever plan to parse yt-dlp errors
JsonError, JsonError,
NoFormats, NoFormats,
MakePathError,
NoFilePresent, NoFilePresent,
} }
// ^(?:ERROR: \[.*\] \S* )(.*$) - regex for matching yt-dlp's youtube errors // ^(?:ERROR: \[.*\] \S* )(.*$) - regex for matching yt-dlp's youtube errors
@ -213,6 +214,12 @@ impl From<SpawnError> for YtDlpError {
} }
} }
impl From<TmpFileError> for YtDlpError {
fn from(_value: TmpFileError) -> Self {
Self::MakePathError
}
}
impl From<serde_json::Error> for YtDlpError { impl From<serde_json::Error> for YtDlpError {
fn from(_value: serde_json::Error) -> Self { fn from(_value: serde_json::Error) -> Self {
Self::JsonError Self::JsonError
@ -227,6 +234,7 @@ impl fmt::Display for YtDlpError {
YTE::ErrorMessage(msg) => write!(f, "yt-dlp error - {}", msg), YTE::ErrorMessage(msg) => write!(f, "yt-dlp error - {}", msg),
YTE::JsonError => write!(f, "json parsing error"), YTE::JsonError => write!(f, "json parsing error"),
YTE::NoFormats => write!(f, "no formats were parsed"), YTE::NoFormats => write!(f, "no formats were parsed"),
YTE::MakePathError => write!(f, "make path error"),
YTE::NoFilePresent => write!(f, "downloaded file doesn't exists"), YTE::NoFilePresent => write!(f, "downloaded file doesn't exists"),
} }
} }
@ -247,7 +255,14 @@ impl YtDlp {
Ok(info) 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<TmpFile, YtDlpError> {
let file =
TmpFile::new(format!("{}_{}.{}", info.id, format.format_id, format.ext).as_str())?;
spawn( spawn(
"python", "python",
&[ &[
@ -255,17 +270,17 @@ impl YtDlp {
"yt_dlp", "yt_dlp",
url, url,
"-f", "-f",
format_id, &format.format_id,
"-o", "-o",
output_path, &file.path,
"--force-overwrites", "--force-overwrites",
], ],
) )
.await?; .await?;
match fs::metadata(output_path) { match file.exists() {
Ok(_) => Ok(()), true => Ok(file),
Err(_) => Err(YtDlpError::NoFilePresent), false => Err(YtDlpError::NoFilePresent),
} }
} }
} }