about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-02-14 17:15:18 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-02-14 17:15:18 +0100
commit66c739237cc352fedf6276a3163097c1f1f32bd4 (patch)
treeefdc61c079473e4d86f38454cd1568337e39f219
parentfix(yt/watch): Always open a `mpv` window (diff)
downloadyt-66c739237cc352fedf6276a3163097c1f1f32bd4.zip
fix(crates/libmpv2/Mpv::command): Correctly escape arguments
This allows us to avoid all these ad-hoc command escaping `format!`
invocations.
-rw-r--r--crates/libmpv2/src/mpv.rs26
-rw-r--r--yt/src/watch/events/handlers/mod.rs2
-rw-r--r--yt/src/watch/events/mod.rs9
-rw-r--r--yt/src/watch/mod.rs16
4 files changed, 21 insertions, 32 deletions
diff --git a/crates/libmpv2/src/mpv.rs b/crates/libmpv2/src/mpv.rs
index 07d0976..6f26d0f 100644
--- a/crates/libmpv2/src/mpv.rs
+++ b/crates/libmpv2/src/mpv.rs
@@ -526,19 +526,6 @@ impl Mpv {
         })
     }
 
-    /// Execute a command
-    pub fn execute(&self, name: &str, args: &[&str]) -> Result<()> {
-        if args.is_empty() {
-            debug!("Running mpv command: '{}'", name);
-        } else {
-            debug!("Running mpv command: '{} {}'", name, args.join(" "));
-        }
-
-        self.command(name, args)?;
-
-        Ok(())
-    }
-
     /// Load a configuration file. The path has to be absolute, and a file.
     pub fn load_config(&self, path: &str) -> Result<()> {
         let file = CString::new(path)?.into_raw();
@@ -562,7 +549,7 @@ impl Mpv {
     /// Send a command to the `Mpv` instance. This uses `mpv_command_string` internally,
     /// so that the syntax is the same as described in the [manual for the input.conf](https://mpv.io/manual/master/#list-of-input-commands).
     ///
-    /// Note that you may have to escape strings with `""` when they contain spaces.
+    /// Note that this function escapes the arguments for you.
     ///
     /// # Examples
     ///
@@ -583,12 +570,19 @@ impl Mpv {
     /// # }
     /// ```
     pub fn command(&self, name: &str, args: &[&str]) -> Result<()> {
-        let mut cmd = name.to_owned();
+        fn escape(input: &str) -> String {
+            input.replace('"', "\\\"")
+        }
+
+        let mut cmd = escape(name);
 
         for elem in args {
             cmd.push(' ');
-            cmd.push_str(elem);
+            cmd.push('"');
+            cmd.push_str(&escape(elem));
+            cmd.push('"');
         }
+        debug!("Running mpv command: '{}'", cmd);
 
         let raw = CString::new(cmd)?;
         mpv_err((), unsafe {
diff --git a/yt/src/watch/events/handlers/mod.rs b/yt/src/watch/events/handlers/mod.rs
index 715896d..3f30812 100644
--- a/yt/src/watch/events/handlers/mod.rs
+++ b/yt/src/watch/events/handlers/mod.rs
@@ -178,7 +178,7 @@ impl MpvEventHandler {
     /// # Panics
     /// Only if internal assertions fail.
     pub fn handle_client_message_yt_mark_watch_later(&mut self, mpv: &Mpv) -> Result<()> {
-        mpv.execute("write-watch-later-config", &[])?;
+        mpv.command("write-watch-later-config", &[])?;
 
         let hash = self.remove_cvideo_from_playlist(mpv)?;
         assert!(
diff --git a/yt/src/watch/events/mod.rs b/yt/src/watch/events/mod.rs
index b63b33b..10e8bc8 100644
--- a/yt/src/watch/events/mod.rs
+++ b/yt/src/watch/events/mod.rs
@@ -113,11 +113,8 @@ impl MpvEventHandler {
                     orig_cache_path.display()
                 )
             })?;
-            let fmt_cache_path = format!("\"{cache_path}\"");
 
-            let args = &[&fmt_cache_path, "append-play"];
-
-            mpv.execute("loadfile", args)?;
+            mpv.command("loadfile", &[cache_path, "append-play"])?;
             self.playlist_handler
                 .add(cache_path.to_owned(), play_thing.extractor_hash);
         }
@@ -133,7 +130,7 @@ impl MpvEventHandler {
     }
 
     fn message(mpv: &Mpv, message: &str, time: &str) -> Result<()> {
-        mpv.execute("show-text", &[format!("\"{message}\"").as_str(), time])?;
+        mpv.command("show-text", &[message, time])?;
         Ok(())
     }
 
@@ -215,7 +212,7 @@ impl MpvEventHandler {
     /// This also returns the hash of the current video
     fn remove_cvideo_from_playlist(&mut self, mpv: &Mpv) -> Result<ExtractorHash> {
         let hash = self.get_cvideo_hash(mpv, 0)?;
-        mpv.execute("playlist-remove", &["current"])?;
+        mpv.command("playlist-remove", &["current"])?;
         Ok(hash)
     }
 
diff --git a/yt/src/watch/mod.rs b/yt/src/watch/mod.rs
index 9b2cbef..7247999 100644
--- a/yt/src/watch/mod.rs
+++ b/yt/src/watch/mod.rs
@@ -13,7 +13,7 @@ use std::{collections::HashMap, time::Duration};
 use anyhow::{Context, Result};
 use events::{IdleCheckOutput, MpvEventHandler};
 use libmpv2::{events::EventContext, Mpv};
-use log::{debug, info, warn};
+use log::{debug, info, trace, warn};
 use tokio::time;
 
 use crate::{
@@ -47,12 +47,13 @@ pub async fn watch(app: &App) -> Result<()> {
         // As mpv does not have cli access, no window means no control and no user feedback.
         mpv.set_option("force-window", "yes")?;
         Ok(())
-    })?;
+    })
+    .context("Failed to initialize mpv")?;
 
     let config_path = &app.config.paths.mpv_config_path;
     if config_path.try_exists()? {
         info!("Found mpv.conf at '{}'!", config_path.display());
-        mpv.execute(
+        mpv.command(
             "load-config-file",
             &[config_path
                 .to_str()
@@ -68,7 +69,7 @@ pub async fn watch(app: &App) -> Result<()> {
     let input_path = &app.config.paths.mpv_input_path;
     if input_path.try_exists()? {
         info!("Found mpv.input.conf at '{}'!", input_path.display());
-        mpv.execute(
+        mpv.command(
             "load-input-conf",
             &[input_path
                 .to_str()
@@ -103,11 +104,8 @@ pub async fn watch(app: &App) -> Result<()> {
                 orig_cache_path.display()
             )
         })?;
-        let fmt_cache_path = format!("\"{cache_path}\"");
 
-        let args = &[&fmt_cache_path, "append-play"];
-
-        mpv.execute("loadfile", args)?;
+        mpv.command("loadfile", &[&cache_path, "append-play"])?;
 
         playlist_cache.insert(cache_path.to_owned(), play_thing.extractor_hash);
     }
@@ -145,7 +143,7 @@ pub async fn watch(app: &App) -> Result<()> {
         if let Some(ev) = ev_ctx.wait_event(600.) {
             match ev {
                 Ok(event) => {
-                    debug!("Mpv event triggered: {:#?}", event);
+                    trace!("Mpv event triggered: {:#?}", event);
                     if mpv_event_handler.handle_mpv_event(app, &mpv, event).await? {
                         break;
                     }