Added the PropertyChange event. Implemented all variantes of MpvDataType. Documented event_listen
This commit is contained in:
parent
ed9f35ba5d
commit
1916f60dd6
|
@ -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"
|
||||||
|
|
289
src/ipc.rs
289
src/ipc.rs
|
@ -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
|
||||||
|
}
|
59
src/lib.rs
59
src/lib.rs
|
@ -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>) {
|
||||||
|
|
Loading…
Reference in New Issue