implement default format fallback

This commit is contained in:
mykola2312 2024-03-16 03:03:33 +02:00
parent 144ae00e7d
commit 2c23c8c6a1
2 changed files with 32 additions and 2 deletions

View file

@ -70,7 +70,16 @@ pub async fn download(url: &str) -> Result<String, DownloadError> {
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)?;

View file

@ -79,6 +79,8 @@ pub struct YtDlpInfo {
}
impl YtDlpInfo {
const H_LIMIT: u16 = 720;
pub fn parse(json: &[u8]) -> Result<YtDlpInfo, serde_json::Error> {
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<YtDlpInfo, YtDlpError> {
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> {