From 2910036a4c14faeeae63ccd0f8f213731254e2fd Mon Sep 17 00:00:00 2001 From: mykola2312 Date: Sat, 16 Mar 2024 03:03:33 +0200 Subject: [PATCH] implement default format fallback --- src/dl.rs | 11 ++++++++++- src/dl/yt_dlp.rs | 23 ++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/dl.rs b/src/dl.rs index ca1876b..882bfb9 100644 --- a/src/dl.rs +++ b/src/dl.rs @@ -70,7 +70,16 @@ pub async fn download(url: &str) -> Result { let info = YtDlp::load_info(url).await?; let av = match info.best_av_format() { Some(av) => av, - None => return Err(DownloadError::NoFormatFound), + None => { + event!(Level::WARN, "no best format found for {}, reverting to default", url); + match info.default_format() { + Some(format) => format, + None => { + event!(Level::ERROR, "no formats found for {}", url); + return Err(DownloadError::NoFormatFound) + } + } + }, }; let output_path = make_download_path(&info, &av)?; diff --git a/src/dl/yt_dlp.rs b/src/dl/yt_dlp.rs index af7b92a..83f69aa 100644 --- a/src/dl/yt_dlp.rs +++ b/src/dl/yt_dlp.rs @@ -79,6 +79,8 @@ pub struct YtDlpInfo { } impl YtDlpInfo { + const H_LIMIT: u16 = 720; + pub fn parse(json: &[u8]) -> Result { let mut info: YtDlpInfo = serde_json::from_slice(json)?; for format in &mut info.formats { @@ -88,6 +90,18 @@ impl YtDlpInfo { Ok(info) } + pub fn default_format(&self) -> Option<&YtDlpFormat> { + match self + .formats + .iter() + .filter(|f| f.height.is_some_and(|h| h <= Self::H_LIMIT)) + .last() + { + Some(format) => Some(format), + None => self.formats.last(), + } + } + pub fn best_av_format(&self) -> Option<&YtDlpFormat> { let format = self .formats @@ -141,6 +155,7 @@ pub enum YtDlpError { SpawnError(SpawnError), ErrorMessage(String), // keep it separate type if we ever plan to parse yt-dlp errors JsonError, + NoFormats, NoFilePresent, } // ^(?:ERROR: \[.*\] \S* )(.*$) - regex for matching yt-dlp's youtube errors @@ -167,6 +182,7 @@ impl fmt::Display for YtDlpError { YTE::SpawnError(e) => write!(f, "{}", e), 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::NoFilePresent => write!(f, "downloaded file doesn't exists"), } } @@ -179,7 +195,12 @@ impl YtDlp { pub async fn load_info(url: &str) -> Result { let output = spawn("python", &["-m", "yt_dlp", url, "-j"]).await?; - Ok(YtDlpInfo::parse(&output.stdout)?) + let info = YtDlpInfo::parse(&output.stdout)?; + if info.formats.is_empty() { + return Err(YtDlpError::NoFormats); + } + + Ok(info) } pub async fn download(url: &str, format_id: &str, output_path: &str) -> Result<(), YtDlpError> {