Added the PropertyChange event. Implemented all variantes of MpvDataType. Documented event_listen

This commit is contained in:
Jonas Frei 2017-06-06 23:08:27 +02:00
parent ed9f35ba5d
commit 1916f60dd6
3 changed files with 283 additions and 67 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mpvipc" name = "mpvipc"
version = "1.1.2" version = "1.1.3"
authors = ["Jonas Frei <freijon@gmail.com>"] authors = ["Jonas Frei <freijon@gmail.com>"]
description = "A small library which provides bindings to control existing mpv instances through sockets." description = "A small library which provides bindings to control existing mpv instances through sockets."
license = "GPL-3.0" license = "GPL-3.0"

View File

@ -126,20 +126,13 @@ impl TypeHandler for usize {
} }
} }
impl TypeHandler for HashMap<String, String> { impl TypeHandler for HashMap<String, MpvDataType> {
fn get_value(value: Value) -> Result<HashMap<String, String>, Error> { fn get_value(value: Value) -> Result<HashMap<String, MpvDataType>, Error> {
if let Value::Object(map) = value { if let Value::Object(map) = value {
if let Value::String(ref error) = map["error"] { if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") { if error == "success" && map.contains_key("data") {
if let Value::Object(ref inner_map) = map["data"] { if let Value::Object(ref inner_map) = map["data"] {
let mut output_map: HashMap<String, String> = HashMap::new(); Ok(json_map_to_hashmap(inner_map))
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)
} else { } else {
Err(Error(ErrorCode::ValueDoesNotContainHashMap)) Err(Error(ErrorCode::ValueDoesNotContainHashMap))
} }
@ -165,29 +158,7 @@ impl TypeHandler for Vec<PlaylistEntry> {
if let Value::String(ref error) = map["error"] { if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") { if error == "success" && map.contains_key("data") {
if let Value::Array(ref playlist_vec) = map["data"] { if let Value::Array(ref playlist_vec) = map["data"] {
let mut output: Vec<PlaylistEntry> = Vec::new(); Ok(json_array_to_playlist(playlist_vec))
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)
} else { } else {
Err(Error(ErrorCode::ValueDoesNotContainPlaylist)) Err(Error(ErrorCode::ValueDoesNotContainPlaylist))
} }
@ -312,38 +283,129 @@ pub fn observe_mpv_property(instance: &Mpv, id: &usize, property: &str) -> Resul
/// ``` /// ```
/// listen("/tmp/mpvsocket"); /// listen("/tmp/mpvsocket");
/// ``` /// ```
pub fn listen(instance: &Mpv, tx: &Sender<Event>) { pub fn listen(instance: &Mpv, tx: &Sender<Event>) -> Result<(), Error> {
let mut response = String::new(); let mut response = String::new();
let mut reader = BufReader::new(&instance.0); let mut reader = BufReader::new(&instance.0);
reader.read_line(&mut response).unwrap(); reader.read_line(&mut response).unwrap();
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"] {
let event: Event = match name.as_str() { let event: Event;
"shutdown" => Event::Shutdown, match name.as_str() {
"start-file" => Event::StartFile, "shutdown" => {
"file-loaded" => Event::FileLoaded, event = Event::Shutdown;
"seek" => Event::Seek, }
"playback-restart" => Event::PlaybackRestart, "start-file" => {
"idle" => Event::Idle, event = Event::StartFile;
"tick" => Event::Tick, }
"video-reconfig" => Event::VideoReconfig, "file-loaded" => {
"audio-reconfig" => Event::AudioReconfig, event = Event::FileLoaded;
"tracks-changed" => Event::TracksChanged, }
"track-switched" => Event::TrackSwitched, "seek" => {
"pause" => Event::Pause, event = Event::Seek;
"unpause" => Event::Unpause, }
"metadata-update" => Event::MetadataUpdate, "playback-restart" => {
"chapter-change" => Event::ChapterChange, event = Event::PlaybackRestart;
"end-file" => Event::EndFile, }
_ => Event::Unimplemented, "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(); 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<String>) { pub fn listen_raw(instance: &Mpv, tx: &Sender<String>) {
@ -371,3 +433,122 @@ fn send_command_sync(instance: &Mpv, command: &str) -> String {
} }
} }
} }
fn json_map_to_hashmap(map: &serde_json::map::Map<String, Value>) -> HashMap<String, MpvDataType> {
let mut output_map: HashMap<String, MpvDataType> = 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<Value>) -> Vec<MpvDataType> {
let mut output: Vec<MpvDataType> = 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<Value>) -> Vec<PlaylistEntry> {
let mut output: Vec<PlaylistEntry> = 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
}

View File

@ -39,21 +39,23 @@ pub enum Event {
MetadataUpdate, MetadataUpdate,
Seek, Seek,
PlaybackRestart, PlaybackRestart,
// PropertyChange { PropertyChange {
// name: &'static str, name: String,
// change: MpvDataType, id: usize,
// reply_userdata: u32, data: MpvDataType,
// }, },
ChapterChange, ChapterChange,
Unimplemented, Unimplemented,
} }
#[derive(Debug)]
pub enum MpvDataType { pub enum MpvDataType {
Bool(bool), Bool(bool),
String(String), String(String),
Double(f64), Double(f64),
Usize(usize), Usize(usize),
HashMap(HashMap<String, String>), HashMap(HashMap<String, MpvDataType>),
Array(Vec<MpvDataType>),
Playlist(Playlist), Playlist(Playlist),
} }
@ -92,6 +94,7 @@ pub enum ErrorCode {
MpvError(String), MpvError(String),
JsonParseError(String), JsonParseError(String),
ConnectError(String), ConnectError(String),
JsonContainsUnexptectedType,
UnexpectedResult, UnexpectedResult,
UnexpectedValue, UnexpectedValue,
UnsupportedType, UnsupportedType,
@ -104,6 +107,7 @@ pub enum ErrorCode {
} }
pub struct Mpv(UnixStream); pub struct Mpv(UnixStream);
#[derive(Debug)]
pub struct Playlist(pub Vec<PlaylistEntry>); pub struct Playlist(pub Vec<PlaylistEntry>);
#[derive(Debug)] #[derive(Debug)]
pub struct Error(pub ErrorCode); 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 { impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.0, f) Display::fmt(&self.0, f)
@ -130,6 +144,9 @@ impl Display for ErrorCode {
ErrorCode::MpvError(ref msg) => { ErrorCode::MpvError(ref msg) => {
f.write_str(&format!("mpv returned an error value: {}", 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::UnexpectedResult => f.write_str("Unexpected result received"),
ErrorCode::UnexpectedValue => f.write_str("Unexpected value received"), ErrorCode::UnexpectedValue => f.write_str("Unexpected value received"),
ErrorCode::UnsupportedType => f.write_str("Unsupported type received"), ErrorCode::UnsupportedType => f.write_str("Unsupported type received"),
@ -189,11 +206,11 @@ impl GetPropertyTypeHandler for Vec<PlaylistEntry> {
} }
} }
impl GetPropertyTypeHandler for HashMap<String, String> { impl GetPropertyTypeHandler for HashMap<String, MpvDataType> {
fn get_property_generic(instance: &Mpv, fn get_property_generic(instance: &Mpv,
property: &str) property: &str)
-> Result<HashMap<String, String>, Error> { -> Result<HashMap<String, MpvDataType>, Error> {
get_mpv_property::<HashMap<String, String>>(instance, property) get_mpv_property::<HashMap<String, MpvDataType>>(instance, property)
} }
} }
@ -233,7 +250,7 @@ impl Mpv {
} }
} }
pub fn get_metadata(&self) -> Result<HashMap<String, String>, Error> { pub fn get_metadata(&self) -> Result<HashMap<String, MpvDataType>, Error> {
match get_mpv_property(self, "metadata") { match get_mpv_property(self, "metadata") {
Ok(map) => Ok(map), Ok(map) => Ok(map),
Err(err) => Err(err), Err(err) => Err(err),
@ -296,8 +313,26 @@ impl Mpv {
run_mpv_command(self, "quit", &[]) run_mpv_command(self, "quit", &[])
} }
pub fn event_listen(&self, tx: &Sender<Event>) { /// #Description
listen(self, tx); ///
/// 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<Event>) -> Result<(), Error> {
listen(self, tx)
} }
pub fn event_listen_raw(&self, tx: &Sender<String>) { pub fn event_listen_raw(&self, tx: &Sender<String>) {