mpvipc-async/src/ipc.rs

585 lines
20 KiB
Rust
Raw Normal View History

2019-06-19 00:51:11 +02:00
use super::*;
use log::{debug, warn};
2017-05-24 00:13:23 +02:00
use serde_json::{self, Value};
use std::collections::HashMap;
use std::io::prelude::*;
2019-06-19 00:51:11 +02:00
use std::io::BufReader;
2017-05-24 00:13:23 +02:00
use std::iter::Iterator;
2017-05-22 18:31:20 +02:00
#[derive(Debug)]
pub struct PlaylistEntry {
pub id: usize,
pub filename: String,
pub title: String,
pub current: bool,
}
pub trait TypeHandler: Sized {
fn get_value(value: Value) -> Result<Self, Error>;
2017-05-22 18:31:20 +02:00
fn as_string(&self) -> String;
}
impl TypeHandler for String {
fn get_value(value: Value) -> Result<String, Error> {
2017-05-22 18:31:20 +02:00
if let Value::Object(map) = value {
if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") {
if let Value::String(ref s) = map["data"] {
Ok(s.to_string())
} else {
Err(Error(ErrorCode::ValueDoesNotContainString))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
}
fn as_string(&self) -> String {
self.to_string()
}
}
impl TypeHandler for bool {
fn get_value(value: Value) -> Result<bool, Error> {
2017-05-22 18:31:20 +02:00
if let Value::Object(map) = value {
if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") {
if let Value::Bool(ref b) = map["data"] {
Ok(*b)
} else {
Err(Error(ErrorCode::ValueDoesNotContainBool))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
}
fn as_string(&self) -> String {
if *self {
"true".to_string()
} else {
"false".to_string()
}
}
}
impl TypeHandler for f64 {
fn get_value(value: Value) -> Result<f64, Error> {
2017-05-22 18:31:20 +02:00
if let Value::Object(map) = value {
if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") {
if let Value::Number(ref num) = map["data"] {
Ok(num.as_f64().unwrap())
} else {
Err(Error(ErrorCode::ValueDoesNotContainF64))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
}
fn as_string(&self) -> String {
self.to_string()
}
}
impl TypeHandler for usize {
fn get_value(value: Value) -> Result<usize, Error> {
2017-05-22 18:31:20 +02:00
if let Value::Object(map) = value {
if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") {
if let Value::Number(ref num) = map["data"] {
Ok(num.as_u64().unwrap() as usize)
} else {
Err(Error(ErrorCode::ValueDoesNotContainUsize))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
}
fn as_string(&self) -> String {
self.to_string()
}
}
impl TypeHandler for HashMap<String, MpvDataType> {
fn get_value(value: Value) -> Result<HashMap<String, MpvDataType>, Error> {
2017-05-22 18:31:20 +02:00
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"] {
Ok(json_map_to_hashmap(inner_map))
2017-05-22 18:31:20 +02:00
} else {
Err(Error(ErrorCode::ValueDoesNotContainHashMap))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
}
fn as_string(&self) -> String {
format!("{:?}", self)
}
}
impl TypeHandler for Vec<PlaylistEntry> {
fn get_value(value: Value) -> Result<Vec<PlaylistEntry>, Error> {
2017-05-22 18:31:20 +02:00
if let Value::Object(map) = value {
if let Value::String(ref error) = map["error"] {
if error == "success" && map.contains_key("data") {
if let Value::Array(ref playlist_vec) = map["data"] {
Ok(json_array_to_playlist(playlist_vec))
2017-05-22 18:31:20 +02:00
} else {
Err(Error(ErrorCode::ValueDoesNotContainPlaylist))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedValue))
2017-05-22 18:31:20 +02:00
}
}
fn as_string(&self) -> String {
format!("{:?}", self)
}
}
pub fn get_mpv_property<T: TypeHandler>(instance: &Mpv, property: &str) -> Result<T, Error> {
2017-05-22 18:31:20 +02:00
let ipc_string = format!("{{ \"command\": [\"get_property\",\"{}\"] }}\n", property);
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
2017-05-22 18:31:20 +02:00
Ok(val) => T::get_value(val),
Err(why) => Err(Error(ErrorCode::JsonParseError(why.to_string()))),
2017-05-22 18:31:20 +02:00
}
}
pub fn get_mpv_property_string(instance: &Mpv, property: &str) -> Result<String, Error> {
2017-05-22 18:31:20 +02:00
let ipc_string = format!("{{ \"command\": [\"get_property\",\"{}\"] }}\n", property);
2022-07-19 20:54:44 +02:00
let val = serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string))
.map_err(|why| Error(ErrorCode::JsonParseError(why.to_string())))?;
let map = if let Value::Object(map) = val {
Ok(map)
} else {
Err(Error(ErrorCode::UnexpectedValue))
}?;
let error = if let Value::String(ref error) = map["error"] {
Ok(error)
} else {
Err(Error(ErrorCode::UnexpectedValue))
}?;
let data = if error == "success" {
Ok(&map["data"])
2022-07-19 20:54:44 +02:00
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
}?;
match data {
Value::Bool(b) => Ok(b.to_string()),
Value::Number(ref n) => Ok(n.to_string()),
Value::String(ref s) => Ok(s.to_string()),
Value::Array(ref array) => Ok(format!("{:?}", array)),
Value::Object(ref map) => Ok(format!("{:?}", map)),
Value::Null => Err(Error(ErrorCode::MissingValue)),
2017-05-22 18:31:20 +02:00
}
}
pub fn set_mpv_property<T: TypeHandler>(
instance: &Mpv,
property: &str,
value: T,
) -> Result<(), Error> {
let ipc_string = format!(
"{{ \"command\": [\"set_property\", \"{}\", {}] }}\n",
property,
value.as_string()
);
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
2017-05-22 18:31:20 +02:00
Ok(_) => Ok(()),
Err(why) => Err(Error(ErrorCode::JsonParseError(why.to_string()))),
2017-05-22 18:31:20 +02:00
}
}
pub fn run_mpv_command(instance: &Mpv, command: &str, args: &[&str]) -> Result<(), Error> {
2022-07-19 21:07:20 +02:00
let mut ipc_string = format!(r#"{{ "command": ["{}""#, command);
for arg in args {
ipc_string.push_str(&format!(r#", "{}""#, arg));
2017-05-22 18:31:20 +02:00
}
ipc_string.push_str("] }\n");
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
2017-05-22 18:31:20 +02:00
Ok(feedback) => {
if let Value::String(ref error) = feedback["error"] {
if error == "success" {
Ok(())
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
} else {
Err(Error(ErrorCode::UnexpectedResult))
2017-05-22 18:31:20 +02:00
}
}
Err(why) => Err(Error(ErrorCode::JsonParseError(why.to_string()))),
2017-05-22 18:31:20 +02:00
}
}
pub fn observe_mpv_property(instance: &Mpv, id: &isize, property: &str) -> Result<(), Error> {
let ipc_string = format!(
"{{ \"command\": [\"observe_property\", {}, \"{}\"] }}\n",
2019-06-19 00:51:11 +02:00
id, property
);
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
2017-05-24 00:13:23 +02:00
Ok(feedback) => {
if let Value::String(ref error) = feedback["error"] {
if error == "success" {
Ok(())
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
2017-05-22 18:31:20 +02:00
}
2017-05-24 00:13:23 +02:00
} else {
Err(Error(ErrorCode::UnexpectedResult))
2017-05-22 18:31:20 +02:00
}
}
Err(why) => Err(Error(ErrorCode::JsonParseError(why.to_string()))),
2017-05-22 18:31:20 +02:00
}
}
pub fn unobserve_mpv_property(instance: &Mpv, id: &isize) -> Result<(), Error> {
let ipc_string = format!("{{ \"command\": [\"unobserve_property\", {}] }}\n", id);
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
Ok(feedback) => {
if let Value::String(ref error) = feedback["error"] {
if error == "success" {
Ok(())
} else {
Err(Error(ErrorCode::MpvError(error.to_string())))
}
} else {
Err(Error(ErrorCode::UnexpectedResult))
}
}
Err(why) => Err(Error(ErrorCode::JsonParseError(why.to_string()))),
}
}
fn try_convert_property(name: &str, id: usize, 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);
2019-06-19 00:51:11 +02:00
Property::Unknown {
name: name.to_string(),
data,
}
}
};
Event::PropertyChange { id, property }
}
pub fn listen(instance: &mut Mpv) -> Result<Event, Error> {
let mut e;
// sometimes we get responses unrelated to events, so we read a new line until we receive one
// with an event field
let name = loop {
let mut response = String::new();
instance.reader.read_line(&mut response).unwrap();
response = response.trim_end().to_string();
debug!("Event: {}", response);
e = serde_json::from_str::<Value>(&response)
.map_err(|why| Error(ErrorCode::JsonParseError(why.to_string())))?;
match e["event"] {
Value::String(ref name) => break name,
_ => {
// It was not an event - try again
debug!("Bad response: {:?}", response)
}
}
};
2022-07-19 20:34:11 +02:00
let 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,
"property-change" => {
let name = match e["name"] {
Value::String(ref n) => Ok(n.to_string()),
_ => Err(Error(ErrorCode::JsonContainsUnexptectedType)),
}?;
let id: usize = match e["id"] {
Value::Number(ref n) => n.as_u64().unwrap() as usize,
_ => 0,
};
let data: MpvDataType = match e["data"] {
Value::String(ref n) => MpvDataType::String(n.to_string()),
Value::Array(ref a) => {
if name == "playlist".to_string() {
MpvDataType::Playlist(Playlist(json_array_to_playlist(a)))
} else {
MpvDataType::Array(json_array_to_vec(a))
2022-07-19 20:34:11 +02:00
}
}
2022-07-19 20:34:11 +02:00
Value::Bool(b) => MpvDataType::Bool(b),
Value::Number(ref n) => {
if n.is_u64() {
MpvDataType::Usize(n.as_u64().unwrap() as usize)
} else if n.is_f64() {
MpvDataType::Double(n.as_f64().unwrap())
} else {
return Err(Error(ErrorCode::JsonContainsUnexptectedType));
2022-07-19 20:34:11 +02:00
}
}
Value::Object(ref m) => MpvDataType::HashMap(json_map_to_hashmap(m)),
Value::Null => MpvDataType::Null,
};
2022-07-19 20:34:11 +02:00
try_convert_property(name.as_ref(), id, data)
}
"client-message" => {
let args = match e["args"] {
Value::Array(ref a) => json_array_to_vec(a)
.iter()
.map(|arg| match arg {
MpvDataType::String(s) => Ok(s.to_owned()),
_ => Err(Error(ErrorCode::JsonContainsUnexptectedType)),
})
.collect::<Result<Vec<_>, _>>(),
_ => return Err(Error(ErrorCode::JsonContainsUnexptectedType)),
}?;
Event::ClientMessage { args }
}
_ => Event::Unimplemented,
};
Ok(event)
2017-05-22 18:31:20 +02:00
}
pub fn listen_raw(instance: &mut Mpv) -> String {
let mut response = String::new();
instance.reader.read_line(&mut response).unwrap();
response.trim_end().to_string()
}
fn send_command_sync(instance: &Mpv, command: &str) -> String {
let mut stream = &instance.stream;
match stream.write_all(command.as_bytes()) {
Err(why) => panic!("Error: Could not write to socket: {}", why),
Ok(_) => {
debug!("Command: {}", command.trim_end());
let mut response = String::new();
{
let mut reader = BufReader::new(stream);
while !response.contains("\"error\":") {
response.clear();
reader.read_line(&mut response).unwrap();
2017-05-22 18:31:20 +02:00
}
}
debug!("Response: {}", response.trim_end());
response
2017-05-22 18:31:20 +02:00
}
}
}
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
2019-06-18 17:34:20 +02:00
}