about summary refs log tree commit diff stats
path: root/crates
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-16 13:53:36 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-16 13:53:36 +0200
commitada9550b02ee13a8378bd2ee27d536b83eec4820 (patch)
treee59876669d3cc3bcec2a3b97f543c9a01ade321d /crates
parentbuild(.envrc): Also disable ytdlp plugins by default (diff)
downloadyt-ada9550b02ee13a8378bd2ee27d536b83eec4820.zip
refactor(yt_dlp/lib): Explicitly convert python exceptions into an error
This avoids having to wrap all blocks into a `match` statement.
Diffstat (limited to 'crates')
-rw-r--r--crates/yt_dlp/src/lib.rs89
1 files changed, 50 insertions, 39 deletions
diff --git a/crates/yt_dlp/src/lib.rs b/crates/yt_dlp/src/lib.rs
index 1f859fe..536b0d1 100644
--- a/crates/yt_dlp/src/lib.rs
+++ b/crates/yt_dlp/src/lib.rs
@@ -78,7 +78,7 @@ impl YoutubeDL {
     ///
     /// # Errors
     /// If a python call fails.
-    pub fn from_options(mut options: YoutubeDLOptions) -> Result<Self, build::Error> {
+    pub fn from_options(options: YoutubeDLOptions) -> Result<Self, build::Error> {
         let mut settings = vm::Settings::default();
         if let Ok(python_path) = env::var("PYTHONPATH") {
             for path in python_path.split(':') {
@@ -110,9 +110,8 @@ impl YoutubeDL {
             let yt_dlp_module = vm.import("yt_dlp", 0)?;
             let class = yt_dlp_module.get_attr("YoutubeDL", vm)?;
 
-            let maybe_hook = mem::take(&mut options.progress_hook);
-            let opts = options.into_py_dict(vm);
-            if let Some(function) = maybe_hook {
+            let opts = json_loads(options.options, vm);
+            if let Some(function) = options.progress_hook {
                 opts.get_or_insert(vm, vm.new_pyobj("progress_hooks"), || {
                     let hook: PyObjectRef = vm.new_function("progress_hook", function).into();
                     vm.new_pyobj(vec![hook])
@@ -285,7 +284,7 @@ impl YoutubeDL {
         download: bool,
         process: bool,
     ) -> Result<InfoJson, extract_info::Error> {
-        match self.interpreter.enter(|vm| {
+        self.interpreter.enter(|vm| {
             let pos_args = PosArgs::new(vec![vm.new_pyobj(url.to_string())]);
 
             let kw_args = KwArgs::new({
@@ -297,9 +296,13 @@ impl YoutubeDL {
 
             let fun_args = FuncArgs::new(pos_args, kw_args);
 
-            let inner = self.youtube_dl_class.get_attr("extract_info", vm)?;
+            let inner = self
+                .youtube_dl_class
+                .get_attr("extract_info", vm)
+                .map_err(|exc| PythonError::from_exception(vm, &exc))?;
             let result = inner
-                .call_with_args(fun_args, vm)?
+                .call_with_args(fun_args, vm)
+                .map_err(|exc| PythonError::from_exception(vm, &exc))?
                 .downcast::<PyDict>()
                 .expect("This is a dict");
 
@@ -313,7 +316,9 @@ impl YoutubeDL {
                     });
 
                     let mut out = vec![];
-                    let next = generator.get_attr("__next__", vm)?;
+                    let next = generator
+                        .get_attr("__next__", vm)
+                        .map_err(|exc| PythonError::from_exception(vm, &exc))?;
                     while let Ok(output) = next.call((), vm) {
                         out.push(output);
 
@@ -321,7 +326,9 @@ impl YoutubeDL {
                             break;
                         }
                     }
-                    result.set_item("entries", vm.new_pyobj(out), vm)?;
+                    result
+                        .set_item("entries", vm.new_pyobj(out), vm)
+                        .map_err(|exc| PythonError::from_exception(vm, &exc))?;
                 }
             }
 
@@ -334,14 +341,8 @@ impl YoutubeDL {
 
             let result_json = json_dumps(result, vm);
 
-            Ok::<_, PyRef<PyBaseException>>(result_json)
-        }) {
-            Ok(ok) => Ok(ok),
-            Err(err) => self.interpreter.enter(|vm| {
-                let buffer = process_exception(vm, &err);
-                Err(extract_info::Error::Python(buffer))
-            }),
-        }
+            Ok(result)
+        })
     }
 
     /// Take the (potentially modified) result of the information extractor (i.e.,
@@ -362,7 +363,7 @@ impl YoutubeDL {
         ie_result: InfoJson,
         download: bool,
     ) -> Result<InfoJson, process_ie_result::Error> {
-        match self.interpreter.enter(|vm| {
+        self.interpreter.enter(|vm| {
             let pos_args = PosArgs::new(vec![vm.new_pyobj(json_loads(ie_result, vm))]);
 
             let kw_args = KwArgs::new({
@@ -373,9 +374,13 @@ impl YoutubeDL {
 
             let fun_args = FuncArgs::new(pos_args, kw_args);
 
-            let inner = self.youtube_dl_class.get_attr("process_ie_result", vm)?;
+            let inner = self
+                .youtube_dl_class
+                .get_attr("process_ie_result", vm)
+                .map_err(|exc| PythonError::from_exception(vm, &exc))?;
             let result = inner
-                .call_with_args(fun_args, vm)?
+                .call_with_args(fun_args, vm)
+                .map_err(|exc| PythonError::from_exception(vm, &exc))?
                 .downcast::<PyDict>()
                 .expect("This is a dict");
 
@@ -385,34 +390,47 @@ impl YoutubeDL {
 
                 value.downcast::<PyDict>().expect("This should stay a dict")
             };
+            Ok(result)
+        })
+    }
 
-            let result_json = json_dumps(result, vm);
 
-            Ok::<_, PyRef<PyBaseException>>(result_json)
-        }) {
-            Ok(ok) => Ok(ok),
-            Err(err) => self.interpreter.enter(|vm| {
-                let buffer = process_exception(vm, &err);
-                Err(process_ie_result::Error::Python(buffer))
-            }),
         }
+
+#[derive(thiserror::Error, Debug)]
+pub struct PythonError(pub String);
+
+impl Display for PythonError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "Python threw an exception: {}", self.0)
+    }
+}
+
+impl PythonError {
+    fn from_exception(vm: &VirtualMachine, exc: &PyRef<PyBaseException>) -> Self {
+        let buffer = process_exception(vm, exc);
+        Self(buffer)
     }
 }
 
 #[allow(missing_docs)]
 pub mod process_ie_result {
+    use crate::{PythonError, prepare};
+
     #[derive(Debug, thiserror::Error)]
     pub enum Error {
-        #[error("Python threw an exception: {0}")]
-        Python(String),
+        #[error(transparent)]
+        Python(#[from] PythonError),
     }
 }
 #[allow(missing_docs)]
 pub mod extract_info {
+    use crate::{PythonError};
+
     #[derive(Debug, thiserror::Error)]
     pub enum Error {
-        #[error("Python threw an exception: {0}")]
-        Python(String),
+        #[error(transparent)]
+        Python(#[from] PythonError),
     }
 }
 
@@ -480,10 +498,6 @@ impl YoutubeDLOptions {
     pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
         self.options.get(key)
     }
-
-    fn into_py_dict(self, vm: &VirtualMachine) -> PyRef<PyDict> {
-        json_loads(self.options, vm)
-    }
 }
 
 #[allow(missing_docs)]
@@ -492,9 +506,6 @@ pub mod build {
     pub enum Error {
         #[error("Python threw an exception: {0}")]
         Python(String),
-
-        #[error("Io error: {0}")]
-        Io(#[from] std::io::Error),
     }
 }