diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-02-14 16:11:49 +0100 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-02-14 16:13:17 +0100 |
commit | 4d1b8136bb23d009ee04d863780225ad9d9f9eed (patch) | |
tree | 636f197771692086b734144a7a93d1748907579e /crates | |
parent | fix(crates/yt_dlp): Avoid printing the file extension in the progress display (diff) | |
download | yt-4d1b8136bb23d009ee04d863780225ad9d9f9eed.zip |
fix(crates/yt_dlp): Actually return errors instead of panicing
Diffstat (limited to 'crates')
-rw-r--r-- | crates/yt_dlp/src/error.rs | 45 | ||||
-rw-r--r-- | crates/yt_dlp/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/yt_dlp/src/python_json_decode_failed.error_msg | 5 |
3 files changed, 62 insertions, 8 deletions
diff --git a/crates/yt_dlp/src/error.rs b/crates/yt_dlp/src/error.rs new file mode 100644 index 0000000..4327f0d --- /dev/null +++ b/crates/yt_dlp/src/error.rs @@ -0,0 +1,45 @@ +use std::{fmt::Display, io}; + +#[derive(Debug)] +#[allow(clippy::module_name_repetitions)] +pub enum YtDlpError { + ResponseParseError { error: serde_json::error::Error }, + PythonError { error: Box<pyo3::PyErr> }, + IoError { error: io::Error }, +} + +impl std::error::Error for YtDlpError {} + +impl Display for YtDlpError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + YtDlpError::ResponseParseError { error } => write!( + f, + include_str!("./python_json_decode_failed.error_msg"), + error + ), + YtDlpError::PythonError { error } => write!(f, "Python error: {error}"), + YtDlpError::IoError { error } => write!(f, "Io error: {error}"), + } + } +} + +impl From<serde_json::error::Error> for YtDlpError { + fn from(value: serde_json::error::Error) -> Self { + Self::ResponseParseError { error: value } + } +} + +impl From<pyo3::PyErr> for YtDlpError { + fn from(value: pyo3::PyErr) -> Self { + Self::PythonError { + error: Box::new(value), + } + } +} + +impl From<io::Error> for YtDlpError { + fn from(value: io::Error) -> Self { + Self::IoError { error: value } + } +} diff --git a/crates/yt_dlp/src/lib.rs b/crates/yt_dlp/src/lib.rs index 88ef996..8bd2748 100644 --- a/crates/yt_dlp/src/lib.rs +++ b/crates/yt_dlp/src/lib.rs @@ -12,8 +12,8 @@ #![allow(unsafe_op_in_unsafe_fn)] #![allow(clippy::missing_errors_doc)] -use std::env; use std::io::stdout; +use std::{env, process}; use std::{fs::File, io::Write}; use std::{path::PathBuf, sync::Once}; @@ -21,6 +21,7 @@ use std::{path::PathBuf, sync::Once}; use crate::{duration::Duration, logging::setup_logging, wrapper::info_json::InfoJson}; use bytes::Bytes; +use error::YtDlpError; use log::{info, log_enabled, Level}; use pyo3::types::{PyString, PyTuple, PyTupleMethods}; use pyo3::{ @@ -33,6 +34,7 @@ use serde_json::{Map, Value}; use url::Url; pub mod duration; +pub mod error; pub mod logging; pub mod wrapper; @@ -125,7 +127,7 @@ pub fn progress_hook(py: Python<'_>, input: &Bound<'_, PyDict>) -> PyResult<()> .expect("Will always work") .to_owned(), )?) - .expect("Python should always produce valid json"); + .expect("python's json is valid"); macro_rules! get { (@interrogate $item:ident, $type_fun:ident, $get_fun:ident, $name:expr) => {{ @@ -266,7 +268,10 @@ pub fn progress_hook(py: Python<'_>, input: &Bound<'_, PyDict>) -> PyResult<()> println!("-> Finished downloading."); } "error" => { - panic!("-> Error while downloading: {}", get_title()) + // TODO: This should probably return an Err. But I'm not so sure where the error would + // bubble up to (i.e., who would catch it) <2025-01-21> + eprintln!("-> Error while downloading: {}", get_title()); + process::exit(1); } other => unreachable!("'{other}' should not be a valid state!"), }; @@ -312,8 +317,8 @@ pub async fn extract_info( url: &Url, download: bool, process: bool, -) -> PyResult<InfoJson> { - Python::with_gil(|py| { +) -> Result<InfoJson, YtDlpError> { + Python::with_gil(|py| -> Result<InfoJson, YtDlpError> { let opts = json_map_to_py_dict(yt_dlp_opts, py)?; let instance = get_yt_dlp(py, opts)?; @@ -339,8 +344,7 @@ pub async fn extract_info( } } - Ok(serde_json::from_str(&result_str) - .expect("Python should be able to produce correct json")) + serde_json::from_str(&result_str).map_err(Into::into) }) } @@ -372,7 +376,7 @@ pub fn unsmuggle_url(smug_url: &Url) -> PyResult<Url> { pub async fn download( urls: &[Url], download_options: &Map<String, Value>, -) -> PyResult<Vec<PathBuf>> { +) -> Result<Vec<PathBuf>, YtDlpError> { let mut out_paths = Vec::with_capacity(urls.len()); for url in urls { diff --git a/crates/yt_dlp/src/python_json_decode_failed.error_msg b/crates/yt_dlp/src/python_json_decode_failed.error_msg new file mode 100644 index 0000000..d10688e --- /dev/null +++ b/crates/yt_dlp/src/python_json_decode_failed.error_msg @@ -0,0 +1,5 @@ +Failed to decode yt-dlp's response: {} + +This is probably a bug. +Try running the command again with the `YT_STORE_INFO_JSON=yes` environment variable set +and maybe debug it further via `yt check info-json output.info.json`. |