use tmp file struct instead of paths and manual file deletion
This commit is contained in:
parent
9cc888258f
commit
a85e36f1f2
4 changed files with 47 additions and 56 deletions
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
51
src/dl.rs
51
src/dl.rs
|
|
@ -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())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,20 +10,20 @@ 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()
|
||||||
.into_string()
|
.into_string()
|
||||||
.map_err(|_| TmpFileError::MakePathError)?;
|
.map_err(|_| TmpFileError::MakePathError)?;
|
||||||
|
|
||||||
Ok(Self { path })
|
Ok(Self { path })
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,4 +41,4 @@ impl Drop for TmpFile {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.delete_if_exists();
|
self.delete_if_exists();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue