From 0473bbd23c0b7dd7d83dadf56bd6de25d629e432 Mon Sep 17 00:00:00 2001 From: Ari Rios Date: Tue, 18 Aug 2026 10:21:28 -0400 Subject: [PATCH 1/6] Add basic readpicture command --- src/client.rs | 53 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/src/client.rs b/src/client.rs index 19452bf0..fa02374d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -31,7 +31,8 @@ use std::net::{TcpStream, ToSocketAddrs}; /// Client connection #[derive(Debug)] pub struct Client -where S: Read + Write +where + S: Read + Write, { socket: BufStream, /// MPD protocol version @@ -395,7 +396,9 @@ impl Client { /// Find songs matching Query conditions. pub fn find(&mut self, query: &Query, window: W) -> Result> - where W: Into { + where + W: Into, + { self.find_generic("find", query, window.into()) } @@ -422,9 +425,43 @@ impl Client { Ok(buf) } + /// Find album art for file + pub fn readpicture(&mut self, path: &P) -> Result> { + let mut buf = vec![]; + loop { + self.run_command("readpicture", (path, &*format!("{}", buf.len())))?; + let (_, size) = self.read_pair()?; + let (type_or_binary, mut bytes) = self.read_pair()?; + match type_or_binary.as_str() { + "type" => { + // TODO: pass along image type + bytes = self.read_pair()?.1; + } + "binary" => {} + _ => return Err(ProtoError::NoField("Expected 'type' or 'binary'").into()), + } + let mut chunk = self.read_bytes(bytes.parse()?)?; + buf.append(&mut chunk); + + // Read empty newline + let _ = self.read_line()?; + let result = self.read_line()?; + if result != "OK" { + return Err(ProtoError::NotOk)?; + } + + if size.parse::()? == buf.len() { + break; + } + } + Ok(buf) + } + /// Case-insensitively search for songs matching Query conditions. pub fn search(&mut self, query: &Query, window: W) -> Result> - where W: Into { + where + W: Into, + { self.find_generic("search", query, window.into()) } @@ -465,11 +502,7 @@ impl Client { /// Set given output enabled state pub fn output(&mut self, id: T, state: bool) -> Result<()> { - if state { - self.out_enable(id) - } else { - self.out_disable(id) - } + if state { self.out_enable(id) } else { self.out_disable(id) } } /// Disable given output @@ -685,7 +718,9 @@ impl Proto for Client { } fn run_command(&mut self, command: &str, arguments: I) -> Result<()> - where I: ToArguments { + where + I: ToArguments, + { self.socket .write_all(command.as_bytes()) .and_then(|_| arguments.to_arguments(&mut |arg| write!(self.socket, " {}", Quoted(arg)))) From 1f4b64e9d9638a6055248e2d938e437d4a81bb98 Mon Sep 17 00:00:00 2001 From: Ari Rios Date: Tue, 18 Aug 2026 17:34:42 -0400 Subject: [PATCH 2/6] Fix incorrect handling of optional type field --- src/client.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/client.rs b/src/client.rs index fa02374d..03dbe8ef 100644 --- a/src/client.rs +++ b/src/client.rs @@ -425,22 +425,26 @@ impl Client { Ok(buf) } - /// Find album art for file - pub fn readpicture(&mut self, path: &P) -> Result> { + /// Find embedded album art for file. Returns a tuple of an optional mimetype and the artwork binary data. If the song was recognized but there is no picture, the response is successful but otherwise empty. + pub fn readpicture(&mut self, path: &P) -> Result<(Option, Vec)> { let mut buf = vec![]; + let mut mime_type; + loop { self.run_command("readpicture", (path, &*format!("{}", buf.len())))?; let (_, size) = self.read_pair()?; - let (type_or_binary, mut bytes) = self.read_pair()?; + let (type_or_binary, mut mime_type_or_bytes) = self.read_pair()?; match type_or_binary.as_str() { "type" => { - // TODO: pass along image type - bytes = self.read_pair()?.1; + mime_type = Some(mime_type_or_bytes); + mime_type_or_bytes = self.read_pair()?.1; // type field is optional so read one more pair to get bytes + } + "binary" => { + mime_type = None; } - "binary" => {} _ => return Err(ProtoError::NoField("Expected 'type' or 'binary'").into()), } - let mut chunk = self.read_bytes(bytes.parse()?)?; + let mut chunk = self.read_bytes(mime_type_or_bytes.parse()?)?; buf.append(&mut chunk); // Read empty newline @@ -454,7 +458,7 @@ impl Client { break; } } - Ok(buf) + Ok((mime_type, buf)) } /// Case-insensitively search for songs matching Query conditions. From 86ba4de92365314596ac8b692034422d632bc18a Mon Sep 17 00:00:00 2001 From: Ari Rios Date: Fri, 21 Aug 2026 14:33:44 -0400 Subject: [PATCH 3/6] Revert formatting changes --- src/client.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/client.rs b/src/client.rs index 03dbe8ef..1d4a257a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -31,8 +31,7 @@ use std::net::{TcpStream, ToSocketAddrs}; /// Client connection #[derive(Debug)] pub struct Client -where - S: Read + Write, +where S: Read + Write, { socket: BufStream, /// MPD protocol version @@ -396,9 +395,7 @@ impl Client { /// Find songs matching Query conditions. pub fn find(&mut self, query: &Query, window: W) -> Result> - where - W: Into, - { + where W: Into { self.find_generic("find", query, window.into()) } @@ -463,9 +460,7 @@ impl Client { /// Case-insensitively search for songs matching Query conditions. pub fn search(&mut self, query: &Query, window: W) -> Result> - where - W: Into, - { + where W: Into { self.find_generic("search", query, window.into()) } @@ -506,7 +501,12 @@ impl Client { /// Set given output enabled state pub fn output(&mut self, id: T, state: bool) -> Result<()> { - if state { self.out_enable(id) } else { self.out_disable(id) } + if state { + self.out_enable(id) + } + else { + self.out_disable(id) + } } /// Disable given output @@ -722,9 +722,7 @@ impl Proto for Client { } fn run_command(&mut self, command: &str, arguments: I) -> Result<()> - where - I: ToArguments, - { + where I: ToArguments { self.socket .write_all(command.as_bytes()) .and_then(|_| arguments.to_arguments(&mut |arg| write!(self.socket, " {}", Quoted(arg)))) From c87ae3943ac39deeb115e2debe8f095ee19c6398 Mon Sep 17 00:00:00 2001 From: Ari Rios Date: Fri, 21 Aug 2026 14:34:59 -0400 Subject: [PATCH 4/6] Couple more formatting changes --- src/client.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/client.rs b/src/client.rs index 1d4a257a..df1711b3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -31,7 +31,7 @@ use std::net::{TcpStream, ToSocketAddrs}; /// Client connection #[derive(Debug)] pub struct Client -where S: Read + Write, +where S: Read + Write { socket: BufStream, /// MPD protocol version @@ -503,8 +503,7 @@ impl Client { pub fn output(&mut self, id: T, state: bool) -> Result<()> { if state { self.out_enable(id) - } - else { + } else { self.out_disable(id) } } From dc4443f6ff7a64de1178237a5e1c73699c81f5ea Mon Sep 17 00:00:00 2001 From: Ari Rios Date: Mon, 24 Aug 2026 21:34:53 -0400 Subject: [PATCH 5/6] New package name for publish --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index abbe833f..637b4b56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -authors = ["Konstantin Stepanov "] -description = "A client library for MPD (music player daemon), like libmpdclient but in Rust" +authors = ["Konstantin Stepanov ", "Ari Rios "] +description = "NOT meant to be a maintained fork of rust-mpd, just for use in my own projects" documentation = "http://kstep.me/rust-mpd/mpd/index.html" -homepage = "https://github.com/kstep/rust-mpd" +homepage = "https://github.com/ariririos/rust-mpd" license = "MIT/Apache-2.0" -name = "mpd" -repository = "https://github.com/kstep/rust-mpd.git" +name = "mpd-aririos" +repository = "https://github.com/ariririos/rust-mpd.git" version = "0.1.0" edition = "2018" From 0afa4036e12230a7db251316c8bb31d144c3aab0 Mon Sep 17 00:00:00 2001 From: Ari Rios Date: Tue, 22 Sep 2026 02:17:53 -0400 Subject: [PATCH 6/6] binarylimit command; find_paths to find several paths with a command list --- src/client.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/client.rs b/src/client.rs index df1711b3..14b74faa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -399,6 +399,11 @@ impl Client { self.find_generic("find", query, window.into()) } + /// Ask MPD to send larger chunks + pub fn binary_limit(&mut self, bytes: usize) -> Result<()> { + self.run_command("binarylimit", bytes).and_then(|_| self.expect_ok()) + } + /// Find album art for file pub fn albumart(&mut self, path: &P) -> Result> { let mut buf = vec![]; @@ -468,6 +473,36 @@ impl Client { self.run_command(cmd, (query, window)).and_then(|_| self.read_structs("file")) } + /// Find several files by full path, batching requests + pub fn find_paths(&mut self, paths: I) -> Result>> + where + I: IntoIterator, + P: AsRef, + { + let mut queries: Vec = Vec::new(); + for path in paths { + let mut query = Query::new(); + query.and(Term::File, path.as_ref().to_owned()); + queries.push(query); + } + if queries.is_empty() { + return Ok(Vec::new()); + } + + self.run_command("command_list_ok_begin", ())?; + for query in &queries { + self.run_command("find", (query, Window::from(None)))?; + } + self.run_command("command_list_end", ())?; + + let mut songs = Vec::with_capacity(queries.len()); + for _ in &queries { + songs.push(self.read_structs("file")?); + } + self.expect_ok()?; + Ok(songs) + } + /// Lists unique tags values of the specified type for songs matching the given query. // TODO: list type [filtertype] [filterwhat] [...] [group] [grouptype] [...] // It isn't clear if or how `group` works