Merge branch 'fixes' into 'master'

Misc improvements to this crate

See merge request mpv-ipc/mpvipc!1
This commit is contained in:
Jonas Frei 2019-06-19 17:52:58 +00:00
commit a4bcf78229
5 changed files with 272 additions and 133 deletions

View File

@ -7,8 +7,12 @@ license = "GPL-3.0"
homepage = "https://gitlab.com/mpv-ipc/mpvipc" homepage = "https://gitlab.com/mpv-ipc/mpvipc"
repository = "https://gitlab.com/mpv-ipc/mpvipc" repository = "https://gitlab.com/mpv-ipc/mpvipc"
documentation = "https://docs.rs/mpvipc/" documentation = "https://docs.rs/mpvipc/"
edition = "2018"
[dependencies] [dependencies]
serde = "1.0.1" serde = "1.0.1"
serde_json = "1.0.0" serde_json = "1.0.0"
log = "0.4.6"
[dev-dependencies]
env_logger = "*"

15
examples/fetch_state.rs Normal file
View File

@ -0,0 +1,15 @@
use env_logger;
use mpvipc::{Error as MpvError, Mpv};
fn main() -> Result<(), MpvError> {
env_logger::init();
let mpv = Mpv::connect("/tmp/mpvsocket")?;
let meta = mpv.get_metadata()?;
println!("metadata: {:?}", meta);
let playlist = mpv.get_playlist()?;
println!("playlist: {:?}", playlist);
let playback_time: f64 = mpv.get_property("playback-time")?;
println!("playback-time: {}", playback_time);
Ok(())
}

72
examples/media_player.rs Normal file
View File

@ -0,0 +1,72 @@
use env_logger;
use mpvipc::{Error, Event, Mpv, MpvDataType, Property};
use std::io::{self, Write};
fn seconds_to_hms(total: f64) -> String {
let total = total as u64;
let seconds = total % 60;
let total = total / 60;
let minutes = total % 60;
let hours = total / 60;
format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
}
fn main() -> Result<(), Error> {
env_logger::init();
let mut mpv = Mpv::connect("/tmp/mpvsocket")?;
let mut pause = false;
let mut playback_time = std::f64::NAN;
let mut duration = std::f64::NAN;
mpv.observe_property(&1, "path")?;
mpv.observe_property(&2, "pause")?;
mpv.observe_property(&3, "playback-time")?;
mpv.observe_property(&4, "duration")?;
mpv.observe_property(&5, "metadata")?;
loop {
let event = mpv.event_listen()?;
match event {
Event::PropertyChange(property) => match property {
Property::Path(Some(value)) => println!("\nPlaying: {}", value),
Property::Path(None) => (),
Property::Pause(value) => pause = value,
Property::PlaybackTime(Some(value)) => playback_time = value,
Property::PlaybackTime(None) => playback_time = std::f64::NAN,
Property::Duration(Some(value)) => duration = value,
Property::Duration(None) => duration = std::f64::NAN,
Property::Metadata(Some(value)) => {
println!("File tags:");
if let Some(MpvDataType::String(value)) = value.get("ARTIST") {
println!(" Artist: {}", value);
}
if let Some(MpvDataType::String(value)) = value.get("ALBUM") {
println!(" Album: {}", value);
}
if let Some(MpvDataType::String(value)) = value.get("TITLE") {
println!(" Title: {}", value);
}
if let Some(MpvDataType::String(value)) = value.get("TRACK") {
println!(" Track: {}", value);
}
}
Property::Metadata(None) => (),
Property::Unknown {
name: _,
id: _,
data: _,
} => (),
},
Event::Shutdown => return Ok(()),
Event::Unimplemented => panic!("Unimplemented event"),
_ => (),
}
print!(
"{}{} / {} ({:.0}%)\r",
if pause { "(Paused) " } else { "" },
seconds_to_hms(playback_time),
seconds_to_hms(duration),
100. * playback_time / duration
);
io::stdout().flush().unwrap();
}
}

View File

@ -1,9 +1,10 @@
use super::*;
use log::{debug, warn};
use serde_json::{self, Value}; use serde_json::{self, Value};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::BufReader;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::BufReader;
use std::iter::Iterator; use std::iter::Iterator;
use super::*;
#[derive(Debug)] #[derive(Debug)]
pub struct PlaylistEntry { pub struct PlaylistEntry {
@ -256,11 +257,10 @@ pub fn run_mpv_command(instance: &Mpv, command: &str, args: &[&str]) -> Result<(
} }
} }
pub fn observe_mpv_property(instance: &Mpv, id: &usize, property: &str) -> Result<(), Error> { pub fn observe_mpv_property(instance: &Mpv, id: &isize, property: &str) -> Result<(), Error> {
let ipc_string = format!( let ipc_string = format!(
"{{ \"command\": [\"observe_property\", {}, \"{}\"] }}\n", "{{ \"command\": [\"observe_property\", {}, \"{}\"] }}\n",
id, id, property
property
); );
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) { match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
Ok(feedback) => { Ok(feedback) => {
@ -278,10 +278,49 @@ pub fn observe_mpv_property(instance: &Mpv, id: &usize, property: &str) -> Resul
} }
} }
fn try_convert_property(name: &str, id: isize, data: MpvDataType) -> Event {
let property = match name {
"path" => match data {
MpvDataType::String(value) => Property::Path(Some(value)),
MpvDataType::Null => Property::Path(None),
_ => unimplemented!(),
},
"pause" => match data {
MpvDataType::Bool(value) => Property::Pause(value),
_ => unimplemented!(),
},
"playback-time" => match data {
MpvDataType::Double(value) => Property::PlaybackTime(Some(value)),
MpvDataType::Null => Property::PlaybackTime(None),
_ => unimplemented!(),
},
"duration" => match data {
MpvDataType::Double(value) => Property::Duration(Some(value)),
MpvDataType::Null => Property::Duration(None),
_ => unimplemented!(),
},
"metadata" => match data {
MpvDataType::HashMap(value) => Property::Metadata(Some(value)),
MpvDataType::Null => Property::Metadata(None),
_ => unimplemented!(),
},
_ => {
warn!("Property {} not implemented", name);
Property::Unknown {
name: name.to_string(),
id,
data,
}
}
};
Event::PropertyChange(property)
}
pub fn listen(instance: &mut Mpv) -> Result<Event, Error> { pub fn listen(instance: &mut Mpv) -> Result<Event, Error> {
let mut response = String::new(); let mut response = String::new();
instance.reader.read_line(&mut response).unwrap(); instance.reader.read_line(&mut response).unwrap();
response = response.trim_right().to_string(); response = response.trim_end().to_string();
debug!("Event: {}", response);
match serde_json::from_str::<Value>(&response) { match serde_json::from_str::<Value>(&response) {
Ok(e) => { Ok(e) => {
if let Value::String(ref name) = e["event"] { if let Value::String(ref name) = e["event"] {
@ -337,7 +376,7 @@ pub fn listen(instance: &mut Mpv) -> Result<Event, Error> {
} }
"property-change" => { "property-change" => {
let name: String; let name: String;
let id: usize; let id: isize;
let data: MpvDataType; let data: MpvDataType;
if let Value::String(ref n) = e["name"] { if let Value::String(ref n) = e["name"] {
@ -347,9 +386,9 @@ pub fn listen(instance: &mut Mpv) -> Result<Event, Error> {
} }
if let Value::Number(ref n) = e["id"] { if let Value::Number(ref n) = e["id"] {
id = n.as_u64().unwrap() as usize; id = n.as_i64().unwrap() as isize;
} else { } else {
return Err(Error(ErrorCode::JsonContainsUnexptectedType)); id = 0;
} }
match e["data"] { match e["data"] {
@ -389,7 +428,7 @@ pub fn listen(instance: &mut Mpv) -> Result<Event, Error> {
} }
} }
event = Event::PropertyChange { name, id, data } event = try_convert_property(name.as_ref(), id, data);
} }
_ => { _ => {
event = Event::Unimplemented; event = Event::Unimplemented;
@ -406,7 +445,7 @@ pub fn listen(instance: &mut Mpv) -> Result<Event, Error> {
pub fn listen_raw(instance: &mut Mpv) -> String { pub fn listen_raw(instance: &mut Mpv) -> String {
let mut response = String::new(); let mut response = String::new();
instance.reader.read_line(&mut response).unwrap(); instance.reader.read_line(&mut response).unwrap();
response.trim_right().to_string() response.trim_end().to_string()
// let mut stream = &instance.0; // let mut stream = &instance.0;
// let mut buffer = [0; 32]; // let mut buffer = [0; 32];
// stream.read(&mut buffer[..]).unwrap(); // stream.read(&mut buffer[..]).unwrap();
@ -418,6 +457,7 @@ fn send_command_sync(instance: &Mpv, command: &str) -> String {
match stream.write_all(command.as_bytes()) { match stream.write_all(command.as_bytes()) {
Err(why) => panic!("Error: Could not write to socket: {}", why), Err(why) => panic!("Error: Could not write to socket: {}", why),
Ok(_) => { Ok(_) => {
debug!("Command: {}", command.trim_end());
let mut response = String::new(); let mut response = String::new();
{ {
let mut reader = BufReader::new(stream); let mut reader = BufReader::new(stream);
@ -426,6 +466,7 @@ fn send_command_sync(instance: &Mpv, command: &str) -> String {
reader.read_line(&mut response).unwrap(); reader.read_line(&mut response).unwrap();
} }
} }
debug!("Response: {}", response.trim_end());
response response
} }
} }

View File

@ -1,13 +1,10 @@
extern crate serde;
extern crate serde_json;
pub mod ipc; pub mod ipc;
use ipc::*; use ipc::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::io::{BufReader, Read};
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::io::{Read, BufReader};
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
@ -39,15 +36,25 @@ pub enum Event {
MetadataUpdate, MetadataUpdate,
Seek, Seek,
PlaybackRestart, PlaybackRestart,
PropertyChange { PropertyChange(Property),
name: String,
id: usize,
data: MpvDataType,
},
ChapterChange, ChapterChange,
Unimplemented, Unimplemented,
} }
#[derive(Debug)]
pub enum Property {
Path(Option<String>),
Pause(bool),
PlaybackTime(Option<f64>),
Duration(Option<f64>),
Metadata(Option<HashMap<String, MpvDataType>>),
Unknown {
name: String,
id: isize,
data: MpvDataType,
},
}
#[derive(Debug)] #[derive(Debug)]
pub enum MpvDataType { pub enum MpvDataType {
Array(Vec<MpvDataType>), Array(Vec<MpvDataType>),
@ -110,6 +117,7 @@ pub enum ErrorCode {
pub struct Mpv { pub struct Mpv {
stream: UnixStream, stream: UnixStream,
reader: BufReader<UnixStream>, reader: BufReader<UnixStream>,
name: String,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Playlist(pub Vec<PlaylistEntry>); pub struct Playlist(pub Vec<PlaylistEntry>);
@ -122,6 +130,12 @@ impl Drop for Mpv {
} }
} }
impl fmt::Debug for Mpv {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Mpv").field(&self.name).finish()
}
}
impl Clone for Mpv { impl Clone for Mpv {
fn clone(&self) -> Self { fn clone(&self) -> Self {
let stream = self.stream.try_clone().expect("cloning UnixStream"); let stream = self.stream.try_clone().expect("cloning UnixStream");
@ -129,6 +143,7 @@ impl Clone for Mpv {
Mpv { Mpv {
stream, stream,
reader: BufReader::new(cloned_stream), reader: BufReader::new(cloned_stream),
name: self.name.clone(),
} }
} }
@ -138,6 +153,7 @@ impl Clone for Mpv {
*self = Mpv { *self = Mpv {
stream, stream,
reader: BufReader::new(cloned_stream), reader: BufReader::new(cloned_stream),
name: source.name.clone(),
} }
} }
} }
@ -167,9 +183,7 @@ impl Display for ErrorCode {
f.write_str("The received value is not of type \'std::f64\'") f.write_str("The received value is not of type \'std::f64\'")
} }
ErrorCode::ValueDoesNotContainHashMap => { ErrorCode::ValueDoesNotContainHashMap => {
f.write_str( f.write_str("The received value is not of type \'std::collections::HashMap\'")
"The received value is not of type \'std::collections::HashMap\'",
)
} }
ErrorCode::ValueDoesNotContainPlaylist => { ErrorCode::ValueDoesNotContainPlaylist => {
f.write_str("The received value is not of type \'mpvipc::Playlist\'") f.write_str("The received value is not of type \'mpvipc::Playlist\'")
@ -263,6 +277,7 @@ impl Mpv {
return Ok(Mpv { return Ok(Mpv {
stream, stream,
reader: BufReader::new(cloned_stream), reader: BufReader::new(cloned_stream),
name: String::from(socket),
}); });
} }
Err(internal_error) => Err(Error(ErrorCode::ConnectError(internal_error.to_string()))), Err(internal_error) => Err(Error(ErrorCode::ConnectError(internal_error.to_string()))),
@ -271,9 +286,9 @@ impl Mpv {
pub fn disconnect(&self) { pub fn disconnect(&self) {
let mut stream = &self.stream; let mut stream = &self.stream;
stream.shutdown(std::net::Shutdown::Both).expect( stream
"socket disconnect", .shutdown(std::net::Shutdown::Both)
); .expect("socket disconnect");
let mut buffer = [0; 32]; let mut buffer = [0; 32];
for _ in 0..stream.bytes().count() { for _ in 0..stream.bytes().count() {
stream.read(&mut buffer[..]).unwrap(); stream.read(&mut buffer[..]).unwrap();
@ -316,9 +331,13 @@ impl Mpv {
/// ///
/// #Example /// #Example
/// ``` /// ```
/// let mpv = Mpv::connect("/tmp/mpvsocket").unwrap(); /// # use mpvipc::{Mpv, Error};
/// let paused: bool = mpv.get_property("pause").unwrap(); /// # fn main() -> Result<(), Error> {
/// let title: String = mpv.get_property("media-title").unwrap(); /// let mpv = Mpv::connect("/tmp/mpvsocket")?;
/// let paused: bool = mpv.get_property("pause")?;
/// let title: String = mpv.get_property("media-title")?;
/// # Ok(())
/// # }
/// ``` /// ```
pub fn get_property<T: GetPropertyTypeHandler>(&self, property: &str) -> Result<T, Error> { pub fn get_property<T: GetPropertyTypeHandler>(&self, property: &str) -> Result<T, Error> {
T::get_property_generic(self, property) T::get_property_generic(self, property)
@ -336,8 +355,12 @@ impl Mpv {
/// #Example /// #Example
/// ///
/// ``` /// ```
/// let mpv = Mpv::connect("/tmp/mpvsocket").unwrap(); /// # use mpvipc::{Mpv, Error};
/// let title = mpv.get_property_string("media-title").unwrap(); /// # fn main() -> Result<(), Error> {
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
/// let title = mpv.get_property_string("media-title")?;
/// # Ok(())
/// # }
/// ``` /// ```
pub fn get_property_string(&self, property: &str) -> Result<String, Error> { pub fn get_property_string(&self, property: &str) -> Result<String, Error> {
get_mpv_property_string(self, property) get_mpv_property_string(self, property)
@ -353,10 +376,10 @@ impl Mpv {
/// ///
/// #Example /// #Example
/// ///
/// ``` /// ```ignore
/// let mpv = Mpv::connect("/tmp/mpvsocket").unwrap(); /// let mut mpv = Mpv::connect("/tmp/mpvsocket")?;
/// loop { /// loop {
/// let event = mpv.event_listen().unwrap(); /// let event = mpv.event_listen()?;
/// println!("{:?}", event); /// println!("{:?}", event);
/// } /// }
/// ``` /// ```
@ -372,7 +395,7 @@ impl Mpv {
run_mpv_command(self, "playlist-next", &[]) run_mpv_command(self, "playlist-next", &[])
} }
pub fn observe_property(&self, id: &usize, property: &str) -> Result<(), Error> { pub fn observe_property(&self, id: &isize, property: &str) -> Result<(), Error> {
observe_mpv_property(self, id, property) observe_mpv_property(self, id, property)
} }
@ -399,13 +422,17 @@ impl Mpv {
/// ///
/// #Example /// #Example
/// ``` /// ```
/// let mpv = Mpv::connect("/tmp/mpvsocket").unwrap(); /// # use mpvipc::{Mpv, Error};
/// # fn main() -> Result<(), Error> {
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
/// ///
/// //Run command 'playlist-shuffle' which takes no arguments /// //Run command 'playlist-shuffle' which takes no arguments
/// mpv.run_command("playlist-shuffle", &[]); /// mpv.run_command("playlist-shuffle", &[])?;
/// ///
/// //Run command 'seek' which in this case takes two arguments /// //Run command 'seek' which in this case takes two arguments
/// mpv.run_command("seek", &["0", "absolute"]); /// mpv.run_command("seek", &["0", "absolute"])?;
/// # Ok(())
/// # }
/// ``` /// ```
pub fn run_command(&self, command: &str, args: &[&str]) -> Result<(), Error> { pub fn run_command(&self, command: &str, args: &[&str]) -> Result<(), Error> {
run_mpv_command(self, command, args) run_mpv_command(self, command, args)
@ -418,34 +445,26 @@ impl Mpv {
option: PlaylistAddOptions, option: PlaylistAddOptions,
) -> Result<(), Error> { ) -> Result<(), Error> {
match file_type { match file_type {
PlaylistAddTypeOptions::File => { PlaylistAddTypeOptions::File => match option {
match option {
PlaylistAddOptions::Replace => { PlaylistAddOptions::Replace => {
run_mpv_command(self, "loadfile", &[file, "replace"]) run_mpv_command(self, "loadfile", &[file, "replace"])
} }
PlaylistAddOptions::Append => { PlaylistAddOptions::Append => run_mpv_command(self, "loadfile", &[file, "append"]),
run_mpv_command(self, "loadfile", &[file, "append"])
}
PlaylistAddOptions::AppendPlay => { PlaylistAddOptions::AppendPlay => {
run_mpv_command(self, "loadfile", &[file, "append-play"]) run_mpv_command(self, "loadfile", &[file, "append-play"])
} }
} },
}
PlaylistAddTypeOptions::Playlist => { PlaylistAddTypeOptions::Playlist => match option {
match option {
PlaylistAddOptions::Replace => { PlaylistAddOptions::Replace => {
run_mpv_command(self, "loadlist", &[file, "replace"]) run_mpv_command(self, "loadlist", &[file, "replace"])
} }
PlaylistAddOptions::Append | PlaylistAddOptions::Append | PlaylistAddOptions::AppendPlay => {
PlaylistAddOptions::AppendPlay => {
run_mpv_command(self, "loadlist", &[file, "append"]) run_mpv_command(self, "loadlist", &[file, "append"])
} }
},
} }
} }
}
}
pub fn playlist_clear(&self) -> Result<(), Error> { pub fn playlist_clear(&self) -> Result<(), Error> {
run_mpv_command(self, "playlist-clear", &[]) run_mpv_command(self, "playlist-clear", &[])
@ -461,13 +480,11 @@ impl Mpv {
pub fn playlist_play_next(&self, id: usize) -> Result<(), Error> { pub fn playlist_play_next(&self, id: usize) -> Result<(), Error> {
match get_mpv_property::<usize>(self, "playlist-pos") { match get_mpv_property::<usize>(self, "playlist-pos") {
Ok(current_id) => { Ok(current_id) => run_mpv_command(
run_mpv_command(
self, self,
"playlist-move", "playlist-move",
&[&id.to_string(), &(current_id + 1).to_string()], &[&id.to_string(), &(current_id + 1).to_string()],
) ),
}
Err(msg) => Err(msg), Err(msg) => Err(msg),
} }
} }
@ -502,21 +519,17 @@ impl Mpv {
match option { match option {
Switch::On => enabled = true, Switch::On => enabled = true,
Switch::Off => {} Switch::Off => {}
Switch::Toggle => { Switch::Toggle => match get_mpv_property_string(self, "loop-file") {
match get_mpv_property_string(self, "loop-file") { Ok(value) => match value.as_ref() {
Ok(value) => {
match value.as_ref() {
"false" => { "false" => {
enabled = true; enabled = true;
} }
_ => { _ => {
enabled = false; enabled = false;
} }
} },
}
Err(msg) => return Err(msg), Err(msg) => return Err(msg),
} },
}
} }
set_mpv_property(self, "loop-file", enabled) set_mpv_property(self, "loop-file", enabled)
} }
@ -526,21 +539,17 @@ impl Mpv {
match option { match option {
Switch::On => enabled = true, Switch::On => enabled = true,
Switch::Off => {} Switch::Off => {}
Switch::Toggle => { Switch::Toggle => match get_mpv_property_string(self, "loop-playlist") {
match get_mpv_property_string(self, "loop-playlist") { Ok(value) => match value.as_ref() {
Ok(value) => {
match value.as_ref() {
"false" => { "false" => {
enabled = true; enabled = true;
} }
_ => { _ => {
enabled = false; enabled = false;
} }
} },
}
Err(msg) => return Err(msg), Err(msg) => return Err(msg),
} },
}
} }
set_mpv_property(self, "loop-playlist", enabled) set_mpv_property(self, "loop-playlist", enabled)
} }
@ -550,14 +559,12 @@ impl Mpv {
match option { match option {
Switch::On => enabled = true, Switch::On => enabled = true,
Switch::Off => {} Switch::Off => {}
Switch::Toggle => { Switch::Toggle => match get_mpv_property::<bool>(self, "mute") {
match get_mpv_property::<bool>(self, "mute") {
Ok(value) => { Ok(value) => {
enabled = !value; enabled = !value;
} }
Err(msg) => return Err(msg), Err(msg) => return Err(msg),
} },
}
} }
set_mpv_property(self, "mute", enabled) set_mpv_property(self, "mute", enabled)
} }
@ -579,8 +586,12 @@ impl Mpv {
/// ///
/// #Example /// #Example
/// ``` /// ```
/// let mpv = Mpv::connect("/tmp/mpvsocket").unwrap(); /// # use mpvipc::{Mpv, Error};
/// mpv.set_property("pause", true); /// # fn main() -> Result<(), Error> {
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
/// mpv.set_property("pause", true)?;
/// # Ok(())
/// # }
/// ``` /// ```
pub fn set_property<T: SetPropertyTypeHandler<T>>( pub fn set_property<T: SetPropertyTypeHandler<T>>(
&self, &self,
@ -592,8 +603,7 @@ impl Mpv {
pub fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), Error> { pub fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), Error> {
match get_mpv_property::<f64>(self, "speed") { match get_mpv_property::<f64>(self, "speed") {
Ok(speed) => { Ok(speed) => match option {
match option {
NumberChangeOptions::Increase => { NumberChangeOptions::Increase => {
set_mpv_property(self, "speed", speed + input_speed) set_mpv_property(self, "speed", speed + input_speed)
} }
@ -603,16 +613,14 @@ impl Mpv {
} }
NumberChangeOptions::Absolute => set_mpv_property(self, "speed", input_speed), NumberChangeOptions::Absolute => set_mpv_property(self, "speed", input_speed),
} },
}
Err(msg) => Err(msg), Err(msg) => Err(msg),
} }
} }
pub fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), Error> { pub fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), Error> {
match get_mpv_property::<f64>(self, "volume") { match get_mpv_property::<f64>(self, "volume") {
Ok(volume) => { Ok(volume) => match option {
match option {
NumberChangeOptions::Increase => { NumberChangeOptions::Increase => {
set_mpv_property(self, "volume", volume + input_volume) set_mpv_property(self, "volume", volume + input_volume)
} }
@ -622,8 +630,7 @@ impl Mpv {
} }
NumberChangeOptions::Absolute => set_mpv_property(self, "volume", input_volume), NumberChangeOptions::Absolute => set_mpv_property(self, "volume", input_volume),
} },
}
Err(msg) => Err(msg), Err(msg) => Err(msg),
} }
} }