From 1916f60dd62c7b7329779d24cda91d4e97dc5050 Mon Sep 17 00:00:00 2001 From: Jonas Frei Date: Tue, 6 Jun 2017 23:08:27 +0200 Subject: [PATCH] Added the PropertyChange event. Implemented all variantes of MpvDataType. Documented event_listen --- Cargo.toml | 2 +- src/ipc.rs | 289 +++++++++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 59 ++++++++--- 3 files changed, 283 insertions(+), 67 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dcb2c98..a1ca32b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mpvipc" -version = "1.1.2" +version = "1.1.3" authors = ["Jonas Frei "] description = "A small library which provides bindings to control existing mpv instances through sockets." license = "GPL-3.0" diff --git a/src/ipc.rs b/src/ipc.rs index 55fa524..32c4a8e 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -126,20 +126,13 @@ impl TypeHandler for usize { } } -impl TypeHandler for HashMap { - fn get_value(value: Value) -> Result, Error> { +impl TypeHandler for HashMap { + fn get_value(value: Value) -> Result, Error> { if let Value::Object(map) = value { if let Value::String(ref error) = map["error"] { if error == "success" && map.contains_key("data") { if let Value::Object(ref inner_map) = map["data"] { - let mut output_map: HashMap = HashMap::new(); - for (ref key, ref value) in inner_map.iter() { - if let Value::String(ref val) = **value { - output_map.insert(key.to_string(), val.to_string()); - } - } - let output_map = output_map; - Ok(output_map) + Ok(json_map_to_hashmap(inner_map)) } else { Err(Error(ErrorCode::ValueDoesNotContainHashMap)) } @@ -165,29 +158,7 @@ impl TypeHandler for Vec { if let Value::String(ref error) = map["error"] { if error == "success" && map.contains_key("data") { if let Value::Array(ref playlist_vec) = map["data"] { - let mut output: Vec = Vec::new(); - for (id, entry) in playlist_vec.iter().enumerate() { - let mut filename: String = String::new(); - let mut title: String = String::new(); - let mut current: bool = false; - if let Value::String(ref f) = entry["filename"] { - filename = f.to_string(); - } - if let Value::String(ref t) = entry["title"] { - title = t.to_string(); - } - if let Value::Bool(ref b) = entry["current"] { - current = *b; - } - output.push(PlaylistEntry { - id: id, - filename: filename, - title: title, - current: current, - }); - } - let output = output; - Ok(output) + Ok(json_array_to_playlist(playlist_vec)) } else { Err(Error(ErrorCode::ValueDoesNotContainPlaylist)) } @@ -312,38 +283,129 @@ pub fn observe_mpv_property(instance: &Mpv, id: &usize, property: &str) -> Resul /// ``` /// listen("/tmp/mpvsocket"); /// ``` -pub fn listen(instance: &Mpv, tx: &Sender) { +pub fn listen(instance: &Mpv, tx: &Sender) -> Result<(), Error> { let mut response = String::new(); let mut reader = BufReader::new(&instance.0); reader.read_line(&mut response).unwrap(); match serde_json::from_str::(&response) { Ok(e) => { if let Value::String(ref name) = e["event"] { - let event: Event = match name.as_str() { - "shutdown" => Event::Shutdown, - "start-file" => Event::StartFile, - "file-loaded" => Event::FileLoaded, - "seek" => Event::Seek, - "playback-restart" => Event::PlaybackRestart, - "idle" => Event::Idle, - "tick" => Event::Tick, - "video-reconfig" => Event::VideoReconfig, - "audio-reconfig" => Event::AudioReconfig, - "tracks-changed" => Event::TracksChanged, - "track-switched" => Event::TrackSwitched, - "pause" => Event::Pause, - "unpause" => Event::Unpause, - "metadata-update" => Event::MetadataUpdate, - "chapter-change" => Event::ChapterChange, - "end-file" => Event::EndFile, - _ => Event::Unimplemented, + let event: Event; + match name.as_str() { + "shutdown" => { + event = Event::Shutdown; + } + "start-file" => { + event = Event::StartFile; + } + "file-loaded" => { + event = Event::FileLoaded; + } + "seek" => { + event = Event::Seek; + } + "playback-restart" => { + event = Event::PlaybackRestart; + } + "idle" => { + event = Event::Idle; + } + "tick" => { + event = Event::Tick; + } + "video-reconfig" => { + event = Event::VideoReconfig; + } + "audio-reconfig" => { + event = Event::AudioReconfig; + } + "tracks-changed" => { + event = Event::TracksChanged; + } + "track-switched" => { + event = Event::TrackSwitched; + } + "pause" => { + event = Event::Pause; + } + "unpause" => { + event = Event::Unpause; + } + "metadata-update" => { + event = Event::MetadataUpdate; + } + "chapter-change" => { + event = Event::ChapterChange; + } + "end-file" => { + event = Event::EndFile; + } + "property-change" => { + let name: String; + let id: usize; + let data: MpvDataType; + + if let Value::String(ref n) = e["name"] { + name = n.to_string(); + } else { + return Err(Error(ErrorCode::JsonContainsUnexptectedType)); + } + + if let Value::Number(ref n) = e["id"] { + id = n.as_u64().unwrap() as usize; + } else { + return Err(Error(ErrorCode::JsonContainsUnexptectedType)); + } + + match e["data"] { + Value::String(ref n) => { + data = MpvDataType::String(n.to_string()); + } + + Value::Array(ref a) => { + if name == "playlist".to_string() { + data = + MpvDataType::Playlist(Playlist(json_array_to_playlist(a))); + } else { + data = MpvDataType::Array(json_array_to_vec(a)); + } + } + + Value::Bool(ref b) => { + data = MpvDataType::Bool(*b); + } + + Value::Number(ref n) => { + if n.is_u64() { + data = MpvDataType::Usize(n.as_u64().unwrap() as usize); + } else if n.is_f64() { + data = MpvDataType::Double(n.as_f64().unwrap()); + } else { + return Err(Error(ErrorCode::JsonContainsUnexptectedType)); + } + } + + Value::Object(ref m) => { + data = MpvDataType::HashMap(json_map_to_hashmap(m)); + } + + _ => { + unimplemented!(); + } + } + + event = Event::PropertyChange { name, id, data } + } + _ => { + event = Event::Unimplemented; + } }; tx.send(event).unwrap(); } } - Err(why) => panic!("{}", why.to_string()), + Err(why) => return Err(Error(ErrorCode::JsonParseError(why.to_string()))), } - response.clear(); + Ok(()) } pub fn listen_raw(instance: &Mpv, tx: &Sender) { @@ -371,3 +433,122 @@ fn send_command_sync(instance: &Mpv, command: &str) -> String { } } } + +fn json_map_to_hashmap(map: &serde_json::map::Map) -> HashMap { + let mut output_map: HashMap = HashMap::new(); + for (ref key, ref value) in map.iter() { + match **value { + Value::Array(ref array) => { + output_map.insert(key.to_string(), + MpvDataType::Array(json_array_to_vec(array))); + } + Value::Bool(ref b) => { + output_map.insert(key.to_string(), MpvDataType::Bool(*b)); + } + Value::Number(ref n) => { + if n.is_u64() { + output_map.insert(key.to_string(), + MpvDataType::Usize(n.as_u64().unwrap() as usize)); + } else if n.is_f64() { + output_map.insert(key.to_string(), MpvDataType::Double(n.as_f64().unwrap())); + } else { + panic!("unimplemented number"); + } + } + Value::String(ref s) => { + output_map.insert(key.to_string(), MpvDataType::String(s.to_string())); + } + Value::Object(ref m) => { + output_map.insert(key.to_string(), + MpvDataType::HashMap(json_map_to_hashmap(m))); + } + Value::Null => { + unimplemented!(); + } + } + } + output_map +} + +fn json_array_to_vec(array: &Vec) -> Vec { + let mut output: Vec = Vec::new(); + if array.len() > 0 { + match array[0] { + Value::Array(_) => { + for entry in array { + if let Value::Array(ref a) = *entry { + output.push(MpvDataType::Array(json_array_to_vec(a))); + } + } + } + + Value::Bool(_) => { + for entry in array { + if let Value::Bool(ref b) = *entry { + output.push(MpvDataType::Bool(*b)); + } + } + } + + Value::Number(_) => { + for entry in array { + if let Value::Number(ref n) = *entry { + if n.is_u64() { + output.push(MpvDataType::Usize(n.as_u64().unwrap() as usize)); + } else if n.is_f64() { + output.push(MpvDataType::Double(n.as_f64().unwrap())); + } else { + panic!("unimplemented number"); + } + } + } + } + + Value::Object(_) => { + for entry in array { + if let Value::Object(ref map) = *entry { + output.push(MpvDataType::HashMap(json_map_to_hashmap(map))); + } + } + } + + Value::String(_) => { + for entry in array { + if let Value::String(ref s) = *entry { + output.push(MpvDataType::String(s.to_string())); + } + } + } + + Value::Null => { + unimplemented!(); + } + } + } + output +} + +fn json_array_to_playlist(array: &Vec) -> Vec { + let mut output: Vec = Vec::new(); + for (id, entry) in array.iter().enumerate() { + let mut filename: String = String::new(); + let mut title: String = String::new(); + let mut current: bool = false; + if let Value::String(ref f) = entry["filename"] { + filename = f.to_string(); + } + if let Value::String(ref t) = entry["title"] { + title = t.to_string(); + } + if let Value::Bool(ref b) = entry["current"] { + current = *b; + } + output.push(PlaylistEntry { + id, + filename, + title, + current, + }); + } + output +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ad0aa9f..644dd6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,21 +39,23 @@ pub enum Event { MetadataUpdate, Seek, PlaybackRestart, - // PropertyChange { - // name: &'static str, - // change: MpvDataType, - // reply_userdata: u32, - // }, + PropertyChange { + name: String, + id: usize, + data: MpvDataType, + }, ChapterChange, Unimplemented, } +#[derive(Debug)] pub enum MpvDataType { Bool(bool), String(String), Double(f64), Usize(usize), - HashMap(HashMap), + HashMap(HashMap), + Array(Vec), Playlist(Playlist), } @@ -92,6 +94,7 @@ pub enum ErrorCode { MpvError(String), JsonParseError(String), ConnectError(String), + JsonContainsUnexptectedType, UnexpectedResult, UnexpectedValue, UnsupportedType, @@ -104,6 +107,7 @@ pub enum ErrorCode { } pub struct Mpv(UnixStream); +#[derive(Debug)] pub struct Playlist(pub Vec); #[derive(Debug)] pub struct Error(pub ErrorCode); @@ -116,6 +120,16 @@ impl Drop for Mpv { } } +impl Clone for Mpv { + fn clone(&self) -> Self { + Mpv(self.0.try_clone().expect("cloning UnixStream")) + } + + fn clone_from(&mut self, source: &Self) { + *self = Mpv(source.0.try_clone().expect("cloning UnixStream")); + } +} + impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self.0, f) @@ -130,6 +144,9 @@ impl Display for ErrorCode { ErrorCode::MpvError(ref msg) => { f.write_str(&format!("mpv returned an error value: {}", msg)) } + ErrorCode::JsonContainsUnexptectedType => { + f.write_str("Mpv sent a value with an unexpected type") + } ErrorCode::UnexpectedResult => f.write_str("Unexpected result received"), ErrorCode::UnexpectedValue => f.write_str("Unexpected value received"), ErrorCode::UnsupportedType => f.write_str("Unsupported type received"), @@ -189,11 +206,11 @@ impl GetPropertyTypeHandler for Vec { } } -impl GetPropertyTypeHandler for HashMap { +impl GetPropertyTypeHandler for HashMap { fn get_property_generic(instance: &Mpv, property: &str) - -> Result, Error> { - get_mpv_property::>(instance, property) + -> Result, Error> { + get_mpv_property::>(instance, property) } } @@ -233,7 +250,7 @@ impl Mpv { } } - pub fn get_metadata(&self) -> Result, Error> { + pub fn get_metadata(&self) -> Result, Error> { match get_mpv_property(self, "metadata") { Ok(map) => Ok(map), Err(err) => Err(err), @@ -296,8 +313,26 @@ impl Mpv { run_mpv_command(self, "quit", &[]) } - pub fn event_listen(&self, tx: &Sender) { - listen(self, tx); + /// #Description + /// + /// Listens for mpv events and triggers the channel once an event has been received. + /// + /// ##Input arguments + /// + /// - **tx** A reference to the sender halve of the channel + /// + /// #Example + /// + /// ``` + /// let (tx, rx) = std::sync::mpsc::channel(); + /// loop { + /// mpv.event_listen(&tx); + /// let event = rx.recv().unwrap(); + /// println!("{:?}", event); + /// } + /// ``` + pub fn event_listen(&self, tx: &Sender) -> Result<(), Error> { + listen(self, tx) } pub fn event_listen_raw(&self, tx: &Sender) {